iter remove-mut-var-assign.1: atomic removal of mut/var/assign

mut/var/assign removed from AILang entirely and atomically. Deleted:
Term::Mut/Term::Assign/struct MutVar; the three Form-A keywords +
parse_mut/parse_assign + grammar EBNF; the 4 mut CheckError variants;
the mut_scope_stack synth threading (param dropped from synth + every
internal/external/test caller); the two lower_term arms; and every
exhaustive no-_ Term::Mut/Term::Assign match arm across 17 source
files — cut in lockstep with DESIGN.md, fixtures, the drift trio,
carve-out and roadmap so the schema is honest at every commit. No
catch-all wildcard introduced (verified). loop/recur + let/if are
the surviving forms.

The shared codegen alloca machinery survives (loop reuses it):
mut_var_allocas renamed binder_allocas (representation-only, loop
codegen byte-identical) and the shared Term::Lam escape guard
simplified to !loop_stack.is_empty() with the loop half
(LoopBinderCapturedByLambda) byte-equivalent. Feature-acceptance
applied inverted: the removed feature fails clause 2 (redundant)
and clause 3 (IS the iterated-mutable-state bug class).

Behaviour preservation is executable: mut_counter/mut_sum_floats
still print 55 after the faithful let/if rewrite. The removal is
made executable by the new mut_removed_pin.rs (4 must-fail pins).
Independent verification: cargo test --workspace 605/0, zero
residual mut symbols in any crate source, loop/recur non-regression
all green (55 / 500000500000 / infinite-compiles / the
lambda_capturing_loop_binder pin), roundtrip_cli PASS.

One DONE_WITH_CONCERNS: a 4th recurrence of the recon-undercount
class (in-source mod tests + a drift-pin fn + 5 orphaned mut
.ail.json carve-outs + a non-enumerated E0599); all resolved within
implementer remit, no behaviour change. Milestone-close audit then
fieldtest remain.

