Files
AILang/docs/journals/2026-05-13-iter-24.tidy.md
T
Brummel 4e8447d15d iter 24.tidy: close 5 actionable drift items from audit-24
Documents the three iter-24.3 strengthenings as load-bearing
invariants and tightens two error-handling sites:

T1: DESIGN.md gains new subsection §Cross-module references in
synthesised bodies (between Resolution-and-monomorphisation and
Defaults-and-superclasses) documenting three invariants installed
in iter 24.3 — (1) MonoTarget::FreeFn::type_args carries canonical
types post-collection via normalize_type_for_lookup; (2) post-mono
synthesised body cross-module refs may bypass the source template's
import_map (codegen falls back to module_user_fns / module_def_ail_types);
(3) FreeFnCall synth pushes one ResidualConstraint per declared
forall-constraint with rigid vars substituted by fresh metavars.

T2: codegen_import_map_fallback_pin.rs (integration test) asserts
the synthesised prelude.print__<IntBox> body references
show_user_adt.show__<IntBox> AND prelude module's imports do not
contain show_user_adt — proving the cross-module ref bypasses
import_map at codegen.

T3: polyfn_dot_qualified_branch_pin.rs (integration test) asserts
bare-name print f (f : Int -> Int) fires exactly one no-instance
diagnostic at typecheck with zero unknown-variable diagnostics —
proving the bare-name resolution reaches the dot-qualified synth
branch where the constraint-residual push fires.

T4: check/lib.rs:2858 unwrap_or_default() replaced with .expect()
carrying the registry-coherence message — class_methods index
drift now surfaces explicitly rather than rendering NoInstance
with an empty method name.

T5: mono.rs gains apply_subst_and_normalize helper (Option<Type>
return) extracted from two byte-identical call sites at
collect_mono_targets and collect_residuals_ordered. Each call site
retains its own rigid-var / unit-default policy in the None arm
(site 1: rigid → has_rigid+break, unbound → Type::unit; site 2:
non-concrete → Type::unit). Byte-identity invariant on mono-symbol
hashes enforced by construction.

Tests: 558 passed (was 556 + 2 new pins). No production semantic
change — pure documentation + test pin + error-handling tightening
+ helper refactor. bench/cross_lang exit 0; bench/compile_check +
bench/check exit 0 this run (latency.implicit_at_rc / latency.explicit_at_rc /
bench_list_sum.bump_s noise envelope unobserved, lineage continues
at 10th consecutive observation without firing this run).
2026-05-13 04:28:15 +02:00

8.1 KiB
Raw Blame History

iter 24.tidy — close 5 actionable drift items from audit-24

Date: 2026-05-13 Started from: 0e27533 Status: DONE Tasks completed: 6 of 6

Summary

Closes the 5 actionable drift items audit-24 surfaced (3× [high] + 2× [medium]): T1 adds a new DESIGN.md subsection ### Cross-module references in synthesised bodies under ## Decision 11's ### Resolution and monomorphisation, anchoring the three iter-24.3 strengthenings as load-bearing lockstep invariants (canonical-form type_args / import_map-fallback / FreeFnCall constraint-residual push). T2 pins the codegen import_map-fallback path with an integration test asserting the synthesised prelude.print__IntBox body contains a show_user_adt.show__IntBox Var AND the prelude source module does not import show_user_adt. T3 pins the bare-name poly-fn dot-qualified-branch invariant by asserting show_no_instance.ail.json produces exactly one no-instance diagnostic and zero unknown-variable / internal diagnostics. T4 tightens crates/ailang-check/src/lib.rs:2852 from unwrap_or_default() to .expect(...) with a registry-coherence panic message; the strict tightening surfaced no existing violations (558 tests pass post-edit). T5 extracts the duplicated subst.apply + is_fully_concrete + normalize_type_for_lookup body into a single apply_subst_and_normalize helper returning Option<Type>, replacing the two byte-identical sites at mono.rs::collect_mono_targets (685-714) and ::collect_residuals_ordered (1284-1303); the byte-identity invariant is now enforced by construction, mono_hash_stability green on both primitive Show + Eq/Ord hashes. T6 verifies: 558 tests pass (was 556 + 2 new pins, exactly the expected count); bench/cross_lang.py exit 0 (25/25 stable); bench/compile_check.py exit 0 with 4 check_ms.* noise-class regressions in the documented audit-24 lineage; bench/check.py exit 0 with 3 latency.implicit_at_rc.*

  • 4 latency.explicit_at_rc.* noise-class observations in the same lineage (10th consecutive observation; baseline pristine).

