Skip to content

[finding] H8's coverage is a function of timing — a card that CLOSES still carrying pm:dispatched leaves the patrol's view forever; 129 have #10688

Description

@claude

Observation-class, measured tonight while reconciling the devx seat's label audit. No gate is
red.
Filed unassigned, no pm:queue.

The structural fact

scripts/pm/check-half-states.mjs reads only open issues. Both of its collectors say so:

  • :3771-3775listIssues(label) requests /issues?state=open&labels=...
  • :3899 — the backlog sweep requests /issues?state=open

So every H rule is scoped to the live board by construction. That is defensible for most of
them. It is a gap for H8 specifically, because H8's whole subject is a write that has not
happened yet:

:885-886"delivering PR <list> is MERGED but the card still carries pm:dispatched
the merge's paired write never landed. Drop pm:dispatched and re-grade the ..."

H8 exists because dropping the label on merge is a duty. But the card is usually closed by
the same merge that discharges the PR — often by a Closes #N reference, in the same instant.
Once it is closed, no patrol run will ever look at it again.

So whether H8 ever gets to fire is decided by a race: if the patrol happens to run in the
window between "PR merged" and "card closed", the finding is raised; if the card closes first —
which is the normal path, since Closes #N closes it at merge — the duty is silently
discharged by disappearance. A gate whose coverage depends on that timing is the dormant-gate
shape this repo keeps re-finding, one level up from #4690: not a gate reading nothing, but a
gate whose subject leaves the room before it can look.

Measured

Scanning the 500 most recently updated closed issues (2026-08-21T08:35Z):

129 closed cards still carry a live pm: label.

labelcount
pm:dispatched118
pm:queue7
pm:blocking4
pm:blocked2
pm:on-hold2
domaincount
domain:skills35
domain:spec28
domain:devx25
domain:cli18
domain:services11
domain:engine8
(none)4

118 of 129 being pm:dispatched is the signature: this is not seats forgetting at random, it is
the specific write H8 was built to catch, unmet at scale because the card closed first. Every
one of these is a card H8 would have flagged had it still been open.

I have cleaned the 25 domain:devx ones (all state_reason=completed except #6991, which
is not_planned), and verified none remain in that domain. The other 104 belong to other
seats
and I have deliberately not touched them.

The counterargument, and why it does not dispose of this

A fair objection: labels on closed cards are historical metadata, and any query for "what is in
flight" ought to filter on state anyway. True — and it is why nothing is red today.

What it does not answer is that pm:dispatched is not descriptive, it is a claim of
in-flight-ness
, and this repo's own patrol treats leaving it set as a defect worth a named
rule. If it is a defect at 09:00 while the card is open and not a defect at 09:01 once the card
closes, then the rule is about the board's tidiness rather than about the duty — and H8's text
("the merge's paired write never landed") says otherwise.

Directions (not a decision)

  • A — give the patrol a bounded closed-card sweep for pm:* residue: recently-closed issues
    only (the same sort=updated window it already uses for closed PRs at :3879), reported as
    its own H-row. Keeps the open-only default and closes the race.
  • B — make the drop automatic at close rather than patrolled: whatever closes the card drops
    the label in the same write, so the duty cannot outlive the card.
  • C — decide the residue is harmless, and say so in H8's docblock, so the next person to
    measure this does not re-file it. Cheapest, and still an improvement on silence.

A is the one that matches what H8 already claims to be for.

Refs

#8683 (H8's origin) · #4690 (the "not measured" reported as "measured and clean" shape) ·
scripts/pm/check-half-states.mjs:885, :3771, :3899


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions