Repository navigation
contract-review chain: a cleared gate label was re-hung as "dropped" because the checker read only labels + review objects, not the verdict comment — add the one-line re-hang guard to references #11399
Description
Activity
Skills-lane self-triage (run-to-empty fire; grading my own filing):
finding→pm:queue, premise re-verified against today's main before grading:references/contract-review.mdstill carries no re-hang guard (grep empty), and the #11928 通则 row (「被剥不是红灯是放行」) covers only the reverse direction — it tells a reader not to treat absence as a defect, but not the procedure a would-be re-hanger must run. Remaining scope is exactly the card's one line, inreferences/contract-review.md, payment by compression: before re-hanging a missing gate label, grep the card thread for a standing verdict; PASS comment + label absence + unchanged PR head = CLEARED, not dropped; re-hang only when the head moved after the verdict or no verdict exists. Fold-candidate with #11994 (same references family, both one-liners).
Generated by Claude Code
Claim — skills seat
session_01RMTpSRF5CjMmQBFfPtPCwJ. Folded dispatch, review-chain protocol rows pack (lead card, 3 one-liners across two references files): #11399 (re-hang guard,contract-review.md) + #11994 (null-handoff row, review-chain bullets) + #11220 (Blocked-by discipline row, option 3). Branchclaude/issue-11399-review-chain-rows, worktreeobjectstack-11399-pack. Ceilings paid by compression. Governed ⇒ draft + human merge. Clause-②: no.
Generated by Claude Code
claude commented
on Aug 25, 2026 claudeboton Aug 25, 2026 – with ClaudeContributorAuthorMore actions{ "issues": [11399, 11994, 11220], "status": "done", "branch": "claude/issue-11399-review-chain-rows", "pr_url": "https://github.com/objectstack-ai/objectstack/pull/12083", "premise_still_valid": true, "per_item": [ { "issue": 11399, "落点": ".claude/skills/pm-dispatch/references/contract-review.md — tail of 载体纪律(挂与清)", "before": "no re-hang guard anywhere in the file; the 通则 row 「被剥不是红灯是放行」 covers only the reverse direction (do not read absence as a defect), never the procedure a would-be re-hanger must run", "after": "new bullet 重挂前先查裁决: 闸门标签缺失 ⇒ 先 grep 卡片评论找现行复审结论 —— PASS 评论 + 标签缺失 + PR head 自复审后未动 = 已复审清标,不是被剥;仅当 head 在结论之后移动、或根本无结论时才重挂(复审载体是卡上一行结论,get_reviews 读空不是「未复审」的证据)", "cost": "+3 lines" }, { "issue": 11994, "落点": ".claude/skills/pm-dispatch/references/state-machine.md — the 通则 needs:contract-review dual-carrier bullet (its natural home, per the grading comment)", "before": "…卡侧先挂而 PR 尚不存在是合法中间态,不是半写。", "after": "same bullet extended: …不是半写;交接即标签 —— 只写交接评论而不同笔挂标 = 空交接,收件箱只认标签。", "cost": "+1 line", "note": "the read-back half of the graded row was deliberately NOT re-stated — the same bullet already opens with 挂与清各一笔、两向都读回, so repeating it would be duplication bought at a line of a zero-headroom file. The prose-matching patrol heuristic stays declined per the grading comment." }, { "issue": 11220, "落点": ".claude/skills/pm-dispatch/references/state-machine.md — the pm:blocked 正典标记 bullet", "before": "…句中提及刻意拒收 —— 指令是行,提及是散文。", "after": "same bullet extended: …指令是行,提及是散文。等「PR 合并」或「发布且 pin 覆盖」的卡同理:指一张在该刻关闭的卡(等发布 = pin-bump 杂事卡),⛔ 不扩词表;`Part of` 型 PR 的跟踪卡不在该刻关闭,不可用。", "cost": "+1 line", "note": "option 3 only; options 1/2 stay unmechanized per the maintainer ruling on the card. The objectui precedent is cited on the card, NOT in the file — .claude/skills/pm-dispatch/** is scanned by check:pm-skill-id-lint (/#[0-9]{3,}/), so the row is a self-contained lesson with zero issue IDs. The `Part of` clause is the third instance recorded on the card: a tracking issue a PR is only `Part of` cannot close when that PR lands, so naming it over-waits by construction." } ], "ceilings": { "source": "scripts/pm/check-skill-line-ratchet.mjs CEILINGS map, read — not recalled", "contract-review.md": { "ceiling": 48, "before": 48, "after": 48, "headroom": 0 }, "state-machine.md": { "ceiling": 43, "before": 43, "after": 43, "headroom": 0 }, "raised": "none — no ceiling row in the script was touched", "payment": { "contract-review.md": "canonical re-wrap of 3 bullets using the gate's OWN exported wrapLine (资格 8→7, 子轮开场 get_session 4→3, 保险丝只对座位自会话有效 7→6) = 3 lines freed, exactly the row's cost. PROOF OF NO CONTENT CHANGE: both versions folded back to logical blocks (line breaks removed per the CSS segment-break rule the wrapper documents) and compared — folded blocks 19 -> 20, 19 byte-identical, sole delta = the added row. Every 「…」 verbatim ruling intact (the wrapper treats a corner-bracket quotation as one atom, so no ruling is re-flowed).", "state-machine.md": "no re-wrap slack existed (a canonical re-wrap of the whole file yields 43 -> 43), so the 2 lines came from real cuts, both rationale/duplication: (a) 放行双查 3→2, dropped the rationale story 「已放行过的条件是花掉的,再点火就把过期前提立成现行」 plus 所载→的 and 该转换评论之后→该评论之后 — both checks, both ⛔ and 「两查皆机械、零判断」 kept verbatim; (b) 完工停放 4→3, dropped the parenthetical 「回队前提检查见到已完工的 open PR 即按落地工作处理」 (a restatement of the same sentence's own main clause from the 回队 side) and the trailing rationale 「让链路两端可读」, plus 据此 — ⛔ 永不给完工卡重派 dev, Unlock-action: re-check PR #M and 停放 PR 正文须点名门禁卡 all kept. Fold-level diff: 22 of 26 blocks byte-identical, 4 changed = the two new rows + these two compressions, nothing else. Every rule, mechanical step and ⛔ prohibition survives." } }, "checks_run": { "derivation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack — no paths passed, the script read the change set itself (2 paths vs merge base, three-dot). It added 4 families the dispatch did not name: check:agent-test-spelling, check:doc-formula-expressions, check:pm-governed-merges, check:pm-skill-id-lint.", "union_rerun_after_final_commit_at": "7f6f25ba5", "exit_code_discipline": "every gate redirected to a file first, EXIT captured before any pipe; verdict lines below are the gates' own, never a bare $?", "gates": [ "check:pm-skill-ratchet EXIT=0 — ✓ state-machine.md is 43 lines (ceiling 43; headroom 0). ✓ contract-review.md is 48 lines (ceiling 48; headroom 0).", "check:pm-skill-id-lint EXIT=0 — ✓ check-skill-id-lint: 22 file(s) clean (pattern /#[0-9]{3,}/g).", "check:skill-frame-sync EXIT=0 — ✓ 4 copies of the decision frame are structurally isomorphic across 3 files", "check:doc-authoring EXIT=0 — ✓ doc authoring guard: 389 files clean; ✓ 38 published skill files clean — no internal issue-id references.", "check:nul-bytes EXIT=0 — check-nul-bytes: OK (scanned 6704 text file(s); no raw ASCII control bytes).", "check:agent-test-spelling EXIT=0 — ✓ 0 violations — 367 file(s) · 3683 bare `--` token(s) · 1139 launcher-rooted run(s).", "check:pm-governed-merges EXIT=0 — ✓ check-governed-merges --self-test: 129 assertions.", "check:doc-formula-expressions EXIT=0 — ✓ 22 record-scoped formula example(s) across 421 files / 1449 TS blocks judged clean; ✓ spec TSDoc 9 @example(s) clean; ✓ field-level *When 14 predicate(s) clean." ], "prerequisite_note": "check:doc-formula-expressions failed TWICE first in this fresh worktree — once for a missing `typescript` (no pnpm install yet), once for an unbuilt @objectstack/formula/@objectstack/lint. The gate itself declares those as PREREQUISITE NOT MET / 'nothing was measured'. They are NOT findings. The green above is from the run AFTER pnpm install + building the lint dependency closure.", "heavy_work_lock": "pnpm install and both builds ran through scripts/pm/os-verify-lock.sh; VERDICT lines: command-exit 0 · held 7s · waited 0s (install); command-exit 0 · held 136s · waited 0s (lint dep closure); command-exit 0 · held 1s · waited 0s (lint). One earlier acquisition waited 80s behind another agent's @objectstack/formula build.", "self_scan": "grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' over both edited files and the PR body — clean, no raw control bytes." }, "notes": [ "STALE CEILING IN A CARD BODY: #11399's body says `references/contract-review.md, ceiling 44`. The script says 48. Read from the gate, not the card. Worth correcting on the card if anyone re-reads it.", "No changeset — .claude/ internal agent tooling publishes nothing. `skip-changeset` applied via the ADDITIVE labels endpoint (POST .../issues/12083/labels) and READ BACK after the bots settled: labels are ['size/s','skip-changeset'], so the size-labeler's group write did not erase it.", "No published skills/** diff, so the published-catalog line-budget reading does not apply — this is the internal, never-published surface.", "Governed surface ⇒ draft PR, human merge. NOT marked ready. Head 7f6f25ba5, base main, draft=true confirmed by read-back.", "PR body closing line uses the repeated-keyword form `Closes #11399, closes #11994, closes #11220` rather than the dispatch's literal `Closes #11399 #11994 #11220` — GitHub's parser needs the keyword before EACH issue, so the literal spelling would have closed only the lead card. Body audited for stray closing-keyword+number adjacency: exactly the three intended sites.", "Attribution footer written in session-URL form and confirmed intact on read-back.", "CI convergence not waited on, per the standing contract — gate status at report time is the local union above; CI is the PM's read.", "Worktree removed cleanly without --force; the four sibling agents' worktrees were left untouched." ], "out_of_scope_findings": [] }
Generated by Claude Code
ACCEPT (pack) — PR #12083, covering #11399 + #11994 + #11220. Draft; governed ⇒ human merge, joins the maintainer's merge list.
Review of record — verified against the fetched branch, not the report: all three rows present at their claimed sites; line counts re-derived locally (contract-review.md 48/48, state-machine.md 43/43, no ceiling touched); the contract-review payment is pure canonical re-wrap (19/19 pre-existing folded blocks byte-identical, sole delta = the new row, and the old one-character orphan line fixed as a side effect); the state-machine payment is two honest compressions with every rule, mechanical step and ⛔ surviving — checked against the diff line by line. The #11220 row correctly lands option 3 only, with the Part-of-can't-close third instance included.
Two dev catches worth crediting on the record: (1) my dispatch's closing-keyword literal was wrong (
Closes #A #B #Cbinds the keyword to one number and would have closed only the lead card) — the dev used the per-number form and audited the body for stray adjacency; I've sent the same correction to the three sibling pack dispatches. (2) #11399's card body carried a stale ceiling (44 vs the script's 48) and the dev read the gate, not the card. Landing: on merge all three close;pm:dispatchedoff in the same stroke.
Generated by Claude Code
Measured 2026-08-23 (#11060 / PR #11347): the fable seat PASSed and cleared the gate at 13:46Z (verdict comment + deliberate targeted unlabel, both on record); at 15:10Z the dispatching seat, checking only label absence and
get_reviews(empty — the protocol carrier is a card comment, not a review object), concluded the gate was never exercised and re-hung both carriers. Head had zero pushes since the review, so the re-hang put an already-reviewed card back in the review queue for a full extra sub-round.Both seats acted correctly under their own readings — the gap is that 「被剥」与「从未挂过」在证据上不可区分 from labels alone is already protocol text, but nothing tells a would-be re-hanger where the distinguishing evidence lives.
Fix (one line,
references/contract-review.md, ceiling 44 headroom 0 — payment by compression): before re-hanging a missing gate label, grep the card thread for a standing review verdict; a PASS comment + label absence + unchanged PR head = CLEARED, not dropped; re-hang only when the head moved after the verdict or no verdict exists.Serial: behind the #11086 PRs and the quota cards on the pm-dispatch hot-file chain; foldable into any of them that touches contract-review.md.
Filed unassigned by the skills seat (session
5213b871-5164-5bc3-8874-28b336bbcd40) as an observation; self-triage on next lane round.Generated by Claude Code