Files
AILang/crates/ailang-check/tests/duplicate_ctor_pin.rs
T
Brummel 54f0ced148 workflow: delete docs/journals/ and docs/journal-archive.md
Strict application of the "Future-Use, not Verlauf" criterion to the
two remaining journal artefacts:

- `docs/journals/` (110 files): the per-iter and audit journals from
  2026-05-11 onward. Pure history; no live reader after the previous
  sweep. Entire directory removed.
- `docs/journals/2026-05-19-design-decision-records.md`: the one file
  that had live readers (`docs_honesty_pin.rs:108`, `parse.rs:80`,
  `duplicate_ctor_pin.rs:8`, three roadmap.md mentions) and was framed
  as a "relitigation guard". On re-examination its three asserted
  pinned phrases ("Regions were considered and rejected", the
  "demands annotations *because*" rationale, the prose-render
  placeholder statement) are rationale-prose, not load-bearing
  invariants — the test pinned itself, not a system property. Any
  Decision that still holds lives in the code, `design/contracts/`,
  or `design/models/`. Removed with the rest.
- `docs/journal-archive.md` (pre-2026-05-11 history): content-frozen
  long-tail history with no live reader. Removed; if anyone ever
  needs the pre-cutoff rationale they can `git log --before=2026-05-11
  --grep=<keyword>`.

Live-file consequences:
- `docs_honesty_pin.rs` `design_md_present_tense_anchors_present`:
  the three `records.*` assertions and the `read("docs/journals/…")`
  are dropped; the contract/model anchor pins remain.
- `duplicate_ctor_pin.rs`: the "why-two-overlays rationale lives in
  docs/journals/…" comment is replaced with the rationale inline.
- `parse.rs`: the "form-refinement rationale lives in docs/journals/…"
  comment is dropped (the rule body already states the refinement).
- `roadmap.md`: the decision-records mention in the DESIGN.md →
  design/ entry's body is dropped; the journal-archive.md `context:`
  pointers across ~12 closed entries are either rephrased to point
  at the relevant iter/audit (no path), or simplified to a one-line
  comment when the iter name alone carries the story.
- `CLAUDE.md` Roles section: the `docs/journal-archive.md` slot is
  removed; vocabulary note rephrased.
- `design/INDEX.md`: the Docs bullet drops `journal-archive.md`.
- `skills/README.md` bootstrap-rationale pointer rephrased.

`docs/specs/` and `docs/plans/` (per-milestone specs and per-iteration
plans) are unaffected — they remain the structured-design artefacts
they were before this sweep.

Workspace builds, full test suite green.
2026-05-20 11:25:15 +02:00

78 lines
2.6 KiB
Rust

//! Pin for `CheckError::DuplicateCtor` — same-named ctors in two
//! different ADTs within one module. Ratifies the load-bearing
//! consumer of the per-module `env.ctor_index` rebuild in
//! `check_in_workspace`. Env construction is code-SoT (the
//! `env-construction` ledger row in design/INDEX.md is source-link
//! only). The two overlays — typecheck-side rebuild + mono-side
//! narrowed — are deliberately asymmetric: the typecheck-side
//! `ctor_index` half exists *for this very diagnostic*, while the
//! mono-side overlay is narrowed to types-only because the runtime
//! ctor lookup is type-driven. If the per-module rebuild is ever
//! removed without an equivalent replacement path, this test goes
//! red.
use ailang_check::check_workspace;
use ailang_core::ast::{Ctor, Def, Module, TypeDef};
use ailang_core::workspace::{Registry, Workspace};
use ailang_core::SCHEMA;
use std::collections::BTreeMap;
#[test]
fn duplicate_ctor_across_two_adts_in_one_module() {
let m = Module {
schema: SCHEMA.into(),
name: "M".into(),
imports: vec![],
defs: vec![
Def::Type(TypeDef {
name: "Cat".into(),
vars: vec![],
ctors: vec![Ctor {
name: "Twins".into(),
fields: vec![],
}],
doc: None,
drop_iterative: false,
}),
Def::Type(TypeDef {
name: "Dog".into(),
vars: vec![],
ctors: vec![Ctor {
name: "Twins".into(),
fields: vec![],
}],
doc: None,
drop_iterative: false,
}),
],
};
let mut modules = BTreeMap::new();
modules.insert(m.name.clone(), m.clone());
let ws = Workspace {
entry: m.name.clone(),
modules,
root_dir: std::path::PathBuf::from("."),
registry: Registry::default(),
};
let diags = check_workspace(&ws);
let dup = diags
.iter()
.find(|d| d.code == "duplicate-ctor")
.unwrap_or_else(|| {
panic!(
"expected at least one `duplicate-ctor` diagnostic, got: {diags:?}"
)
});
assert!(
dup.message.contains("Twins"),
"expected message to name the duplicate ctor `Twins`, got: {}",
dup.message
);
assert!(
dup.message.contains("Cat") && dup.message.contains("Dog"),
"expected message to name both ADTs `Cat` and `Dog`, got: {}",
dup.message
);
}