Milestone-close audit for remove-mut-var-assign (48e7774..07f0802).
Architect drift: one [high] — crates/ailang-core/specs/form_a.md
still documented the full mut/var/assign construct (EBNF 286-288,
prose 308-328, dangling `like mut` at 332). The spec named it for
deletion; the iter missed it. Root cause = the recon-undercount
class (4th recurrence) plus a Boss error: a plan-time grep checked
only docs/form_a.md (dir-scoped), got "No such file", and that
unverified non-existence claim propagated into the recon brief —
the real file is crates/ailang-core/specs/form_a.md. A stale doc
never compile-fails so the implement compile-sweep could not catch
it. Fixed inline as a Boss .tidy (CLAUDE.md context-already-loaded
carve-out; full review discipline kept — tree-wide verified zero
construct residue except the roadmap entry naming the milestone
itself; grammar now reads coherently reuse-as -> loop -> recur).
Bench causally exonerated: HEAD vs 48e7774 bench binaries
cmp-byte-identical (the -2520-line removal alters zero bytes of the
firing fixtures' machine code). check.py exit 1's 3 firings are the
tracked P2 bump_s environmental staleness + single-sample -n5
max_us jitter (explicit_at_rc.max_us +108% -> -2.30% ok on
identical-code rerun). PRISTINE met on the merits; NO baseline
ratify (Iron Law + spec foreclosure).
Roadmap: P0 refreshed (audit clean, fieldtest remains); the
ailang-plan-recon P2 escalated in place to a structural
recon-contract defect (2 named gaps + concrete fix); a new distinct
P2 filed for the check.py *.max_us -n5 3-for-3 false-positive.
5.4 KiB
audit — remove-mut-var-assign milestone close
Date: 2026-05-18
Scope: 48e7774..07f0802 (prev close → the single terminal iter)
Outcome: CLEAN after one inline .tidy fix; bench causally
exonerated; two P2 roadmap todos filed/strengthened. Fieldtest
remains (surface-touching milestone).
Step 1 — Architect drift review
[high] — crates/ailang-core/specs/form_a.md (EBNF 286-288,
prose 308-328, dangling like mut at 332). The live in-tree
Form-A grammar reference still documented the full mut/var/
assign construct. The spec (Components #2, Architecture table
row 2) explicitly named "form_a.md" for deletion; the
schema-vs-doc honesty invariant the milestone advertises was
broken. Root cause = the recon-undercount class (4th
recurrence), and specifically a Boss error: at plan time the
Boss grepped docs/form_a.md (dir-scoped), got "No such file",
concluded "form_a.md does not exist", and propagated that
unverified non-existence claim into the recon brief. The file
lives at crates/ailang-core/specs/form_a.md and is actively
maintained (form-a.tidy edited it) — not frozen historical
record. A compile-driven sweep cannot catch this: a stale doc
never produces E0004/E0061.
Resolution — inline Boss .tidy, not planner+implement.
Rationale (CLAUDE.md "When NOT to delegate": context already
loaded, an agent would redo all milestone reading for a ~40-line
single-file doc deletion whose design was already settled by the
approved spec): deleted the three EBNF productions (keeping
reuse-as→loop→recur), the three mut/var/assign prose
bullets, and reworded the surviving loop bullet so its
"right-folded into Term::Seq" no longer back-references the
deleted mut. Full review-and-commit discipline kept (no
discipline shed for the carve-out): verified tree-wide that the
only remaining Term::Mut/Term::Assign/MutVar strings in
crates/ + docs/DESIGN.md + docs/roadmap.md are the roadmap
entry describing the milestone itself (not residue), and
form_a.md's two remaining mut/var substrings are the unrelated
[unbound-var] / reuse-as-source-not-bare-var diagnostic names.
Grammar + notes read coherently after the cut.
[medium] — process-drift, recon-undercount 4th recurrence.
The existing loop-recur.tidy P2 (ailang-plan-recon
cross-crate-caller-undercount) is demonstrably insufficient: the
class now recurs through a new site type each time (test bodies,
drift pins, carve-out files, and now a spec-named non-compiled
doc + an unverified existence claim feeding the recon brief).
Escalated that P2 in place — broadened from "cross-crate caller"
to a structural recon-contract defect with two named gaps
(non-compile-checked spec-named sites; unverified "X does not
exist" claims inheriting into a recon brief), and a concrete
agent-definition fix (compile-driven enumeration as authoritative
code-site set plus an ls-verified spec-named-path existence
table). Not a code defect; an agent-discipline fix, parked P2.
Step 2 — Bench-regression
bench/check.py exit 1 (3 firings); compile_check.py
exit 0 (24/24 stable — no check-time tax, consistent with a
pure removal); cross_lang.py exit 0 (25/25 stable, incl. the
independent bench_list_sum.ail_bump_s +2.95% ok).
check.py firings: throughput.bench_list_sum.bump_s +12.51%;
latency.explicit_at_rc.max_us +108.38%;
latency.implicit_at_rc.max_us +66.46%. All medians/p99 stable.
Bencher localisation — causal exoneration (decisive). Built
ail at HEAD and at pre-milestone 48e7774; the three
firing-relevant bench binaries (list_sum_bump, lat_expl_rc,
lat_impl_rc) are cmp-byte-identical across the milestone.
The −2520-line removal diff does not alter one byte of these
fixtures' machine code (none contain mut); no causal mechanism
exists. By elimination every firing is non-code-caused.
Per-firing verdict: bump_s = the tracked P2 environmental
staleness (stable ~+11–14% across 4 runs, byte-identical binary,
cross_lang corroborates); both *.max_us = single-sample -n 5
jitter (explicit_at_rc.max_us collapsed +108%→-2.30% ok by
an identical-code rerun3; implicit_at_rc.max_us swings
477→1172→843→756 on the identical binary while its median holds
+3.1…3.9%).
Resolution: PRISTINE met on the merits. NO baseline ratify.
The Iron Law forbids ratifying noise into the baseline and the
spec explicitly forecloses opportunistic ratification here; the
non-pristine exit code is bench-harness noise, not a code defect,
and does not block close. Filed a new, distinct P2 (separate
from the *.bump_s staleness P2): check.py RC-latency
*.max_us at -n 5 is a structural false-positive — 3-for-3 on
consecutive no-runtime-change milestones — recommending drop
max_us from gating / raise -n / cpuset-pin (mitigation choice
left to a future iteration).
Step 3/4 — Classification & resolution
| Item | Class | Outcome |
|---|---|---|
| form_a.md construct doc | fix | inline Boss .tidy (this commit) |
| recon-undercount 4th recurrence | process-drift | P2 escalated in place (roadmap) |
bump_s +12.51% |
carry-on | tracked P2 (no ratify) |
*.max_us ×2 |
carry-on | new P2 filed (no ratify) |
Milestone audit CLEAN post-tidy. Next pipeline step:
fieldtest (the removal is a user-visible Form-A surface change —
mut/var/assign no longer lex; an LLM author must now reach
for let/if/loop/recur).