50%

S04 Cutover I - pure move/borrow/liveness family

Goal

The ~8 pure move/borrow/liveness scans are retired; their shapes emit correctly from the class ledger; each retirement is a KEPT ledger cycle decrementing the collapse metric.

Family (verified module list)

borrowed.rs, dead_param.rs, live_out.rs, branch_release.rs, reassign_release(.rs+/), forwarder.rs, forwarder_release.rs, loop_invariant_dead_local.rs under compiler_repo/compiler/ori_arc/src/lower/burden_lower/ownership_scans/. (The dup/store/call-arg + move_alias scans are same-allocation-entangled and belong to S05 per reviewer refinement.)

Work Items

  • Cut the pure move/borrow/liveness family to the ledger emitter: borrowed.rs, dead_param.rs, live_out.rs, branch_release.rs, reassign_release, forwarder.rs, forwarder_release.rs, loop_invariant_dead_local.rs consumers replaced by class-ledger events
  • Family matrix per tests.md (types x patterns: str/[int]/Option/closures/structs x full/break/branch/dead-param/forwarder-ttr-chain) + >=1 semantic pin per retired scan behavior + negative pins (no dec past merge; no inc-only split)
  • Gated burden-sole probe zero-new vs baseline_failing_ids.txt (debug AND release); full ./test-all.sh green; cross-executor behavior, exact logical ownership/drop/unwind-event, and leak legs

Governing rules (embedded per plan self-containment)

  • Calculus-first per arc.md CP-1: the governing theorem lands in compiled Lean BEFORE the consumer ships; never weaken a theorem to pass a shape; four-surface sync per CP-2 on every AIMS change.
  • Verdict surface until S08 lands: the gated burden-sole probe ORI_DISABLE_PREDICATE_STACK_RC=1 ORI_VERIFY_ARC=1 ORI_VERIFY_EACH=1 via compiler_repo/diagnostics/aot-guardrail.sh --floor (debug AND release), zero-new vs compiler_repo/compiler/ori_llvm/tests/aot/fixtures/corpus_under_flag_gate/baseline_failing_ids.txt. The default path is FALSE-GREEN until S08 (per arc.md STOP); never cite it as an RC verdict.
  • test-all is the compiler-state SSOT: every section-close verdict comes from a full ./test-all.sh run (background, flock-serialized; consume test-all-summary.json), never targeted runs alone.
  • Banned cures (each a carried dead-end): use-count/type-membership PROXY discriminators (DE-150/174/175); broadening compute_genuine_same_alloc_reps / ForwarderUnionFind phi edges (DE-181); relocating a release past a multi-pred merge (DE-225/243/249/250); inc-only pair splits (attempt-287 290-UAF class); broad caller-side keep-alive incs (DE-254).
  • Every retired scan/toggle records a KEPT ledger cycle with before/after teeth (collapse metric + floor).
  • Cross-executor parity over evaluator, VM, LLVM debug/release/AOT, and admitted native/direct-WASM/JIT projections, plus exact logical ownership/drop/unwind-event correspondence and leak freedom, are per-section backend legs (L9 + L10). The AOT burden probe remains a current LLVM-projection migration gate, not the definition of AIMS correctness.

