Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion scripts/pm/check-half-states.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,9 @@ import { execFileSync, spawnSync } from 'node:child_process';
import { readFileSync, writeSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { isEntrypoint } from '../invoked-as.mjs';
// The seat record's heading and verdict have ONE reading, shared with
// fleet-roster's landing-verdict read; H69 asks it, never re-spells it.
import { SEAT_RECORD_MARKER, seatRecordAccepts, seatRecordOf } from './seat-record.mjs';

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

/** Governed globs named inline before the sentence degrades to a count — H43's. */
export const H69_SURFACE_LIST_CAP = 4;
Expand Down Expand Up @@ -32331,6 +32336,14 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
b(BATTERY69, 'record marker: ⛔ a landing comment that cites the record (⛔ 不复述 `## 席位核对`) is not one', SEAT_RECORD_MARKER.test('落地评论 = 两个读数的探针表,⛔ 不复述 `## 席位核对`'), false);
b(BATTERY69, 'record marker: a verdict-bearing brief comment is not the record', SEAT_RECORD_MARKER.test('## 维护者速读\n\n**ACCEPT** — lands as ruled'), false);
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');
// The live inline spelling: the verdict rides the heading line itself.
const INLINE69 = '## 席位核对 — ACCEPT · domain:skills#2 · session_01x · 2026-10-26T12:30Z\n\n1. 范围 ✓';
const BARE69 = '## 席位核对\n\n**ACCEPT** · domain:skills seat 2 · head `7cc88889`';
const CONTRACT69 = '## Contract review\n\nServed-tier: x\n\n**VERDICT: PASS**';
b(BATTERY69, 'record marker: the inline `— ACCEPT` heading is a record', SEAT_RECORD_MARKER.test(INLINE69), true);
b(BATTERY69, 'H69: an inline `— ACCEPT` record NEWER than the head -> clean', h69(pr69(), [cm69(INLINE69, NEWER69)], HEAD69), null);
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');
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');

// The sentence.
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);
Expand Down
14 changes: 12 additions & 2 deletions scripts/pm/fleet-roster.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
* awaiting landing open pulls on a `claude/` head that close an in-flight
* card of the seat (`Fixes|Closes|Resolves #n`) and carry a
* review verdict — a `## 席位核对` comment whose verdict
* line is ACCEPT, or a `## Contract review` with
* is ACCEPT, inline on the heading or on a line below it
* (`seat-record.mjs`), or a `## Contract review` with
* `VERDICT: PASS`. ⛔ `pm:mailbox` pulls never count
* (`LEDGER_EXCLUSION`).
* wall note a line-initial `Wall:` (or `撞墙:`) line in the post
Expand Down Expand Up @@ -91,6 +92,10 @@ import { ghApi, PER_PAGE, readAllPages } from './gh-pages.mjs';
// the mailbox opener (#25417); this file reads them, never re-spells them.
import * as mailboxOpen from './mailbox-open.mjs';
import { LEDGER_EXCLUSION, MAILBOX_BRANCH_PREFIX, MAILBOX_LABEL } from './mailbox-open.mjs';
// The seat record's heading and verdict have ONE reading, shared with H69 in
// check-half-states; this file asks it, never re-spells the heading.
import * as seatRecord from './seat-record.mjs';
import { seatRecordAccepts } from './seat-record.mjs';

const SELF_PATH = fileURLToPath(import.meta.url);
const PM_DIR = dirname(SELF_PATH);
Expand Down Expand Up @@ -222,7 +227,7 @@ export function closingRefs(body, repo) {
export function hasLandingVerdict(comments) {
return (Array.isArray(comments) ? comments : []).some((c) => {
const body = String(c?.body ?? '');
if (/^##[ \t]*席位核对[ \t]*$/m.test(body) && /^[ \t]*(?:\*\*)?ACCEPT(?:\*\*)?\b/m.test(body)) return true;
if (seatRecordAccepts(body)) return true;
return /^##[ \t]*Contract review\b/m.test(body) && /^[ \t]*(?:\*\*)?VERDICT:[ \t]*PASS\b/im.test(body);
});
}
Expand Down Expand Up @@ -590,6 +595,11 @@ async function selfTest() {
t('席位核对 with REJECT is not', hasLandingVerdict([{ body: '## 席位核对\n\nREJECT · rework' }]), false);
t('Contract review VERDICT: PASS is a verdict', hasLandingVerdict([{ body: '## Contract review\n\nServed-tier: x\n\n**VERDICT: PASS**' }]), true);
t('an ACCEPT outside a seat record is not', hasLandingVerdict([{ body: 'ACCEPT this please' }]), false);
t('the live inline heading `## 席位核对 — ACCEPT · …` is a verdict', hasLandingVerdict([{ body: '## 席位核对 — ACCEPT · domain:skills#2 · session_01x · 2026-10-26T12:30Z\n\n1. 范围 ✓' }]), true);
t('⛔ an inline REWORK heading is not, whatever a later line opens with', hasLandingVerdict([{ body: '## 席位核对 — REWORK · 补测试\n\nACCEPT once the test lands' }]), false);
t('a blockquoted record is read as H69 reads it', hasLandingVerdict([{ body: '> ## 席位核对\n>\n> **ACCEPT** · seat 1' }]), true);
t('⛔ an ACCEPT line ABOVE the heading is not the record\'s verdict', hasLandingVerdict([{ body: 'ACCEPT\n\n## 席位核对\n\nREWORK · rework' }]), false);
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']);
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]);
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]);

