Skip to content

Commit f9a39c2

Browse files
committed
fix(pm): one reading of the 席位核对 record for fleet-roster and H69
fleet-roster's hasLandingVerdict required a bare `## 席位核对` heading, so the seat's live inline records (`## 席位核对 — ACCEPT · …`) read as no verdict while check-half-states' H69 marker read them as records. Both now import the heading marker and the verdict reading from scripts/pm/seat-record.mjs: the inline verdict on the heading line, else the first line below it that opens with a verdict word; only ACCEPT lands. Self-test cases for the inline form in both scripts. Claude-Session: https://claude.ai/code/session_01ADHkmPquMegy4jNfKiBsAf Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7c34e5d commit f9a39c2

3 files changed

Lines changed: 105 additions & 3 deletions

File tree

‎scripts/pm/check-half-states.mjs‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,9 @@ import { execFileSync, spawnSync } from 'node:child_process';
983983
import { readFileSync, writeSync } from 'node:fs';
984984
import { fileURLToPath } from 'node:url';
985985
import { isEntrypoint } from '../invoked-as.mjs';
986+
// The seat record's heading and verdict have ONE reading, shared with
987+
// fleet-roster's landing-verdict read; H69 asks it, never re-spells it.
988+
import { SEAT_RECORD_MARKER, seatRecordAccepts, seatRecordOf } from './seat-record.mjs';
986989

987990
/**
988991
* The repo this file sweeps when nothing says otherwise. It is a FALLBACK for a
@@ -11401,8 +11404,10 @@ export function h48GovernedVerdictWithoutBrief(pr, governed, commentRows) {
1140111404
* mirrored properties on the literal `## 席位核对` heading: line-anchored, an
1140211405
* optional leading blockquote, no `g` flag. A `###` sub-heading or a mention
1140311406
* in prose (the landing comment is told ⛔ 不复述 `## 席位核对`) is no record.
11407+
* DEFINED in `seat-record.mjs`, the one reading fleet-roster's landing-verdict
11408+
* read shares, and re-exported here under its old name.
1140411409
*/
11405-
export const SEAT_RECORD_MARKER = /^\s*>?\s*## 席位核对/m;
11410+
export { SEAT_RECORD_MARKER };
1140611411

1140711412
/** Governed globs named inline before the sentence degrades to a count — H43's. */
1140811413
export const H69_SURFACE_LIST_CAP = 4;
@@ -32331,6 +32336,14 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
3233132336
b(BATTERY69, 'record marker: ⛔ a landing comment that cites the record (⛔ 不复述 `## 席位核对`) is not one', SEAT_RECORD_MARKER.test('落地评论 = 两个读数的探针表,⛔ 不复述 `## 席位核对`'), false);
3233232337
b(BATTERY69, 'record marker: a verdict-bearing brief comment is not the record', SEAT_RECORD_MARKER.test('## 维护者速读\n\n**ACCEPT** — lands as ruled'), false);
3233332338
b(BATTERY69, 'record marker: no `g` flag, so two reads of one body agree', [SEAT_RECORD_MARKER.test(RECORD69), SEAT_RECORD_MARKER.test(RECORD69)].join(','), 'true,true');
32339+
// The live inline spelling: the verdict rides the heading line itself.
32340+
const INLINE69 = '## 席位核对 — ACCEPT · domain:skills#2 · session_01x · 2026-10-26T12:30Z\n\n1. 范围 ✓';
32341+
const BARE69 = '## 席位核对\n\n**ACCEPT** · domain:skills seat 2 · head `7cc88889`';
32342+
const CONTRACT69 = '## Contract review\n\nServed-tier: x\n\n**VERDICT: PASS**';
32343+
b(BATTERY69, 'record marker: the inline `— ACCEPT` heading is a record', SEAT_RECORD_MARKER.test(INLINE69), true);
32344+
b(BATTERY69, 'H69: an inline `— ACCEPT` record NEWER than the head -> clean', h69(pr69(), [cm69(INLINE69, NEWER69)], HEAD69), null);
32345+
b(BATTERY69, 'shared reading: the three measured bodies read record + ACCEPT alike (bare, inline, Contract review)', [BARE69, INLINE69, CONTRACT69].map((x) => `${SEAT_RECORD_MARKER.test(x)}/${seatRecordAccepts(x)}`).join(), 'true/true,true/true,false/false');
32346+
b(BATTERY69, 'shared reading: ⛔ an inline REWORK or a REJECT below is a record that never accepts', [seatRecordOf('## 席位核对 — REWORK · 补测试'), seatRecordOf('## 席位核对\n\nREJECT · rework')].map((r) => r?.verdict).join(), 'REWORK,REJECT');
3233432347