Test strategy

  • Matrix rows: str / [int] / Option / closures / structs x full-iteration / break / branch-dead / dead-param / forwarder-ttr-chain.
  • Pins: one semantic pin per retired scan’s cured shape (the shape stays green with the scan DELETED); negative pins per carried dead-ends (no proxy discriminator, no relocation past merge).
  • Cut the pure move/borrow/liveness family to the ledger emitter as ONE joint cutover covering BOTH Phase-5 dispatchers: scan_orchestration::emit_burden_ops (emission-assembly) AND scan_helpers::compute_owned_rc_filter (suppression-filter prologue, runs first, hands emit_burden_ops a settled OwnedRcFilter struct). dead_param.rs, live_out.rs, branch_release.rs, reassign_release, and loop_invariant_dead_local.rs wire into emit_burden_ops only; forwarder_release.rs wires into compute_owned_rc_filter only; borrowed.rs and forwarder.rs EACH split their own functions across both dispatchers and must be retired as one joint Phase-A+Phase-B cutover per file — verify every threaded OwnedRcFilter struct field (forwarder_identity_transparent_aliases, forwarder_result_releases, claimed_no_sink_vars, owned_vars_needing_rc, borrowed_aliases, construct_fed_dead_param) against scan_helpers.rs:101-233 before removing either dispatcher’s call site for either file, so neither is left half-migrated with an orphaned dispatcher-half.
  • Family matrix per tests.md (types x patterns: str/[int]/Option/closures/structs x full/break/branch/dead-param/forwarder-ttr-chain/loop-invariant-heap-local) + >=1 semantic pin per retired scan behavior + negative pins (no dec past merge; no inc-only split). The loop-invariant-heap-local pattern row is NEW coverage, not preservation of existing coverage: compute_loop_invariant_dead_local_releases (loop_invariant_dead_local.rs) has zero dedicated unit or integration tests today (no tests.rs submodule, zero hits for the file or function name in lower/burden_lower/tests.rs or ownership_scans/tests.rs) despite being the family’s single highest-churn function — author its walking-skeleton and matrix rows, and get them green, BEFORE w-7e09ea0f deletes the scan module and its two ORI_DISABLE_* toggles, so the retirement has a regression floor and a bisection fallback to prove against instead of neither.

Items

  • Fold in BUG-04-180 (high), BUG-04-183 (medium), BUG-04-186 (high), and BUG-04-241 (high) per the plan mission’s standing directive (‘Fold-in: every failing burden/RC floor cell surfacing during this plan is worked HERE’). All four are verified open in bug-tracker/open-bugs.json with no scaffolded plan dir, and sit in S04’s exact RL-2/RL-4/RL-5 failure-mode space: BUG-04-180’s title (‘loop-invariant heap locals into a DEAD loop-exit/merge block-param the RL-5 dead-param scans cannot resolve’) is a near-verbatim restatement of loop_invariant_dead_local.rs’s own doc comment; BUG-04-183 and BUG-04-241 are RL-5/RL-2 dead-param and dead-binding gaps in dead_param.rs/live_out.rs/borrowed.rs territory; BUG-04-186 names ‘the dup-funded/branch-exclusive event model’ — branch_release.rs’s own territory plus S05’s dup-funded model — as its own blocker. None of the four appear in S09’s 8-bug folded list (BUG-04-145/-164/-167/-175/-181/-204/-224/-238, fixed at plan creation before BUG-04-241 was even filed) or anywhere else in this plan. Read all four before finalizing the ledger-emitter consumer wiring (w-d3b8ee7d); treat each as a kill-criterion pin folded into w-ca013cd3’s matrix. For BUG-04-180, BUG-04-183, and BUG-04-241 (within S04’s own borrowed/dead_param/live_out/loop_invariant_dead_local scope): verify the repro against the new class-ledger emission and close via the bug_tracker atomic flip API citing this plan once green. For BUG-04-186 (also needs S05’s dup-funded model per its own title): verify S04’s branch-exclusive-event half of the repro and record the residual S05 cross-section dependency explicitly rather than closing prematurely. None may stay silently open through the cutover with no recorded disposition.
  • Schema-reconciliation entry: this work_item id existed in plan.json with no corresponding line in this section’s body (an orphan-in-json violation) and no recoverable original intent anywhere in the plan corpus (no ledger entry, no HISTORY reference, no status.md derivation naming distinct scope). Recorded here to satisfy the id-reference contract; retired via abandon-work-item with a matching reason.
  • Fold in BUG-04-180 (high), BUG-04-183 (medium), BUG-04-186 (high), and BUG-04-241 (high) per the plan mission’s standing directive (‘Fold-in: every failing burden/RC floor cell surfacing during this plan is worked HERE’). All four are verified open in bug-tracker/open-bugs.json with no scaffolded plan dir, and sit in S04’s exact RL-2/RL-4/RL-5 failure-mode space: BUG-04-180’s title (‘loop-invariant heap locals into a DEAD loop-exit/merge block-param the RL-5 dead-param scans cannot resolve’) is a near-verbatim restatement of loop_invariant_dead_local.rs’s own doc comment; BUG-04-183 and BUG-04-241 are RL-5/RL-2 dead-param and dead-binding gaps in dead_param.rs/live_out.rs/borrowed.rs territory; BUG-04-186 names ‘the dup-funded/branch-exclusive event model’ — branch_release.rs’s own territory plus S05’s dup-funded model — as its own blocker. None of the four appear in S09’s 8-bug folded list (BUG-04-145/-164/-167/-175/-181/-204/-224/-238, fixed at plan creation before BUG-04-241 was even filed) or anywhere else in this plan. Read all four before finalizing the ledger-emitter consumer wiring (w-481e6f7c); treat each as a kill-criterion pin folded into w-6c58d672’s matrix. For BUG-04-180, BUG-04-183, and BUG-04-241 (within S04’s own borrowed/dead_param/live_out/loop_invariant_dead_local scope): verify the repro against the new class-ledger emission and close via the bug_tracker atomic flip API citing this plan once green. For BUG-04-186 (also needs S05’s dup-funded model per its own title): verify S04’s branch-exclusive-event half of the repro and record the residual S05 cross-section dependency explicitly rather than closing prematurely. None may stay silently open through the cutover with no recorded disposition.

Fresh intel (regenerated)

The ground truth flipped since this section was planned: the class-ledger emitter S04 cuts over to is no longer a future deliverable — it is LIVE at pipeline Step 4b (aims_pipeline/mod.rs:269 -> class_ledger::pipeline_step_4b), gated per-function, and its first checked-in differential report (compiler_repo/build/class-ledger-diff.json, 2026-07-06T15:01Z) says: 15 programs compared, 0 divergent, 0 build failures — and 0 functions replaced. Every RC-carrying function in the sample falls back to the legacy path, ranked by fail-closed reason: readiness-not-clean (19 functions) > zero-classes (14) > op-var-placement (1). S04’s real work is therefore NOT “delete 8 files” — it is drive the readiness-not-clean verdicts to Clean on exactly the pure move/borrow/liveness shapes those 8 files special-case, until attempt_replacement actually fires, and only THEN retire the scans. The five-clause replacement gate is explicit in source (replace.rs:gate_rejection): legacy-emission-disabled / zero-classes / readiness-not-clean / user-drop-glue / op-var-placement; the per-class verdict lattice is Clean | LeakOnly | Unprovable (verify.rs:ClassVerdict) and the decline lattice is NonConverged | MergeDisagree | UnplaceableRelease | UnresolvedOpVar (emit.rs:DeclineReason). [JOIN] the section’s own success shape (“shapes emit correctly from the class ledger”) is now a MEASURABLE number: replaced_functions in the diff-harness summary going from 0 to covering the family’s shapes, with divergent staying 0.

