Repository navigation
Description
Summary
A task can be requeued while a claimant-bound session is still live or while a
dead binding still exists, and nothing clears progress.session_id:
- Launch race (live, task 7787). The claim lease expired 2026-11-03T15:31:38.44Z.
The 09:20 CT claimant fire — using a poll snapshot taken before expiry —
created and bound sessionses_e5e5df699f1ffeTb5Z3gWDNsYIfor it at
15:31:40.62Z, two seconds after expiry. The task row is now
status=queued,worker_id=null,lease_until=null, while that session is
LIVE inGET /api/session/activeand executing the task. - Stale bindings on requeued rows. Of 36
queuedtasks that still carry a
boundsession_id, 33 have a terminal session, 2 a marker-less stale one, and
1 (7787) a live one. The claim was released; the binding was not.
Impact
- Double execution. A
queuedtask is claimable by any lane. For 7787 the
next claim executes the same issue alongside the live session. The atomic bind
in migration 0021 cannot prevent this: binding runs at launch, and by then
the task is no longer claimed, sobindraises no conflict at all. - Orphan sessions that can never bind.
bind_repo_task_lane_sessionrefuses
to overwrite a different existing binding. So a relaunch of any of the 35
dead-session queued rows would create a session whose bind is rejected —
exactly the Oct-28 unbound-launch defect Move opencode executions to laptop-side scheduled tasks — retire the Muse boot handoff #3026 exists to close, reached by the
opposite path. - The claimant's step 4a re-verify does not catch facet 1 unless it re-reads the
ledger in the same instant as the create; the lease can die between poll and
POST /api/session.
Reproduction / current state
Read-only snapshot from the 2026-11-03 09:30 CT claimant fire
(~/.local/state/opencode-claimant/fires.jsonl):
task 7787 | repo-health-code-pilot | status=queued | stage=launched
| session_id=ses_e5e5df699f1ffeTb5Z3gWDNsYI
| launched_by=opencode-claimant | worker_id=null | lease=null
| stage_at=2026-11-03T15:31:40.617952Z | attempts=3
session ses_e5e5df699f1ffeTb5Z3gWDNsYI -> in GET /api/session/active (LIVE)
Ledger query for facet 2:
GET tasks?status=eq.queued&progress->>session_id=not.is.null -> 36 rows
per-id session read: terminal 33 | markerless-stale 2 | LIVE 1
State at fire close (~15:40Z)
The bound session noticed its own claim had lapsed and re-claimed the task
itself (status=claimed, worker_id=repo-worker-code, stage verified), so
no second executor appeared and no duplicate work occurred. But that repair was
ad-hoc, taken by a worker whose prompt said the task was "already claimed", and
it was only possible because a worker may claim. For ~6.5 minutes (15:31:38 →
~15:38) the task was queued and claimable by any lane while a live session
was already executing it. Avoid the exposure window, not the repair.
Suggested direction (not prescriptive)
- Requeue/reap must disposition the binding, not just the claim: clear
progress.session_id+launched_bywhen the bound session is terminal and
the task returns toqueued, record the old id in history, and leavebind
free to overwrite. - Claimant launch must re-assert the claim under the launch, e.g. heartbeat
or claim-guard the row in the same transaction/step as create+bind, and treat
"lease lost between poll and create" as a skip (no session created). - A queued task with a live bound session should be unclaimable (claim should
refuse, or the row should carry abound_liveguard). - Task 7787's live session needs a decision from the ledger backstop: adopt it
(re-claim to the worker) or let it finish without any lane claiming the row.
Scope note
Found during supervision of #3026 slice 1; the claimant correctly launched and
mutated nothing this fire (cap saturated, 14 active roots vs cap 12). Related:
#4757 (session-list truncation), #4758 (bound session with no prompt, stuck
claimed), #4912 (session contract design), #3026 (slice 1).
Acceptance criteria
- When a task is requeued (lease expiry, park expiry, or lane release), any
progress.session_idbound to a previous claim is cleared or superseded in the same transition, so a queued row never carries a live or stale binding. - A relaunch of a previously bound task can bind its new session (the 0021 bind no longer refuses because of a dead predecessor binding).
- The launch race is closed: a session created from a pre-expiry poll snapshot cannot end up executing a task that has already been requeued — the claimant re-verifies the claim at bind time and aborts/rebinds on mismatch.
- The 36 queued rows carrying bound session_ids measured at filing are reconciled (bindings cleared or verified live-and-owned).
Activity
developing-today commentedon Nov 3, 2026
agent-note:v1
state: reviewed (coverage loop-back)
Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 8570 (triage, queued, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
Found via: coverage loop-back 2026-11-03
dezren39 commentedon Nov 3, 2026
Intake review
Fleet monitor intake (Duty 2), 2026-11-03. (Posted on a delayed retry: the original comment attempts were rejected by a GitHub secondary rate limit; the label/body enrichment itself landed during the run.)
progress.session_idinside the requeue path itself (one writer, atomic with the status change), then add the bind-time claim re-verification.35 remaining items
dezren39 commentedon Nov 4, 2026
🤖 Agent note · picked up
awaiting_cicheckpoint)pm-stack/opencode-claimant/migration-0022.sqlfor issue opencode-claimant: bound session that never received its prompt (task 6722) has no recovery path — stuck claimed forever #4758 (commit 89e951c, 08:20Z) after this migration was applied live (08:08Z). Fix in this pass: renumber the record copy tomigration-0023.sql(free on main), merge current main, push. The failing aggregator check is pre-existing on main (main run 41797308755 fails the same health-inbox tests for issue feat(aggregator): source-conflict inbox — one resolvable queue for Project-vs-derived and Kaneo-vs-GitHub disagreements, with per-class trust rulings #1282; this PR touches only the claimant SQL record).opencode-claimant: record #5001 session-binding migration as 0023 (00…
dezren39 commentedon Nov 4, 2026
🤖 Agent note · parked
pm-stack/opencode-claimant/migration-0023.sql(main took 0022 for issue opencode-claimant: bound session that never received its prompt (task 6722) has no recovery path — stuck claimed forever #4758), merged current origin/main cleanly, pushed (head 8bdc8b1; one push hit a GitHub Internal Server Error and landed on retry). Codex's two P2 findings on the live-applied function bodies were dispositioned on their review threads and tracked as follow-up issue opencode-claimant: fix Codex P2s in the live session-binding functions (bind lock order, revalidate task_id qualification) #5393 / task 9348 — the record copy stays faithful to the applied SQL. The aggregator-tests failure on the previous head is pre-existing on main (main run 41797308755 fails the same issue feat(aggregator): source-conflict inbox — one resolvable queue for Project-vs-derived and Kaneo-vs-GitHub disagreements, with per-class trust rulings #1282 health-inbox tests; this PR touches no aggregator code) and contract corpus passed.Merge pull request #5349 from developing-today/queue/9043-5001-sessio…
dezren39 commentedon Nov 4, 2026
🤖 Agent note · done