3233532348
// The sentence.
3233632349
b(BATTERY69, 'H69 row: the remedy is the record as a COMMENT for the current head', h69row(pr69(), []).includes('post the `## 席位核对` record as a COMMENT for head `7cc8888`'), true);

‎scripts/pm/fleet-roster.mjs‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
* awaiting landing open pulls on a `claude/` head that close an in-flight
4444
* card of the seat (`Fixes|Closes|Resolves #n`) and carry a
4545
* review verdict — a `## 席位核对` comment whose verdict
46-
* line is ACCEPT, or a `## Contract review` with
46+
* is ACCEPT, inline on the heading or on a line below it
47+
* (`seat-record.mjs`), or a `## Contract review` with
4748
* `VERDICT: PASS`. ⛔ `pm:mailbox` pulls never count
4849
* (`LEDGER_EXCLUSION`).
4950
* wall note a line-initial `Wall:` (or `撞墙:`) line in the post
@@ -91,6 +92,10 @@ import { ghApi, PER_PAGE, readAllPages } from './gh-pages.mjs';
9192
// the mailbox opener (#25417); this file reads them, never re-spells them.
9293
import * as mailboxOpen from './mailbox-open.mjs';
9394
import { LEDGER_EXCLUSION, MAILBOX_BRANCH_PREFIX, MAILBOX_LABEL } from './mailbox-open.mjs';
95+
// The seat record's heading and verdict have ONE reading, shared with H69 in
96+
// check-half-states; this file asks it, never re-spells the heading.
97+
import * as seatRecord from './seat-record.mjs';
98+
import { seatRecordAccepts } from './seat-record.mjs';
9499

