From 0a36294e61f08d8de85c084415d694d3736e0c67 Mon Sep 17 00:00:00 2001 From: Brummel Date: Tue, 12 May 2026 22:02:58 +0200 Subject: [PATCH] =?UTF-8?q?plan:=20ctt.1=20=E2=80=94=20env-overlay=20shape?= =?UTF-8?q?=20ratification,=203=20tasks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plans/ctt.1.md | 339 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 docs/plans/ctt.1.md diff --git a/docs/plans/ctt.1.md b/docs/plans/ctt.1.md new file mode 100644 index 0000000..a9a4311 --- /dev/null +++ b/docs/plans/ctt.1.md @@ -0,0 +1,339 @@ +# ctt.1 — Env-overlay shape ratification — Implementation Plan + +> **Parent spec:** `docs/specs/2026-05-12-ct-tidy.md` +> +> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement` +> to run this plan. Steps use `- [ ]` checkboxes for tracking. + +**Goal:** Anchor the `env.types` / `env.ctor_index` split decision +in `docs/DESIGN.md`, pin the `DuplicateCtor` consumer the spec's +asymmetry-with-mono-side argument rests on, and close two +roadmap todos. + +**Architecture:** One new behavioural-pin integration test under +`crates/ailang-check/tests/duplicate_ctor_pin.rs` exercising +`CheckError::DuplicateCtor` via the public `check_workspace` +entry point. One DESIGN.md top-level section insertion +(`## Env construction`). Two strike-out edits in +`docs/roadmap.md`. No production-code edits. + +**Tech Stack:** `ailang-check` (`check_workspace`, `Diagnostic` +shape), `ailang-core` (`Module` / `TypeDef` / `Ctor` / `Workspace` +inline construction via `SCHEMA`). + +**Files this plan creates or modifies:** + +- Create: `crates/ailang-check/tests/duplicate_ctor_pin.rs` — + pinning test for `CheckError::DuplicateCtor` emitted by the + per-module overlay rebuild at + `crates/ailang-check/src/lib.rs:1366-1376`. +- Modify: `docs/DESIGN.md:1957-1959` — insert new top-level + section `## Env construction` between + `## Convention: qualified cross-module references` and + `## Data model`. +- Modify: `docs/roadmap.md:79` — strike `[ ]` → `[x]` on the + "`types` / `ctor_index` overlay shape question" todo. +- Modify: `docs/roadmap.md:183` — strike `[ ]` → `[x]` on the + "`check_in_workspace` per-module overlay narrowing" todo. + +--- + +### Task 1: Pinning test for `CheckError::DuplicateCtor` + +**Files:** +- Create: `crates/ailang-check/tests/duplicate_ctor_pin.rs` + +This task pins existing production behaviour — the +`DuplicateCtor` variant is emitted by the per-module rebuild at +`crates/ailang-check/src/lib.rs:1366-1376` and surfaced as a +`Diagnostic` with `code == "duplicate-ctor"` via the +`CheckError::code()` arm at `lib.rs:610`. No production code +changes; the test catches a future regression that silently +removes the per-module `env.ctor_index` rebuild. + +The TDD discipline an implementer might apply (write the test +first, expect it to fail) does not map cleanly to a +behavioural-pin test: the behaviour already exists. The +expected outcome on first run is PASS. If the test fails on +first run, that is itself a finding — it means either the +inline-fixture is malformed or the production-code path has +drifted from its documented shape. + +- [ ] **Step 1: Write the pinning test file** + +Path: `crates/ailang-check/tests/duplicate_ctor_pin.rs` + +```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` that DESIGN.md §"Env construction" rests +//! on as the asymmetry-with-mono-side argument. 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 + ); +} +``` + +- [ ] **Step 2: Run the test, confirm PASS** + +Run: `cargo test --workspace -p ailang-check --test duplicate_ctor_pin` +Expected: `test duplicate_ctor_across_two_adts_in_one_module ... ok` (1 passed; 0 failed). + +If the test fails: the production-code path has drifted from +its documented shape. Inspect +`crates/ailang-check/src/lib.rs:1366-1376`; the in-band +`DuplicateCtor` emission is the load-bearing consumer the test +pins. Drift here invalidates the spec's asymmetry-with-mono-side +argument and is itself a finding — pause the iter and surface it. + +- [ ] **Step 3: Confirm full workspace test suite stays green** + +Run: `cargo test --workspace` +Expected: all existing tests pass (no regression). The new test +adds one passing case; no existing test exercises the +two-`Def::Type`-same-ctor shape, so no test changes its outcome. + +--- + +### Task 2: DESIGN.md `## Env construction` section + +**Files:** +- Modify: `docs/DESIGN.md:1957-1959` + +Inserts a new top-level section between the existing +`## Convention: qualified cross-module references` (DESIGN.md:1941) +and `## Data model` (DESIGN.md:1959). The initial body is +three paragraphs: one naming the split, one stating the +rationale (owning-vs-reverse-index roles), one documenting the +check-side / mono-side asymmetry as intentional. + +- [ ] **Step 1: Insert the new section** + +Apply this exact Edit to `docs/DESIGN.md`: + +`old_string`: +``` +Hash stability: no new AST node, no renamed fields — all previous module +hashes stay bit-identical. + +## Data model +``` + +`new_string`: +``` +Hash stability: no new AST node, no renamed fields — all previous module +hashes stay bit-identical. + +## Env construction + +The check environment carries two parallel per-module overlays for +ADT definitions: `env.types: IndexMap` is the +**owning index** — it stores the full definition keyed by bare type +name. `env.ctor_index: IndexMap` is the **reverse +index** — it maps each constructor name back to its owning type, so +ctor-by-name lookups run in O(1) rather than searching every type's +ctor list. + +The split is not a refactor wart. The two maps have distinct +semantic roles: an owning data index, and a derived lookup +accelerator over the same data. Collapsing them into one +variant-keyed map (`Map`) would force every +type-iterator and every ctor-lookup site to discriminate the variant +tag, reversing the optimisation the reverse index exists to +provide. + +The check-side per-module rebuild of both overlays at +`crates/ailang-check/src/lib.rs:1342-1384` is intentionally +retained: the in-band `DuplicateCtor` diagnostic at the same site +consumes the rebuilt per-module `env.ctor_index`. The mono-side +overlay was narrowed to types-only at iter ct.3.2 because its +consumer is the runtime ctor lookup, which became type-driven +post-ct.2.2 — a different consumer story, hence a different +overlay shape. The asymmetry between the check side and the mono +side is by design and is pinned by +`crates/ailang-check/tests/duplicate_ctor_pin.rs`. + +## Data model +``` + +- [ ] **Step 2: Verify section presence and ordering** + +Run: `grep -n '^## ' docs/DESIGN.md | grep -E 'Convention: qualified|Env construction|Data model'` +Expected: three lines, in this order (line numbers approximate; +the relative ordering is what matters): +``` +1941:## Convention: qualified cross-module references +1959:## Env construction +1989:## Data model +``` +The "Env construction" heading must appear between the other two. + +--- + +### Task 3: roadmap.md strikes + +**Files:** +- Modify: `docs/roadmap.md:79` +- Modify: `docs/roadmap.md:183` + +Closes two P2 `[todo]` lines now superseded by the DESIGN.md +anchor in Task 2 and the pinning test in Task 1. The strike +follows the roadmap's `[ ]` → `[x]` convention; per +`docs/roadmap.md`'s own header guidance, "a finished entry may +stay briefly for context, then is removed (with a one-line +mirror in `docs/journals/`)". The mirror lands in the per-iter +journal at iter close. + +- [ ] **Step 1: Strike the "overlay shape question" todo** + +Apply this exact Edit to `docs/roadmap.md`: + +`old_string`: +``` +- [ ] **\[todo\]** `types` / `ctor_index` overlay shape question — + decide whether the env's two parallel ctor maps should collapse + into one overlay, or stay split. Surfaced during the + env-construction unify audit. + - context: JOURNAL 2026-05-10 ("Audit close: env-construction unify") +``` + +`new_string`: +``` +- [x] **\[todo\]** `types` / `ctor_index` overlay shape question — + decide whether the env's two parallel ctor maps should collapse + into one overlay, or stay split. Surfaced during the + env-construction unify audit. + - context: JOURNAL 2026-05-10 ("Audit close: env-construction unify"); closed by iter ctt.1 — DESIGN.md §"Env construction" anchors the split decision. +``` + +- [ ] **Step 2: Strike the "check_in_workspace per-module overlay narrowing" todo** + +Apply this exact Edit to `docs/roadmap.md`: + +`old_string`: +``` +- [ ] **\[todo\]** `check_in_workspace` per-module overlay narrowing — + `crates/ailang-check/src/lib.rs:1234` still clears+rebuilds + `env.ctor_index` per-module; ct.3.2 narrowed the analogous mono + overlay to types-only. The typecheck-side overlay's `env.ctor_index` + half serves the duplicate-detection diagnostic at workspace-build + time, not the runtime ctor lookup (which is type-driven post-ct.2.2). + Narrowing is mechanical but needs a careful read to confirm no + other consumer survives. + - context: JOURNAL 2026-05-11 ("Iteration ct.4") — milestone close + follow-up. +``` + +`new_string`: +``` +- [x] **\[todo\]** `check_in_workspace` per-module overlay narrowing — + `crates/ailang-check/src/lib.rs:1234` still clears+rebuilds + `env.ctor_index` per-module; ct.3.2 narrowed the analogous mono + overlay to types-only. The typecheck-side overlay's `env.ctor_index` + half serves the duplicate-detection diagnostic at workspace-build + time, not the runtime ctor lookup (which is type-driven post-ct.2.2). + Narrowing is mechanical but needs a careful read to confirm no + other consumer survives. + - context: JOURNAL 2026-05-11 ("Iteration ct.4") — milestone close + follow-up; closed by iter ctt.1 — recon confirmed the rebuild is the load-bearing consumer of in-band DuplicateCtor (pinned by `crates/ailang-check/tests/duplicate_ctor_pin.rs`); the asymmetry with the mono side is intentional. +``` + +- [ ] **Step 3: Verify both strikes** + +Run: `grep -nE '^- \[x\] \*\*\\\[todo\\\]\*\* (`types`|`check_in_workspace`)' docs/roadmap.md` +Expected: two lines, both with `[x]`: +``` +79:- [x] **\[todo\]** `types` / `ctor_index` overlay shape question — +183:- [x] **\[todo\]** `check_in_workspace` per-module overlay narrowing — +``` + +- [ ] **Step 4: Workspace-wide regression check** + +Run: `cargo test --workspace` +Expected: all tests pass, including the new pinning test from +Task 1. + +The bench scripts (`bench/check.py`, `bench/compile_check.py`, +`bench/cross_lang.py`) are not run per-iter at the plan level — +they are run by the audit skill at milestone close. ctt.1 makes +zero codegen-relevant edits, so no bench movement is plausibly +attributable to it; the milestone-close audit confirms. + +--- + +## Acceptance criteria recap (from spec) + +- `docs/DESIGN.md` carries the new `## Env construction` section + with the three-paragraph initial body. ✓ Task 2. +- A new pinning test in `crates/ailang-check/tests/` asserts + `code == "duplicate-ctor"` for a one-module fixture with two + `Def::Type`s declaring same-named ctors, plus message-substring + match for the ctor name and both ADT names. ✓ Task 1. +- Both relevant roadmap todos struck `[x]` with a one-line + forward reference to this iter. ✓ Task 3. +- `cargo test --workspace` green. ✓ Task 1 Step 3 and Task 3 + Step 4.