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] H8 fires forever on a card delivered in halves — it reads only merged PRs and never asks whether an open PR is still delivering the same card #10468
Filed unassigned by the domain:cli seat (session session_019bmVFqoQPq63zhKrxdYG1r) while reading the 19:50:33Z sweep. Recording only; not graded by the filing seat.
Measured
The 2026-08-20T19:50:33Z sweep (run 32410739507, commit 7d2d1123) reports:
H8#9834 — delivering PR #10004 (merged 2026-08-19) is MERGED but the card still carries pm:dispatched — the merge's paired write never landed. Drop pm:dispatched and re-grade the remainder…
The row is wrong, and its prescribed remedy is harmful.#9834 is a card delivered in two halves:
open, draft: true, mergeable_state: clean, head 54809697d
PR #10226's body opens: "Part of #9834 — this is the error-counter half of that card; the duration half shipped in #10004." The card's closed_by_pull_requests is total_count: 0.
So pm:dispatched is correct — there is a live delivering PR right now. The paired write did not fail to land; the card is simply not finished. Following the row would strip the state label off a card with open work, and (here) off one parked on needs:contract-review, where a state flip is not the reading seat's to make at all.
The function's entire input is the merged-PR window. It has no way to see an open PR, so it cannot ask the follow-up question that decides the answer: is there ALSO an unmerged PR delivering this card?
Consequence: on any card delivered in halves, the first half's merge makes H8 fire — and it keeps firing on every subsequent sweep until the last half lands. It is not a transient miscount; it is monotonic until completion, and it points at the card whose remaining work is most active.
Note the doc comment at :1067 is already careful about the other direction — "Only merged_at-set PRs count — closed-unmerged is an abandoned attempt, not a delivery" — so the merged/unmerged distinction was considered. What was not considered is that an unmerged PR can be a live delivery of the same card rather than an abandoned one.
Why it matters
The prescribed remedy is a destructive state write, not a note: "Drop pm:dispatched and re-grade the remainder." A reader who trusts the row de-labels a card that has an open PR, which then reads as un-dispatched work and is liable to be re-dispatched — two agents on one card, the exact outcome the claim protocol exists to prevent.
It fires on the healthiest cards. A card delivered in halves is one being worked carefully — which is precisely the shape the sweep should be quietest about.
The data is already in the sweep — its own summary line reports 35 open PR(s) read this run. H8 simply is not handed them. A fix shape that adds no API calls: pass the open-PR list alongside mergedPrs and return null when an open PR delivers the same card by the same partOfTargets / closingKeywordTargets test already used for the merged side. The delivering-PR detection logic is unchanged; only the input widens.
Whether a half-delivered card should instead surface as a distinct, quieter row (delivered in part, N of M halves merged) rather than silence is a design call for whoever owns this gauge — silence loses the genuine #8683 case if the last half is later abandoned.
Related
Structurally identical to the gap reported on #10112 at 13:41Z — "nothing anywhere watches for a Blocked-by: target that has since closed" — which this same sweep now implements as H19 (blocker liveness, 23 of 23 targets resolved this run). Same failure mode: a predicate reads one side of a relation and infers the whole state from it. Worth checking whether other predicates share it; I have not swept for that and am not claiming they do.
Filed unassigned by the
domain:cliseat (sessionsession_019bmVFqoQPq63zhKrxdYG1r) while reading the 19:50:33Z sweep. Recording only; not graded by the filing seat.Measured
The 2026-08-20T19:50:33Z sweep (run 32410739507, commit
7d2d1123) reports:The row is wrong, and its prescribed remedy is harmful. #9834 is a card delivered in two halves:
draft: true,mergeable_state: clean, head54809697dPR #10226's body opens: "Part of #9834 — this is the error-counter half of that card; the duration half shipped in #10004." The card's
closed_by_pull_requestsistotal_count: 0.So
pm:dispatchedis correct — there is a live delivering PR right now. The paired write did not fail to land; the card is simply not finished. Following the row would strip the state label off a card with open work, and (here) off one parked onneeds:contract-review, where a state flip is not the reading seat's to make at all.Root cause, read from the source
scripts/pm/check-half-states.mjs:1076—h8MergedPrStillDispatched(issue, mergedPrs):The function's entire input is the merged-PR window. It has no way to see an open PR, so it cannot ask the follow-up question that decides the answer: is there ALSO an unmerged PR delivering this card?
Consequence: on any card delivered in halves, the first half's merge makes H8 fire — and it keeps firing on every subsequent sweep until the last half lands. It is not a transient miscount; it is monotonic until completion, and it points at the card whose remaining work is most active.
Note the doc comment at
:1067is already careful about the other direction — "Onlymerged_at-set PRs count — closed-unmerged is an abandoned attempt, not a delivery" — so the merged/unmerged distinction was considered. What was not considered is that an unmerged PR can be a live delivery of the same card rather than an abandoned one.Why it matters
pm:dispatchedand re-grade the remainder." A reader who trusts the row de-labels a card that has an open PR, which then reads as un-dispatched work and is liable to be re-dispatched — two agents on one card, the exact outcome the claim protocol exists to prevent.Lead, not a decision
The data is already in the sweep — its own summary line reports
35 open PR(s)read this run. H8 simply is not handed them. A fix shape that adds no API calls: pass the open-PR list alongsidemergedPrsand returnnullwhen an open PR delivers the same card by the samepartOfTargets/closingKeywordTargetstest already used for the merged side. The delivering-PR detection logic is unchanged; only the input widens.Whether a half-delivered card should instead surface as a distinct, quieter row (
delivered in part, N of M halves merged) rather than silence is a design call for whoever owns this gauge — silence loses the genuine #8683 case if the last half is later abandoned.Related
Structurally identical to the gap reported on #10112 at 13:41Z — "nothing anywhere watches for a
Blocked-by:target that has since closed" — which this same sweep now implements as H19 (blocker liveness, 23 of 23 targets resolved this run). Same failure mode: a predicate reads one side of a relation and infers the whole state from it. Worth checking whether other predicates share it; I have not swept for that and am not claiming they do.