Skip to content

feat(pm): H51 + H53 — the two contract-review carrier states no sweep reads, and the FAIL end-state H51 applies - #17138

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-16836-contract-review-fail-handoff-h51
Sep 9, 2026
Merged

feat(pm): H51 + H53 — the two contract-review carrier states no sweep reads, and the FAIL end-state H51 applies#17138
os-zhuang merged 2 commits into
mainfrom
claude/issue-16836-contract-review-fail-handoff-h51

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #16836
Fixes #16995

Two report-only patrol rows on the needs:contract-review gate, plus the one governed sentence that names the end-state the first of them tells a seat to apply. One file surface, two commits, one commit per card.

What each card asked for, and what landed

#16836 deliverable 1 — the FAIL end-state (governed). The PASS branch was precise (「PASS ⇒ 同席同笔剥双载体,清标同笔留 provenance 评论引该 PASS」); the FAIL branch said only 「转回相应工作态(重派/返工)」 into a state model with no rework state, so nothing said what the label set looks like after a FAIL. references/contract-review.md 载体纪律 now names it:

  • FAIL 同 PASS 剥双载体:同笔留卡上交接评论(引复审、独立性对、欠改);卡态与 assignee 不动。

#16836 deliverable 2 — H51. An OPEN PR carrying needs:contract-review whose own thread already holds a contract-review verdict for the current head, older than 60 minutes. Verdict-agnostic on purpose: both branches owe a label stroke inside the window, and a row that parsed the verdict would be issuing one.

#16995 half (1) — H53. An open card carrying the gate with no Claim: comment and no open or merged PR delivering it: 「carrier without increment」. H31-style and report-only — it never removes a carrier. Half (2) is #17007 and is not addressed here.

The one place the card's premise did not survive contact — H51's anchor

The card states the review-comment title as one literal format and calls the anchor structural on that basis. Measured against the live board it is not one format. Six contract-review comments across the seven gated open PRs, same day, in four shapes:

shape where the head sha is
## Contract review (…, isolated seat) — PR #N @ \sha`` in the heading (the card's cited form)
## Contract review (clause ②) — … · head \sha`` in the heading, other separator
## Contract review at \TIER` — Verdict: …` not in the heading — first body line
**Director seat adoption record** … + the review verbatim below the adoption sentence, and the embedded heading

A regex pinned to the card's literal would have been silent on two of the three dialects plus every adoption record, while reporting a clean board — a phantom check on more than half of its own population. So the row reads the two things all four shapes share and nothing else: a ## Contract review heading line (line-anchored with m, H48's MAINTAINER_BRIEF_MARKER register — body-anchoring would drop the adoption record, and 「逐字采纳」 is one of the two legal acts on a subagent verdict) and the head sha as a code span, tested as a case-insensitive prefix of the PR's head.sha. The looseness m admits — a comment merely quoting a heading — is answered by the second gate rather than by the regex: no head sha, no verdict on this head. Pinned as a case.

Live behaviour on the board at the time of writing, both directions firing:

PR head verdict on it verdict age row
#17061 9b4c0af7f416 yes 292 min finding
#17066 904c1dceb0da yes 309 min finding
#17090 ba3d95a4f351 yes 213 min finding
#17116 57421b4ae457 yes 80 min finding
#17073 4d15856e3e8e names an OLDER head clean (head moved)
#17115 05f5c96dfeb8 names an OLDER head clean (head moved)
#17126 9a52eaaa4438 none clean

H53 and the shape H31 deliberately declined

H31's header refuses 「gate label on a card with no PR carrier is premature」 because card-side-first is legal and expected (「报告先于 PR 到达则先挂卡侧」), and reporting it would flag the protocol's own prescribed sequence as a defect. The Claim: leg is what lets H53 read the half that premise excluded, and it is not decoration: a dev executing card-side-first has been dispatched, so its thread carries a claim. Claim present ⇒ silent. Claim absent and no PR ⇒ nobody is executing anything. With no PR there is no carrier pair at all, so H31 here is not clean but inapplicable — the row says so in its own sentence, and H31's live #10025 reading is pinned unchanged.

Bands, and why neither row is gate

Both are state. The gate band's own criterion is a gate whose absence reads as a green light, where 「被剥」 and 「从未挂过」 are indistinguishable — the reader H31/H35 are. Both new rows read the opposite direction: a carrier present and false. H51's nearest structural sibling is H48 (a verdict recorded, its second write missing, on an open PR, aged), which is state. H53 is ⛔ not stall either: whether a card-side carrier actually blocks dispatch is unmeasured — the filing thread says so and declines to grade on it — so the row does not claim the card is stopped.

Cost — measured, and the decision stated either way

