Skip to content

Commit d3bb7e0

Browse files
os-devclaude
andcommitted
fix(governed): self-test attribution, Tier S replays judged on an ABSENT record, six-surface head pins, core-rules line under the byte cap
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
1 parent d3c87d5 commit d3bb7e0

3 files changed

Lines changed: 29 additions & 15 deletions

File tree

‎.claude/skills/pm-dispatch/references/core-rules.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119
- 停摆永不自愈,按梯度复位、三次即判不可靠重派;报告丢失时按草稿 PR 直接验收。
120120
- 复核对 GitHub 核验 ⛔ 不对自述核验,逐项过清单并亲核形态、范围与整包价值密度。
121121
- CI 收敛读数只属于复核侧;判决三种:验收落卡、返工最多两轮、升级走决策通道。
122-
- 验收后取路径面,命中规则层即分叉 ⛔ 不翻正式不入队;Tier S 席内达档复核 PASS 在案后入队。
122+
- 验收后取路径面,命中规则层即分叉 ⛔ 不翻正式不入队;Tier S 席内达档复核 PASS 后入队。
123123
- 受管面 PR 留 draft 并向两个授权批准账户请审;契约卡无同形复核记录 PASS ⛔ 禁止入队。
124124
- 入队资格是每一个检查全绿 ⛔ 不是必查子集;碰生成物的 PR 入队前先同步再重生成。
125125
- 唯一例外:源码自述 pushed 上按设计而红、不跑 `merge_group`、评论记明门与因,三条全立。

