Files
AILang/docs/journals/2026-05-13-audit-mq.md
T
Brummel f382931eb8 audit-mq: milestone close (module-qualified-class-names) — 4 drift items routing to mq.tidy
Architect drift review surfaces:
- 2x [high]: rigid-var refinement misses type-unification leg
  (refine_multi_candidate_residual filters declared constraints on
  class only, breaks forall a b. Show a, Show b => ... shapes);
  same-module bare-class qualifier Show.show unreachable because
  qualifier_is_class_shape = q.contains('.') excludes the no-dot
  case (contradicts mq.1 canonical-form symmetry).
- 2x [medium]: class-method-shadowed-by-fn warning over-fires on
  locals shadowing prelude method names without
  class-candidate-for-arg-type check; DESIGN.md Data Model schema
  fragments don't carry the canonical-form rule for class-ref
  fields (only the prose at 1156-1162 does).
- 1x [medium] acknowledged debt without fix: synth(...) grown to
  10 mut-ref parameters — refactor cost disproportionate to gain.
- 2x [low] roadmap backlog: Trajectories B + D have unit-test
  coverage only; collect_mono_targets rebuilds env without
  active_declared_constraints (currently latent).

Bench: compile_check.py + cross_lang.py exit 0; check.py exit 1
across 4 consecutive re-runs with metric identity shifting
between runs — 5th-consecutive audit observation of the same
noise envelope. Baseline pristine.

Resolution: mq.tidy iter covers 4 actionable items; 3 deferred.
2026-05-13 02:29:03 +02:00

8.3 KiB

audit-mq — milestone close (module-qualified-class-names)

Date: 2026-05-13 Milestone: module-qualified-class-names Iters covered: mq.1 (0eb3323) → mq.2 (2e6a4ca) → mq.3 (99d3968) Range from previous close: 0dcdaab..99d3968 (audit-ct-tidy → mq.3) Status: drift_found — mq.tidy queued

Summary

The module-qualified-class-names milestone landed in three iters: mq.1 lifted the canonical-form rule to the three class-ref schema fields and qualified all workspace-internal class-name keys; mq.2 installed the type-driven dispatch mechanism without exercising it end-to-end; mq.3 retired WorkspaceLoadError::MethodNameCollision, re-keyed class_methods to a (class, method) tuple, plumbed Env.active_declared_constraints, added the class-method-shadowed-by-fn warning, and shipped three positive E2E fixtures exercising trajectories C (ambiguous), E (explicit qualifier), and the class-fn shadow case.

Architect drift review surfaces seven items: two [high] (rigid-var refinement type-unification leg missing; same-module bare-class qualifier shape unreachable), three [medium] (warning over-fires on unrelated locals shadowing prelude method names; DESIGN.md Data Model schema fragments don't reflect the canonical-form rule for class-refs; synth(...) has grown to 10 mut-ref parameters), and two [low] backlog items (trajectories B + D covered by unit tests only — no E2E fixture; collect_mono_targets rebuilds env without active_declared_constraints, currently latent).

Bench: check.py exit 1 across four consecutive runs with metric identity shifting between runs (3 → 1 → 1 → 0 regressions across re-runs, different metrics each time; one run showed only improvements). Pattern is consistent with 5th-consecutive audit noise-class observation. compile_check.py exit 0 (24/24 stable); cross_lang.py exit 0 (25/25 stable). Baseline left pristine per the 5th-consecutive conservative-call convention; see Bench notes below.

Resolution path: mq.tidy iteration covers the four actionable drift items (two [high] + two [medium]). The 5th [medium] (synth(...) signature growth) is acknowledged as accepted post-mq.3 cost and not fixed — refactoring synth(...) would ripple through 15+ recursive callsites and 5 external callers, disproportionate to the readability gain. The two [low] items join the roadmap backlog.

Architect drift items

[high-1] Rigid-var refinement misses type-unification leg

Path: crates/ailang-check/src/lib.rs:2163-2168

The refine_multi_candidate_residual rigid-var branch filters declared constraints on dc.class == c only. Spec §"Constraint- discharge refinement" (line 130-138) requires the filter to also unify the declared constraint's type_ with the residual's type_. Two same-class declared constraints on different typevars (forall a b. Show a, Show b => (a, b) -> String style) cannot currently be discriminated; the filter returns the first match regardless of which typevar the residual actually came from. No unit test pins the type-unification leg.

Outcome: fix in mq.tidy.

[high-2] Same-module bare-class qualifier shape unreachable

Path: crates/ailang-check/src/lib.rs:2570-2575

qualifier_is_class_shape = q.contains('.') makes same-module bare- class qualifiers (Show.show) unreachable; the gate only admits cross-module class qualifiers (<module>.<Class>.<method>). This contradicts the canonical-form rule's symmetry — bare class refs are legal at every other class-ref schema field (per mq.1's rule). Trajectory E E2E coverage is cross-module-only as a consequence.

