Repository navigation
pm-dispatch(contract-review): the lane reviews its own PRs, triage takes only an owed review nobody claims (drafts included); one Contract-review-claim line per head; an in-repo owed-review list - #27295
Conversation
…trigger text (WIP) Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg Co-authored-by: Claude <noreply@anthropic.com>
…lf-test (WIP) Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg Co-authored-by: Claude <noreply@anthropic.com>
|
domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg Contract reviewServed-tier: Read at 2026-10-28T15:05Z by the ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
|
domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg 维护者速读 — 席位终稿 · PR #27295 · head
|
Fixes #27062
Clause-②: no
Metadata-protocol: none
维护者速读
Contract-review-claim:;欠审清单从会话临时脚本改成仓内脚本scripts/pm/check-owed-reviews.mjs。.claude/skills/pm-dispatch/references/contract-review.md新增 6 行(L23–L28:触发条件、认领行形状、失效与车道优先)与 L60 改写;②scripts/pm/record-recognisers.mjs的readContractReviewClaim/contractReviewClaimLapse/contractReviewClaims(风险点:同 head 双认时谁持有);③scripts/pm/check-owed-reviews.mjs的owedReviewRow(TAKE/WAIT/HELD/DONE 判定)。node scripts/pm/check-owed-reviews.mjs,预期首行check-owed-reviews — objectstack-ai/objectstack, …: N open PR(s) read (M draft); K owe a contract review — …,其后每个欠审 PR 一行,TAKE 在前,退出码 0。What changes
The card's Wanted 1–6 as amended by the 12:23Z scope amendment (drafts are in; an in-repo owed-review list).
Item 1, the lane reviews its own — unchanged (
contract-review.mdL20 「谁跑 = 派发席」).Items 2–3, the gap-only trigger and the claim line —
references/contract-review.md〈复核归属与资格〉, six new lines after the hand-off line: triage takes only an owed PR (draft or ready) with no record and no live claim on the current head, and one of (a) a hand-off line naming the current head or the waiting label, taken the same round; (b) the head has waited one full triage round (about 60 minutes); (c) the lane seat has stood down or is absent from the roster. Every reviewer reads the PR first; a record or a live claim on the current head means do not start; otherwise it posts one claim line per head first:(the file spells the placeholders in angle brackets; they are spelled out here because the platform strips tag-shaped fragments). A claim lapses when the head moves or 2 hours after posting with no record; when a lane claim and a triage claim name one head the lane wins and triage replies once to withdraw and discards its subagent's work. Delta re-reviews stay with the earlier reviewer — the existing rule (now L41) 「只落发现的 commit 由同一复核者 delta 复核」 already says it for both sides, so no line was added for it.
〈等复核标记〉 — L54 「只带标记无交接行者仍归本席,分诊 ⛔ 不碰…」 is deleted: the amendment makes the label condition (a), so that sentence is now false; its second half (a hand-off goes stale when the head moves) is carried by the new (a) line, which counts only a hand-off naming the current head. The ⛔ line (now L60) keeps check, workflow and queue guard off the label and adds the claim line to what they never read; 「巡查脚本」 leaves it because the owed-review list now reads the label as one owed limb (report-only, never a verdict).
Item 6, the round step —
references/triage-duties.mdL11–L12: each round runscheck-owed-reviews.mjsfirst (open PRs including drafts, hand-off and claim lines read), and an owed review is claimed and taken the same round; L12 keeps the isolated at-tier subagent signed by the seat and the quota rule. The dropped clause 「交接行读法record-recognisers.mjs」 has its home in the script, which reads hand-off lines through that file.Item 5, the round record —
references/triage-duties.mdL16 in place: the review table row names the claimer (认领者 · 记录 head→现 head→状态).Item 4, the recogniser —
scripts/pm/record-recognisers.mjs, besidereadContractReviewHandoff:CONTRACT_REVIEW_CLAIM_KEY/CONTRACT_REVIEW_CLAIM_TEMPLATE/CONTRACT_REVIEW_CLAIM_LAPSE_HOURS;readContractReviewClaim(body)(session in the signature line's session shape, imported; the FULL 40-hex head; the UTC minute, seconds allowed; read throughstripSignatureLine);contractReviewClaimLapse(pure:live/head-moved/expired, timed from the comment'screated_at);contractReviewClaims(rows, head, { now })(the holder, lane first on a tie; a claim is triage's only when its comment is signedtriage#N). ⛔ No gate reads it.Item 6, the owed-review list — new
scripts/pm/check-owed-reviews.mjs(named asscripts/pm/names its report-only readers,check-half-states.mjsthe model): read-only, prints one row per open PR (draft or ready) whose face owes a review — the face fromreviewFace's path hits (reused, not re-implemented), the PR body'sClause-②:line throughreadClause2Line, and the waiting label; the record on the current head throughlocateReviewOfRecord(stale records name their reviewer, the delta owner); the hand-off throughcontractReviewHandoff; the claim throughcontractReviewClaims; the lane seat from the card'sdomain:*label against the open seat posts (fleet-roster.mjsparseSeat); a statusTAKE/WAIT/HELD/DONEand the trigger that holds. Exit 0 on every read; a failed read printssweep INCOMPLETE, never clean; usage errors exit 2.--self-testcarries a battery roster, floor and verdict handshake.Wiring (no workflow line) — root
package.jsoncheck:pm-record-recognisersnow runsrecord-recognisers.mjs --self-test && check-owed-reviews.mjs --self-test, so the existinglint.ymlstep runs the new self-test;check:self-test-wiredis green on it.Companion registration — the recogniser's claim battery pins the template and its 2-hour lapse verbatim in
contract-review.md, a read of a governed file: the existingself-test-readsdeclaration (line 77, unmoved) names both files andGOVERNED_READ_FLOOR(scripts/pm/dispatch-gates.data.mjs) gains the row.dispatch-gates.self-test.mjs's census is unchanged (same declaration, same line).Line budget
.claude/skills/pm-dispatch/references/contract-review.md.claude/skills/pm-dispatch/references/triage-duties.mdEvery edited line is ≤120 bytes (
wc -c): contract-review.md L23–L28 116, 116, 105, 117, 64 (the code line), 120, L60 113; triage-duties.md L11 115, L12 118, L16 120. contract-review.md spends its 5 free lines plus the one freed by deleting L54 (home above). No re-wrap was used to buy a line.Tests
Self-tests, counts before → after
node scripts/pm/record-recognisers.mjs --self-test: 276 → 297 cases (batteries 15 → 16; the claim battery 21 cases, floor 21).node scripts/pm/check-owed-reviews.mjs --self-test: new, 49 cases in 8 batteries (floor 8), including a fixture modelled on the two Tier H drafts that waited (a workflow-step draft with a hand-off on its current head since 07:43Z; a narrowing draft whose first hand-off went stale on a merge-forward and was renewed): both list asTAKEunder(a) hand-off, and readHELDonce the triage claim is posted.blob == HEADand an emptygit diff HEAD(scripts/ablation-replace.mjs): the tie rule (holder = live[0]) reds 「the LANE wins a tie」 (1 of 297); the lapse boundary (>=→>) reds the lapse case (1 of 297); excluding drafts from the owed list reds the owed-review self-test (exit 1).Live run, read-only (14:39Z)
node scripts/pm/check-owed-reviews.mjsexit 0 in 47 s: 22 open PRs read (16 draft); 16 owe a contract review — 3 to take, 8 waiting on the lane, 0 claimed, 5 reviewed on this head. The narrowing draft named on the card readsDONE(record on its current head, PASS, by the triage session; hand-offtriage; claimlapsed); the workflow-step draft has since merged and is not listed.Acceptance notes
dispatch-runbook.md〈巡查常设项〉 still tells a cross-account reviewer to post aClaim:-shaped comment before a review; that is a second spelling of the review claim. Not touched here (that file is held by another claim this wave); noted for the seat.lint.ymlstep comment abovecheck:pm-record-recogniserssays it runs one file's self-test; it now runs two through the package script. A workflow comment is Tier H, so it is left as is.VERDICT:line; owed-ness never depends on it.Verification
Local gates at dca7881 (derived by `dispatch-gates --commands`, reconciled with `--ran`)
node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack→ exit 0: 「50 derived famil(ies) accounted for — 46 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3)」. Exit 0 on all 46 run, among themcheck:pm-skill-ratchet(contract-review.md 60/60, triage-duties.md 102/102, headroom 0),check:pm-skill-id-lint(104 files clean),check:pm-record-recognisers(297 + 49),check:pm-dispatch-gates(1935 cases, fast tier),check:pm-half-states(5034),check:pm-governed-merges,check:self-test-wired(244 scripts),check:ratchet-remedy-authority,check:entry-guard,check:parse-guard,check:nul-bytes;check:doc-formula-expressionsafter building its closure under the verify lock. Run by hand beyond the derivation:check:pm-governed-proseexit 0,check-governed-queue-guard.mjs --self-testexit 0 (401 cases). NOT MEASURED (exit 3, PREREQUISITE NOT MET — a fullpnpm build; the diff touches only a script string in the rootpackage.json):check:dts-closure,check:dual-build-cjs-loads,check:lean-entry-closure,check:sourcemap-no-sources-content— CI runs them.Generated by Claude Code