pm-dispatch: make check-clause2-carriers C4 recognise the live contract-review verdict dialect, and name the session a reviewer writes - #17368
Conversation
…review verdict dialect C4 (the independence carrier) discriminated a verdict comment with `VERDICT_MARKER` alone -- measured on the 2026-09-01 board, where a verdict opened a fenced `VERDICT: PASS` block. Every verdict on the 2026-09-09 board is written the other way: a `## Contract review` heading, the head as a code span, `**Verdict: PASS WITH FINDINGS**` in bold. All four live specimens read `null`, so C4 had no live population and `--pair` exit 0 said nothing about independence for any pair reviewed in the current dialect. Three halves, all measured offline against the live comment rows: 1. Recognition: `isVerdictComment` accepts EITHER the fenced marker OR H51's heading plus `contractReviewHeadMatch` against the pair's head -- both IMPORTED from `check-half-states.mjs`, the same two facts C6 already reuses, so the two rows cannot disagree about what a verdict looks like. No regex for the verdict WORD in either dialect. 2. Threads: C4 judges the card thread always and the PR thread when `gather` already holds it (COMPLETED pairs). Measured 2026-09-10: all four live verdicts sit on the PR thread and none of the three delivering cards carries a `## Contract review` heading at all, so a card-only C4 stays silent however good its discriminator is. A pending pair's PR-thread verdict is a declared limit, not a bought read. 3. Value decoration: the key regexes tolerate `**Implemented-by:**` -- the colon inside the bold -- but the value readers admitted a leading `**` only with no space after it, so `- **Implemented-by:** `+backticked token read as unreadable. Decoration is now stripped symmetrically; words are not, so `branch `+token and prose values stay refused. `Reviewed-by:` still admits a session only (2026-09-02 reading a, unchanged). The live prose values therefore read `malformed`, which is the true reading of a verdict written without its reviewer's session; the remedy is on the writing side, and `references/contract-review.md` :35-:37 now name it -- the seat that renders or ADOPTS the verdict writes its own session, and an adoption record already carries one. Self-test: C4 battery 52 -> 89 cases (390 -> 396 total), fixtures taken from the live bodies, one per dialect. The C6 reference record's `Implemented-by:` value is corrected to put its token first after the colon: written `branch `+token it reads `malformed` the moment C4 can see the dialect, which is a real carrier defect and belongs on the defective specimen, never on the reference one. Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK Co-authored-by: Claude <noreply@anthropic.com>
…tures `dispatch-gates --self-test` holds the tier constant's VALUE to exactly one spelling under `.claude/skills/pm-dispatch` + `scripts/pm` -- the maintainer's own rule, 「needs:fable-review 这个标签不好,下次模型升级怎么办」. The C4 fixtures added for the 2026-09-09 dialect quoted the model id out of the live bodies (five sites), which reds that case. The fixtures carry the SHAPE, not the id: a `Reviewed-by:` value that is prose with no session token is what the reader is being pinned on, and the model name is decoration. Importing the constant was tried and rejected -- it makes `check:pm-clause2-carriers` name a path while its own marker declares no-path-population, which reds a different case in the same self-test. Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK Co-authored-by: Claude <noreply@anthropic.com>
维护者速读改了什么 —— 一个 PM 门禁脚本( 为什么改 —— 改之前 C4 在真实看板上一条都没触发过: 风险与代价(含回滚) —— 判别面变宽后的误伤防线两条未动:两行皆无的历史裁决恒静默; 席位意见 —— 通过。本席在契约复审档对照 diff 自读,并在 head 的独立检出上复跑:自测 396 例通过;对本 PR 的实况 你要做的 —— 一个动作:人工合并本 draft PR。 skills 席,session Generated by Claude Code |
Fixes #17346
check-clause2-carriersrow C4 — the independence carrier — discriminated "a contract-review verdict comment" withVERDICT_MARKERalone, measured on the 2026-09-01 board where a verdict opened a fencedVERDICT: PASSblock. Every verdict written on the 2026-09-09 board uses the other dialect: a## Contract reviewheading, the reviewed head as a code span,**Verdict: PASS WITH FINDINGS**in bold mixed case. C4 saw none of them, so it had no live population and--pairexit 0 said nothing about independence for any pair reviewed in the current dialect.Probe — the four live comments the card names, before and after
Bodies read via repo-scoped REST on 2026-09-10 and judged offline with the file's own exported reader.
BEFOREisreadVerdictAuthorship(body)ate669e5ee;AFTERisreadVerdictAuthorship(body, head)plus the C4 row on a pair carrying that comment.nullmalformednullmalformednullmalformednullmalformedmalformedis the correct reading, not a false positive: every one of the four writes itsReviewed-by:value as prose naming the reviewing MODEL, and none carries a session token. Two examples of the row's own detail text, verbatim from the run:The remedy is on the writing side, and the identity it needs already exists: specimen 5597841101 names
session_01Tep4AYXZvyBA7jsvne5KZVin its own adoption sentence, one line above aReviewed-by:line that names no session at all.What changed, in three parts
1. Recognition —
isVerdictComment, one shape shared with C6. A comment is a verdict if it carries the fencedVERDICT:marker (2026-09-01, kept) or H51'sCONTRACT_REVIEW_HEADING_MARKERpluscontractReviewHeadMatchagainst the pair's head. Both are IMPORTED fromcheck-half-states.mjs, exactly as C6 imports them, so the two rows cannot disagree about what a verdict looks like. No regex for the verdict WORD in either dialect — the file's自查放行boundary is unmoved. The head is REQUIRED for the heading path, so a caller with no head in hand reads exactly the pre-#17346 behaviour rather than something looser.2. Threads — measured, and a declared limit rather than a bought read. Zone 2 assumption A holds mechanically but its practical half is falsified: reusing C6's helpers is free, but a card-only C4 still fires on nothing. Measured 2026-09-10 over the three delivering cards:
## Contract reviewheadingAll four live verdicts sit on the PR thread. C4 therefore judges
verdictThreadRows(pair)— the card thread always, plus the PR thread whengatheralready holds it. Per pair state:needsRecordReadtrue, the pass C6 added): card thread + PR thread.3. Value decoration — a third gap the same corpus exposed. The key regexes tolerate
**Implemented-by:**(bold wrapping the key and its colon), which is what every 2026-09-09 specimen writes. But the colon inside the bold leaves the captured value opening with the closing**, and the token regexes admitted a leading**only when a backtick or the token followed with no space between. Measured:So a comment whose author had written a perfectly good identity read
malformed— a false positive produced by the reader, and a different fact from the corpus's own prose values.VALUE_DECORATIONnow strips spaces,**and backticks symmetrically. It strips decoration only, never a word:branch+token,the dev on claude/…andisolated+model+subagentall still refuse.Rulings held
Reviewed-by:still admits a session only (2026-09-02 reading a, 「同意」). ⛔ Not widened to prose, a branch or a model name. Admitting the prose would retire the reading outright: a value that names no identity compares to nothing, so the equality test that is the independence clause would answer about every pair the way it answers about none.Implemented-by:keeps both grammars (session id, or amode:subagentdev's branch).dispatch-gates.mjs, and the fixtures carry the SHAPE of a prose reviewer value, never the model name (maintainer, 2026-08-16: 「needs:fable-review 这个标签不好,下次模型升级怎么办」).Zone 2 B — one direction falsified, and pinned
B says recognising more verdicts "can only turn a silent 0 into a 4, never a 4 into a 0". Newest-governs makes one narrow exception real: a newer INDEPENDENT verdict, previously invisible because it was in the heading dialect or on the PR thread, displaces an older card-thread self-review and the row clears. That is C4's own declared remedy arriving through a widened reading — the row's text already prescribes exactly it ("an independent re-review CLEARS this row rather than leaving the pair red forever"). It is documented on
verdictAuthorshipand pinned in both directions in the self-test rather than left to be discovered. The--pairexit table itself is untouched:malformedandself-revieware still exit 4.references/contract-review.md:35–:37 — per line, with byte countsNet-zero: 60 lines before, 60 after. Ceiling 60, unchanged. Every line at or under the 120-byte cap.
The layout moved to one key per line so the new fact fits without a new line: :35 now carries
Implemented-by:and both dev grammars, :36 carriesReviewed-by:and the isolated-reviewer rule (the deliverable), :37 carries the comparison, the legacy silence and — newly — the placement rule the corpus fails 4 times out of 4 (值紧跟冒号,前置词即不可读).Self-test roster delta
+37C4 cases, in five groups: the second dialect and its controls (a head is required; another head is not this one; a###sub-heading is not the marker; the verdict WORD alone is still not a discriminator); the reviewer grammar held (bold**Reviewed-by:**+ session ⇒ judged normally; prose ⇒malformed, naming the missing session token and the writing-side remedy); the decoration asymmetry with its word-stripping controls; the thread set per pair state including the declared limit; and newest-governs across the two threads in both directions. Fixtures are the live bodies, trimmed to what the reader reads — one per dialect (adoption record with the heading on a later line; bold-bullet; verdict word bolded inside the heading) plus one written the way :36 now names.In-surface bounded fix, declared: the C6 reference fixture
RECORDwrote- **Implemented-by:** branch+token — a word before the token, which C4's grammar has always refused. It was invisible while C4 could neither see the heading dialect nor read the PR thread; with this change the reference pair readsmalformedand the#14155clean-specimen pin goes red. Corrected to put the token first, with a comment saying why and a new pin asserting the fixture is legal under both rows. The defective spelling is deliberately kept on theADOPTIONfixture, which is a live-shaped specimen. Same defect class, mechanical, same gate family, no other claim on the file.Verification
Recorded in the report comment on #17346, with each exit code captured before any pipe.
Acceptance notes
readVerdictAuthorshipgained an optional second parameter andcardVerdictAuthorshipwas renamed toverdictAuthorship(it never was card-specific, and it now reads both threads — a name that lies is the drift this file exists against). Grep confirms no caller outside this file.CONTRACT_REVIEW_TIERfromdispatch-gates.mjswas tried and rejected: it makescheck:pm-clause2-carriersname a path while its ownno-path-populationmarker declares none, which reds a different case in the same self-test. The fixtures carry the shape instead.GET /repos/{o}/{r}/issues/comments/5600627944answers 404 twice while the same row is served fine byGET /repos/{o}/{r}/issues/17116/comments; a known-good control id on the same endpoint answers 200 in the same session. The card's stated probe recipe is not reproducible for that one row. A platform reading, not a repo defect, andreferences/platform-readings.mdis outside this card's file surface. Successor: the next card that touches the REST comment-read channel.Implemented-by:with a word before the token (branch+token,the+seat+dev+session, backticked handle first). The reader is right and the corpus is wrong; :37 now states the placement rule, which is the whole remedy available inside this card's byte budget.维护者速读(草稿)
改了什么 — 契约复核裁决的机读判别器只认 2026-09-01 那版写法(围栏块里一行大写
VERDICT:)。9 月 9 日起所有席位改用另一种写法(## Contract review题头 + head 码段 + 加粗**Verdict: …**),判别器一条都不认。本 PR 让 C4 同时认这两种写法(题头那半直接复用 C6 已导入的 H51 判据,不新写正则),并顺带修好一处读值缺陷:键名可以加粗、值却不能,导致写法完全正确的裁决也被判成残缺。参考文档contract-review.md三行原地改写,行数不变。为什么改 — 改之前 C4 在真实看板上一条都没触发过:
--pair答 0 时,对「复核人是不是自己审自己」这件事什么都没说。四条真实裁决现在全部触发,读数是malformed(裁决里没写复核席位的 session)——这是真实的载体缺陷,不是误报,补救在写的一侧,所以参考文档同轮点明该写谁的 session:渲染或采纳该裁决的席位自己的 session(隔离复核子代理没有 session,采纳它的席位有,采纳记录里本来就带着)。风险与代价(含回滚) — 主要风险是判别面变宽后误伤历史裁决:两条防线未动 —— 两行皆无的老裁决恒静默(legacy),
Reviewed-by:仍只收 session、⛔ 没有放宽成散文。一处方向确实变了并已写明:更新的独立复核可以清掉更早的自审读数(那正是这行规则自己写的补救办法),自测双向钉住。改动只在一个门禁脚本和一份参考文档里,不动任何发布包;回滚 = revert 本 PR 的提交,门禁即回到今天的沉默状态。席位意见 — (留空,待席位定稿)
你要做的 — 人工合并本 draft PR(
.claude/**属受管面,AI 席位不合并、不入队、不 approve)。Generated by Claude Code
Generated by Claude Code