The four mut-local CheckError variants (MutAssignOutOfScope, AssignTypeMismatch, UnsupportedMutVarType, MutVarCapturedByLambda) embedded the bracketed kebab code in their thiserror Display body, while the non-JSON CLI formatter independently prepends [code] from CheckError::code() — doubling it. Non-mut variants never embedded the bracket; this brings the four in line with that convention. RED-first via the debug skill: ct1_check_cli.rs pins the observable property (code appears exactly once in the rendered human diagnostic). GREEN was a trivial four-string mechanical edit. Tests 598 -> 599.
2.3 KiB
bugfix mut-diag-double-code — doubled [code] prefix in mut-local human diagnostics
Date: 2026-05-15
Status: DONE
Trigger: fieldtest finding F2 (docs/specs/2026-05-15-fieldtest-mut-local.md).
Started from: 1faee67
Symptom
Non-JSON ail check stderr rendered the kebab diagnostic code
twice for the four mut-local CheckError variants, e.g.
error: [mut-var-captured-by-lambda] main: [mut-var-captured-by-lambda] mut-var `x` cannot be captured...
Cause
MutAssignOutOfScope, AssignTypeMismatch,
UnsupportedMutVarType, MutVarCapturedByLambda were authored in
iters mut.2 / mut.4-tidy with their #[error("[<code>] ...")]
thiserror Display bodies embedding the bracketed code. The
non-JSON Cmd::Check formatter in crates/ail/src/main.rs
independently prepends [{d.code}] from CheckError::code().
Embedded + prepended = doubled. The non-mut CheckError variants
never embedded the bracket, so only these four doubled — they were
the inconsistent ones, brought in line here.
Fix
RED-first via the debug skill (ailang-debugger wrote the
failing test; the GREEN side was a trivial four-string mechanical
edit applied inline by the Boss per the CLAUDE.md
trivial-mechanical-edit carve-out — no review-and-commit
discipline shed).
- RED test:
crates/ail/tests/ct1_check_cli.rs—check_human_mode_renders_mut_diagnostic_code_exactly_once. Pins the observable property: the code appears exactly once in the rendered human diagnostic, exercising the same formatter path the CLI uses (not a brittle assertion on the raw#[error]body). - GREEN: dropped the leading
[<code>]literal from the four#[error(...)]Display strings incrates/ailang-check/src/lib.rs.
The mut-typecheck pin tests (mut_typecheck_pin.rs) and the
in-source unit assertions match on the struct variants / code(),
not the Display string, so the prefix removal did not touch them.
Tests
598 → 599 green (the new RED-now-GREEN human-formatter test). Full workspace clean, zero failures.
Not in scope (stays queued)
F1/F4 (accumulator-over-iteration: the milestone motivation is
unmet without a loop construct) and F3 (zero-arg (app f) rejected
at parse) from the same fieldtest are NOT addressed here — they are
design/roadmap items, not this bug.