Expand Down
79 changes: 79 additions & 0 deletions scripts/pm/seat-record.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* seat-record — the ONE reading of the seat's `## 席位核对` record on a pull
* request: where the record is, and which verdict it carries.
*
* ## Why it is its own file
*
* Two tools read this one artefact. `check-half-states.mjs` (H69) asks whether
* a record is on the thread at all; `fleet-roster.mjs` asks whether the record
* lets the pull land. Each used to carry its own pattern for the heading, and
* they read the live inline spelling `## 席位核对 — ACCEPT · …` differently: H69
* saw a record, the roster saw no verdict. Neither tool imports the other, so
* the heading and the verdict reading live here and both import them. ⛔ Never
* re-spell either pattern in a reader; a new spelling is added here, with a
* case in BOTH self-tests.
*
* It is a sibling of its two readers, ⛔ not under a `lib/` subdirectory: a
* multi-segment import specifier counts as a dispatch-gates watch hint, and a
* module that inherits one from its gate files loses it when they import here.
*
* ## The record, as it is written
*
* The heading opens a line, optionally inside a blockquote, as `## 席位核对`
* (pm-dispatch SKILL.md ①, `references/execution-duties.md`). The verdict is
* read in this order, and the FIRST verdict word met decides:
*
* inline on the heading line itself, after a separator:
* `## 席位核对 — ACCEPT · domain:skills#2 · …`
* below the first line under the heading that opens with a verdict word,
* bold or bare: `**ACCEPT** · …`, `REWORK · …`
*
* `ACCEPT` is the only verdict that lets a pull land; `REWORK` and `REJECT`
* never do, and a record whose verdict cannot be read carries `null`.
*
* ⛔ No network, no I/O, no imports: a body in, a reading out.
*/

/**
* The seat's record, as a heading line — line-anchored, an optional leading
* blockquote, no `g` flag (so two reads of one body agree). A `###`
* sub-heading or a mention in prose (the landing comment is told ⛔ 不复述
* `## 席位核对`) is no record.
*/
export const SEAT_RECORD_MARKER = /^\s*>?\s*## 席位核对/m;

/** The verdict words a record can carry; only the first lands a pull. */
export const SEAT_RECORD_VERDICTS = Object.freeze(['ACCEPT', 'REWORK', 'REJECT']);

const VERDICT_WORD = SEAT_RECORD_VERDICTS.join('|');

/** The inline arm: the heading's own tail, after an optional separator. */
const INLINE_VERDICT = new RegExp(`^[ \\t]*(?:[—–::·|-][ \\t]*)*(?:\\*\\*)?(${VERDICT_WORD})(?:\\*\\*)?\\b`, 'u');

/** The below arm: a line under the heading that opens with a verdict word. */
const LINE_VERDICT = new RegExp(`^[ \\t]*>?[ \\t]*(?:\\*\\*)?(${VERDICT_WORD})(?:\\*\\*)?\\b`, 'u');

/**
* The record a comment body carries: `null` when there is none, else
* `{ verdict }` with the verdict word, or `null` when none could be read. Pure.
*/
export function seatRecordOf(body) {
const text = String(body ?? '');
const m = SEAT_RECORD_MARKER.exec(text);
if (!m) return null;
const [headingTail, ...below] = text.slice(m.index + m[0].length).split(/\r?\n/);
const inline = INLINE_VERDICT.exec(headingTail);
if (inline) return { verdict: inline[1] };
for (const line of below) {
const v = LINE_VERDICT.exec(line);
if (v) return { verdict: v[1] };
}
return { verdict: null };
}

/** Does this comment body carry a seat record whose verdict is ACCEPT? Pure. */
export function seatRecordAccepts(body) {
return seatRecordOf(body)?.verdict === 'ACCEPT';
}
Loading