# 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.