‎scripts/pm/check-governed-merges.mjs‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3394,6 +3394,12 @@ async function selfTest() {
33943394
// register governs, and what that gate requires the prose to say.
33953395
assert('the-declared-form-is-neither-an-exact-nor-a-glob-value', !GOVERNED_SURFACES.some((s) => ROOT_FILE_WATCH_HINTS.includes(s.exact) || ROOT_FILE_WATCH_HINTS.includes(s.glob)));
33963396

3397+
// ── subject → PR (both GitHub spellings; the trailing parenthetical wins) ─
3398+
assert('squash-subject', pullNumberFromSubject('fix(api): envelope the error paths (#9456)') === 9456);
3399+
assert('merge-subject', pullNumberFromSubject('Merge pull request #123 from x/y') === 123);
3400+
assert('mid-title-issue-citation-is-not-the-pr', pullNumberFromSubject('docs: checklist names the renamed check run (#9420) (#9490)') === 9490);
3401+
assert('no-pr-in-subject', pullNumberFromSubject('chore: direct push') === null);
3402+
33973403
// ── the two landing tiers (#19133, ruled 2026-09-18) ─────────────────────
33983404
//
33993405
// Membership is pinned above and unchanged; what this battery pins is the
@@ -3435,12 +3441,6 @@ async function selfTest() {
34353441
assert('and-null-once-every-hit-is-lifted', allLifted.governed === false && allLifted.tier === null, JSON.stringify(allLifted.tier));
34363442
assert('the-tier-words-name-the-landing-each-waits-for', /GOVERNED_APPROVERS/.test(GOVERNED_TIERS.H.landing) && /Contract review/.test(GOVERNED_TIERS.S.landing) && /--pair/.test(GOVERNED_TIERS.S.landing));
34373443

3438-
// ── subject → PR (both GitHub spellings; the trailing parenthetical wins) ─
3439-
assert('squash-subject', pullNumberFromSubject('fix(api): envelope the error paths (#9456)') === 9456);
3440-
assert('merge-subject', pullNumberFromSubject('Merge pull request #123 from x/y') === 123);
3441-
assert('mid-title-issue-citation-is-not-the-pr', pullNumberFromSubject('docs: checklist names the renamed check run (#9420) (#9490)') === 9490);
3442-
assert('no-pr-in-subject', pullNumberFromSubject('chore: direct push') === null);
3443-
34443444
// ── --since parsing ───────────────────────────────────────────────────────
34453445
battery('since parsing');
34463446
const now = new Date('2026-08-18T12:00:00Z');
@@ -4765,13 +4765,13 @@ async function selfTest() {
47654765
const liftedHead = renderTestVerdict(liftedTest).split('\n')[0];
47664766
assert(
47674767
'⭐ the-test-head-no-longer-reports-a-post-lift-zero-as-if-nothing-had-hit-the-register',
4768-
liftedHead === 'governed-surface predicate: 0 of 4 path(s) hit the register after 1 generated-artifact lift(s) (5 surfaces, repo-agnostic).',
4768+
liftedHead === 'governed-surface predicate: 0 of 4 path(s) hit the register after 1 generated-artifact lift(s) (6 surfaces, repo-agnostic).',
47694769
liftedHead,
47704770
);
47714771
const plainHead = renderTestVerdict(testVerdict(['packages/spec/src/ui/view.zod.ts'])).split('\n')[0];
47724772
assert(
47734773
'and-a-verdict-with-no-lift-keeps-its-head-line-byte-for-byte',
4774-
plainHead === 'governed-surface predicate: 0 of 1 path(s) hit the register (5 surfaces, repo-agnostic).',
4774+
plainHead === 'governed-surface predicate: 0 of 1 path(s) hit the register (6 surfaces, repo-agnostic).',
47754775
plainHead,
47764776
);
47774777

‎scripts/pm/check-governed-queue-guard.mjs‎

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
447447
'decomposition, and the multi-PR group trap': 6,
448448
'the verdict table, both events': 10,
449449
'the pull_request leg is an EARLY WARNING and never reddens': 5,
450-
'the replay fixtures: the three incidents this guard descends from': 9,
450+
'the replay fixtures: the three incidents this guard descends from': 11,
451451
'⭐ the ordering guarantee, measured with a spy that THROWS': 7,
452452
'the words a reader acts on (requirement (e))': 26,
453453
'⭐ #14063 END TO END: what the dependency install actually buys': 9,
@@ -2214,10 +2214,14 @@ export async function selfTest() {
22142214
// The same authorized approval, on a commit that is no longer the head. Under
22152215
// the retired sha pin this exact fixture was the REFUSAL case.
22162216
const authorizedPassOnOlder = (login = GOVERNED_APPROVERS[0]) => authorizedApprovalVerdict([approvedAt(login, OLD)], HEAD);
2217-
const run = (event, rows, approvals = new Map()) => {
2217+
const run = (event, rows, approvals = new Map(), records = new Map()) => {
22182218
const { governed, unattributed } = decomposeGovernedWork(rows);
2219-
return guardVerdict({ event, governed, unattributed, approvals, apiCalls: governed.length });
2219+
return guardVerdict({ event, governed, unattributed, approvals, records, apiCalls: governed.length });
22202220
};
2221+
// The record reading a Tier S entry gets when the thread was READ and held
2222+
// nothing — the incident shape (#19133): "no record" is a refusal of its own
2223+
// kind, distinct from "the thread could not be read".
2224+
const absentRecord = (pr) => new Map([[pr, { state: 'absent', read: { pr: 0, card: 0 }, readSummary: '0 on the PR and 0 on its card', cardNote: 'replay fixture, no card thread searched' }]]);
22212225

22222226
// ── the register is READ, never restated (#9840) ──────────────────────────
22232227
//
@@ -2440,8 +2444,18 @@ export async function selfTest() {
24402444
battery('the replay fixtures: the three incidents this guard descends from');
24412445
for (const replay of REPLAYS) {
24422446
const rows = [row(replay.pr, replay.files, 'e'.repeat(40), replay.subject)];
2443-
const queued = run('merge_group', rows, new Map([[replay.pr, authorizedApprovalVerdict([], HEAD)]]));
2444-
assert(`replay-REFUSES-at-the-queue: ${replay.name}`, queued.exitCode === EXIT_REFUSED_UNAPPROVED, JSON.stringify(queued.conclusion));
2447+
// #19133: two of the three incidents are `.claude/**` and therefore Tier S
2448+
// today, so the queue judges them on the record leg too. Each incident's
2449+
// thread carried no record — the reading is ABSENT, and the refusal is the
2450+
// same "unapproved" the incident deserved. A Tier S entry with NO reading
2451+
// at all refuses as UNREADABLE instead: "could not find out" never clears.
2452+
const tier = governedTierFor(replay.files);
2453+
const queued = run('merge_group', rows, new Map([[replay.pr, authorizedApprovalVerdict([], HEAD)]]), tier === TIER_S ? absentRecord(replay.pr) : new Map());
2454+
assert(`replay-REFUSES-at-the-queue: ${replay.name}`, queued.exitCode === EXIT_REFUSED_UNAPPROVED, JSON.stringify([queued.conclusion, tier]));
2455+
if (tier === TIER_S) {
2456+
const unread = run('merge_group', rows, new Map([[replay.pr, authorizedApprovalVerdict([], HEAD)]]));
2457+
assert(`replay-is-Tier-S-and-with-NO-record-reading-refuses-UNREADABLE-never-clears: ${replay.name}`, unread.exitCode === EXIT_REFUSED_UNREADABLE, JSON.stringify(unread.conclusion));
2458+
}
24452459
const early = run('pull_request', rows, new Map([[replay.pr, approvalVerdict([])]]));
24462460
assert(`replay-only-WARNS-on-the-pr: ${replay.name}`, early.conclusion === 'warned' && early.exitCode === EXIT_CLEAR);
24472461
const text = renderGuardVerdict(queued);
@@ -3677,7 +3691,7 @@ export async function selfTest() {
36773691

36783692
// The words a reader acts on — requirement (e) reaches the new leg too.
36793693
const tierRefusalText = renderGuardVerdict(tierAbsent);
3680-
assert('the-refusal-names-the-tier-on-the-entry-so-a-reader-knows-why-a-record-would-help', /landing tier: REFERENCES/.test(tierRefusalText));
3694+
assert('the-refusal-names-the-tier-on-the-entry-so-a-reader-knows-why-a-record-would-help', /landing tier: S/.test(tierRefusalText), tierRefusalText);
36813695
assert('and-offers-the-record-as-a-THIRD-remedy-naming-the-three-facts-it-must-carry', /3\. Or — ONLY for a pull request whose governed paths all lie under/.test(tierRefusalText) && /Reviewed-by:/.test(tierRefusalText) && /Served-tier:/.test(tierRefusalText));
36823696
assert('⛔ and-a-RULES-layer-refusal-is-offered-no-such-remedy-the-control-for-the-line-above', !/3\. Or — ONLY for a pull request/.test(renderGuardVerdict(rulesDismissed)));
36833697
assert('the-below-tier-refusal-names-the-reading-it-actually-got', /does not stand/.test(renderGuardVerdict(tierBelow)) && /a-lesser-tier/.test(renderGuardVerdict(tierBelow)));

0 commit comments

Comments
 (0)