Repository navigation
loop: dispatching lanes without arming a wake leaves every lane stalled after it opens its PR #192
Description
Problem
cxc-loop tells a main session how to fan work out into parallel worktree threads, and says nothing about how that main session is supposed to wake up afterwards. The result in practice is that every lane does its work, opens its PR, goes idle, and then nothing happens — because the only thing that would have driven the next step was the main session, which had already ended its turn.
This happened in full today and the user had to be the one to notice. Five lanes were dispatched for OpenCodex issues #4991, #4997, #5008, #5060 and #5066. Within the hour all five had pushed branches and opened PRs (#5077 through #5082). Then everything stopped for roughly five hours. The PRs sat with cancelled legs nobody rrran, reviews nobody read, and a stacked child whose parent was never merged. The user's message was "지금 다 멈췄어".
The cause was mundane: a heartbeat automation had been deleted at the end of a previous round, on explicit request, and the new dispatch never armed one. Nothing in the loop skill connects those two facts. references/waiting.md covers waiting on dispatched work inside a turn, and references/runtime-lifecycle.md covers HOTL continuation, but neither says that dispatching N threads whose results arrive minutes-to-hours later requires an out-of-band wake, nor that deleting the previous round's wake leaves the next round with none.
Why the existing mechanisms do not cover it
wait_threadsworks inside one turn. A lane that finishes forty minutes later arrives after the turn has ended.- Stop-continuation keeps a turn alive; it does not bring a session back once the turn is genuinely over.
- The heartbeat is the only mechanism that spans turns, and it is currently treated as a user-facing convenience rather than as part of the dispatch contract.
Proposal
Make the wake part of dispatch rather than a separate decision.
- In
cxc-loop, state that dispatching work whose completion is not observable within the current turn requires an armed wake before the turn ends, and that the absence of one is a preflight failure to report rather than something to discover later. - Say explicitly that deleting a wake ends the loop it was driving. A request to stop notifications for round N is not a decision about round N+1, and the next dispatch has to arm its own.
- Give the wake prompt a minimum shape: the lane-to-PR map, the evidence command, the merge-ownership rule, and a quiet-by-default notification policy. A heartbeat whose prompt is "check on things" produces noise; one that carries the procedure produces merges. The prompt used to recover today's round is a workable starting point.
- Where the host exposes no wake mechanism, say so as a limitation and keep the work inside one turn, rather than dispatching and hoping.
Evidence
- Five lanes dispatched, six PRs opened, zero progress for about five hours until the user intervened: fix(providers): stop the Antigravity rename migration re-announcing itself on every boot opencodex#5077 test(ci): give the Windows nested live-lock case its own lock owner opencodex#5078 diag(google): describe the Antigravity wire request without its contents (#5008) opencodex#5079 fix(tests): judge cold-spawn warm-up registration structurally opencodex#5080 test(ci): split phase timing for the four #4997 budget overruns opencodex#5081 test(ci): move fixture and host-probe cost out of the #4997 measured windows opencodex#5082
- Once a 40-minute heartbeat carrying the procedure was armed, the same backlog cleared in two heartbeat runs: all six lane PRs plus six contributor PRs merged, four issues closed with evidence.
- Related but distinct: docs(workflow): make parallel worktree lanes mergeable across Codex tasks #184 covers lane manifests and merge ownership across tasks. This issue is about the wake that makes any of that reachable after the dispatching turn ends.
Activity