Outcome: fix in mq.tidy.

[medium-1] class-method-shadowed-by-fn over-fires

Path: crates/ailang-check/src/lib.rs:2479-2502

The warning fires whenever any class declares the method, not when a class candidate for the actual arg type exists per spec §"Class-fn collisions" (line 161-162). Will spam on unrelated locals shadowing prelude method names (e.g. let show = … inside an arbitrary body where show has no Show-context).

Outcome: fix in mq.tidy.

[medium-2] DESIGN.md Data Model schema fragments don't reflect canonical-form rule for class-refs

Path: docs/DESIGN.md:2139, 2152, 2273

Data Model schema fragments for InstanceDef, Constraint, SuperclassRef describe class-ref <id> without flagging the canonical-form rule. Only the prose at lines 1156-1162 carries the rule. A schema-section reader without the prose context sees bare-only.

Outcome: fix in mq.tidy (doc-only).

[medium-3] synth(...) signature growth (accepted)

Path: crates/ailang-check/src/lib.rs:2415

synth(...) now carries 10 mut-ref parameters (added warnings in mq.3). Two re-entries (mono.rs:614, lift.rs:700) discard the warnings channel by convention.

Outcome: acknowledged debt, NOT fixed in mq.tidy. Rationale: refactoring synth(...) (e.g. into a SynthCtx struct carrying the mut-refs) would ripple through 15+ recursive callsites and 5 external callers, with the only payoff a marginal readability gain. The mut-ref pattern is consistent with the rest of the crate (check_in_workspace's Vec<CheckError> accumulator), so this is project-style rather than drift.

[low-1] Trajectories B + D no E2E

Paths: spec §"Data flow" trajectories B (type-driven narrow- to-one) + D (rigid-var narrow-to-one) are covered by unit tests only — tests/method_dispatch_pin.rs 5-step rule tests. No on-disk fixture exercises either path end-to-end.

Outcome: roadmap backlog (P3). Single-class-per-method workspaces don't exercise these branches; the real coverage gap opens when downstream code starts producing the multi-class shapes the unit tests already pin.

[low-2] collect_mono_targets rebuilds env without active_declared_constraints

Path: crates/ailang-check/src/mono.rs:574-625

Structural divergence from check_fn's env setup. Currently latent: mono's residuals are concrete-type so the rigid-var filter never runs. Env-shape invariant unpinned.

Outcome: roadmap backlog (P3). Test that pins the invariant (env.active_declared_constraints populated consistently wherever Env is built) belongs with the [low-1] E2E work for trajectory D.

Bench

  • bench/check.py: exit 1 across four consecutive re-runs with metric identity shifting between runs:
    • Run 1: 1 regression (latency.implicit_at_rc.p99_over_median), 5 improvements (across bench_list_sum.gc_over_bump + bench_closure_chain.gc_over_bump + 3 latency.explicit_at_rc.* metrics).
    • Run 2: 3 regressions (bench_list_sum.bump_s 3rd-consecutive sighting + 2 latency.implicit_at_rc.* max-tail), 3 improvements (same latency.explicit_at_rc cluster).
    • Run 3: 1 regression (different metric again), 5 improvements.
    • Run 4: 0 regressions, 2 improvements (bench_list_sum.gc_over_bump + bench_closure_chain.gc_over_bump).
    • Pattern: metric identity is not stable; the recurring bench_list_sum.bump_s 3rd-consecutive sighting per mq.3 journal lineage shows up in 1 of 4 runs only. Audit-ct-tidy (2026-05-12), audit-eob (2026-05-12), audit-ms (2026-05-12), audit-cma (2026-05-12) all left baseline pristine on the same latency.explicit_at_rc improvement cluster reappearing across milestones — this is the 5th consecutive audit observation of the same noise envelope.
    • Conservative call: baseline pristine for 5th consecutive audit. The latency cluster has narrowed-then-widened-then- narrowed across audits, which is itself attribution evidence that this is variance, not signal. If signal were present the metric would not migrate between runs at this rate.
  • bench/compile_check.py: exit 0; 24/24 stable.
  • bench/cross_lang.py: exit 0; 25/25 stable.

No --update-baseline invocation this audit.

Resolution

  • mq.tidy iter to land four drift fixes: [high-1], [high-2], [medium-1], [medium-2]. Routes through plannerimplement.
  • [medium-3] (synth(...) signature growth): acknowledged debt, no action.
  • [low-1] (Trajectory B + D E2E) + [low-2] (mono env shape): roadmap backlog P3. Same scope — single follow-up iter when the multi-class workspace shape lands.

Files touched

  • docs/journals/2026-05-13-audit-mq.md (new, this file)
  • docs/journals/INDEX.md (one new line)

No production code or test edits in this audit; all recommendations land in mq.tidy.