Skip to content

feat(pm): age-alarm for the domain-without-pm-state half-state (H13) + heal-backlog oldest-first discipline - #9842

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-9800-half-state-age-alarm
Aug 19, 2026
Merged

feat(pm): age-alarm for the domain-without-pm-state half-state (H13) + heal-backlog oldest-first discipline#9842
os-warren merged 1 commit into
mainfrom
claude/issue-9800-half-state-age-alarm

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#9800

Implements all three mechanizable directions adopted at grading (maintainer verbatim, untranslated: 「项目经理等分诊,但是没有切换 label,导致挂了很久。」「我刚和他说了他才处理。」).

1. scripts/pm/check-half-states.mjs — new H13 detector

Delta vs the card's framing (PM mechanism assumption falsified): the domain-without-pm-state shape was NOT one of the sweep's existing checks — no predicate covered it, and structurally no listing could even fetch it: every listing in the sweep is label-scoped (pm:dispatched, pm:queue, pm:blocked, pm:seat, pm:on-hold, priority:p0), and this shape is defined by the absence of every pm-state label. The same property that hides these cards from seat candidate queries hid them from the sweep itself. So this PR adds both the detector AND the sweep's only unscoped listing (listAllOpenIssues, 10-page cap — a stated boundary, same convention as H8's merged window).

  • h13DomainWithoutPmState(issue, nowMs) — flags an open card with a domain:* label, no pm-state label (PM_STATE_LABELS: pm:queue / pm:dispatched / pm:blocked / pm:on-hold / pm:epic / pm:seat / needs-user-decision / finding — each has a named reader; pm:blocking deliberately excluded: it is a derived priority cache, not a state, and a card carrying only it is exactly as invisible to candidate queries), none of the sweep-exempt labels (tracking / status:parked / qa-run, mirroring the protocol's own exclusions), older than DOMAIN_HALF_STATE_STALE_HOURS = 2.
  • Threshold rationale lives in a comment at the constant, per the dispatch route: the triage Routine fires hourly and its disjunct ③ heals this shape every round, so 2h means at least one full healing round was survived — the alarm reads the healing loop's failure, never intake latency (a just-landed label sits here only for the sweep's ~2-minute settle window). Age reads updated_at (no timeline fetch; every healing write bumps it); an unreadable timestamp flags rather than reads as fresh, same as H10/H11/H12.
  • Louder line when the card's own title/body self-declares P0/data-integrity (read through stripMarkdownCode, so quoting the token in backticks is not a self-declaration): prefixed P0-SUSPECT, prescribing the emergency-triage channel as the mandated move, never the hourly Routine.
  • Report-only conventions unchanged: exit 0 on a completed sweep, the 3/2 failure split untouched; findings stay patrol input, not a gate verdict. 22 new self-test cases (162 total).

2. references/dispatch-runbook.md — 半状态治愈积压排序

New clause in the 紧急卡 fable 直接分诊 section: sweep half-state disjuncts ②③ process oldest-first, P0-suspect first; 优先最新 applies to fresh intake only, never to the healing backlog (starvation inversion: per-round cap + newest-first structurally starves the oldest half-states); self-declared P0/data-integrity routes through the emergency channel; the mechanical age alarm lives in the half-state patrol script (script header is the authority).

Ratchet payment (223/223 ceiling, zero headroom; net 0 lines): +5 lines of new clause paid by −5 lines of genuine dedup, not re-wrap — every deleted passage is restated elsewhere and now points at its single home: the preamble's self-referential enumeration of the file's own sections; the replay-dedup principle restatement (principle lives in SKILL.md; the identity triple stays here as the detail); the stall-reset principle restatement; the Claim: spelling restatement (the claim protocol lives in SKILL.md); the draft-PR-early Definition-of-done restatement (lives in the os-dev definition; its "insurance, not an optimization" qualifier kept inline).

3. SKILL.md — two protocol edits, net 0 lines (681 before and after; ceiling 682)

  • Backlog sweep line: 「存量大时每轮限量、优先最新」 is now scoped — fresh intake ① keeps 优先最新; the healing backlog ②③ goes oldest-first, P0-suspect first (detail in the runbook).
  • 状态变更不过夜 clause gains: waiting on another seat is a state — it exists only if written on the card (who is awaited, since when; never in session memory); a P0-suspect wait makes the emergency-triage channel mandatory, not optional (维护者 2026-08-19).
  • Content payment: two restated details now point at their existing single homes — the pm:blocking ruling's verbatim quote (kept in the runbook's 全序 section) and the replay identity triple (kept in the runbook).