95100
const SELF_PATH = fileURLToPath(import.meta.url);
96101
const PM_DIR = dirname(SELF_PATH);
@@ -222,7 +227,7 @@ export function closingRefs(body, repo) {
222227
export function hasLandingVerdict(comments) {
223228
return (Array.isArray(comments) ? comments : []).some((c) => {
224229
const body = String(c?.body ?? '');
225-
if (/^##[ \t]*席位核对[ \t]*$/m.test(body) && /^[ \t]*(?:\*\*)?ACCEPT(?:\*\*)?\b/m.test(body)) return true;
230+
if (seatRecordAccepts(body)) return true;
226231
return /^##[ \t]*Contract review\b/m.test(body) && /^[ \t]*(?:\*\*)?VERDICT:[ \t]*PASS\b/im.test(body);
227232
});
228233
}
@@ -590,6 +595,11 @@ async function selfTest() {
590595
t('席位核对 with REJECT is not', hasLandingVerdict([{ body: '## 席位核对\n\nREJECT · rework' }]), false);
591596
t('Contract review VERDICT: PASS is a verdict', hasLandingVerdict([{ body: '## Contract review\n\nServed-tier: x\n\n**VERDICT: PASS**' }]), true);
592597
t('an ACCEPT outside a seat record is not', hasLandingVerdict([{ body: 'ACCEPT this please' }]), false);
598+
t('the live inline heading `## 席位核对 — ACCEPT · …` is a verdict', hasLandingVerdict([{ body: '## 席位核对 — ACCEPT · domain:skills#2 · session_01x · 2026-10-26T12:30Z\n\n1. 范围 ✓' }]), true);
599+
t('⛔ an inline REWORK heading is not, whatever a later line opens with', hasLandingVerdict([{ body: '## 席位核对 — REWORK · 补测试\n\nACCEPT once the test lands' }]), false);
600+
t('a blockquoted record is read as H69 reads it', hasLandingVerdict([{ body: '> ## 席位核对\n>\n> **ACCEPT** · seat 1' }]), true);
601+
t('⛔ an ACCEPT line ABOVE the heading is not the record\'s verdict', hasLandingVerdict([{ body: 'ACCEPT\n\n## 席位核对\n\nREWORK · rework' }]), false);
602+
t('the heading is H69\'s marker, imported not re-spelled: the three measured bodies read record alike', ['## 席位核对\n\n**ACCEPT** · x', '## 席位核对 — ACCEPT · x', '## Contract review\n\n**VERDICT: PASS**'].map((b) => `${seatRecord.SEAT_RECORD_MARKER.test(b)}/${hasLandingVerdict([{ body: b }])}`), ['true/true', 'true/true', 'false/true']);
593603
t('the mailbox constants are the opener\'s own, imported not re-spelled', [MAILBOX_LABEL, LEDGER_EXCLUSION, MAILBOX_BRANCH_PREFIX], [mailboxOpen.MAILBOX_LABEL, mailboxOpen.LEDGER_EXCLUSION, mailboxOpen.MAILBOX_BRANCH_PREFIX]);
594604
t('a mailbox is read by head, then by body line; no label ⇒ none', [mailboxLogin({ labels: ['pm:mailbox'], head: { ref: 'mailbox/os-bill' } }), mailboxLogin({ labels: ['pm:mailbox'], head: { ref: 'claude/x' }, body: 'Mailbox-of: os-sales\n' }), mailboxLogin({ labels: [], head: { ref: 'mailbox/os-bill' } })], ['os-bill', 'os-sales', null]);
595605

‎scripts/pm/seat-record.mjs‎

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* seat-record — the ONE reading of the seat's `## 席位核对` record on a pull
5+
* request: where the record is, and which verdict it carries.
6+
*
7+
* ## Why it is its own file
8+
*
9+
* Two tools read this one artefact. `check-half-states.mjs` (H69) asks whether
10+
* a record is on the thread at all; `fleet-roster.mjs` asks whether the record
11+
* lets the pull land. Each used to carry its own pattern for the heading, and
12+
* they read the live inline spelling `## 席位核对 — ACCEPT · …` differently: H69
13+
* saw a record, the roster saw no verdict. Neither tool imports the other, so
14+
* the heading and the verdict reading live here and both import them. ⛔ Never
15+
* re-spell either pattern in a reader; a new spelling is added here, with a
16+
* case in BOTH self-tests.
17+
*
18+
* It is a sibling of its two readers, ⛔ not under a `lib/` subdirectory: a
19+
* multi-segment import specifier counts as a dispatch-gates watch hint, and a
20+
* module that inherits one from its gate files loses it when they import here.
21+
*
22+
* ## The record, as it is written
23+
*
24+
* The heading opens a line, optionally inside a blockquote, as `## 席位核对`
25+
* (pm-dispatch SKILL.md ①, `references/execution-duties.md`). The verdict is
26+
* read in this order, and the FIRST verdict word met decides:
27+
*
28+
* inline on the heading line itself, after a separator:
29+
* `## 席位核对 — ACCEPT · domain:skills#2 · …`
30+
* below the first line under the heading that opens with a verdict word,
31+
* bold or bare: `**ACCEPT** · …`, `REWORK · …`
32+
*
33+
* `ACCEPT` is the only verdict that lets a pull land; `REWORK` and `REJECT`
34+
* never do, and a record whose verdict cannot be read carries `null`.
35+
*
36+
* ⛔ No network, no I/O, no imports: a body in, a reading out.
37+
*/
38+
39+
/**
40+
* The seat's record, as a heading line — line-anchored, an optional leading
41+
* blockquote, no `g` flag (so two reads of one body agree). A `###`
42+
* sub-heading or a mention in prose (the landing comment is told ⛔ 不复述
43+
* `## 席位核对`) is no record.
44+
*/
45+
export const SEAT_RECORD_MARKER = /^\s*>?\s*## 席位核对/m;
46+
47+
/** The verdict words a record can carry; only the first lands a pull. */
48+
export const SEAT_RECORD_VERDICTS = Object.freeze(['ACCEPT', 'REWORK', 'REJECT']);
49+
50+
const VERDICT_WORD = SEAT_RECORD_VERDICTS.join('|');
51+
52+
/** The inline arm: the heading's own tail, after an optional separator. */
53+
const INLINE_VERDICT = new RegExp(`^[ \\t]*(?:[—–::·|-][ \\t]*)*(?:\\*\\*)?(${VERDICT_WORD})(?:\\*\\*)?\\b`, 'u');
54+
55+
/** The below arm: a line under the heading that opens with a verdict word. */
56+
const LINE_VERDICT = new RegExp(`^[ \\t]*>?[ \\t]*(?:\\*\\*)?(${VERDICT_WORD})(?:\\*\\*)?\\b`, 'u');
57+
58+
/**
59+
* The record a comment body carries: `null` when there is none, else
60+
* `{ verdict }` with the verdict word, or `null` when none could be read. Pure.
61+
*/
62+
export function seatRecordOf(body) {
63+
const text = String(body ?? '');
64+
const m = SEAT_RECORD_MARKER.exec(text);
65+
if (!m) return null;
66+
const [headingTail, ...below] = text.slice(m.index + m[0].length).split(/\r?\n/);
67+
const inline = INLINE_VERDICT.exec(headingTail);
68+
if (inline) return { verdict: inline[1] };
69+
for (const line of below) {
70+
const v = LINE_VERDICT.exec(line);
71+
if (v) return { verdict: v[1] };
72+
}
73+
return { verdict: null };
74+
}
75+
76+
/** Does this comment body carry a seat record whose verdict is ACCEPT? Pure. */
77+
export function seatRecordAccepts(body) {
78+
return seatRecordOf(body)?.verdict === 'ACCEPT';
79+
}

0 commit comments

Comments
 (0)