Per-task notes

  • iter 24.tidy.1 (T1): DESIGN.md +68 lines, new ### Cross-module references in synthesised bodies subsection inserted between ### Resolution and monomorphisation (ends line 1693) and the re-anchored ### Defaults and superclasses. Subsection enumerates three lockstep invariants with cross-references to source files (mono.rs::collect_mono_targets / ::collect_residuals_ordered, codegen/lib.rs::resolve_top_level_fn / ::lower_app / ::synth_with_extras, check/lib.rs synth FreeFnCall arm) and to the protective test pins. Closing paragraph names the lockstep partnership and the regression cost of loosening any one.

  • iter 24.tidy.2 (T2): New crates/ail/tests/codegen_import_map_fallback_pin.rs. Loads examples/show_user_adt.ail.json, runs check_workspace + monomorphise_workspace, finds the synthesised Def::Fn whose name starts with print__ in the prelude post-mono module, recursively walks its body looking for a Term::Var { name } whose name starts with show_user_adt. and contains show__, and confirms the prelude source module's Vec<Import> does NOT include show_user_adt. Plan draft adapted at three points: (a) Term::App field name is callee not fn (the latter is the serde rename; Rust field is callee); (b) imports.iter() gives &Import not &String so the check is on imp.module; (c) recursive walker extended with Seq / Clone / ReuseAs / LetRec / If / Match / Ctor arms for exhaustiveness — all data-shape adaptations the plan explicitly authorised. Test passes first-shot (1 passed).

  • iter 24.tidy.3 (T3): New crates/ail/tests/polyfn_dot_qualified_branch_pin.rs. Loads examples/show_no_instance.ail.json, asserts exactly one no-instance diagnostic AND zero unknown-variable / internal diagnostics. The plan's draft d.code field accesses match the actual Diagnostic struct (verified against crates/ailang-check/src/diagnostic.rs:131). Test passes first-shot.

  • iter 24.tidy.4 (T4): One-line tightening at crates/ailang-check/src/lib.rs:2858.unwrap_or_default().expect("class_methods registry coherence ...") with the verbatim plan message. The .expect(...) covers the registry-coherence invariant: a declared constraint's class is expected to be in env.class_methods because the pre-pass MissingClass diagnostic should have rejected it earlier; reaching this point with None means workspace-registry / class-index drift. No existing test violates the new invariant — 558 tests pass.

  • iter 24.tidy.5 (T5): New apply_subst_and_normalize helper at crates/ailang-check/src/mono.rs:555-583 (immediately preceding collect_mono_targets). Signature fn apply_subst_and_normalize(env: &crate::Env, module_name: &str, m: &Type, subst: &crate::Subst) -> Option<Type>. Returns Some(normalised) if subst.apply(m) is fully concrete (via crate::is_fully_concrete); else None. Two call sites retrofit: site 1 at collect_mono_targets (now ~ lines 707-727) keeps its rigid-var / Unit-default branching at the call site (helper-None → check contains_rigid_var(&subst.apply(m)); if rigid set has_rigid = true and break; else push Type::unit()); site 2 at collect_residuals_ordered (now ~ lines 1284-1290) uses apply_subst_and_normalize(&env, ...).unwrap_or_else(Type::unit). Site 1 needed &env (local owned Env) where the plan draft had bare env — one-character edit. mono_hash_stability both tests pass (primitive Show + Eq/Ord mono-symbol hashes bit-identical), full workspace 558 green. Byte-identity invariant now enforced by construction at the helper boundary; each call site explicitly documents its post-helper-None policy.

  • iter 24.tidy.6 (T6): Integration verification. Full workspace cargo test --workspace --no-fail-fast = 558 passed, 0 failed (matches plan's prediction: 556 baseline + 2 new pins). No FAILED entries among milestone-24 specific test groups (show_/print_/prelude_free_fns/mono_hash_stability/typeclass_22b/mq3). Bench: cross_lang.py exit 0, 25/25 stable; compile_check.py exit 0 with 4 check_ms.* ms-level noise-class regressions (hello, borrow_own_demo, bench_tree_walk, bench_hof_pipeline); check.py exit 0 with 3 noise-class regressions (latency.implicit_at_rc.{p99_9_us, max_us} first-sightings in this iter) + 4 noise-class improvements (latency.explicit_at_rc.{p99_us, p99_over_median} improvements continuing the documented audit-cma → audit-24 lineage). 10th consecutive observation of metric-identity-migrating noise; baseline pristine, conservative-call convention holds per the documented lineage.

Concerns

  • T5 pre-extraction reading vs plan: the plan said site 1's unbound- meta path "likely a continue without push" — actual pre-extraction code did type_args.push(Type::unit()) for unbound metas (iter 23.4 behaviour, comment in place). I preserved the actual behaviour, not the plan's guess. Lockstep with site 2 (which has the same Unit-default policy) is improved post-extraction. Observation, not correctness.

  • T2 plan draft had three data-shape mismatches against actual Rust schema (Term::App.callee vs plan's fn, Vec<Import> vs Vec<String>, exhaustive Term variants). The plan explicitly flagged these as "implementer adapts to the actual field name" cases; not plan defects but recon imprecisions.

Known debt

  • audit-24 [medium-3] negative-test coverage breadth → deferred to P3 downstream-corpus-migration milestone (carried over from audit-24, not closed here).
  • audit-24 [low-1] bench/architect_sweeps.sh noise → carry-on, pre-milestone-24 lines not new drift.

Files touched

  • docs/DESIGN.md — new subsection (T1), +68 lines.
  • crates/ail/tests/codegen_import_map_fallback_pin.rs — new pin (T2).
  • crates/ail/tests/polyfn_dot_qualified_branch_pin.rs — new pin (T3).
  • crates/ailang-check/src/lib.rs — one-line tightening at :2858 (T4).
  • crates/ailang-check/src/mono.rs — new helper + two call-site replacements (T5).

Stats

bench/orchestrator-stats/2026-05-13-iter-24.tidy.json