PM mechanism assumption B asked whether the sweep already holds the threads. Measured in the script: it does not, for either row.

  • H51 reuses prCommentCache — H48's, whose header reserved exactly this ("a future PR-comment reader finds a thread this row already bought here and pays nothing for it"). Governed and gated are independent properties, so the marginal cost is one issue-comment walk per gated open PR that H48 never visited: 7 gated of 16 open PRs, single-page walks. The head-identity leg costs nothing at all — head.sha and labels both ride the open-PR list row already in hand.
  • H53 BUYS a complete card thread per gated open card, and that is a deliberate purchase. commentCache cannot serve it: H2 buys a thread only for an assigned card, and the measured population is unassigned by construction (a card nobody is executing has no assignee), so a cache-only reading would have reported the entire target shape UNJUDGED while looking perfectly healthy. Bounded by the gated population, not the open one: 9 gated of 599 open cards, single-page walks. Bought COMPLETE via H50's walk, because a full first page may hide the newest Claim: and reading "no claim" off a partial thread would invent the absence the row fires on.

Both coverage clauses render unconditionally and both keep UNJUDGED apart from clean.

Ablation — each new pin shown to fail

Self-restoring script, absolute paths, trap … EXIT INT TERM; every leg proves the mutation reached disk (target text count 1→0, injected text present, mutated blob hash ≠ the HEAD blob) and every restore leg is proven by git diff HEAD empty and the restored blob hash equal to HEAD's 74441227870ada406df6e207f81c1cf562dd1cd1.

leg (mutation) self-test named red cases
H51: accept any review comment, ignore the head sha exit 1 H51 clean: a review comment on an OLDER head; a comment MENTIONING a review heading…; 2 latestContractReviewOnHead cases — 4 of 2976
H51: remove the age gate exit 1 a verdict INSIDE the window is clean; one exactly AT it is clean too — 2 of 2976
H53: stop reading the thread for a Claim: exit 1 the same card once its thread carries a Claim: + 3 boundary cases — 4 of 2976
H53: stop excluding cards that already have a delivering PR exit 1 open-PR, merged-PR and Part of cases — 3 of 2976

⚠️ One honest weakness recorded rather than smoothed: leg 1's injected text (true,) occurs 80 times in the file, so its injection count is not a unique proof. The load-bearing half is — the target text went 1 → 0 and the blob hash moved.

Gates

Re-derived on this diff with no paths (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack) — identical set to the dispatch's lead, zero additions. All run, exit codes captured before any pipe:

