From c0dd2bc334d490e5e90ad10501c33e48076ff2bd Mon Sep 17 00:00:00 2001 From: Brummel Date: Thu, 14 May 2026 13:04:18 +0200 Subject: [PATCH] plan: pd.3 retire prelude.ail.json + carve-out cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 8 tasks. Closes the prelude-decouple milestone by deleting examples/prelude.ail.json and sweeping the surviving consumers + spec text + inventory. Three production consumers retire: the migrate-bare- cross-module-refs defensive include in ail/src/main.rs (with its lockstep skip-branch), the cross-form-identity preflight test in ailang-surface (its purpose discharged at pd.2-close), and one in-mod test in core's workspace.rs (re-pointed from inline include_str! to ailang_surface::parse_prelude via the dev-dep). Adds a carve-out retirement pin asserting the JSON file does NOT exist. Updates carve_out_inventory.rs (eight → seven; §C4(b) row dropped) and the form-a-default-authoring spec §C4(b) (RETIRED status marker added, historical text preserved). --- docs/plans/2026-05-14-iter-pd.3.md | 618 +++++++++++++++++++++++++++++ 1 file changed, 618 insertions(+) create mode 100644 docs/plans/2026-05-14-iter-pd.3.md diff --git a/docs/plans/2026-05-14-iter-pd.3.md b/docs/plans/2026-05-14-iter-pd.3.md new file mode 100644 index 0000000..72523d9 --- /dev/null +++ b/docs/plans/2026-05-14-iter-pd.3.md @@ -0,0 +1,618 @@ +# pd.3 — retire prelude.ail.json + carve-out cleanup — Implementation Plan + +> **Parent spec:** `docs/specs/2026-05-14-prelude-decouple.md` +> +> **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement` +> to run this plan. Steps use `- [ ]` checkboxes for tracking. + +**Goal:** Close the prelude-decouple milestone by deleting +`examples/prelude.ail.json` from the working tree and sweeping the +remaining `include_str!` consumers + spec text + carve-out inventory +that depended on it. After this iter the only on-disk source of the +prelude is `examples/prelude.ail`, the form-a-default-authoring spec's +§C4 (b) carve-out category is empty, and a new pin test guarantees the +JSON file stays gone. + +**Architecture:** Pure deletion + adapt iter. Two `include_str!` of +`prelude.ail.json` survive pd.2: (a) the migrate-bare-cross-module-refs +defensive include in `crates/ail/src/main.rs:472-484` (a documented +no-op — the inserted entry is unconditionally skipped by the loop's +`if mod_name == "prelude" && path.as_os_str().is_empty() { continue; }` +guard at lines 500-505); (b) the cross-form-identity preflight test +in `crates/ailang-surface/tests/prelude_module_hash_pin.rs` (which the +spec explicitly scoped as pd.2-only — its purpose was to ratify pd.3's +load-bearing assumption, which it did at hash `3abe0d3fa3c11c99`). +Both must go before the JSON file. Plus one in-mod test in +`crates/ailang-core/src/workspace.rs` (~line 2654 area, pd.2-introduced) +re-points from inline `serde_json::from_str(include_str!("prelude.ail.json"))` +to `ailang_surface::parse_prelude()` via the existing core→surface +dev-dep edge. + +**Tech Stack:** `ail` CLI (one block delete + lockstep skip-branch +delete), `ailang-surface/tests/` (one test fn + supporting bytes +delete + new carve-out pin), `ailang-core` (one in-mod test re-point), +`ailang-core/tests/carve_out_inventory.rs` (list + comment update), +`docs/specs/2026-05-13-form-a-default-authoring.md` (§C4 (b) status +note), `examples/prelude.ail.json` (deleted). + +--- + +**Files this plan creates or modifies:** + +- Create: `crates/ailang-surface/tests/prelude_decouple_carve_out_pin.rs` — + asserts `examples/prelude.ail.json` does NOT exist on disk. +- Modify: `crates/ailang-surface/tests/prelude_module_hash_pin.rs` — delete + the `prelude_ail_and_json_parse_to_identical_module` fn and its + supporting `PRELUDE_JSON` const + `PRELUDE_AIL` orphan-import + + the `const _: &str = PRELUDE_AIL;` shape-pin. Trim the module + doc-comment to describe only the surviving long-term hash anchor. +- Modify: `crates/ailang-core/src/workspace.rs` — re-point the in-mod + test that uses `include_str!("../../../examples/prelude.ail.json")` + (around line 2654 area; pd.2 introduced this as a replacement for the + deleted `load_prelude()` call) to `ailang_surface::parse_prelude()` + via the core→surface dev-dep edge. +- Modify: `crates/ail/src/main.rs:472-505` — delete the defensive + `include_str!` block (472-484) AND its lockstep skip-branch (500-505, + the comment + the `if mod_name == "prelude" && path.as_os_str().is_empty() + { continue; }` guard). The guard is dead-code without the synthetic + insert. +- Modify: `crates/ailang-core/tests/carve_out_inventory.rs` — drop the + `"prelude.ail.json"` entry from the EXPECTED list (line 25); rewrite + the doc-comment block (lines 6-11) to "Seven carve-outs" and "§C4 (a) + subject-matter: 7 fixtures (canonical-form rejection / unbound / + class-def rejection); §C4 (b) compile-time-embed: retired by + prelude-decouple milestone, 2026-05-14"; update the header sentence + at line 2 from "eight" to "seven". +- Modify: `docs/specs/2026-05-13-form-a-default-authoring.md:270-311` + — add a "Status: RETIRED 2026-05-14 by prelude-decouple milestone" + marker at the top of §C4 (b) and a one-paragraph epitaph at the + bottom; preserve the original §C4 (b) text below as historical + context. Upstream callouts (lines 232-236, 462-465, 477-484) stay as + historical references — their "as of 2026-05-13" temporal qualifiers + preserve correctness. +- Delete: `examples/prelude.ail.json` (the milestone's primary + artefact retirement). + +--- + +### Task 1: Re-point the in-mod test in `workspace.rs` from `include_str!("prelude.ail.json")` to `ailang_surface::parse_prelude()` + +**Files:** +- Modify: `crates/ailang-core/src/workspace.rs` — the in-mod test that + inlined `serde_json::from_str(include_str!("prelude.ail.json"))` as a + pd.2 replacement for the deleted `load_prelude()` call. Per recon, the + reference is at lines 2654 / 2656 (inside `#[cfg(test)] mod tests`). + +The dev-dep edge `ailang-core → ailang-surface` (declared in +`crates/ailang-core/Cargo.toml`'s `[dev-dependencies]`) is reachable +from in-mod tests when surface is consumed via fully-qualified path +in the test body, but the lib-test-target compilation cycle blocks +top-level `use ailang_surface::*;` lines. Use the fully-qualified +`ailang_surface::parse_prelude()` at the call site to avoid the import +issue (per pd.2's experience). + +- [ ] **Step 1: Read the existing in-mod test to confirm shape** + +Open `crates/ailang-core/src/workspace.rs`, navigate to lines 2640-2670. +Identify the test fn that contains the `include_str!("../../../examples/prelude.ail.json")` +and `serde_json::from_str` pair. Per the pd.2 journal this is +`build_workspace_accepts_assembled_modules_and_runs_validation` (and +possibly one sibling using the same pattern; check for both occurrences +at lines 2654 and 2656). + +If the line numbers shifted post-pd.2 (very possible given pd.2's +deletions), use: + +```bash +git grep -n 'include_str!.*prelude\.ail\.json' crates/ailang-core/src/workspace.rs +``` + +- [ ] **Step 2: Verify `ailang_surface` is reachable from in-mod tests** + +Read `crates/ailang-core/Cargo.toml` `[dev-dependencies]`. Confirm +`ailang-surface = { path = "../ailang-surface" }` is present (per +brainstorm-recon: yes). + +This is sanity-checking; if absent, **STOP** — pd.2 was wrong about the +edge existing and the migration target needs revisiting. + +- [ ] **Step 3: Replace the inlined `serde_json::from_str` with + `ailang_surface::parse_prelude()`** + +In each affected test fn body, replace the pattern: + +Old (per pd.2 journal): +```rust +let prelude: Module = serde_json::from_str(include_str!( + "../../../examples/prelude.ail.json" +)).expect("embedded prelude must parse"); +``` + +New: +```rust +let prelude = ailang_surface::parse_prelude(); +``` + +The `Module` type annotation can drop because `parse_prelude` returns +`Module` directly. The import line for `Module` (if newly orphan) +either stays or is removed depending on other test bodies in the same +mod. + +- [ ] **Step 4: Run the affected tests to verify they still pass** + +Run: `cargo test -p ailang-core build_workspace_accepts_assembled_modules_and_runs_validation` + +(And the second test name if recon surfaces one — Step 1 will have +named it.) + +Expected: PASS. The semantic content is unchanged (same prelude +Module, just constructed via the canonical post-pd.2 path). + +- [ ] **Step 5: Run the full core test suite** + +Run: `cargo test -p ailang-core` +Expected: all green. + +--- + +### Task 2: Delete the cross-form-identity preflight + supporting bytes + +**Files:** +- Modify: `crates/ailang-surface/tests/prelude_module_hash_pin.rs` — delete + the `prelude_ail_and_json_parse_to_identical_module` test fn (lines + 22-43 per recon), the `PRELUDE_JSON` const (line 20), the + `PRELUDE_AIL` orphan import (line 18 — keep `parse_prelude` only), + the `const _: &str = PRELUDE_AIL;` shape-pin (line 60), and trim + the module doc-comment (lines 1-15) to describe only the surviving + long-term hash anchor. + +- [ ] **Step 1: Delete the cross-form-identity test fn + its supporting bytes** + +Edit `crates/ailang-surface/tests/prelude_module_hash_pin.rs`. Delete: + +- Lines 1-15: the module doc-comment block (will be replaced in Step 2). +- Line 18: the import `use ailang_surface::{parse_prelude, PRELUDE_AIL};` + — replace with `use ailang_surface::parse_prelude;` (PRELUDE_AIL no + longer consumed). +- Line 20: `const PRELUDE_JSON: &str = include_str!(...);` +- Lines 22-43: the entire `#[test] fn prelude_ail_and_json_parse_to_identical_module` + body. +- Lines 59-60: the shape-pin comment + `const _: &str = PRELUDE_AIL;`. + +The surviving content is the `prelude_parse_yields_canonical_hash` test +fn (post-edit lines will be ~3-15). + +- [ ] **Step 2: Replace the module doc-comment with the surviving-only version** + +At the top of the file, insert: + +```rust +//! pd.2/pd.3 (`prelude-decouple` milestone): pin the canonical +//! identity of the parsed prelude as a literal hex hash. Updates +//! intentionally on prelude-content changes (record the why in the +//! per-iter journal); accidental drift trips immediately. +//! +//! pd.2 also carried a `prelude_ail_and_json_parse_to_identical_module` +//! cross-form-identity preflight that compared `parse(prelude.ail)` +//! against `serde_json::from_str(prelude.ail.json)`. The preflight +//! discharged its purpose at pd.2 close (PASSED at hash +//! `3abe0d3fa3c11c99`) and was retired in pd.3 alongside the +//! `prelude.ail.json` file. +``` + +- [ ] **Step 3: Run the surviving test** + +Run: `cargo test -p ailang-surface --test prelude_module_hash_pin` +Expected: `prelude_parse_yields_canonical_hash` PASSES (still pinning +hash `3abe0d3fa3c11c99`); the deleted `prelude_ail_and_json_parse_to_identical_module` +no longer appears in the suite. + +If the hash assertion fails, the prelude content has changed since +pd.2 close — investigate. With no source-edit between pd.2 commit and +pd.3 task 2, the hash MUST be unchanged. + +- [ ] **Step 4: Verify orphan imports are clean** + +Run: `cargo build -p ailang-surface --tests 2>&1 | grep -E '(warning|error)' | head -10` +Expected: no `unused import` warnings on the edited file. If `Module` +or any other type was the only consumer of an import line that now has +no consumer, drop the line. + +--- + +### Task 3: Delete the defensive include in `crates/ail/src/main.rs` + its lockstep skip-branch + +**Files:** +- Modify: `crates/ail/src/main.rs` — two adjacent code blocks inside + the `Cmd::MigrateBareCrossModuleRefs` arm: the defensive include + + insert at lines 472-484, and the skip-branch at lines 500-505. + +- [ ] **Step 1: Delete the defensive include block** + +Edit `crates/ail/src/main.rs`. Delete lines 472-484 (verify with +`git grep -n 'Include the embedded prelude' crates/ail/src/main.rs`): + +```rust + // Include the embedded prelude (so the implicit fallback + // matches the loader's behaviour). + let prelude_json: &str = include_str!( + "../../../examples/prelude.ail.json" + ); + let prelude: Module = serde_json::from_str(prelude_json) + .expect("embedded prelude must parse"); + if !modules.contains_key("prelude") { + modules.insert( + "prelude".to_string(), + (PathBuf::new(), prelude), + ); + } +``` + +Block-locals `prelude_json` and `prelude` are never read after their +binding scope ends; deletion is safe. + +- [ ] **Step 2: Delete the lockstep skip-branch** + +Same file, locate the loop body. Delete the comment + guard at lines +500-505: + +```rust + // Pass 2: rewrite each module's bare cross-module refs. + // Skip the synthetic prelude entry (no path to write to). + let mut rewritten = 0usize; + for (mod_name, (path, m)) in modules.iter_mut() { + if mod_name == "prelude" && path.as_os_str().is_empty() { + continue; + } +``` + +becomes: + +```rust + // Pass 2: rewrite each module's bare cross-module refs. + let mut rewritten = 0usize; + for (mod_name, (path, m)) in modules.iter_mut() { +``` + +The `mod_name` binding is no longer read inside the loop after the +guard's removal (verify by reading the next ~15 lines). If the +unused binding generates a warning, change to `for (_, (path, m)) in +modules.iter_mut() {`. + +- [ ] **Step 3: Verify the subcommand still compiles** + +Run: `cargo check -p ail 2>&1 | head -20` +Expected: clean. No new warnings on `main.rs`. + +- [ ] **Step 4: Spot-test the migrate subcommand (optional sanity check)** + +Run: `cargo run -p ail -- migrate-bare-cross-module-refs --help 2>&1 | head -10` +Expected: prints help text successfully (the binary builds and the +subcommand enumerates). + +The subcommand's substantive behaviour (rewriting bare cross-module +refs across a directory of `.ail.json` files) hasn't been exercised +since the canonical-type-names milestone; no in-tree fixtures are +known to need rewriting today, so a deeper test is out of scope. + +--- + +### Task 4: Update `carve_out_inventory.rs` — drop §C4 (b) + +**Files:** +- Modify: `crates/ailang-core/tests/carve_out_inventory.rs` — three + edits: header sentence count (line 2), comment block (lines 6-11), + EXPECTED list (line 24-25 area). + +- [ ] **Step 1: Update the header sentence** + +Edit `crates/ailang-core/tests/carve_out_inventory.rs`. Change line 2 +from: + +``` +//! spec. Asserts that exactly the eight named carve-out files exist +``` + +to: + +``` +//! spec. Asserts that exactly the seven named carve-out files exist +``` + +- [ ] **Step 2: Rewrite the carve-out comment block (lines 6-11)** + +Old: +``` +//! Eight carve-outs at iter form-a.1 close: +//! - §C4 (a) subject-matter: 7 fixtures (canonical-form rejection / unbound / class-def rejection). +//! - §C4 (b) compile-time-embed: 1 fixture (prelude.ail.json). +//! +//! When the prelude-embed-refactor milestone retires §C4 (b), this +//! test's pass-count drops to seven and `EXPECTED` is re-published. +``` + +New: +``` +//! Seven carve-outs post prelude-decouple (2026-05-14): +//! - §C4 (a) subject-matter: 7 fixtures (canonical-form rejection / unbound / class-def rejection). +//! - §C4 (b) compile-time-embed: retired 2026-05-14 by milestone +//! prelude-decouple; the prelude is now embedded as `prelude.ail` +//! in `ailang-surface` and parsed at compile time via +//! `ailang_surface::parse_prelude`. +``` + +- [ ] **Step 3: Drop the `"prelude.ail.json"` entry from EXPECTED** + +Edit lines around 24-25. Delete: + +```rust + // §C4 (b) — compile-time-embed + "prelude.ail.json", +``` + +If the comment "// §C4 (b) — compile-time-embed" is on its own line +above the entry, delete the comment too (it has no other content +beneath it after this edit). + +- [ ] **Step 4: Run the test — should now PASS against the post-pd.3 corpus** + +Run: `cargo test -p ailang-core --test carve_out_inventory` +Expected: PASS — but note this test STILL READS `examples/prelude.ail.json` +because the file hasn't been deleted yet (Task 7). The EXPECTED list +no longer contains it, so the assertion `matches_carve_outs` will FAIL +saying "unexpected file: prelude.ail.json". That's expected at this +task — it goes green only after Task 7 deletes the file. + +If you want intermediate green between this task and Task 7, swap the +task order (Task 7 before Task 4). The plan's order keeps the test +edits adjacent and defers the file-system mutation to last; either +order is correct. + +--- + +### Task 5: Update `docs/specs/2026-05-13-form-a-default-authoring.md` §C4 (b) + +**Files:** +- Modify: `docs/specs/2026-05-13-form-a-default-authoring.md:270-311` — + add a "Status: RETIRED" marker at top of §C4 (b) and a one-paragraph + epitaph at the bottom; preserve the original text as historical + context. + +The spec is a historical document about the closed form-a-default-authoring +milestone; the convention is to retire sections in place rather than +delete them, so future readers tracing back the prelude-embed-refactor +milestone's prerequisites can find the original framing. + +Per the recon, upstream callouts at lines 232-236, 462-465, 477-484 +stay as historical references — their "as of 2026-05-13" temporal +qualifiers preserve correctness. + +- [ ] **Step 1: Insert a status marker at the top of §C4 (b)** + +Edit `docs/specs/2026-05-13-form-a-default-authoring.md`. Find the +`**(b) Compile-time-embed carve-outs (one file as of 2026-05-13):**` +heading (line 270 area). Insert immediately above: + +```markdown +> **Status: RETIRED 2026-05-14 by milestone prelude-decouple.** The +> §C4 (b) compile-time-embed carve-out is empty post that milestone; +> the prelude is now authored exclusively as `examples/prelude.ail` +> and parsed at compile time via `ailang_surface::parse_prelude`. The +> original §C4 (b) framing below is preserved as historical context; +> it documents the doctrine inconsistency that prelude-decouple +> resolved. See `docs/specs/2026-05-14-prelude-decouple.md` and the +> per-iter journals 2026-05-14-iter-pd.{1,2,3}.md for the resolution. +``` + +- [ ] **Step 2: Verify the spec still reads coherently** + +Read the §C4 (b) section top-to-bottom (lines ~268-315 post-insert). +Confirm the new status marker reads as a forward-pointer to the +resolution and the original text reads as the historical motivation. +The intent: a future reader seeing "the prelude is the only file +authored as JSON" understands that statement as past-tense and finds +the resolution one click away. + +No other edits in this task. Upstream callouts (lines 232-236, 462-465, +477-484) stay verbatim because their qualifiers ("as of 2026-05-13", +"one file as of 2026-05-13") remain truthful as historical statements. + +--- + +### Task 6: Create the carve-out retirement pin + +**Files:** +- Create: `crates/ailang-surface/tests/prelude_decouple_carve_out_pin.rs` + — single `#[test]` fn asserting `examples/prelude.ail.json` does NOT + exist on disk. + +- [ ] **Step 1: Write the test** + +Create `crates/ailang-surface/tests/prelude_decouple_carve_out_pin.rs`: + +```rust +//! pd.3 (`prelude-decouple` milestone): pin that +//! `examples/prelude.ail.json` does NOT exist on disk. Surfaces +//! immediately if the file is recreated by accident or a future +//! refactor reverts the embed-source swap. +//! +//! Companion to the `examples_ail_json_inventory_matches_carve_outs` +//! test in `crates/ailang-core/tests/carve_out_inventory.rs`: that +//! one asserts the carve-out list is exactly seven; this one +//! asserts the eighth (retired) file specifically does not exist. +//! Either test detects an accidental re-introduction; this one +//! pinpoints the file by name in the error message. + +use std::path::Path; + +#[test] +fn prelude_ail_json_does_not_exist_on_disk() { + let path = Path::new("../../examples/prelude.ail.json"); + assert!( + !path.exists(), + "examples/prelude.ail.json must NOT exist after the prelude-\ + decouple milestone (closed 2026-05-14). The prelude is now \ + embedded as prelude.ail in ailang-surface. If you intentionally \ + re-introduced this file, you must also: (a) remove this pin, \ + (b) restore the §C4 (b) entry in carve_out_inventory.rs, \ + (c) revert the §C4 (b) status marker in \ + docs/specs/2026-05-13-form-a-default-authoring.md, and \ + (d) record the rationale in a per-iter journal." + ); +} +``` + +- [ ] **Step 2: Run the test — must FAIL today** + +Run: `cargo test -p ailang-surface --test prelude_decouple_carve_out_pin` +Expected: FAIL because `examples/prelude.ail.json` still exists. The +test is RED-on-purpose at this point; Task 7 turns it GREEN by +deleting the file. + +If the test PASSES at this step, the file is already gone from the +working tree — investigate (was it deleted in an earlier task by +mistake? Recon should not have permitted that). + +--- + +### Task 7: Delete `examples/prelude.ail.json` + +**Files:** +- Delete: `examples/prelude.ail.json` — the milestone's primary artefact + retirement. + +- [ ] **Step 1: Verify zero remaining production / test consumers** + +Run: + +```bash +git grep -n 'include_str!.*prelude\.ail\.json' -- '*.rs' +``` + +Expected: NO matches. (Tasks 1-3 cleared all three.) + +```bash +git grep -n 'prelude\.ail\.json' -- '*.rs' | grep -vE '(//|//!|/\*)' +``` + +Expected: NO matches outside comments / doc-comments / string-literals +in test fixtures. The non-`include_str!` references should all be +either documentation, comments, or path-string constructions in tests +(which fail gracefully on missing files). + +If any reference appears as a file-system access (read/open/etc), STOP +and migrate it before proceeding. + +- [ ] **Step 2: Delete the file** + +Run: + +```bash +rm examples/prelude.ail.json +``` + +(Plain shell delete; not `git rm`. The file will appear as a deletion +in `git status`. The Boss's commit step stages the deletion via +`git add` of the path or `git add -u`.) + +- [ ] **Step 3: Run the carve-out retirement pin — must now PASS** + +Run: `cargo test -p ailang-surface --test prelude_decouple_carve_out_pin` +Expected: PASS — `prelude.ail.json` no longer exists. + +- [ ] **Step 4: Run the carve-out inventory test — must now PASS** + +Run: `cargo test -p ailang-core --test carve_out_inventory` +Expected: PASS — `EXPECTED` is exactly seven names, and exactly seven +files exist under `examples/*.ail.json`. + +- [ ] **Step 5: Run the full workspace test suite** + +Run: `cargo test --workspace` +Expected: all tests green. + +--- + +### Task 8: Bench regressions + final acceptance + +**Files:** none directly. + +- [ ] **Step 1: Run regression baselines** + +Run: `python bench/check.py && python bench/compile_check.py && python bench/cross_lang.py` +Expected: all three exit 0. pd.3 changes are file-system + spec text ++ test deletion + dead-code cleanup; no codegen, no runtime, no +typecheck-result deltas. + +If `check.py` fires the same `bench_list_sum.bump_s` envelope-noise +as in pd.1 / pd.2 / earlier audits, treat as established noise (per +the prior 13-audit pattern) — pd.3 cannot be the source. + +- [ ] **Step 2: Confirm milestone-level acceptance** + +Per the parent spec (§Acceptance criteria): + +1. `examples/prelude.ail.json` is removed from the working tree — + verified by Task 7 Step 3. +2. `cargo build --workspace` succeeds with no `include_str!` of + `prelude.ail.json` — verified by Task 7 Step 1. +3. `git grep -l '"prelude"' crates/ailang-core/src/` returns zero + matches — already true post-pd.2; spot-check now to confirm pd.3 + didn't regress. +4. `cargo test --workspace` is green — verified Task 7 Step 5 + Task 8 Step 1. +5. Baselines green at unchanged values — verified Task 8 Step 1. +6. `docs/specs/2026-05-13-form-a-default-authoring.md` §C4 (b) is + updated — verified by Task 5. +7. CLAUDE.md and `docs/DESIGN.md` reviewed — spot-check now. + +For item 7: + +```bash +git grep -n 'prelude\.ail\.json' CLAUDE.md docs/DESIGN.md +``` + +Expected: NO production-doctrine assertions; only historical references +acceptable. If a CLAUDE.md sentence still asserts that core embeds the +prelude, edit it to reflect the post-milestone state. + +```bash +git grep -n 'authors write `\.ail`' CLAUDE.md +``` + +Expected: this sentence is consistent with the post-milestone state +(authors write `.ail` for ALL files now, including prelude — no +exception). If the wording still names a prelude exception, edit it to +drop the exception. + +- [ ] **Step 3: Confirm zero literal-`"prelude"` strings in core/src/** + +Run: `git grep '"prelude"' crates/ailang-core/src/` +Expected: NO matches (already true post-pd.2; pd.3 is purely +file-system + downstream test/spec adjustments, no core/src/ source +edits). + +If any match appears in production code, STOP — pd.3 should not have +modified core's src/ source. + +--- + +## Out of scope for pd.3 + +The following are NOT part of this milestone and MUST NOT be touched: + +- The `WorkspaceLoadError::ReservedModuleName` variant declaration in + `crates/ailang-core/src/workspace.rs` — stays per spec (generic + shape, no literal `"prelude"`). +- The `crates/ail/src/main.rs:1220-1229` `W::ReservedModuleName` + match-arm in `workspace_error_to_diagnostic` — variant tag preserved, + no edit. +- The `ailang-check` and `ailang-codegen` implicit-prelude resolution + paths (`crates/ailang-check/src/lib.rs:143` area, codegen iter 23.2.4 + behaviour) — these consume the prelude module from a populated + workspace, do not embed prelude bytes, and were explicitly listed as + out-of-scope in the parent spec. +- The 3 cargo doc warnings on dangling `[`load_workspace`]` references + (in `Workspace` and `Registry` rustdoc) noted as known debt in the + pd.2 journal — pre-existing baseline; unrelated to pd.3 scope. +- Audit (Boss-dispatched after pd.3 closes — separate skill invocation + per `skills/audit/SKILL.md`).