You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
afx spawn --resume with a live builder-start.sh lineage creates duplicate live agents in one worktree #1322
A wedged builder's claude PID was killed by the operator — the natural recovery move. Its parent .builder-start.sh loop (by design, for crash recovery) auto-respawned a fresh claude with the original spawn prompt, on the ORIGINAL terminal, unregistered with Tower. The operator's afx spawn <id> --resume then started a second, registered session. Both agents implemented the same plan phase simultaneously in one worktree until the registered one noticed foreign files appearing, reverted its own edits, and escalated. Resolution required a parent-first manual kill of the ghost lineage.
afx spawn --resume performs no liveness preflight: it consults registry state, not process reality, so it happily spawns into a worktree that already has a live (orphaned) lineage.
Asks
Preflight on --resume (and plain respawn): scan for live processes whose command line references .builders/<id>/.builder-start.sh (and/or a live shellper bound to that worktree). If found: refuse, name the PIDs, and print the recipe — afx interrupt <id> / afx reset <id> for a wedge, or parent-first kill for a true orphan. --force overrides with eyes open.
Make the loop operator-legible: when the child exits by signal, the relaunch banner should say what just happened and how to stop it ("child killed; relaunching per crash policy — to stop this loop, kill THIS script's PID first, or use afx interrupt/reset").
Reported by a production workspace architect with full PID/timestamp forensics; their builder's collision behavior (detect foreign edits → revert own → escalate) is the model response and worth encoding in builder guidance.
Additional fallout from the same incident: porch state corruption (last-writer-wins clobber)
Follow-up forensics from the reporting workspace: after the ghost lineage was killed, the surviving session's porch state showed a regression — phase criteria reset to "not yet run", porch done re-entering an earlier state, and the iteration counter rolled back from the reached 3 to 2 (porch next re-emitting "Fix issues from iteration 1").
Probable mechanism: both agents drove porch in the same worktree pre-kill, and their status.yaml writes interleaved — porch's read-modify-write has no concurrency guard because "one builder per worktree" is a spawn-time invariant, not a runtime-enforced one. The duplicate-agent condition breaks that invariant, so the collision corrupts not just working files (which the agents can see and revert) but porch state, which neither agent can detect as corrupted — it just looks like earlier state.
Strengthens ask 1 (preflight refusal while a sibling lineage is alive): the blast radius is state-machine integrity, not merely duplicated edits. The reporting workspace resolved correctly by running the machine forward (re-running the review cycle, no state edits) per the #1297 sync-the-work-through-the-machine principle.
Enforcement-vs-doctrine datum for the preflight's scope
A production lane reports it was structurally immune to all three porch-state failure classes (#1235 wrong-winner resolution, #1306 stale merged snapshots, this issue's invisible rewind) before any were diagnosed, because its operating discipline is artifact-first: gates read diffs and receipts, stall monitors watch commits and file mtimes, and porch phase values are treated as labels only — never as ground truth to act on.
Implication for scoping: artifact-first operation is a zero-tooling-cost mitigation that already exists in the field, so the preflight doesn't need to carry every guarantee alone — some of the protection can be doctrine. The limit, stated by the reporting architect themselves: doctrine does not protect operators who have not read it, and the invisible-rewind class (state corruption that just looks like earlier state) still warrants runtime detection. Suggested split: preflight/refusal = runtime enforcement (this issue's ask 1); "phase values are labels, anchor decisions on artifacts" = a lessons-ledger entry routed at the next MAINTAIN pass.
Activity
waleedkadous commentedon Aug 1, 2026
Additional fallout from the same incident: porch state corruption (last-writer-wins clobber)
Follow-up forensics from the reporting workspace: after the ghost lineage was killed, the surviving session's porch state showed a regression — phase criteria reset to "not yet run",
porch donere-entering an earlier state, and the iteration counter rolled back from the reached 3 to 2 (porch nextre-emitting "Fix issues from iteration 1").Probable mechanism: both agents drove porch in the same worktree pre-kill, and their
status.yamlwrites interleaved — porch's read-modify-write has no concurrency guard because "one builder per worktree" is a spawn-time invariant, not a runtime-enforced one. The duplicate-agent condition breaks that invariant, so the collision corrupts not just working files (which the agents can see and revert) but porch state, which neither agent can detect as corrupted — it just looks like earlier state.Strengthens ask 1 (preflight refusal while a sibling lineage is alive): the blast radius is state-machine integrity, not merely duplicated edits. The reporting workspace resolved correctly by running the machine forward (re-running the review cycle, no state edits) per the #1297 sync-the-work-through-the-machine principle.
waleedkadous commentedon Aug 1, 2026
Enforcement-vs-doctrine datum for the preflight's scope
A production lane reports it was structurally immune to all three porch-state failure classes (#1235 wrong-winner resolution, #1306 stale merged snapshots, this issue's invisible rewind) before any were diagnosed, because its operating discipline is artifact-first: gates read diffs and receipts, stall monitors watch commits and file mtimes, and porch phase values are treated as labels only — never as ground truth to act on.
Implication for scoping: artifact-first operation is a zero-tooling-cost mitigation that already exists in the field, so the preflight doesn't need to carry every guarantee alone — some of the protection can be doctrine. The limit, stated by the reporting architect themselves: doctrine does not protect operators who have not read it, and the invisible-rewind class (state corruption that just looks like earlier state) still warrants runtime detection. Suggested split: preflight/refusal = runtime enforcement (this issue's ask 1); "phase values are labels, anchor decisions on artifacts" = a lessons-ledger entry routed at the next MAINTAIN pass.