Skip to content

skills(pm-dispatch): pm:awaiting-maintainer has a semantic contract and zero machine carrier — 49 of 55 cards in it are waiting for a JUDGEMENT, not for a hand, and nothing can tell the two apart #17017

Description

@huangyiirene

Filed on the maintainer's direct instruction, 2026-09-09, replying to a director-format decision batch: 「5986 这种任务不影响我们的产品质量,不应该让人类决策,项目经理自己当场决定就可以,而且这个应该开技能卡纠正」 and, on a second card, 「这个不应该是决策卡吗?哪个流程出了问题?」.

⛔ Filed unassigned. .claude/skills/pm-dispatch/** is the domain:skills lane's governed surface.

The measurement that opened this

Full enumeration of both multi-lane repos plus hotcrm, 2026-09-09, reading only state:OPEN:

repo pm:awaiting-maintainer
objectstack 37
objectui 16
hotcrm 2
total 55

Two enumerations ~14h apart: 55 → 55, unchanged, while pm:queue across the three repos moved 562 → 478 (−84) in the same interval. ⇒ This state is the only one on the board that does not move.

Classifying all 55 by what the maintainer actually owes:

~49 of 55 are misfiled, and ~24 of those should never have left a seat at all.

Which process failed — traced on #12034, which is the clean specimen

#12034 was pm:dispatched. Its delivering PR #13060 merged 2026-08-29T03:03:42Z; the card kept the label for ~36h; H8 fired; a seat did the paired write on 2026-08-30 and chose pm:awaiting-maintainer. That comment's own body says why the choice was wrong (5469455426, verbatim):

⇒ 故 pm:awaiting-maintainer裁完之后按你的选择:留在本卡(选项 A)或另立新卡(选项 B)承接。

「裁完之后」 is the definition of needs-user-decision. SKILL.md:110 / :126:

pm:awaiting-maintainer | 决定已做,只剩一次 GitHub 之外的人工动作
needs-user-decision 是决定待做pm:on-hold 是决定已做

Three defects let that stand, and all three are in the skill, not in the seat:

① H8 checks that the paired write happened, never which state it wrote to. The predicate is "delivering PR MERGED while the card still carries pm:dispatched". Any terminal state discharges it. There is no successor predicate asking whether the chosen state matches the card's own thread.

② The distinction between the two states has no machine carrier. Every other parked state carries a mandatory machine-readable line — pm:blocked owes Blocked-by: #N, pm:on-hold owes Restart-when: (and references/state-machine.md calls any other spelling illegal). pm:awaiting-maintainer owes nothing. Anyone can apply it and no reader can falsify it.

③ The half-state patrol is structurally blind here, and says so itself. scripts/pm/check-half-states.mjs:2298, verbatim:

pm:awaiting-maintainer has no machine exit BY CONSTRUCTION (that is why it exists)

Its predicates over this state are H25 (must not coexist with another pm state — shape), H29/H30/H31 (six-state ONE-OF — shape), H22 (residue on a closed card — shape), and the important-parked ager (time). ⇒ Shape and time only. Nothing reads meaning. No machine exit means no machine can notice the state was entered wrongly.

And the misclassification cannot self-correct downstream: references/state-machine.md puts the exit solely with the director seat, which is 人工定期召唤、无 Routine 无 cron and gated at CONTRACT_REVIEW_TIER. ⇒ a wrong entry has exactly one reviewer, who is often absent or under tier. That is the mechanism behind 55 → 55.

What this card asks for — two halves, one landing surface

Half A — give the state a carrier, so the two are separable by machine

A card entering pm:awaiting-maintainer carries a mandatory line naming the action and its verifiable completion, on the model of Blocked-by: / Restart-when:. Working spelling for the claimant to refine:

Maintainer-action: <one action a seat cannot perform> — done when <machine-checkable evidence>

A card in the state without that line is a half-state ⇒ new sweep predicate, and the natural remedy text is "if the line cannot be written because the card is asking for a ruling, the state is needs-user-decision". The line also gives the director seat's 职责三 台账 something to render, and gives the ager a completion criterion instead of just an age.

⚠️ Explicitly not asked for: a machine exit. check-half-states.mjs:2298 is right that the state has none by construction. This is an entry predicate, not an exit one.

Half B — write the floor of what may reach the maintainer at all

The skill says a great deal about how to escalate and almost nothing about what must never be escalated. SKILL.md 〈升级与决策〉 has 具名不升级类 (恢复不变量的修复; 技术任务间的顺序与依赖; 验证策略) — the maintainer's ruling adds a class this list does not cover:

「这种任务不影响我们的产品质量,不应该让人类决策,项目经理自己当场决定就可以」

Specimen: objectui#5986 — two cards were open for one repository-variable click, and the question "which of the two survives" was carried to the maintainer inside a decision batch. That is bookkeeping. It has no product-quality surface, no contract shape, no acceptance-set movement. It belongs on the seat that noticed it.

⇒ Extend 具名不升级类 to name: 去重与卡片合并;台账与记账整理;纯文档措辞更正(无契约声明改动);门禁内部参数与扫描器盲区修复(加强,非削弱). ⛔ The four items above must not be readable as widening — each is bounded by "no product-visible behaviour changes", and the 人工地板 list (SKILL.md:391-393) is untouched and remains the exclusion set.

Constraints the claimant must respect

  • .claude/skills/pm-dispatch/SKILL.md is a 强制条款① surface ⇒ the PR is built at CONTRACT_REVIEW_TIER regardless of its content.
  • check-skill-line-ratchet.mjs: SKILL.md's line count only ever goes down, and it currently sits at its ceiling with 0 headroom (811/811, measured on PR skills(pm-dispatch): clause-② cards build at the default tier and are gated by the contract review at CONTRACT_REVIEW_TIER (#16905) #16915's gate table). ⇒ this edit must pay for itself by deletion; ⛔ re-wrapping does not count. Half B is plausibly net-negative (it collapses a class into an existing list); Half A adds a carrier and will need to find its own payment.
  • Governed surface ⇒ terminal four-piece treatment: draft PR, needs-user-decision, a 「维护者速读」 comment, review requested from both authorised accounts; ⛔ no seat approves or merges.
  • ⚠️ Do not backfill the 55 in the same PR. Reclassifying existing cards is a separate, per-card act that reads each thread; landing a predicate that instantly reds ~49 cards is the "gate goes red on the healthy case" shape objectui#6596 already ruled against. Sequence: rule first, then a bounded reclassification worklist.

Executable acceptance criterion

SKILL.md states the Maintainer-action: carrier and that a card in pm:awaiting-maintainer without it is a half-state; 具名不升级类 names the four bookkeeping classes; pnpm check:pm-skill-ratchet passes (line count not increased); check:skill-frame-sync / -freshness pass; check-half-states.mjs gains the entry predicate with self-test cases, and its own self-test suite stays green.

Refs: #12034 (the specimen, and the comment that misfiled it: 5469455426) · objectui#5986 / objectui#7852 (the bookkeeping specimen, both closed 2026-09-09) · scripts/pm/check-half-states.mjs:2298 (the by-construction blindness) · references/state-machine.md (exit ownership) · objectui#6596 (red-on-healthy)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions