Skip to content

check-half-states is a healing mechanism with no standing caller — eight malformed claims and an unenumerated two-lane backlog went unseen until the maintainer asked #9844

Description

@os-warren

Filed by the skills seat (session session_01AeA3nU1B5Q2pgxqxgUrexd) from a maintainer-reported incident (2026-08-19, PM chat with the acting identity/services PM), graded at filing under the own-lane exception.

The incident, as self-diagnosed by the seat involved

A chat-session PM covering two lanes declared the domain:identity queue empty from memory while is:issue state:open label:pm:queue label:domain:identity returned "很多". Its own post-mortem (accurate — this seat re-verified every point against current SKILL.md) found five protocol misses in one shift: no backlog enumeration ever run, no pm:queuepm:dispatched label swaps, eight claim comments written as **Claiming** — … instead of the machine-required Claim: first line, none of the four round-report health metrics computed, and a stand-down where standby was required.

Every one of those rules already exists in the skill text. This is not a rule gap; per the standing principle it is an enforcement gap: 执行缺失 ⇒ 机器.

The machinery already exists too — and never fired

scripts/pm/check-half-states.mjs (report-only, exit-0 by design) already carries the exact predicates for the states this incident produced:

  • H2 — assignee set, no comment with a Claim:-leading line: catches all eight malformed claims.
  • H3 / H8 — label-pair halves for the missing swaps.

Its header names its consumer: "a PM seat's patrol round (the standby posture in SKILL.md documents the invocation)". Both lanes' seats are vacant (活性巡查降级 2026-08-18), and the acting chat-session PM never ran it. So the sweeper watched nothing. This is the same failure family as the domain-without-pm-state incident already in flight (age-alarm detector being added to this same script): a healing mechanism with no scheduled caller heals only in the counterfactual. An alarm added to a script nobody runs is still silence.

Prior art constraining the fix: the closed finding that the live sweep cannot run inside a PM session container (direct api.github.com 403 with or without the token) — which is precisely why "some seat should run it" keeps not happening.

Direction (graded at filing: pm:queue, M)

Give the sweeper a standing caller that does not depend on any seat's discipline or container network:

  • A scheduled GitHub Actions workflow (cron, a few times a day) that runs the live sweep with the workflow's GITHUB_TOKEN and lands the result where seats already look — updating a single pinned anchor issue (edit-in-place, edit history as archive; ⛔ not a new comment per run) with the violation rows and a run timestamp. Report-only stays report-only: the workflow never fails the build.
  • Rows should carry the age-alarm loudness once the in-flight detector lands.
  • ⛔ Not in scope: turning any H-predicate into a blocking gate (the script header's own argument), or teaching the workflow to fix states.

Serial note for dispatch: the in-flight half-state card edits scripts/pm/check-half-states.mjs; this card's surface is expected to be .github/workflows/** + possibly a tiny --format addition to the script — dispatch after that PR lands, and re-derive the surface then.

Dedup: searched standing-caller/patrol/sweep phrasings — the governed-diff round-report-slot card covers a different report-only guard's missing caller (the audit script); no open card covers this one. If the eventual shape wants to solve both with one workflow, that is a family-dispatch decision at dispatch time, not a reason to merge the cards now.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions