docs(design): ratify the check→codegen boundary (mir.5, typed-MIR close)

mir.5 is the typed-MIR milestone's closing iteration. Its CODE half had
already converged before the iteration began, so mir.5 ships no code —
it ratifies into the design/ ledger the boundary the code already holds.

Verified at iteration entry (empirically, not from the spec sketch):
  - all four named re-derivers grep-clean in codegen: synth_with_extras,
    synth_arg_type, type_home_module, the second infer_module_with_cross;
  - lower_workspace takes &MirWorkspace (codegen consumes MIR);
  - MTerm::New is unreachable!() — raw-buf.4 desugars Term::New to
    (app T.new …) before codegen, so there is no element-type
    re-derivation left to relocate;
  - #51 / #53 (the element-type / new-T codegen crashes) are closed;
    their residue was fixed by ee4107c / 420f75f plus the New-desugar,
    not by a separate raw-buf patch track.

Ledger work (the mir.5 deliverable):
  - NEW design/contracts/0018-check-codegen-boundary.md: the invariant
    "codegen re-derives nothing; MIR is total over what check proved;
    a codegen arm that recomputes a fact instead of reading MIR is
    drift." Ratifier: lower_to_mir_ty.rs::callee_classification_builtin_and_static.
  - 0013-typeclasses invariant 2 retracted: codegen no longer re-resolves
    cross-module names via an import_map fallback; lower_to_mir::classify_callee
    resolves the reference once into Callee::Static and codegen consumes it.
  - 0003-pipeline.md: the "lower to MIR" line names the real stage
    (elaborate_workspace → MirWorkspace → lower_workspace) and a new
    paragraph states the boundary, cross-referencing 0018.
  - INDEX.md: boundary contract row added; qualified-xref re-pointed at
    lower_to_mir + 0018.
  - codegen_import_map_fallback_pin.rs doc-comment made honest — it pins
    the post-mono AST precondition classify_callee relies on, not a
    codegen-side resolution that no longer exists. Assertions unchanged;
    the test stays green.
  - spec 0060 gains a mir.5 refinement note recording the early code
    convergence.

Acceptance criteria 1-7 of docs/specs/0060-typed-mir.md are all met.
Full workspace suite green (exit 0, 0 failed, 2 ignored); 708 passed
carried from mir.4 (no test added or removed).

Not done here (deliberately): the milestone #7 (raw-buf) subsumption
note is an external Gitea tracker write; the /boss auto-mode classifier
declined it as an unauthorised external write and it is surfaced to the
user rather than worked around. The end-to-end milestone fieldtest
remains the deliberate manual close-gate before the tracker milestone
is marked done.

refs #51 #53
This commit is contained in:
2026-06-01 01:34:37 +02:00
parent c5fd16a4eb
commit a378dad0aa
7 changed files with 147 additions and 32 deletions
@@ -1,21 +1,24 @@
//! Pin for the iter-24.3 codegen `import_map`-fallback path
//! Pin for the post-mono cross-module reference precondition
//! (design/contracts/0013-typeclasses.md, "Cross-module references in
//! synthesised bodies" invariant 2).
//! synthesised bodies" invariant 2; resolved per the
//! design/contracts/0018-check-codegen-boundary.md boundary).
//!
//! Property protected: post-mono synthesised body cross-module
//! references resolve at codegen via the fallback to
//! `module_user_fns` when the prefix is
//! NOT in the current module's `import_map`. Specifically, the
//! synthesised `prelude.print__<UserType>` body references
//! `<user_module>.show__<UserType>` even though `prelude` does not
//! import user modules.
//! Property protected: mono synthesises a `prelude.print__<UserType>`
//! body that references `<user_module>.show__<UserType>` even though
//! `prelude` does not import user modules — the cross-module reference
//! exists in the post-mono AST and is import_map-independent. This is
//! the precondition `lower_to_mir::classify_callee` relies on when it
//! resolves the reference once into `Callee::Static`; codegen then
//! consumes that identity and re-derives nothing (the typed-MIR
//! boundary). The pin asserts the AST-level precondition directly, so
//! it stays green and cheap to bisect.
//!
//! Failure mode this pin catches: a future codegen refactor
//! tightens `resolve_top_level_fn` or `lower_app`'s cross-module
//! arm or `synth_with_extras`'s Var arm back to `import_map`-only.
//! Without this pin, the regression surfaces only at the
//! `show_user_adt` E2E (which builds + runs a binary, slow to
//! bisect).
//! Failure mode this pin catches: a future mono refactor stops
//! emitting the cross-module `show_user_adt.show__<UserType>` Var (or
//! routes it through `prelude`'s import_map), which would break the
//! resolution `classify_callee` performs downstream. Without this pin,
//! the regression surfaces only at the `show_user_adt` E2E (which
//! builds + runs a binary, slow to bisect).
use ailang_check::{check_workspace, monomorphise_workspace};
use ailang_core::ast::{Def, Term};