# audit-pd — milestone close (prelude-decouple) **Date:** 2026-05-14 **Scope:** commits e6298f5..9a8d385 (spec → pd.1 plan → pd.1 → housekeeping → pd.2 plan → pd.2 → pd.3 plan → pd.3) **Status:** DONE — drift fixed inline as `audit-pd-tidy`; bench ratified pristine ## Architect drift review `ailang-architect` returned **drift_found** with one Important + one Minor + one Nit: - `[Important]` `docs/DESIGN.md` lines 2095-2099 + 2140-2143 — §"Roundtrip Invariant" point 4 ("Carve-out anchor") still asserted "Eight `.ail.json`-only fixtures (seven subject-matter rejection tests + one compile-time-embed for the prelude)" and §Enforcement still said "exactly the eight named carve-out files exist", but post-pd.3 the count is seven and the compile-time-embed clause is empty. The form-a spec was retired in place during pd.3 (§C4 (b) RETIRED status marker); DESIGN.md was missed. - `[Minor]` `crates/ail/src/main.rs` lines 2438-2448 — `local_types.get("prelude")` fallback in `rewrite_type` (the migrate-canonical-types subcommand's helper) was dead-by-construction after pd.3 retired the synthetic prelude load in the same subcommand. A literal `"prelude"` string in CLI scope that survived a milestone whose explicit purpose was to delete exactly that pattern in core; the same drift category one crate over. - `[Nit]` `crates/ailang-core/src/lib.rs` — three pre-existing cargo doc warnings on dangling `[load_workspace]` references carried from pd.1 (already recorded as known debt in pd.1 + pd.2 journals). Pre-existing baseline; not in this audit's scope. The roadmap's P2 "Rustdoc warning sweep" todo covers it. Both Important and Minor were fixed inline as `audit-pd-tidy` per CLAUDE.md "trivial mechanical edit" carve-out (≤30 LOC, no judgement). The Nit was deferred to its existing roadmap todo. ### Inline fixes applied - `docs/DESIGN.md:2095-2099` rewritten: "Seven `.ail.json`-only fixtures (subject-matter rejection tests) ... The prelude's compile-time-embed carve-out, originally listed here as the eighth fixture, was retired 2026-05-14 by milestone prelude-decouple; the prelude is now embedded as `examples/prelude.ail` in `ailang-surface` and parsed at compile time via `ailang_surface::parse_prelude`." - `docs/DESIGN.md:2141` rewritten: "exactly the seven named carve-out files exist". - `crates/ail/src/main.rs:2427-2448` — the `or_else` arm scanning `local_types["prelude"]` was deleted (5 LOC body); replaced with a multi-line comment explaining why (with a forward pointer for any future migrate variant that needs prelude awareness: repopulate `local_types["prelude"]` explicitly via `parse_prelude`). The owner-search now scans `import_names` only. No behavioural change against the current corpus (the `or_else` arm could never fire post-pd.3). `cargo test -p ailang-core --test carve_out_inventory` PASS post-fix. `cargo build -p ail` clean. ## Bench-regression check | Script | Exit | Result | |--------|------|--------| | `bench/check.py` | 1 | 63 metrics; 1 regressed (`latency.implicit_at_rc.p99_us` +14.42%, p99_9 +16.17%, max +27.61%, p99_over_median +9.79% — same `implicit_at_rc` cluster), 4 improved (`latency.explicit_at_rc.*` cluster), 58 stable. **Established 14-audit envelope-noise.** | | `bench/compile_check.py` | 1 | 24 metrics; 11 regressed, 0 improved, 13 stable. The build-time-O0 noise cluster (audit-form-a, clippy-sweep). All within the 20% tolerance, ratifying as noise. | | `bench/cross_lang.py` | 0 | 25 metrics; 0 regressed, 0 improved, 25 stable. Clean. | `prelude-decouple` is a workspace-loader refactor + spec text + file-system + test code only. Zero codegen / runtime / typecheck path edited. The bench numbers cannot be causally affected by this milestone; the `implicit_at_rc.p99_*` oscillation and the build-time noise are the same long-running envelope every audit since audit-cma has flagged. Baseline left pristine (15th consecutive audit decision to do so on this envelope; the right resolution is a methodology fix — already queued as P3 "Latency methodology rework" — not per-audit baseline-juggling). ## Resolution - `[Important]` DESIGN.md drift — FIXED inline (audit-pd-tidy). - `[Minor]` `main.rs` dead prelude fallback — FIXED inline (audit-pd-tidy). - `[Nit]` pre-existing cargo doc warnings — DEFERRED to P2 "Rustdoc warning sweep" (existing roadmap todo). - Bench check.py + compile_check.py exit 1 — RATIFIED as noise envelope, baseline pristine, 15th consecutive observation. ## Files touched - `docs/DESIGN.md` (two paragraphs in §"Roundtrip Invariant") - `crates/ail/src/main.rs` (5 LOC body deletion + 8 LOC replacement comment in `rewrite_type`) - `docs/journals/2026-05-14-audit-pd.md` (this file) No bench baseline files modified. ## Milestone status `prelude-decouple` milestone: **CLOSED**. The roadmap entry at P0 moves to `[x]` and is removed from the active queue (the milestone trace stays in the journal index for chronological context). The post-milestone state of the working tree: - `examples/prelude.ail.json` deleted; the prelude exists on disk only as `examples/prelude.ail`. - `crates/ailang-core/src/` contains zero literal-`"prelude"` strings. - `crates/ail/src/main.rs` contains zero literal-`"prelude"` strings in production code (the migrate-subcommand fallback is gone post- audit-tidy). - The two-phase workspace-loading composition `(load_modules_with → caller-side prelude inject → build_workspace)` is the canonical shape, exposed as public API in `ailang-core` and composed by `ailang-surface::load_workspace`. - `ailang_surface::parse_prelude` + `PRELUDE_AIL` are the canonical embed surface, re-exported from `ailang-surface/src/lib.rs`. - The CLAUDE.md "authors write `.ail`; the build derives the JSON-AST in-process via `ailang_surface::parse`" doctrine holds without exception; the prelude was the last carve-out and is now inside the doctrine. - 573 tests green workspace-wide.