✓ dispatch-gates --ran: 43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED.
✓ check-half-states self-test: 2976 cases pass.        (2926 before H53's 50)
✓ check-skill-line-ratchet: contract-review.md is 60 lines (ceiling 60; headroom 0).
✓ check-skill-line-ratchet: lanes/director.md is 72 lines (ceiling 72; headroom 0).
✓ dispatch-gates self-test: 1586 cases pass.           (check:pm-dispatch-gates, detached + `tail --pid`)

pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 — PREREQUISITE NOT MET, which its own text calls "not a finding"; the two packages it needs were built under the shared verify lock (VERDICT command-exit 0) and it then exited 0. Repo-wide lint is a real reading rather than a declared narrowing: eslint . --no-inline-config --format json over its own population of 6,427 files, 0 messages, run at 375c7961.

Ceilings — paid by deletion, ⛔ no raise, ⛔ no re-wrap

contract-review.md stays 60/60 and lanes/director.md 72/72. The new sentence is paid by deleting the line it subsumes — contract-review.md:47 「审计 FAIL 按状态机 label-flip 交回派发席补丁轮。」, which said the same thing without an end-state. ⛔ No merged bullets, ⛔ no cross-file move declared, ⛔ no ceiling touched. Every touched line is ≤120 bytes (new line: 120 B).

On the director.md mirror — PM assumption C's explicit fork, answered with a measurement. The card asks for the sentence mirrored in lanes/director.md 职责一. A second full copy is what the ratchet's own cross-file-move rationale calls the defect ("a fact can be in the wrong FILE … read by that lane and missed by the other six"), and the director charter already points at this single source twice (:29 「归属与全链见 ../contract-review.md」, :71 「../contract-review.md 为准」). So :36 is rewritten in place — both halves of assumption C's disjunction at once, the in-place rewrite and the pointer:

-- 之二,大的错误 FAIL 或 REWORK ⇒ 改标签即交接:按状态机一笔 replace 转回相应工作态。
+- 之二,大的错误 FAIL 或 REWORK ⇒ 改标签即交接,端态按 `../contract-review.md` 载体纪律。

The 之二 enumeration and the ⛔ 空交接 line at :37 are untouched — the latter is where 「只贴复审评论不改标签 = 空交接」 already lives, alongside state-machine.md's 「交接即标签」, so this PR does not add a third copy of it.

Governed terminal

.claude/** in the diff ⇒ draft PR, maintainer merge; ⛔ never ready, enqueued, auto-merged or approved by a seat. Verbatim (exit 3):

governed-surface predicate: 2 of 3 path(s) hit the register (5 surfaces, repo-agnostic).
  ⛔  GOVERNED — a human merge is the review record for this PR (#9495 regime).
      No seat flips it ready, enqueues it, or arms auto-merge (AGENTS.md Prime Directive #14).
      One hit governs the whole PR — 「混合 diff 一条命中即整 PR 分叉」; proportion is not a question.
      .claude/** ×2 — the agent instruction tree (skills, agents, hooks, settings)
        - .claude/skills/pm-dispatch/references/contract-review.md
        - .claude/skills/pm-dispatch/references/lanes/director.md
  paths not on the register: scripts/pm/check-half-states.mjs

skip-changeset measured, not assumed: 0 of the repo's package manifests carry a files[] entry that could ship .claude/** or scripts/pm/** (positive control — packages/spec files[] reads ["dist","json-schema","liveness",…]).

维护者速读(草稿)

改了什么 — 契约复核闸门 needs:contract-review 上加两条只报告、不写标签的巡检行,外加一句治理文本。① H51:PR 还挂着闸门,而它自己的评论线程里已经有一份针对当前 head 的复核结论,超过 60 分钟没有任何标签动作。② H53:卡上挂着闸门,却既没有 Claim: 认领评论、也没有任何 PR —— 「有载体、无增量」。③ contract-review.md 补上 FAIL 的终态一句(此前只有 PASS 分支写清楚了)。

为什么改 — 一个班次里十二个 PR 拿到了复核结论,2 到 5 小时无人响应,最后是维护者先发现的(「已审 9 个 objectstack PR,为什么还是挂着待契约复审的 label」)。席位的巡检读标签,不读 PR 散文,所以只写成评论的结论谁也到不了。另一侧:裁定落地的第二天早上,同一个分诊席又预挂了 13 个空载体 —— 靠一次总监召唤才发现。两种都是「标签在说谎」,而没有任何机器读得出来。

风险与代价(含回滚) — 风险低:两行都是 report-only,绝不摘任何载体(摘除是有据可查的人的动作)。代价是每轮巡检多买少量评论线程 —— 7 个挂标 PR(与 H48 共用缓存)加 9 张挂标卡,各一页。行数棘轮未抬,靠删掉被取代的那一行付账。回滚 = revert 两个 commit,巡检回到今天的状态,不影响任何已发布物。

席位意见 — (留空,待席位定稿)

你要做的 — 这是受管面(.claude/**),按规矩只能您本人合并;席位不会转 ready、不入队、不挂 auto-merge。合并前值得看一眼的只有一处:contract-review.md 新增的那句 FAIL 终态,和 director.md 由「转回相应工作态」改成指向该单源的那一行。

Acceptance notes

🤖 Generated with Claude Code

https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ


Generated by Claude Code

… label stroke, and the FAIL end-state it applies

`needs:contract-review` is a dual-carrier gate whose verdict is a HANDOFF, and
the handoff's second act is a LABEL STROKE. Twelve PRs took a verdict in one
shift and no owning seat responded for 2-5 hours: the seats' sweeps read labels,
not PR prose, so a verdict recorded only as a comment reached nobody. H31 is
clean throughout — it compares the two carriers with each other, and both were
correctly on.

Two halves, both from the filing card:

- `references/contract-review.md` 载体纪律 names the FAIL end-state, which the
  text never did (the PASS branch was precise, the FAIL branch said only
  「转回相应工作态」 into a state model with no rework state). Paid in place by
  deleting the vague line it subsumes; ceiling untouched at 60/60.
  `lanes/director.md` 职责一 is rewritten in place to point at that single
  source rather than carry a second copy; 72/72 untouched.

- H51 files a report-only row for an OPEN gated PR whose thread already holds a
  contract-review verdict for the CURRENT head, older than 60 minutes. Verdict-
  agnostic on purpose: both branches owe a stroke inside the window, and a row
  that parsed the verdict would be issuing one. A verdict naming an OLDER head
  is clean — the head moved, so the carrier is genuinely live again.

The anchor is measured rather than quoted: three title dialects were live on the
same day plus a director adoption record carrying the review verbatim, so the
row reads the two things all four share — a `## Contract review` heading line
and the head sha as a code span. Pinning the card's literal title would have
been silent on two dialects while reporting a clean board.

Reuses H48's `prCommentCache`, whose header reserved exactly this reader;
`head.sha` and `labels` ride the open-PR list row, so head identity costs no
request. The H52 self-test's `H51` reservation pin flips to its landed side.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ
…m and no PR

The gate rides an increment: 「PR 一存在即挂;报告先于 PR 到达则先挂卡侧」 says
where it is hung, 「⛔ 不前瞻预挂」 says where it is not, and the invariant both
serve is 「开着的载体恒 = 真实待审」. Fourteen open cards across two repos
carried it with no `Claim:` comment and no PR at all — thirteen written the
morning after a ruling restating the discipline merged into the text that seat
reads. All were found by a director summon; no sweep could see them.

H31 needs two carriers to compare, so with no PR it is INAPPLICABLE rather than
clean, and its header deliberately declined the「gate on a card with no PR is
premature」 shape because card-side-first is legal. The CLAIM leg is what lets
this row read the half that premise excluded: a dev executing card-side-first
has been dispatched, so its thread carries a claim. Claim present ⇒ silent.

⛔ Report-only, in H31's own register — the subject is a GATE, the row never
removes a carrier and never asks a script to. Removal is a person's audited act.

It BUYS a complete card thread per gated open card rather than reading a cache:
the measured population is unassigned by construction and H2 buys a thread only
for an ASSIGNED card, so a cache-only reading would report the whole target
shape UNJUDGED while looking healthy. 9 gated of 599 open cards, single-page
walks. H31's live `#10025` reading is pinned unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ

Copy link
Copy Markdown
Collaborator Author

维护者速读 — PR #17138(#16836 + #16995 折叠,skills 席终稿,2026-09-09T12:38Z)

改了什么 — 契约复核闸门 needs:contract-review 上加两条只报告、不写标签的巡检行,外加治理文本各一行。① H51:PR 还挂着闸门,而它自己的评论线程里已有一份针对当前 head 的复核结论,超过 60 分钟没有任何标签动作(结论内容不读、不判 PASS/FAIL)。② H53:卡上挂着闸门,却既没有 Claim: 认领评论、也没有任何 PR 交付它——「有载体、无增量」;卡先挂载体、dev 已派发的合法序列有认领评论,不会被点名。③ contract-review.md 载体纪律补上 FAIL 的终态一句(FAIL 同 PASS 剥双载体、卡上留交接评论、卡态与 assignee 不动),删掉被它取代的旧句;director.md 职责一那行改成指向该单源。行数 60/60、72/72 不动,不抬上限。

为什么改 — 一个班次里十二个 PR 拿到复核结论后 2 到 5 小时无人响应,最后是你先发现的(「已审 9 个 objectstack PR,为什么还是挂着待契约复审的 label」);席位的巡检读标签不读 PR 散文,只写成评论的结论谁也到不了。另一侧:裁定落地的第二天早上,分诊席又预挂了 13 个空载体,靠一次总监召唤才发现。两种都是「标签在说谎」而没有机器读得出来。你在 #16836 上的原话「立一张 domain:skills 卡(一句终态 + 一条 H 检查)」,本 PR 就是那一句加那一条,并把 #16995 的同形行折叠进来。

风险与代价(含回滚) — 两行都是 report-only,永不摘任何载体(摘除是有据可查的人的动作);每轮巡检多买少量评论线程(7 个挂标 PR 与 H48 共用缓存,9 张挂标卡各买一页),未判定的明写 UNJUDGED 不冒充干净。dev 实测复核评论标题在同一天有四种写法,所以 H51 认标题行 + head sha 两样,四种都钉了用例——写侧要不要收敛成一种是协议问题,本 PR 不动。回滚 = revert 两个 commit。

席位意见 — 接受。本席在分支头上复核:三行治理文本原文如上、宽度 120/110 B、两份账本零余量不动;被删句在 SKILL.md :644 有原文;两条谓词读过,新增行里无任何标签/assignee/PR 写入;check-half-states --self-test 本地重跑 2976 全过;merge-tree 干净;CI 20 绿 / 11 跳过 / 3 在跑(12:35Z)。总监席留在 #16836 上的「Co-Authored-By 不是档位证据」那一句,已另立 #17139,不随本卡关闭而丢。

你要做的 — 看一眼 contract-review.md 新增的那句 FAIL 终态与 director.md 那行指针;CI 全绿后人工合并,或由 os-zhuang / hotlong 批准后本席入队(受管面,席位不翻 ready、不入队、不挂 auto-merge)。合并后本席按串行派 #16688#17017#17139


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 9, 2026 15:29
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 7337179 Sep 9, 2026
41 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-16836-contract-review-fail-handoff-h51 branch September 9, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-user-decision size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants