Research — consolidation-claim-rollback-integrity-01M3PD1T
Grounded on main e4a1a55c by a four-lens squad (code-truth with real-CLI repros, similar-issues, campsite, architecture) and a post-spec sizing lens. Findings dispositions below.
D1 — Where to act on a claim-time refusal (#5338)
- Decision: at the end of
_capture_reconciliation_claim, inside_clear_fresh_record_on_pre_mutation_exit, through a new pureclaim_integrity_refusal(claim)inreconciliation.py(same checks as gate steps 1–3). Exempt_resume_reconciliation_already_passed. - Rationale: no phase reordering (frozen-order pin), fresh record auto-cleared, one predicate for claim and gate (single authority).
verify()is rewired to the predicate only after #5359 lands (it is #5359-owned). - Alternatives: a new phase before
_phase_merge_lanes(rejected: breaks the frozen phase list, edits a #5359 function); duplicating_refusal_reason(rejected: second authority). - Evidence: code-truth repro B — main builds a REFUSE claim on
--resumeafter a lane branch is deleted, nothing acts on it, and the gate REFUSE leaves the target at the done-bookkeeping commit.
D2 — Rollback mechanism (#5318) — operator DM 01M3PD3VP1YTQ4D17HT96JA0T2
- Decision: CAS ref restore of every snapshotted branch via one authority (
consolidation/rollback.py), supersedes AC-B3 revert-only on this path; amend ADR 2026-09-19-1. - Rationale: a forward
git revertleaves lane tips ancestors of the mission/coordination branch, so the next run's first-parent authored range stays empty and #5318 persists (campsite finding 4). CAS restore returns the branch to the exact pre-run commit. - Alternatives: revert + durable anchor surviving
--abort(rejected by operator).
D3 — CAS expected value (post-spec finding 2; post-plan split-brain fold)
- Decision: persist
post_mutation_refsafter every mutating phase and immediately before the gate; that is the CAS expected value for both the in-process rollback and a later--abort. A branch with no recorded post tip that differs from its snapshot is NOT_RESTORED — never guessed. - Rejected: "snapshot is-ancestor-of current" fallback — it accepts a third party's later commit (e.g. an operator's own commit on the target between the failed run and
--abort) and overwrites it. - Rationale: reading the live tip as "expected" (today's
_rollback_target_after_failed_reconciliation) makes CAS vacuous against a concurrent move (SC-005 would pass without testing anything).
D4 — Verified-landing guard (post-spec finding 3) — FR-011
- Decision: the authority refuses (retains everything) when
reconciliation_passed_target_shaequals the current target tip, or any snapshotted branch no longer resolves. - Rationale:
--abortafter a crash mid-teardown, or a projection refusal on the resume short-circuit, would otherwise revert a verified landing whose lane branches may already be deleted — destroying the only copy of approved work. This replaces FOLD-5's blanket "never roll back a projection refusal" with a precise guard.
D5 — Hook point for the gate phase (post-spec finding 5)
- Decision: wrap the single
_phase_reconcile_before_teardown(run)call in the driver (except typer.Exitwith non-zero code → rollback → re-raise). One call-site edit in a #5359-touched function; the gate body is not edited. - Alternatives: replace the rollback call inside the gate body (squad proposal; rejected for main: the gate body is #5359-rewritten and on main a REFUSE never reaches that call); a context manager around the whole mutation span (rejected: re-indents a #5359 function, widens the invariant to exits that already have rollbacks — R3).
- Consequence: WP work does not wait on #5359 merging; expect one textual rebase conflict.
D6 — #5296 direction — operator DM 01M3PJWGGKTRT9W03MJHFV44Q2 (supersedes DM 01M3PD41NQ6J6EX8V2HYDDRGPZ)
- Decision: skip the code-lane merge and waive code-lane ancestry when the planning lane's worktree is the repository root checkout on the target branch.
- Rationale: refusing deadlocks —
_assert_mission_terminal_ready(executor.py:501-550) requires every WP (incl. the planning WP) approved before consolidation, and there is no per-lane consolidate (post-spec finding 1). - Alternatives: refuse at finalize-tasks (strands existing missions); gate attributes pre-landed content (green-washes a bypass of the attribution window).
D7 — Scope boundary (post-spec finding 4)
- In scope: claim refusal, gate FAIL/REFUSE, projection refusal,
--abort. Out: post-PASS exits (push, teardown — resumable) and the other in-phase exits with their own rollbacks (residual R3, follow-up under #5001). Residuals R1/R2 (PR #5285/#5305) out.
Deferred (named, not ticketed)
- Fold
_reset_coord_to_checkpoint(revert),_revert_orphan_target_bake_commit, byte restores and the target-only_rollback_target_after_failed_reconciliationinto the single authority; retire the liverun.pre_mutation_coord_*twin.
D8 — Post-plan squad folds
- Single capture:
capture_pre_mutation_snapshotis the only writer; legacy anchors are projections (split-brain fold). - Census:
git/ref_advance.pyis qualname-keyed intest_destructive_op_routing.py, not blanket-exempt → extract_resync_checkouts, re-key the allowlist entry. - Residue: resync passes
is_residue=is_toolchain_generated_churn. _dispatch_abortcomplexity: extract a helper (local ruff ignores C901 for consolidate.py; Sonar does not).- #5296 waiver lives only in
_approved_dependency_lane_refs; discriminator is root-checkout HEAD == target branch (every planning lane resolves to repo_root). - #5332 repro requires coordination topology (projection refusal unreachable in LANES).
- Citation fix: "AC-B3" is the no-raw-update-ref ratchet (honoured); the revert-only rule lives in ADR 2026-09-19-1 "unify, don't fork" and
_reset_coord_to_checkpoint's docstring. - New residual (R3 family, filed as #5385): LANES mission targeting a protected branch — uncaught
BookkeepingPolicyRefusedafter the squash leaves the target advanced. --dry-runnever forecasts a claim refusal today; surfacing it needs a dry-run JSON contract change and overlaps #5329 → follow-up note only.