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
2 changes: 1 addition & 1 deletion .claude/skills/pm-dispatch/references/contract-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
- 新 head 即清;点名现 head 时 (b) 不起;条件满足由持有车道挂等复核标记或落交接行。
- 逾 24 小时条件未满的 hold 列入分诊轮次记录(`check-owed-reviews.mjs` 标出)。
- 复核记录 = 一条评论落 PR 或卡,席内与子代理同形;散文、dev 自评、`os-dev-report` 恒不算。
- 同形 = `## Contract review` 题头、head sha 独占码段、①②③ 逐项、独立性对、PASS/FAIL。
- 同形 = `## Contract review` 题头、`Head-sha:` 行独占码段、①②③ 逐项、独立性对、PASS/FAIL。
- 同形含首行 `Served-tier:`:值写该面所定档的常量名 `CONTRACT_REVIEW_TIER` 或 `TIER_DEFAULT`;
无此行不成裁决;模板 `node scripts/pm/record-recognisers.mjs --template`,申报面加 `--face declaration`。
- 同形带 `Local-runs:` 行:`none` 或 `probe — REASON` 具名唯一探针;有行必校,缺行旧记录不改判。
Expand Down
117 changes: 102 additions & 15 deletions scripts/pm/check-half-states.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12641,9 +12641,21 @@ export const H51_SHA_SPAN = /`([0-9a-fA-F]{7,40})`/g;
* The `Head-sha:` key line — the ONE place a contract-review record names the
* head it reviewed.
*
* The key half is `record-recognisers.mjs`'s `keyLineRegex` convention, spelled
* here because that file imports this one: optional blockquote, bullet, bold
* and backticks around the key, then the colon. The VALUE is a hex code span of
* The key half is `record-recognisers.mjs`'s `keyLineRegex` convention WITHOUT
* its two positional tolerances, spelled here because that file imports this
* one: bold and backticks around the key are tolerated, then the colon. ⛔ A
* leading blockquote or list bullet is NOT — `keyLineRegex` admits both for
* `Served-tier:`, `Reviewed-by:` and `Local-runs:`, whose values describe the
* record itself, but this line decides WHICH HEAD a PASS clears, and a quoted
* or bulleted `Head-sha:` line is one the author CITED rather than wrote (a
* review quoting the seat's hand-off, a record quoting an earlier record), so
* a reader that took it read a quoted sha as reviewed. The key may be indented
* by at most three spaces, CommonMark's limit before a line becomes an
* INDENTED CODE BLOCK: four spaces or a tab render the line as code, which is
* a quotation too, so the regex refuses them. `contractReviewHeadSha`
* adds the two positional rules a single-line regex cannot carry: the line
* sits BELOW the record's own UNQUOTED `## Contract review` heading and
* outside any fenced code block. The VALUE is a hex code span of
* its OWN — the shape `--template` prints — and it is the FIRST thing after the
* colon (spaces and bold may precede it, ⛔ nothing else); what follows the span
* on that line is prose and decides nothing. A key folded into the span with
Expand All @@ -12652,24 +12664,75 @@ export const H51_SHA_SPAN = /`([0-9a-fA-F]{7,40})`/g;
* decoration and the value then opens with hex rather than a backtick, so the
* record names no head — the accept set stays exactly one spelling. A bare hex
* value with no span, a lowercase key and a span below the floor name no head
* either; the repair for every one of them is on the WRITE side.
* either; the repair for every one of them is on the WRITE side, where
* `post-stamped.mjs` refuses a record carrying a `Head-sha` key this reader
* cannot read BEFORE the write.
*/
export const CONTRACT_REVIEW_HEAD_SHA_LINE = new RegExp(
`^[ \\t]*(?:>[ \\t]*)?(?:[-*][ \\t]+)?(?:\\*\\*)?\`?Head-sha\`?(?:\\*\\*)?[ \\t]*:(?:[ \\t]|\\*\\*)*${H51_SHA_SPAN.source}`,
`^[ ]{0,3}(?:\\*\\*)?\`?Head-sha\`?(?:\\*\\*)?[ \\t]*:(?:[ \\t]|\\*\\*)*${H51_SHA_SPAN.source}`,
);

/**
* The head a contract-review record names — the span on its first `Head-sha:`
* line, as written (case kept), or `null` when no line reads.
*
* First line wins, as `readServedTier` reads its key: a record is one comment
* and names one head, and a later `Head-sha:` line (a quoted record, say) is
* prose to this reader. Not judged against any head here — that is
* `contractReviewHeadMatch`'s half — so a caller can also ask which head a
* record is ABOUT, which the sentence a patrol prints needs.
* A fence line, as CommonMark spells one: three or more backticks or tildes at
* the line start (up to three spaces of indent). The run is captured because a
* fence CLOSES only on the same character with a run at least as long as the
* one that opened it: a shorter run, or the other character, is content of the
* open fence (a nested ``` inside a ```` block, a ~~~ inside a ``` block), and
* reading past either would read fenced content as the record's line. An
* unclosed fence runs to the end of the body, as it renders.
*/
const FENCE_LINE = /^[ ]{0,3}(`{3,}|~{3,})/;

/**
* The head a contract-review record names — the span on the first `Head-sha:`
* line BELOW its `## Contract review` heading, as written (case kept), or
* `null` when no such line reads.
*
* Three positional rules, each measured in the review of the line-form reader
* (2026-10-30) as a way a sha the author never reviewed became the record's
* head: a `Head-sha:` line placed BEFORE the heading, a blockquoted or
* bulleted one, and one inside a fenced code block — each naming another head
* ABOVE the record's own line — read as the head, so the record was found at
* the cited head and absent at the real one. None is prose to a first-line
* reader, and all four shapes are what a quotation looks like. So: lines
* before the first UNQUOTED heading-marker line are skipped — the heading is
* read with `CONTRACT_REVIEW_HEADING_MARKER`, but a blockquoted heading opens
* nothing here, because a fully quoted record (heading, head line and
* signature all under `>`) followed by a plain line opening `Head-sha:` would
* otherwise hand the quoted record's signature to the plain line's head; the
* locator still finds such a comment as a record, and it then names no head.
* A fenced line is skipped, fence boundaries included, with the fence closed
* only by its own character and length (`FENCE_LINE`); and
* `CONTRACT_REVIEW_HEAD_SHA_LINE` itself refuses the blockquote, the bullet
* and an indented-code line. Among the lines that remain, the FIRST wins, as
* `readServedTier` reads its key: a record is one comment and names one head,
* and a later `Head-sha:` line (a quoted record, say) is prose to this reader.
* Measured on the 709 records with a line-initial heading posted to this
* repository in the week to 2026-10-30: 707 carry the line plain and below
* the heading, 2 carry no `Head-sha` key at all (the two retired-dialect
* records), and 0 read differently under these rules than under the
* first-line-anywhere reader they replace.
*
* Not judged against any head here — that is `contractReviewHeadMatch`'s half
* — so a caller can also ask which head a record is ABOUT, which the sentence
* a patrol prints needs, and which `post-stamped.mjs` asks before it writes a
* record: a body carrying a `Head-sha` key that reads `null` here is refused.
*/
export function contractReviewHeadSha(body) {
let fence = null; // the OPEN fence — its character and run length — or null outside one
let belowHeading = false;
for (const line of String(body ?? '').split(/\r?\n/)) {
const f = FENCE_LINE.exec(line);
if (f !== null) {
if (fence === null) fence = { ch: f[1][0], len: f[1].length };
else if (f[1][0] === fence.ch && f[1].length >= fence.len) fence = null;
continue;
}
if (fence !== null) continue;
if (!belowHeading) {
belowHeading = CONTRACT_REVIEW_HEADING_MARKER.test(line) && !/^\s*>/.test(line);
continue;
}
const m = CONTRACT_REVIEW_HEAD_SHA_LINE.exec(line);
if (m) return m[1];
}
Expand Down Expand Up @@ -33452,8 +33515,32 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
const DELTA51 = RECORD51(OLDHEAD51, `\nThe PR has since moved to \`${HEAD51.slice(0, 8)}\` (seat comment 6515952505); this record judges \`${OLDHEAD51.slice(0, 8)}\` alone. Runs cancelled by the push of \`${HEAD51.slice(0, 8)}\`; landing waits on \`${HEAD51}\`.\n`);
t('⛔ record reader: a delta record on head A whose PROSE names head B is NO review of B', latestContractReviewOnHead([cm51(1, DELTA51, T51)], HEAD51), null);
t('⭐ record reader: …and IS the review of A — the `Head-sha:` line alone decides', latestContractReviewOnHead([cm51(1, DELTA51, T51)], OLDHEAD51)?.sha, OLDHEAD51);
t('record reader: `contractReviewHeadSha` reads the line\'s span — the first line wins, decoration and a trailing span tolerated', [contractReviewHeadSha(DELTA51), contractReviewHeadSha(`- **Head-sha:** \`${HEAD51}\` (moved to \`${OLDHEAD51}\`)\nHead-sha: \`${OLDHEAD51}\``), contractReviewHeadSha(`> Head-sha: \`${HEAD51}\``)].join(), [OLDHEAD51, HEAD51, HEAD51].join());
t('⛔ record reader: the key folded INTO the span, a bare hex value, a lowercase key and a sub-floor span name no head', [contractReviewHeadSha(`\`Head-sha: ${HEAD51}\``), contractReviewHeadSha(`Head-sha: ${HEAD51}`), contractReviewHeadSha(`head-sha: \`${HEAD51}\``), contractReviewHeadSha('Head-sha: `ba3d95`')].join('|'), '|||');
const UNDER51 = (...lines) => ['## Contract review', ...lines].join('\n');
t('record reader: `contractReviewHeadSha` reads the line\'s span — the first line BELOW the heading wins; bold or backticks on the key, a trailing span and up to three spaces of indent are tolerated', [contractReviewHeadSha(DELTA51), contractReviewHeadSha(UNDER51(`**Head-sha:** \`${HEAD51}\` (moved to \`${OLDHEAD51}\`)`, `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51(`\`Head-sha\`: \`${HEAD51}\``)), contractReviewHeadSha(UNDER51(` Head-sha: \`${HEAD51}\``))].join(), [OLDHEAD51, HEAD51, HEAD51, HEAD51].join());
// ⛔ A BLOCKQUOTED heading opens no scope: a fully quoted signed record followed by a plain prose line opening
// `Head-sha:` would otherwise lend the quoted signature to the plain line's head (the locator still sees a record here,
// through the marker's `>` tolerance — it then names no head). Measured live: 0 of 709 records quote their heading.
const QUOTED_RECORD51 = `> ## Contract review\n> Head-sha: \`${OLDHEAD51}\`\n> Reviewed-by: \`session_01SEAT\`\n> **VERDICT: PASS**\n\nHead-sha: \`${HEAD51}\` is the head the PR has since moved to.`;
t('⛔ record reader: a QUOTED record followed by a plain line opening `Head-sha:` names no head — a blockquoted heading opens no scope, and nor does a quoted heading alone', [contractReviewHeadSha(QUOTED_RECORD51), contractReviewHeadSha(`> ## Contract review\nHead-sha: \`${HEAD51}\``), latestContractReviewOnHead([cm51(1, QUOTED_RECORD51, T51)], HEAD51)].join('|'), '||');
// ⛔ An INDENTED CODE BLOCK (four spaces or a tab) is a quotation too: skipped above the record's own line, nothing alone.
t('⛔ record reader: a `Head-sha:` line indented as CODE — four spaces or a tab — above the record\'s own is skipped, and alone names no head', [contractReviewHeadSha(UNDER51(` Head-sha: \`${HEAD51}\``, `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51(`\tHead-sha: \`${HEAD51}\``, `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51(` Head-sha: \`${HEAD51}\``)), contractReviewHeadSha(UNDER51(`\tHead-sha: \`${HEAD51}\``))].join('|'), [OLDHEAD51, OLDHEAD51, '', ''].join('|'));
// ⛔ A fence closes only on its OWN character and a run at least as long: a nested ``` inside ````, a ~~~ inside ```,
// are content, and a `Head-sha:` line between them stays fenced; a longer closing run closes; a never-closed one runs out.
t('⛔ record reader: a nested or mismatched fence does not close the open one — the `Head-sha:` line inside stays fenced; a longer run closes; an unmatched run leaves it open to the end', [contractReviewHeadSha(UNDER51('````', '```', `Head-sha: \`${HEAD51}\``, '```', '````', `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51('```', '~~~', `Head-sha: \`${HEAD51}\``, '```', `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51('```', `Head-sha: \`${HEAD51}\``, '`````', `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51('```', '~~~', `Head-sha: \`${HEAD51}\``, '~~~', `Head-sha: \`${OLDHEAD51}\``))].join('|'), [OLDHEAD51, OLDHEAD51, OLDHEAD51, ''].join('|'));
t('⛔ record reader: the key folded INTO the span, a bare hex value, a lowercase key and a sub-floor span name no head', [contractReviewHeadSha(UNDER51(`\`Head-sha: ${HEAD51}\``)), contractReviewHeadSha(UNDER51(`Head-sha: ${HEAD51}`)), contractReviewHeadSha(UNDER51(`head-sha: \`${HEAD51}\``)), contractReviewHeadSha(UNDER51('Head-sha: `ba3d95`'))].join('|'), '|||');
// ⛔ The shapes the review of the line-form reader probed (2026-10-30): a `Head-sha:` line naming ANOTHER head above the
// record's own — placed before the heading, blockquoted, bulleted or fenced. Each is a citation, not the record's line,
// so the record reads ABSENT at the cited head and FOUND at its own; the first-line-anywhere reader read all four as cited.
const CITED51 = {
preHeading: `Head-sha: \`${HEAD51}\`\n\n${RECORD51(OLDHEAD51)}`,
quoted: UNDER51('', `> Head-sha: \`${HEAD51}\``, `Head-sha: \`${OLDHEAD51}\``, '**VERDICT: PASS**'),
bulleted: UNDER51('', `- Head-sha: \`${HEAD51}\``, `Head-sha: \`${OLDHEAD51}\``, '**VERDICT: PASS**'),
fenced: UNDER51('', '```', `Head-sha: \`${HEAD51}\``, '```', `Head-sha: \`${OLDHEAD51}\``, '**VERDICT: PASS**'),
};
t('⛔ record reader: a `Head-sha:` line before the heading, blockquoted, bulleted or fenced — naming another head ABOVE the record\'s own — is NO review of that head', Object.values(CITED51).map((body) => latestContractReviewOnHead([cm51(1, body, T51)], HEAD51)).join('|'), '|||');
t('⭐ record reader: …and each IS the review of the head its own line names — the cited line is prose to the reader', Object.values(CITED51).map((body) => latestContractReviewOnHead([cm51(1, body, T51)], OLDHEAD51)?.sha).join(), [OLDHEAD51, OLDHEAD51, OLDHEAD51, OLDHEAD51].join());
t('⛔ record reader: a quoted, bulleted or fenced line ALONE names no head, nor does a line with NO heading above it, nor one inside an unclosed fence', [contractReviewHeadSha(UNDER51(`> Head-sha: \`${HEAD51}\``)), contractReviewHeadSha(UNDER51(`- **Head-sha:** \`${HEAD51}\``)), contractReviewHeadSha(UNDER51('```md', `Head-sha: \`${HEAD51}\``, '```')), contractReviewHeadSha(`Head-sha: \`${HEAD51}\``), contractReviewHeadSha(UNDER51('```', `Head-sha: \`${HEAD51}\``))].join('|'), '||||');
t('record reader: a tilde fence is a fence too, and CRLF line endings read', [contractReviewHeadSha(UNDER51('~~~', `Head-sha: \`${HEAD51}\``, '~~~', `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(`## Contract review\r\nHead-sha: \`${HEAD51}\`\r\n`)].join(), [OLDHEAD51, HEAD51].join());
t('⛔ record reader: the RETIRED dialects — head in the heading (two spellings), on the first body line, in an adoption sentence — name no head', [DIALECT_A, DIALECT_B, DIALECT_C, DIALECT_D].map((d) => latestContractReviewOnHead([cm51(1, d(HEAD51), T51)], HEAD51)).join('|'), '|||');

// -- H54 — a `[Decision]`-titled card sitting in the DISPATCH pool (#16688) -
Expand Down
Loading
Loading