spec docs/specs/2026-05-18-remove-mut-var-assign.md (grounding PASS)
plan docs/plans/remove-mut-var-assign.1.md
This commit is contained in:
2026-05-18 11:06:17 +02:00
parent f355899fdf
commit 07f080256c
48 changed files with 331 additions and 2523 deletions
+6 -54
View File
@@ -2401,26 +2401,6 @@ are real surface forms.
// lowers as in-place rewrite under `--alloc=rc`.
{ "t": "reuse-as", "source": Term, "body": Term }
// Iter mut.1: local mutable-state block. `vars` declares zero or
// more lexically-scoped mutable bindings (initialised in order);
// `body` is a single Term in scope of all vars. The block's static
// type is `body`'s static type. The `vars` array stays present in
// canonical JSON even when empty (no `skip_serializing_if` —
// hash-stable-on-omission is NOT applied here; the field is part of
// the shape). See `docs/specs/2026-05-15-mut-local.md`.
{ "t": "mut",
"vars": [ { "name": "<id>", "type": Type, "init": Term }, ... ],
"body": Term }
// Iter mut.1: in-block update of a mut-var. Legal only as a
// sub-term of a `Term::Mut` whose `vars` includes a var with the
// same `name`. Static type Unit. The lexical-scope rule is enforced
// at typecheck via `mut-assign-out-of-scope`; codegen lowers as a
// `store` to the var's entry-block alloca.
{ "t": "assign",
"name": "<id>",
"value": Term }
// loop-recur iter 1: strict iteration block. `binders` declares
// one or more loop parameters (name, type, init), evaluated in
// order on loop entry; `body` is in scope of all binders. The
@@ -2445,17 +2425,12 @@ In the MVP, `do` is only a direct call to a built-in effect op (no
handler). A `lam` term constructs an anonymous function value; free
variables of its body are captured from the enclosing scope.
Both shapes ship in fully-lowered form as of iter mut.3 (2026-05-15):
typecheck binds mut-vars in a lexical scope stack, codegen lowers
them as entry-block allocas, and `Term::Assign` emits a `store`
yielding the canonical Unit SSA. Mut-vars must be of one of the
stack-resident scalar types `{Int, Float, Bool, Unit}`; capturing a
mut-var into a lambda body is rejected at typecheck via
`CheckError::MutVarCapturedByLambda` (iter mut.4-tidy). Loop binders
are alloca-resident under the same scheme, so capturing a `loop`
binder into a lambda body is rejected by the symmetric
`CheckError::LoopBinderCapturedByLambda` (iter loop-recur.tidy). See
`docs/specs/2026-05-15-mut-local.md`.
Loop binders are alloca-resident: typecheck binds them in the
ordinary local scope plus a positional `loop_stack`, and codegen
lowers them as entry-block allocas. Capturing a `loop` binder into a
lambda body is rejected at typecheck via
`CheckError::LoopBinderCapturedByLambda`. See
`docs/specs/2026-05-17-loop-recur.md`.
**`Literal`**:
@@ -2888,29 +2863,6 @@ What **is** supported (and used as the smoke test for the pipeline):
arg types (ctor) or the scrutinee's `Type::Con.args` (match). An
unresolved `Type::Var` reaching `llvm_type` is a hard error
rather than a silent fallback to `ptr`.
- **Local mutable state.** `(mut (var <name> <type> <init>) ... <body>)`
declares lexically-scoped mutable bindings whose types are restricted
to the stack-resident scalars `Int`, `Float`, `Bool`, `Unit`. Inside
the block, `(assign <name> <value>)` updates a mut-var; references
to a mut-var name resolve to a `load` at codegen. Mut-vars are
alloca-resident — the `alloca` instructions are hoisted to the fn's
entry block via a per-fn side buffer spliced after `lower_term` so
mem2reg eligibility holds regardless of how deeply nested the
originating `Term::Mut` block sits. Exercised end-to-end by
`examples/mut_counter.ail` (Int) and `examples/mut_sum_floats.ail`
(Float). Mut-vars do not escape the enclosing mut block and do not
introduce a `!Mut` effect onto the surrounding fn signature.
Sealed-by-construction: the spec restricts var element types to
non-RC-managed primitives and forbids first-class references, so
Decision 10's "no shared mutable refs" invariant is preserved (each
mut-var is uniquely held by its enclosing block). Future milestones
on the Stateful-islands path lift the type restriction (heap-RC-
managed Str + ADTs), add escaping references (`ref a` + `!Mut`
effect), and introduce composable transducers (`Stateful a b` +
`pipe`). Spec: `docs/specs/2026-05-15-mut-local.md`.
**Accumulator-over-iteration shape (documented idiom, not an enforced rule).** `mut`/`var`/`assign` removes friction from *straight-line* and *cascade-of-if* accumulators, but does **not** by itself express the "one running total updated once per loop iteration" shape: a `var` cannot be captured into a lambda (the seal) and there is no loop construct in the language. Until a future iteration-totality milestone lands (gated behind `Nat`/refinement types — see the deferred roadmap entry), the canonical pattern for the per-iteration accumulator is a tail-recursive helper that threads the running total as an explicit parameter; `examples/mut_counter.ail` is the reference. This is a documented authoring idiom, not a typecheck-enforced constraint — wrong code here does not fail to compile, it is merely less ergonomic, which is exactly why this is documentation and not a language rule.
Pipeline regression smoke tests:
- `examples/sum.ail.json` → prints 55 (recursion, arithmetic).
@@ -0,0 +1,63 @@
# iter remove-mut-var-assign.1 — atomic removal of `mut`/`var`/`assign`
**Date:** 2026-05-18
**Started from:** f355899fdf6071c0aeb7de182e93c08568ca743a
**Status:** DONE
**Tasks completed:** 6 of 6
## Summary
The local-mutable-state construct (`Term::Mut` / `Term::Assign` /
`struct MutVar`, the `mut`/`var`/`assign` Form-A keywords, the 4
`mut` `CheckError` variants, the `mut_scope_stack` synth threading,
the two `lower_term` arms) is removed from AILang entirely and
atomically — AST + surface + checker + codegen + DESIGN.md +
fixtures + drift trio + carve-out + roadmap cut together so the
schema is honest at every commit. `loop`/`recur` + `let`/`if` are
the surviving forms. The shared codegen alloca machinery survives
(loop needs it); its now-misnamed `mut_var_allocas` field is renamed
`binder_allocas` and the shared `Term::Lam` escape guard is
simplified to `!loop_stack.is_empty()` with the loop half kept
byte-equivalent. Removal is symmetric to the additive mut.1
introduction: serde is tag-based so no surviving module's
canonical-JSON hash moves; `loop`/`recur` codegen is byte-identical
(the rename is representation-only). Full `cargo test --workspace`
605/0; loop/recur non-regression hard gates all green
(55 / 500000500000 / infinite-compiles / the
`lambda_capturing_loop_binder` pin). The behaviour-preservation
proof is executable: `mut_counter`/`mut_sum_floats` still print `55`
after the faithful `let`/`if` rewrite. The "removal made executable"
gate is the new `mut_removed_pin.rs` (4 must-fail pins: `mut`/
`assign` keyword rejected, `{"t":"mut"}`/`{"t":"assign"}` serde
unknown-variant).
## Per-task notes
- remove-mut-var-assign.1.1: created `crates/ailang-surface/tests/mut_removed_pin.rs` — 4 RED→GREEN must-fail pins; harness compiled against the real `ailang_surface::parse` + `ailang_core::ast::Term` serde public API unchanged (no adaptation needed). RED-verified (4 fail with the exact expected messages), GREEN after T2.
- remove-mut-var-assign.1.2: the atomic Rust cut. Deleted `Term::Mut`/`Term::Assign`/`MutVar` from ast.rs; every exhaustive no-`_` `Term::Mut`/`Term::Assign` match arm across 17 source files; parse.rs dispatch + `parse_mut`/`parse_assign` + reservation + grammar EBNF (incl. the line-43 `| mut-term | assign-term` alternation that referenced the deleted productions); print.rs arms; the 4 `CheckError` variants + `code()`/`ctx()`; the synth arms + `Term::Var` mut-scope lookup; the `mut_scope_stack` param removed from `synth` + every internal/external/test caller; escape guard simplified to `loop_stack`-only; `lower_term` Mut/Assign arms deleted; `mut_var_allocas``binder_allocas` renamed at all surviving sites + lambda.rs save/restore. Dead `is_supported_mut_var_type` deleted. `cargo build --workspace` 0 errors + 4 Task-1 pins GREEN (the only T2 gate). NO `_ =>` wildcard introduced.
- remove-mut-var-assign.1.3: deleted `mut_typecheck_pin.rs` (whole file); dropped the 4 mut rows in `ct1_check_cli.rs` (kept the surviving loop-binder row, renamed the test + reworded doc honestly); dropped the mut/assign entries + match arms in the drift trio (`schema_coverage`, `design_schema_drift`, `spec_drift`).
- remove-mut-var-assign.1.4: DESIGN.md hard-delete — the `Term::Mut`/`Term::Assign` JSONC blocks, the mut post-schema paragraph (loop-binder half kept + reworded standalone, present-tense, points at the live loop-recur spec), the "Local mutable state" feature bullet, the accumulator-idiom paragraph. Clause-3 rationale ("iterated-mutable-state reasoning", 99/107) untouched. Zero construct residue (only `resolve names + assign hashes` survives — a hashing-pipeline verb, plan-allowed).
- remove-mut-var-assign.1.5: deleted the 3 rejection-probe fixtures; rewrote `mut.ail` (6 fns), `mut_counter.ail`/`mut_sum_floats.ail`, `mut-local_1..4` to the plan's exact faithful `let`/`if` shapes; present-tense docs, no nostalgia. Verified: `mut_counter`/`mut_sum_floats`→55, factorial→120, horner→18, `classify 22`→2, `has_small_factor 91`→true; zero `mut`/`var`/`assign` construct tokens remain.
- remove-mut-var-assign.1.6: `carve_out_inventory.rs` 18→12 (EXPECTED + header doc) AND deleted the 5 orphaned mut `.ail.json` carve-out files the test's disk-inventory assertion requires gone; roadmap false-foundation `**Progress.**` mut-local sub-bullet deleted (milestone header kept). FINAL full-suite green gate 605/0; loop/recur non-regression all green; `roundtrip_cli` PASS.
## Concerns
- DONE_WITH_CONCERNS (T2/T3/T6, recon-undercount class, `feedback_plan_pseudo_vs_reality` / `feedback_recon_undercount`): the plan's Step-7 line-list framed several sites as "delete the *argument* at the test-only synth callers", but those test fns' *bodies* construct deleted `Term::Mut`/`MutVar`/deleted-`CheckError` types — arg-trimming alone cannot make them compile. The faithful execution (identical rationale to Task-3's `mut_typecheck_pin.rs` whole-file deletion) was to delete the pure-mut-behaviour test fns wholesale: ~10 in-source `#[cfg(test)] mod tests` fns in `ailang-check/src/lib.rs` (mut_var_resolution_*, mut_block_*, assign_*, mut_typecheck_diagnostics_have_kebab_codes, mut_var_captured_by_lambda_*, lambda_inside_mut_*), the `design_md_documents_the_accumulator_over_iteration_idiom` test fn (pins deleted DESIGN.md prose), the 5 orphaned mut `.ail.json` carve-out files (Task 5 only deleted 1 of 6; the inventory test asserts disk==EXPECTED so all 6 must go), and the `Term::Mut`/`Term::Assign` arms in two non-enumerated test helpers (`ail/tests/codegen_import_map_fallback_pin.rs` — a hard E0599 the recon missed entirely; `ail/tests/e2e.rs` doc-comments). ~30 now-dangling doc-comments/comments that cross-referenced deleted symbols (`mirrors Term::Mut`, `MutVarCapturedByLambda`, the `loop_recur_typecheck_pin.rs` RED-state doc) were reworded so the source has zero trace/post-mortem (spec acceptance). The kept e2e bodies `mut_counter_prints_55`/`mut_sum_floats_prints_55` were preserved byte-identical per plan line-57's explicit "must stay green" directive — only their lying doc-comments were corrected. None of these are gratuitous; every one is strictly entailed by the atomic removal + the plan-stated final green gate. The class recurred enough (in-source tests, drift-pin test fn, orphaned carve-out files, non-enumerated test helpers) to be a planner-recon defect worth a follow-up countermeasure, not a per-iter fix.
## Known debt
- `crates/ailang-core/src/workspace.rs:1620` `tmp_dir` is a pre-existing `never used` test-helper warning, NOT introduced by this iter (it surfaces only in the test-target build; `cargo build --workspace` is 0 warnings). Out of scope (no surrounding cleanup); left untouched.
## Files touched
Source (17): ail/src/main.rs, ailang-check/src/{builtins,lib,lift,linearity,mono,pre_desugar_validation,reuse_shape,uniqueness}.rs, ailang-codegen/src/{escape,lambda,lib}.rs, ailang-core/src/{ast,desugar,workspace}.rs, ailang-prose/src/lib.rs, ailang-surface/src/{parse,print}.rs
Tests (modified): ailang-core/tests/{carve_out_inventory,design_schema_drift,schema_coverage,spec_drift}.rs, ail/tests/{ct1_check_cli,codegen_import_map_fallback_pin,e2e}.rs, ailang-check/tests/loop_recur_typecheck_pin.rs
Tests (created): ailang-surface/tests/mut_removed_pin.rs
Tests (deleted): ailang-check/tests/mut_typecheck_pin.rs
Docs: docs/DESIGN.md, docs/roadmap.md
Fixtures (rewritten): examples/mut.ail, examples/mut_counter.ail, examples/mut_sum_floats.ail, examples/fieldtest/mut-local_{1,2,3,4}*.ail
Fixtures (deleted, 8): examples/fieldtest/mut-local_{5,6}*.ail, examples/test_mut_{assign_out_of_scope,assign_outside_mut,assign_type_mismatch,nested_shadow_legal,var_captured_by_lambda,var_unsupported_type}.ail.json
## Stats
bench/orchestrator-stats/2026-05-18-iter-remove-mut-var-assign.1.json
+1
View File
@@ -88,3 +88,4 @@
- 2026-05-18 — fieldtest loop-recur (milestone CLOSE, clean on all milestone axes): post-audit downstream-LLM-author field test of the shipped loop/recur surface. The fieldtester (DESIGN.md + public examples only, never compiler source) wrote 3 real iterative programs — integer Newton sqrt (multi-binder, recur buried in if/let/if, loop result into let/seq), Collatz length (recur in match-arm tails), Euclidean gcd (loop as a value sub-expression in argument position) — plus 5 plausible-mistake negatives and 2 no-termination probes, all run through the public `ail` CLI. **0 bugs; 4 working findings, all on the milestone's own axes**: the five rejection diagnostics (recur-outside-loop / -arity / -type / -not-in-tail / loop-binder-captured-by-lambda) are point-exact AND self-fixing (name the fn, describe the fix, no false positives, no crash — the clause-2 silent-failure-class-becomes-compile-error claim made real); recur tail-position threads correctly through match-arm/let/outer-if (the spec only demonstrated `if` — generalises exactly as an author assumes); loop composes as a value sub-expression everywhere + every loop/recur fixture is `ail parse|render|parse` byte-identical (Roundtrip Invariant holds in practice); the no-termination boundary is exactly as specified (infinite loop check+build clean, zero spurious diagnostics — the precise difference from the reverted Iteration-discipline milestone). This empirically substantiates the milestone's "an LLM author can now write iterative programs with loop/recur" claim — the gate fieldtest exists to provide. Two ORTHOGONAL non-blocking findings surfaced incidentally while probing the no-termination/negative axes, neither in loop/recur scope, both routed to P2 todos (not a loop/recur tidy — refusing the scope creep): (spec_gap) niladic `(app f)` is unrepresentable in Form A and DESIGN.md's `Term::App args:[Term...]` states no minimum — INDEPENDENTLY re-confirms the existing mut-local-F3 roadmap todo (two fieldtests now hit it; the accept-`(app f)`-vs-ratify-DESIGN.md decision is a genuine design fork deliberately NOT auto-ratified under autonomous /boss — parked, priority-strengthened); (friction) the module-level `(doc …)` rejection lists valid def heads but omits that doc attaches inside fn/data — a one-line diagnostic-hint tidy. Independent Boss verification: `loop_recur_4_gcd_value_pos.ail` re-run → stdout `27`; `loop_recur_3a` re-run → `ail check` exit 1 `[recur-outside-loop] countdown: …`. **The standalone loop/recur milestone is fully ratified and CLOSED**: 3 iterations + a tidy shipped, audit clean (architect drift resolved, bench pristine carry-on), fieldtest clean on every milestone axis. Fixtures `examples/fieldtest/loop_recur_*.ail` + spec `docs/specs/2026-05-18-fieldtest-loop-recur.md` committed; roadmap P0 entry marked closed; the two orthogonal findings tracked as P2 todos → 2026-05-18-fieldtest-loop-recur.md
- 2026-05-18 — iter prose-loop-binders.1 (single-iteration milestone, DONE): Form-B prose `loop` now renders binders as a parenthesised init-list on the keyword line — `loop(acc = 0, i = 1) { … }` — instead of bare `name = init;` statements inside the body block, which had implied C/Rust re-init-every-iteration semantics. Projection-only single-arm rewrite of the `Term::Loop` case of `write_term` (`crates/ailang-prose/src/lib.rs`); init exprs render at `level` (keyword line, not the indented block), mirroring the untouched adjacent `Term::Recur` `enumerate()`+`", "` idiom — making the binder list positionally isomorphic to `recur(...)`, which teaches the correct "recur re-binds these positionally" model instead of obscuring it. RED-first via the existing stem-explicit `check_snapshot` harness: two new committed `examples/{loop_sum_to_run,loop_forever_build}.prose.txt` whole-file byte-equality fixtures + two `snapshot_loop_*` `#[test]` fns, verified `0 passed; 2 failed` against the old renderer before the arm rewrite turned them green. Loop/recur AST, Form-A, JSON-AST, typecheck, codegen and the Form-A↔JSON round-trip invariant untouched by construction (no signature change, no caller touched, prose is a one-way lossy projection off the round-trip path — spec §Architecture states this explicitly so milestone-close audit does not chase a phantom). Full `ailang-prose` suite 10/0; both `ail prose | diff` live-CLI byte-matches `MATCH`; post-cleanup porcelain exactly the expected paths. Surfaced from the 2026-05-18 user prose review of the just-closed loop/recur milestone; brainstorm spec `docs/specs/2026-05-18-prose-loop-binders.md` (Step-7.5 grounding-check PASS), plan `docs/plans/prose-loop-binders.1.md`. Both implement review phases first-pass, 0 re-loops. Milestone-close `audit` is the Boss's next pipeline step (projection-only, no Form-A surface change → no fieldtest). → 2026-05-18-iter-prose-loop-binders.1.md
- 2026-05-18 — audit prose-loop-binders (milestone CLOSE, clean / carry-on): milestone-close tidy for the single-iteration prose-loop-binders milestone (range `6cbd0fe..c9355d7`). Architect drift review **clean** — zero drift, zero debt, verified against the diff not journal trust: single `Term::Loop` `write_term` hunk in `crates/ailang-prose/src/lib.rs`, the two non-render `Term::Loop` sites + all `Term::Recur` arms absent from the diff, DESIGN.md/PROSE_ROUNDTRIP.md make no claim about prose `loop` rendering (so the spec's "no doc edit needed" is correct not a skipped obligation), no carve-out/hash-pin/round-trip lockstep bypassed (prose is off the Form-A↔JSON round-trip path by construction). Bench: `compile_check.py` 0/24 + `cross_lang.py` 0/25 both **exit 0** (cross_lang's independent `ail_bump_s` +1.50% ±15% ok corroborates check.py's bump_s firing is baseline-staleness not a real regression); `check.py` exit 1, first run 3 regressed but a confirmatory identical re-run (no code change) returned `latency.implicit_at_rc.p99_9_us` +37.30%→+10.22% **ok** and `.max_us` +114.14%→+22.17% **ok** (median/p99 within tol both runs) — the two latency-tail firings proven single-sample 5-run RC-mode jitter by re-run; `bench_list_sum.bump_s` persisted ~+12% = the already-tracked P2 roadmap todo (byte-oracle-proven environmental at the 2026-05-16 iteration-discipline-revert audit, causally impossible to attribute to a projection-only `ailang-prose` change). **All items carry-on**: no fix iteration, no baseline ratify — explicitly NOT bumping the `*.bump_s` baseline opportunistically inside an unrelated projection-only milestone (the P2 todo owns a holistic recapture; piecemeal bump is the "regression is expected" rationalisation the Iron Law forbids). Fieldtest **not applicable / not dispatched**: projection-only change to the Form-B *reading* surface; the fieldtester authors `.ail` Form-A and would not exercise the prose-render change (no friction/bug/spec-gap axis); the two whole-file byte-equality `.prose.txt` snapshots + live-CLI `ail prose|diff` checks are the projection-only E2E gate. Milestone **CLOSED clean**; roadmap P0 entry flipped `[~]``[x]`, WhatsNew.md user-facing entry appended (user present → no notify.sh fired per notification policy). → 2026-05-18-audit-prose-loop-binders.md
- 2026-05-18 — iter remove-mut-var-assign.1 (single terminal iteration, DONE): `mut`/`var`/`assign` removed from AILang entirely and atomically — `Term::Mut`/`Term::Assign`/`struct MutVar`, the three Form-A keywords + `parse_mut`/`parse_assign` + grammar EBNF, the 4 `mut` `CheckError` variants, the `mut_scope_stack` synth threading (param dropped from `synth` + every internal/external/test caller), the two `lower_term` arms, and every exhaustive no-`_` `Term::Mut`/`Term::Assign` match arm across 17 source files — cut in lockstep with DESIGN.md + fixtures + drift trio + carve-out + roadmap so the schema is honest at every commit (no `_ =>` wildcard introduced — Boss-verified). `loop`/`recur` + `let`/`if` are the surviving forms. Shared codegen alloca machinery survives (loop reuses it): `mut_var_allocas``binder_allocas` (representation-only, loop codegen byte-identical) and the shared `Term::Lam` escape guard simplified to `!loop_stack.is_empty()` with the loop half (`LoopBinderCapturedByLambda`) kept byte-equivalent. Feature-acceptance applied inverted (removed feature fails clause 2 redundant + clause 3 IS the iterated-mutable-state bug class). Behaviour-preservation proof executable: `mut_counter`/`mut_sum_floats` still print `55` after the faithful `let`/`if` rewrite (factorial→120, horner→18, classify 22→2, has_small_factor 91→true); the "removal made executable" gate is the new `mut_removed_pin.rs` (4 must-fail pins: `mut`/`assign` keyword rejected, `{"t":"mut"}`/`{"t":"assign"}` serde unknown-variant). Independent Boss verification: full `cargo test --workspace` **605/0**, zero residual mut symbols in any crate source, loop/recur non-regression all green (55 / 500000500000 / infinite-compiles / `lambda_capturing_loop_binder` pin), `roundtrip_cli` PASS. One systemic DONE_WITH_CONCERNS — 4th recurrence of the recon-undercount `feedback_plan_pseudo_vs_reality` class (in-source `mod tests` fns + a drift-pin fn + 5 orphaned mut `.ail.json` carve-outs + a non-enumerated `codegen_import_map_fallback_pin.rs` E0599 the recon missed; all resolved within implementer remit via Task-3's identical whole-file-deletion rationale, no behaviour change, e2e bodies preserved per plan) — worth a planner/recon-agent countermeasure, pairs with the existing loop-recur.tidy P2 todo. Milestone-close `audit` then `fieldtest` (surface-touching) are the Boss's next pipeline steps. spec `docs/specs/2026-05-18-remove-mut-var-assign.md` (grounding-check PASS), plan `docs/plans/remove-mut-var-assign.1.md` → 2026-05-18-iter-remove-mut-var-assign.1.md
+6 -13
View File
@@ -45,9 +45,12 @@ work progresses.
window, JSON tags fail closed as generic unknown-variant, codegen
alloca machinery kept (loop reuses it) + renamed `binder_allocas`,
shared `Term::Lam` escape guard keeps its `loop` half. Explicitly
decoupled from Stateful-islands (separate, deferred). Spec written
+ grounding-check PASS; awaiting user spec review → planner.
- context: `docs/specs/2026-05-18-remove-mut-var-assign.md`
decoupled from Stateful-islands (separate, deferred). Spec
approved + planned + implemented (605/0, loop/recur
non-regression green, removal made executable); milestone-close
`audit` then `fieldtest` (surface-touching) remain.
- context: `docs/specs/2026-05-18-remove-mut-var-assign.md`;
`docs/journals/2026-05-18-iter-remove-mut-var-assign.1.md`
- [x] **\[milestone\]** Prose `loop` binders — projection redesign —
CLOSED 2026-05-18. Form-B prose now renders loop binders as a
@@ -434,16 +437,6 @@ work progresses.
zero-allocation pipe combinator, growing tuple-state types as
pipelines lengthen).
**Progress.** Decomposed at brainstorm time (2026-05-15) into a
sequence of shippable sub-milestones. Sub-milestone 1 closed
2026-05-15: **mut-local** — sealed-by-construction `mut`/`var`/
`assign` blocks for Int/Float/Bool/Unit scalars, alloca-resident,
no escape, no `!Mut` effect leakage. Foundation in place;
fn signatures stay pure while LLM authors can write imperative
accumulators directly. Spec `docs/specs/2026-05-15-mut-local.md`;
iters mut.1/mut.2/mut.3/mut.4-tidy (commits 7b92719, b24718a,
03fb633, 20add51).
**Sub-milestone sequencing is UNDER RE-THINK with the user
(2026-05-16) — no further sub-milestone is planned or brainstormed
until that conversation happens.** Why: the 2026-05-15 decomposition