Verification (all at d41e862b0)

  • node scripts/pm/check-half-states.mjs --self-test — "✓ check-half-states self-test: 162 cases pass." (exit 0, captured before any pipe).
  • Reverse verification, run from the committed state and restored via checkout from the branch: (a) adding pm:blocking to PM_STATE_LABELS turns exactly the H13: pm:blocking alone is NOT a state case red (exit 1, 1 of 162); (b) flipping the threshold boundary to strict-less-than turns exactly the H13: exactly at the threshold case red (exit 1, 1 of 162). These are plain-node scripts with no build step, so no dist rebuild applies to the ablation.
  • Live sweep cannot run in this container (--probe exits 3: proxy-placeholder token 401 + shared-egress anonymous quota exhausted — a container class the script's own header documents), so the new unscoped listing is verified structurally (it mirrors the existing listing helpers verbatim) and through the predicate self-tests; the next Routine fire exercises it live.
  • Gate battery, each verdict from the gate's own printed line, exits captured before any pipe: check:pm-skill-ratchet ✓ (SKILL.md 681 lines, ceiling 682; runbook 223 lines, ceiling 223) · check:pm-skill-id-lint ✓ "9 file(s) clean" · check:pm-half-states ✓ · check:pm-governed-merges self-test ✓ 77 assertions · check:partof-closing-keyword ✓ 28 cases · check:nul-bytes ✓ 6278 files, no raw control bytes · check:doc-authoring ✓ 377 files clean · check:skill-frame-sync ✓ 4 copies agree · check:cross-package-test-inputs ✓ · check:doc-formula-expressions ✓ (lint package's dependency closure built first — the gate reads @objectstack/formula's built dist).
  • node scripts/pm/dispatch-gates.mjs re-derived with no path arguments (the script computes the changed set from the merge-base itself): identical family list to dispatch time, no additional families.

Governed surface (.claude/**): this PR stays draft — a human merges it. Internal tooling and agent-instruction change only, nothing published: skip-changeset applies.


Generated by Claude Code

…eal-backlog oldest-first discipline
- scripts/pm/check-half-states.mjs: new H13 detector — a card carrying
domain:* with no pm-state label, aged past one sweep cycle (2h; the
triage Routine fires hourly), is reported as a defect of the healing
loop, not inventory. Louder line when the card's own title/body
self-declares P0/data-integrity: the emergency-triage channel is the
mandated move. Needs the sweep's only unscoped listing — the shape is
invisible to every label-scoped page by construction (it was not
detected at all before this change). Report-only, exit conventions
unchanged; 22 new self-test cases.
- dispatch-runbook.md: 半状态治愈积压排序 — sweep half-state disjuncts
②③ process oldest-first, P0-suspect first; 优先最新 applies to fresh
intake only. Paid at the 223-line ceiling by deduplicating text
restated from SKILL.md/os-dev (replay-identity triple restatement,
claim-spelling restatement, principle restatements, self-referential
section enumeration).
- SKILL.md: sweep line now scopes 优先最新 to fresh intake; 状态变更不
过夜 gains the waiting-is-a-state clause (who is awaited, since when,
never in session memory; a P0-suspect wait mandates the emergency
channel). Net-zero lines, paid by pointing two restated details at
their runbook homes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeA3nU1B5Q2pgxqxgUrexd
@os-warrenos-warren added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/m labels Aug 19, 2026 — with Claude
@github-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Aug 19, 2026
@os-warren
os-warren marked this pull request as ready for review August 19, 2026 05:11
@os-warren
os-warren added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit 812be6eAug 19, 2026
27 checks passed
@os-warren
os-warren deleted the claude/issue-9800-half-state-age-alarm branch August 19, 2026 05:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

incident: a self-declared P0-suspect sat 26h in the domain-without-pm-state half-state — both healing mechanisms existed and neither fired visibly

2 participants

@os-warren@claude