plan-recon flagged that monomorphise_workspace runs between check and
codegen, and codegen lowers the post-mono workspace. The first draft of
this spec placed lower_to_mir as the final phase of check_workspace
(pre-mono) and claimed "nothing re-walks the AST". That is wrong on
both counts:
- Built pre-mono, MIR would not carry the monomorphic specialisations
monomorphise_workspace appends — exactly the post-mono bodies codegen
emits today — reintroducing the check↔codegen gap this milestone
exists to close.
- synth is a pure `&Term → Type` function and the authoring AST carries
no node-ids, so MIR cannot be a side-table read off a check pass; it
is built inline by a walk. The honest property is therefore not "no
second walk" but "no second *engine*": codegen's synth_with_extras is
a hand-copied mirror of synth that drifts (every raw-buf bug is a
drift point). The milestone replaces the mirror with a single
post-mono call to canonical synth, materialised as MIR.
Corrections: architecture diagram and data flow now run
check → lift → monomorphise → lower_to_mir; lower_to_mir is wrapped by
a new front-end entry elaborate_workspace that subsumes the lift+mono
orchestration the CLI does today; check_workspace stays for the
diagnostics-only path (`ail check`). Node count fixed 27 → 17 Term
variants (ast.rs:436).
Re-dispatched grounding-check after the edit (post-PASS edit
invalidates the prior report) — PASS, all load-bearing post-mono
assumptions ratified against live code (main.rs build order,
mono.rs synth use, 17-variant Term, no node-ids).
Open a new milestone introducing a typed mid-level IR (`ailang-mir`) as
the single artefact crossing the check → codegen boundary, so codegen
re-derives nothing.
Root cause (architect drift review): the boundary today carries no typed
artefact. `crates/ail/src/main.rs:2293` calls `check_workspace`, discards
the result, and hands `lower_workspace` the raw `Workspace`.
`CheckedModule` (ailang-check/src/lib.rs:1283) carries only top-level
(Type, hash). Codegen therefore re-runs four independent derivations of
what check already proved — type synthesis (`synth_with_extras`), callee
resolution (`type_home_module` ×3 + `is_static_callee`), uniqueness/mode
(`infer_module_with_cross`, second run), and Str representation (`!is_str`
recur gate). Every raw-buf bug (#43/#46/#47/#49/#51/#53, all "check-clean,
build-divergent") is a point where one of those re-derivations disagreed
with check and got patched one leg at a time.
Honest framing: this is removal of a re-derivation architecture, not a
RED→GREEN of crashing programs. #51 and #53 build today (patched by
ee4107c / 420703d) and are regression guards — they must keep building as
the codegen mirrors that hold them green are deleted. #49 is the one open
leg (builds, leaks live=3, test #[ignore]'d) and is the single RED→GREEN.
Five iterations, each adding one MIR annotation class and deleting the
matching codegen re-deriver: mir.1 structural MIR + `ty`; mir.2
`Callee::Static`; mir.3 `Mode`/`consume_count`; mir.4 `StrRep` (#49
live=0); mir.5 element-type residue + ledger (new boundary contract,
retract 0013:106-120, fix 0003-pipeline model, INDEX, close #51/#53,
reset milestone #7 to not-met).
Forward rewrite, not revert (the prior raw-buf milestone close was wrong;
the language infrastructure is healthy and survives — only the codegen
re-derivation is the disease). Approach B: a separate typed MIR carrier,
authoring AST unchanged.
Gates: parse-every-block clean (all three .ail witnesses `ail check`-clean,
exit 0); grounding-check PASS (13 load-bearing assumptions ratified against
green tests / verified code facts).