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
[finding] A dev agent killed by a capacity limit leaves a PERFECTLY well-formed claim behind — every half-state predicate passes, and the card is indistinguishable from live work forever #11248
Filed unassigned, recording only. Found by the domain:devx @ objectui seat (objectui#5748) during round R2, PM session session_0124Qg8rLvpXnQDwCmpKUmaJ, 2026-08-23.
What happened
Three os-dev agents were dispatched concurrently at 05:46–05:47Z (objectui#5442, #5436, #5409). At approximately 05:50Z all three died simultaneously on the same API error:
Agent terminated early due to an API error: You've hit your weekly limit · resets 6am (UTC)
Fleet-wide capacity exhaustion, not three independent failures — the shared account limit is a single point of failure for every agent in flight at once, which is what makes the blind spot below matter at scale rather than as a one-off.
The blind spot
After the kill, each of the three cards was left in exactly this state:
pm:dispatched ✅
assignee set ✅
a claim comment whose first line is literally Claim: ✅
a named branch that exists on the remote ✅
That is a textbook-correct claim. Every half-state predicate in scripts/pm/check-half-states.mjs therefore passes:
predicate
why it does not fire
H2 — assignee with no claim comment
a claim comment exists, and it is well-formed
H3 — pm:queue + pm:dispatched
the swap was atomic and clean; readback confirmed no residue
H4 — blocked with no Blocked-by:
not blocked
H8 — merged PR, card still dispatched
no PR was ever opened
H13 — domain with no pm-state
has both
H22 — closed card carrying pm:*
card is open
There is no predicate for "the claim is valid and the claimant no longer exists." The card will look like healthy in-progress work indefinitely. Nothing ages it, nothing pings it, and the only thing that would ever notice is the dispatching PM's own memory — which does not survive its session, and in this fleet the PM's own capacity is exhausted by the same limit that killed the devs.
Why "just re-dispatch" is not the answer to this card
The seat handled the incident correctly (measured what survived, recorded it on all three cards, re-dispatched after the reset). That worked only because the PM was alive and watching. The finding is precisely about the case where it is not: if that session had also ended, three cards sit claimed-and-dead with no ledger entry, and the next PM's round-open mutual-exclusion read — which looks for the latest non-self Claim: on a lane pm:dispatched card — would read those corpses as live claims by another session and stay off them. The protocol's own mutual-exclusion mechanism converts a dead claim into a lane-wide block.
Measurement worth keeping: what actually survives a kill is not uniform
Checked against the remote rather than inferred from each agent's last words, and the three differed:
card
remote state after the kill
last action recorded
objectui#5409
branch 1 commit ahead, on-scope, complete-looking
"Now let's open the draft PR"
objectui#5436
branch 0 commits ahead
"Now the docs page — only my two regions"
objectui#5442
branch 0 commits ahead
starting the install
Two lessons in that table:
An agent's last transcript line is not evidence of its remote state.fix(rest): 4xx 直通截断超长 message,不再整条换成 "Request failed" (#5423) #5436's last line describes editing work that does not exist anywhere — an uncommitted worktree in a terminated sandbox is unrecoverable. Only git ls-remote answers the question.
A surviving commit is not a surviving verification.fix(runtime): 无 setFallbackHandler 的适配器改以 warn 宣告声明式端点不可达 (#5400) #5409's commit is on-scope and looks finished, but its card carried an explicit premise-first STOP condition and no evidence exists that the check was ever run. A later reader — human or agent — who finds a clean pushed branch and no dissent is being invited to assume it was validated. That is the more dangerous half of this failure mode, because it fails toward merging something unverified.
Candidate fixes (not choosing — two surfaces, and the split is a maintainer call)
A patrol predicate in scripts/pm/check-half-states.mjs: a pm:dispatched card whose newest Claim: is older than some threshold and which has no PR and no branch activity since. Needs a real liveness threshold, and this fleet does not have one yet — the devx@objectui lane has only 2 samples (claim → draft PR ~5 min and ~17 min), which is not a distribution.
A protocol rule in .claude/skills/pm-dispatch/SKILL.md: require the dispatching PM to record dev liveness on the card, so a dead claim is distinguishable from a slow one without a heuristic.
Both may be wanted; they answer different halves (detection vs. prevention). ⛔ Deliberately not anchored to a domain:* lane here — the patrol script is domain:devx and the protocol is domain:skills, and which one carries this is exactly the judgement triage should make rather than me.
The same round produced a third instance today of the other known half-state generator: the claim comment on objectui#5409 failed with an HTTP 502 after its label had already flipped, manufacturing a genuine H2 until the retry landed (earlier instances this session: #8140 and #10966). The label write and the claim comment are not atomic with each other, and a transport error between them produces exactly the state the patrol reports. That is a separate mechanism from this card's, and is recorded here only because both were observed in one round and both are unowned.
Filed unassigned, recording only. Found by the
domain:devx@ objectui seat (objectui#5748) during round R2, PM sessionsession_0124Qg8rLvpXnQDwCmpKUmaJ, 2026-08-23.What happened
Three
os-devagents were dispatched concurrently at 05:46–05:47Z (objectui#5442, #5436, #5409). At approximately 05:50Z all three died simultaneously on the same API error:Fleet-wide capacity exhaustion, not three independent failures — the shared account limit is a single point of failure for every agent in flight at once, which is what makes the blind spot below matter at scale rather than as a one-off.
The blind spot
After the kill, each of the three cards was left in exactly this state:
pm:dispatched✅Claim:✅That is a textbook-correct claim. Every half-state predicate in
scripts/pm/check-half-states.mjstherefore passes:pm:queue+pm:dispatchedBlocked-by:pm:*There is no predicate for "the claim is valid and the claimant no longer exists." The card will look like healthy in-progress work indefinitely. Nothing ages it, nothing pings it, and the only thing that would ever notice is the dispatching PM's own memory — which does not survive its session, and in this fleet the PM's own capacity is exhausted by the same limit that killed the devs.
Why "just re-dispatch" is not the answer to this card
The seat handled the incident correctly (measured what survived, recorded it on all three cards, re-dispatched after the reset). That worked only because the PM was alive and watching. The finding is precisely about the case where it is not: if that session had also ended, three cards sit claimed-and-dead with no ledger entry, and the next PM's round-open mutual-exclusion read — which looks for the latest non-self
Claim:on a lanepm:dispatchedcard — would read those corpses as live claims by another session and stay off them. The protocol's own mutual-exclusion mechanism converts a dead claim into a lane-wide block.Measurement worth keeping: what actually survives a kill is not uniform
Checked against the remote rather than inferred from each agent's last words, and the three differed:
Two lessons in that table:
git ls-remoteanswers the question.setFallbackHandler的适配器改以warn宣告声明式端点不可达 (#5400) #5409's commit is on-scope and looks finished, but its card carried an explicit premise-first STOP condition and no evidence exists that the check was ever run. A later reader — human or agent — who finds a clean pushed branch and no dissent is being invited to assume it was validated. That is the more dangerous half of this failure mode, because it fails toward merging something unverified.Candidate fixes (not choosing — two surfaces, and the split is a maintainer call)
scripts/pm/check-half-states.mjs: apm:dispatchedcard whose newestClaim:is older than some threshold and which has no PR and no branch activity since. Needs a real liveness threshold, and this fleet does not have one yet — the devx@objectui lane has only 2 samples (claim → draft PR ~5 min and ~17 min), which is not a distribution..claude/skills/pm-dispatch/SKILL.md: require the dispatching PM to record dev liveness on the card, so a dead claim is distinguishable from a slow one without a heuristic.Both may be wanted; they answer different halves (detection vs. prevention). ⛔ Deliberately not anchored to a
domain:*lane here — the patrol script isdomain:devxand the protocol isdomain:skills, and which one carries this is exactly the judgement triage should make rather than me.Related, not duplicate
One more datum for the patrol-coverage question
The same round produced a third instance today of the other known half-state generator: the claim comment on objectui#5409 failed with an HTTP 502 after its label had already flipped, manufacturing a genuine H2 until the retry landed (earlier instances this session: #8140 and #10966). The label write and the claim comment are not atomic with each other, and a transport error between them produces exactly the state the patrol reports. That is a separate mechanism from this card's, and is recorded here only because both were observed in one round and both are unowned.