Everything else the prior recon found still holds, re-verified fresh at this HEAD:

  1. Two dispatchers, not one. emit_burden_ops (scan_orchestration.rs, emission-assembly) and compute_owned_rc_filter (scan_helpers.rs:101, suppression-filter prologue returning the settled OwnedRcFilter struct at scan_helpers.rs:51) BOTH consume S04’s family: 6 files wire into emit_burden_ops only; borrowed.rs and forwarder.rs each split their own functions across BOTH (graph edges re-verified this sync: 16 resolved dispatcher->family edges + 1 graph-invisible call at scan_helpers.rs:541). A per-file deletion that touches only emit_burden_ops’s call list leaves the filter-phase half live and orphaned.
  2. loop_invariant_dead_local.rs is STILL the family’s concentrated risk: highest churn (hotspot 81.97, 1.21x the #2 file), STILL zero test hits in either shared harness (re-grepped this session: 0 matches), and its walking-skeleton work item w-6c58d672 is still not-started. The pins MUST land before w-7e09ea0f deletes anything.
  3. All four folded bugs are still open (BUG-04-180 high / BUG-04-183 medium / BUG-04-186 high / BUG-04-241 high — re-verified in open-bugs.json); they are S04’s kill-criterion pins per its own w-98394977.
  4. Toggle deletion spans TWO code homes plus two doc homes: 8 of the 12 family toggles are read inside the scan files themselves, but 4 (DEAD_FORWARDER_PARAM_RELEASE, DEAD_OWNED_PARAM_BRANCH_RELEASE, FORWARDER_IDENTITY_ALIAS_DEDUP, FORWARDER_RESULT_RELEASE) are read in lower/burden_lower/mod.rs (verified line numbers §2.1); all 12 have oric/src/debug_flags.rs registrations and 10 of 12 have arc.md §Debugging prose entries. w-7e09ea0f’s same-commit deletion must sweep all four surfaces.

Decisions this package forces on the in-flight work:

  1. Let the diff harness set the cutover order. Run diagnostics/class-ledger-diff.sh --family <shape> per family shape; the ranked fallback-reason table IS the worklist (its own header says so). readiness-not-clean dominating means the FIRST leverage is in events.rs/emit.rs/verify.rs classification-and-planning completeness for these shapes — not in touching the old scans at all (§3D).
  2. Retire borrowed.rs and forwarder.rs as one joint Phase-A+Phase-B cutover; verify every threaded OwnedRcFilter field against scan_helpers.rs:51-101 before removing either dispatcher’s call site (§2.2, §3.1). Note OwnedRcFilter.construct_fed_dead_param is S05-owned lineage data riding the SAME struct — S04 removes its own fields only, never the struct (§4.1).
  3. Author loop_invariant_dead_local.rs’s walking-skeleton + matrix rows (w-6c58d672) BEFORE any deletion — it is untested, highest-churn, and has no plan history predating this section (§3.7, §3.8, §6).
  4. Treat BUG-04-180/183/186/241 as live kill-criteria for the replacement classifier: close via the atomic flip API on green, or record the explicit S05 residual for BUG-04-186 (§4.2, §7.3 move 5).
  5. Ground the Stage-B EventKind classification (Birth | Credit | Consume | Read | Mutate — now REAL code in events.rs:16) against the FULL RL-2 twelve-kind table, not the 8-scan union — the Swift PR#32130 precedent (narrow use-set validation = unsound conversion) and its unreachable-path nuance map directly onto ClassVerdict’s Return/Resume/Unreachable terminal netting (§7.2, §7.3).
  6. Never broaden the constrained symbols below; never absorb loop_carried_dead_param/ (S06’s, at 0.87-0.90 semantic proximity) or construct_fed.rs (S05’s, at 0.88) despite embedding-level near-identity (§3.2).
["ForwarderUnionFind", "ssa_alias_classes::UnionFind", "compute_genuine_same_alloc_reps", "compute_loop_closure_dead_param_lineage", "compute_loop_carried_dead_collection_param_lineage", "apply_sibling_moved_field_union_with", "PerFieldReleaseAccounting", "SoleOwnerFieldOriginCertificate", "PerFieldSameAllocUnionFind", "PostDomCascadeRelocation"]

(All live entries re-verified present at HEAD: ForwarderUnionFind at ownership_scans/union_find.rs:21, compute_genuine_same_alloc_reps at aims/intraprocedural/project_aliases.rs:225, the two loop-carried lineage fns at loop_carried_dead_param/mod.rs:183/315. The last four are DO-NOT-RE-TRY dead-end design names from the aims-burden ledger, retained as known-dead-ends.)

DIG DEEPER

python -m scripts.plan_corpus.read plans/aims-provenance-ledger
python3 -c "import json; d=json.load(open('compiler_repo/build/class-ledger-diff.json')); print(json.dumps(d['summary'],indent=1))"
sed -n '100,150p' compiler_repo/compiler/ori_arc/src/aims/class_ledger/replace.rs   # the five-clause gate
sed -n '40,101p' compiler_repo/compiler/ori_arc/src/lower/burden_lower/scan_helpers.rs

(full dossier: cutover-move-borrow—s-72083ddf.intel.md)