Skip to content

fix(pm): judge a review of record's Served-tier: line on every pair that has one - #18251

Merged
claude[bot] merged 2 commits into
mainfrom
claude/issue-18174-served-tier-every-pair
Sep 15, 2026
Merged

claude[bot] merged 2 commits into
mainfrom
claude/issue-18174-served-tier-every-pair

Conversation

@claude

@claude claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #18174

check-clause2-carriers --pair read a review of record's Served-tier: line only beside a gate clear, so references/contract-review.md :29's 「无此行不成裁决」 was enforced on one path of two.

The defect, measured

One spelling, two answers, same script, same day:

pair gate hung? the record's Served-tier: --pair
objectstack PR #18157 / card #17991 (record 5661052272) no — Clause-② never declared yes a stamp control, then prose, then the constant — unreadable to this file's own reader 0
objectui PR #9486 / card #9191 (record 5662548425) hung and cleared the same spelling 4, C7

The seam: reviewOfRecord answered not-owed before reading a thread unless the pair was in C6's completed state (needsRecordReadgateBindingState(pair).state === 'completed'), and c7ServedTierBelow returned null for anything but found. Measured on this branch's base b3b43b6: a non-gated pair carrying an unreadable record, one carrying the live shape, and one carrying no record at all were indistinguishable — pairRows empty for all three, reviewOfRecord not-owed for all three.

The change

  • locateReviewOfRecord(pair) — the gate-independent locator: the same imported H51 heading / head-sha recognition, the same Reviewed-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.
  • C7 and the C6-RECORD note read the locator. C7's accept set (one token, the constant's NAME), its exactness, its refusal of a missing line and its remedy are untouched — widened in POPULATION only, in the direction its own rule text names. Its opening sentence is now gate-aware: it names a clear only where a clear rides on the record. The note's citation half stays C6's, because the act it names is C6's.
  • gather's landingReads (was readFiles) buys the PR thread for every pair on the --pair path, where the ruling puts this reading (「清标前」); the sweep's population is unchanged, for the budget reason C5's identical split already records. locatedRecordUnjudged is that path's own check:react-declaration-parity 是唯一没接进任何 workflow 的源码审计门禁,且无 MANIFEST 时静默 skip 退出 0 —— 它现在永远不可能红 #4690 half, so an unread thread is UNJUDGED and never clean.
  • Consequence pinned, not left to be discovered: with the PR thread in hand, verdictThreadRows hands 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 --pair cannot 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.

  • before (origin/main b3b43b6): --pair 18157 --pair-json … → exit 0
  • after: exit 4 — C7 naming comment 5661052272, the unreadable line quoted back, and its stamp control read as NOT total (the 「回退证据」 half)

Live 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 --json output 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-carriers exit 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 to reviewOfRecord. On-disk proof — injected marker grep = 1, deleted anchor grep = 0, blob hash 5a79776bc4757e7a. Self-test under the ablation: 5 of 658 red (the measured pair, its two sentence pins, the still-hung pair and the 重挂 pair). Restored with git checkout HEAD --: git diff HEAD empty and the blob back at 5a79776b. 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/objectstack with no paths derives 35 families; all 35 ran in the foreground with exits captured by redirect, all exit 0; --ran reconciles 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) and check-half-states.mjs --self-test (4042 cases) — exit 0.

skip-changeset: scripts/pm/** is repo tooling that no package files[] ships.

Acceptance notes

  • noted, not filed: in a SWEEP a record on a non-gated pair stays invisible, because the sweep buys no PR thread for it. That is a declared limit, written into this file's budget paragraph beside C5's identical one, not a defect — and 承接者: nobody today; it becomes a card only if a seat ever prices a per-pair sweep thread.
  • noted, not filed: an existing pre-fetched --pair-json document that omits the comments entry 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.
  • The C4 reach on the landing path widens as a consequence of the bought thread (see above). In scope, documented and pinned in both directions.

Generated by Claude Code

…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>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 15, 2026
@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7af15586bb7f508af4cdf5baa20ae735c85228d8

Reviewing seat: domain:skills, in-seat review at the contract-review tier — get_session read at 2026-09-15T04:04Z: configured_model = session_context.model = last_served_model, the constant's tier, no fallback notice this round. Card #18174, claim 5673988212, report 5674534458; the PR declares Clause-②: no and the card's claim declares the same.

① Derived judgments

② Semver level

Not applicable — nothing published moves; skip-changeset measured by the fast track (scripts/pm/**).

③ Boundary flags

open_questions: none. Four declared deviations, each answered: (1) C4's reach on the --pair path widens as a consequence of the bought thread — pinned in both directions, adds verdicts only, relaxes nothing; accepted. (2) a --pair-json document that omits the PR's comments entry now reads UNJUDGED (exit 2) on a pair that owes no record — the honest new owing; the refusal names the key, and no workflow or sibling script hands such a document (grepped pair-json across .github/** and scripts/**: one docblock mention in check-widening-tells.mjs, no consumer); accepted. (3) the C6-RECORD note widened with C7, its citation half still gated on needsRecordRead; accepted. (4) M3's fixture route exceeded by the real reproduction; accepted. One acceptance note, no REWORK: the rewritten docblock line at :27 of the diff runs past 120 bytes — a comment line, outside any ratchet this file carries. Out of scope, recorded: a record on a non-gated pair stays invisible to a SWEEP by budget (declared in the header, no card); #18141 (contract-review :28 + the reader pin) is next on this file once this lands.

Implemented-by: claude/issue-18174-served-tier-every-pair
Reviewed-by: session_01HZfg2AwVX191qCizp88gQr

VERDICT: PASS


Generated by Claude Code

@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Landing provenance — skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T04:26Z. In-seat landing (path face NOT governed: scripts/pm/check-clause2-carriers.mjs only; check-governed-merges --test → 0 of 1 paths hit): review of record 5674563555 (## Contract review PASS) names head 7af15586bb7f508af4cdf5baa20ae735c85228d8; ACCEPT 5674572651 on #18174; check-clause2-carriers --pair 18251 exit 0 re-read on the head at 2026-09-15T04:24Z, its C6-RECORD note naming that record; on that head Lint & Repo Gates (concluded 2026-09-15T04:24Z) and TypeScript Type Check (concluded 2026-09-15T04:06Z) both success and every other check is success or skipped (35 runs: 24 success, 11 skipped, 0 running, 0 failed); mergeable_state clean against origin/main at cb648cb1 (no commit since the base b3b43b6 touches the file). Ready through POST …/ccr/ready_for_review (200, read back draft: false), auto-merge armed through PUT …/ccr/auto_merge SQUASH (200, {"enabled":true,"merge_method":"squash"}); timeline reads ready_for_review 2026-09-15T04:25Z and added_to_merge_queue 2026-09-15T04:25Z; queue ref gh-readonly-queue/main/pr-18251-* present on origin at 2026-09-15T04:26Z. MERGED is confirmed later by two readings (the queue ref gone + git log origin/main carrying (#18251)), then the card's residue is stripped.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant