fix(pm): judge a review of record's Served-tier: line on every pair that has one - #18251
Conversation
…at has one `check-clause2-carriers --pair` read the `Served-tier:` provenance line only beside a gate clear: `reviewOfRecord` answered `not-owed` before any thread was read unless the pair was in C6's completed state, so C7 — the row that carries `references/contract-review.md`'s 「无此行不成裁决」 — was consulted on one path of two. Measured on one board in one day, one spelling: record 5661052272 (PR #18157 / card #17991, non-gated) carried a `Served-tier:` line this file's own reader answers `unreadable` for and `--pair` exited 0, while the same spelling on objectui PR #9486 was refused exit 4, because that pair's gate had been hung and cleared. Split the reader in two: `locateReviewOfRecord` finds the record on the head with no reference to the gate, and `reviewOfRecord` is that locator under C6's population gate. C7 and the C6-RECORD note read the locator; C6's row, its 「not-owed」 scope, the accept set, the exactness and the remedy are unmoved. The `--pair` path buys the PR thread for every pair (`landingReads`), the sweep does not, and `locatedRecordUnjudged` is the landing path's own #4690 half so an unread thread is UNJUDGED rather than clean. Claude-Session: https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr Co-authored-by: Claude <noreply@anthropic.com>
The header's budget and doc-shape paragraphs, `verdictThreadRows`'s declared limit and the exit table now describe the reads a `--pair` run actually makes, and the success line no longer prescribes a citation to a pair that owes no clear. Claude-Session: https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Reviewing seat: ① Derived judgments
② Semver levelNot applicable — nothing published moves; ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Landing provenance — skills seat, session Generated by Claude Code |
Fixes #18174
check-clause2-carriers --pairread a review of record'sServed-tier:line only beside a gate clear, soreferences/contract-review.md:29's 「无此行不成裁决」 was enforced on one path of two.The defect, measured
One spelling, two answers, same script, same day:
Served-tier:--pairClause-②never declaredyesThe seam:
reviewOfRecordanswerednot-owedbefore reading a thread unless the pair was in C6's completed state (needsRecordRead→gateBindingState(pair).state === 'completed'), andc7ServedTierBelowreturnednullfor anything butfound. Measured on this branch's baseb3b43b6: a non-gated pair carrying an unreadable record, one carrying the live shape, and one carrying no record at all were indistinguishable —pairRowsempty for all three,reviewOfRecordnot-owedfor all three.The change
locateReviewOfRecord(pair)— the gate-independent locator: the same imported H51 heading / head-sha recognition, the sameReviewed-by:third fact, the same newest-governs choice, with no reference to the gate. No recognition moved.reviewOfRecord(pair)— that locator under C6's population gate. C6's row, its 「not-owed」 scope, its shape sentence and its remedy are unmoved, and both rows still read ONE comment chosen once.gather'slandingReads(wasreadFiles) buys the PR thread for every pair on the--pairpath, where the ruling puts this reading (「清标前」); the sweep's population is unchanged, for the budget reason C5's identical split already records.locatedRecordUnjudgedis that path's own check:react-declaration-parity 是唯一没接进任何 workflow 的源码审计门禁,且无 MANIFEST 时静默 skip 退出 0 —— 它现在永远不可能红 #4690 half, so an unread thread is UNJUDGED and never clean.verdictThreadRowshands C4 a thread it previously saw only on completed pairs, so a verdict on a non-gated or still-hung pair is now judged for the independence pair it declares. That direction only adds verdicts to a reading whose newest-governs rule already lets a later independent verdict displace an older self-review. Pinned in both directions; the sweep's blindness there is restated as the limit that survives.Evidence
Reproduction on the real historical pair, replayed offline. Both PRs are merged, so
--paircannot form the pair from the live board; the document carries the fetched/pulls/18157,/issues/17991, both comment threads and the file list, and nothing else.origin/mainb3b43b6):--pair 18157 --pair-json …→ exit 0Live control, PR #18243 / card #18229 — open, non-gated,
Clause-②: no, record 5673963268 in the template's shape: exit 0 before and after. After, the run also prints the C6-RECORD note naming that record and saying the pair owes no clear (4 reads → 5).The sweep is unmoved, deterministically: the same document in sweep mode produces byte-identical
--jsonoutput before and after. A live gated pair (#18212) reads exit 4 with the same rows on both.Self-test: 629 → 658 cases,
pnpm check:pm-clause2-carriersexit 0. A new battery (#18174: …, 28 cases) carries the measured non-gated spelling beside the live one; the roster floor moves 22 → 23 and C7's own floor 42 → 43. Four of C7's population pins were re-triaged rather than left green for the wrong reason: each now spells a pair whose threads were READ and carry no record, instead of passing because no thread was ever fetched.Reverse verification, from the committed fix:
c7ServedTierBelow's locator call mutated back toreviewOfRecord. On-disk proof — injected marker grep = 1, deleted anchor grep = 0, blob hash5a79776b→c4757e7a. Self-test under the ablation: 5 of 658 red (the measured pair, its two sentence pins, the still-hung pair and the 重挂 pair). Restored withgit checkout HEAD --:git diff HEADempty and the blob back at5a79776b. Direction: turns red, as predicted.Gates, union re-run at
7af15586(git rev-parse --short HEAD), working tree clean:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackwith no paths derives 35 families; all 35 ran in the foreground with exits captured by redirect, all exit 0;--ranreconciles 35 derived / 35 run / 0 NOT-MEASURED / 0 UNRUN, every exit code recorded. Beyond the derivation:pnpm lint(repo-wide,eslint . --no-inline-config) exit 0, and the two siblings that import this module —check-governed-queue-guard.mjs --self-test(238 cases) andcheck-half-states.mjs --self-test(4042 cases) — exit 0.skip-changeset:scripts/pm/**is repo tooling that no packagefiles[]ships.Acceptance notes
--pair-jsondocument that omits thecommentsentry for the PR now reads UNJUDGED (exit 2) on a pair that owes no record, where it read 0 before. That is this change's own documented owing, restated in the doc-shape paragraph, not a separate finding. 承接者: the MCP-only seats that hand this file a document — the file's own refusal text names the key to add.Generated by Claude Code