diff --git a/.claude/skills/pm-dispatch/references/contract-review.md b/.claude/skills/pm-dispatch/references/contract-review.md index 2ee3a4dfdcd..b1029dbe2e7 100644 --- a/.claude/skills/pm-dispatch/references/contract-review.md +++ b/.claude/skills/pm-dispatch/references/contract-review.md @@ -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` 具名唯一探针;有行必校,缺行旧记录不改判。 diff --git a/scripts/pm/check-half-states.mjs b/scripts/pm/check-half-states.mjs index db82b25ba99..9bc7476e2e6 100644 --- a/scripts/pm/check-half-states.mjs +++ b/scripts/pm/check-half-states.mjs @@ -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 @@ -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]; } @@ -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) - diff --git a/scripts/pm/post-stamped.mjs b/scripts/pm/post-stamped.mjs index 65a2c531202..93a4bcce686 100644 --- a/scripts/pm/post-stamped.mjs +++ b/scripts/pm/post-stamped.mjs @@ -947,6 +947,7 @@ import { ISSUE_BODY_LIMIT, PROXY_FLAG, claimSeatNumber, + contractReviewHeadSha, h56EstimatedStamp, h56StampedReadings, hasDecisionFacetMarker, @@ -2059,23 +2060,45 @@ export function keyedLineRefusalText(rows) { /** What the record's owner accepts, printed BY the refusal so the fix is one line away. */ export const RECORD_KEY_SPELLINGS = Object.freeze({ + 'Head-sha': '`Head-sha: ` then the reviewed head as 7 to 40 hex in a code span of its OWN, at the START of a line BELOW the `## Contract review` heading — not blockquoted, not bulleted, not inside a fence, not before the heading; the first such line is the head the record names, and a record written before the line existed may omit it', 'Local-runs': '`Local-runs: none` at the START of a line, or `Local-runs: probe — REASON` naming the ONE local run the read-only rule was broken for and why; a record written before the line existed may omit it', }); +/** PRESENCE only, case-blind: a `Head-sha` key anywhere in the body, however decorated or placed. Consulted solely to + * tell "the author wrote the key and the reader cannot read it" (refused) from "no key at all" (not re-judged). */ +const HEAD_SHA_KEY_ANYWHERE = /head-sha[ \t`*]*:/i; + +/** The first line carrying a `Head-sha` key, however spelled — a LOCATOR for the writer's eye, ⛔ never the judgement. */ +function headShaKeyLine(text) { + const hit = text.split(/\r?\n/).find((line) => HEAD_SHA_KEY_ANYWHERE.test(line)); + return hit === undefined ? '(no single line carries the key)' : offendingSpan(hit.trim(), 160); +} + /** - * The keyed-line problems in a `## Contract review` record about to be written, judged by the reader that OWNS the - * line — ⛔ never by a rule spelled here. One key today, `Local-runs:`, and ONE direction: a line that is PRESENT and - * cannot be graded is refused; an absent line is not re-judged, because the landed records carry none and this act - * must not re-open a verdict over a line nobody had been asked to write. Scoped by the record's own heading marker, - * on `--comment` alone — a record IS a comment. - * @returns {{ key: string, why: string, line: string }[]} — empty when the body is not a record, or when the line reads. + * The keyed-line problems in a `## Contract review` record about to be written, judged by the readers that OWN each + * line — ⛔ never by a rule spelled here. Two keys, ONE direction each: a line that is PRESENT and cannot be read is + * refused; an absent line is not re-judged, because the landed records carry none and this act must not re-open a + * verdict over a line nobody had been asked to write. Scoped by the record's own heading marker, on `--comment` + * alone — a record IS a comment. + * Head-sha `contractReviewHeadSha` (`check-half-states.mjs`) is the ONE reader every guard reaches the record's head + * through: the first plain `Head-sha:` line below the heading, outside any blockquote, bullet or fence. A + * body that carries the key somewhere and reads `null` there would be filed by every reader as a review of + * NO head — absent on the owed list, refused at the queue — so it is refused here, before the write. The + * template's null-oid placeholder reads as a head (it is 40 hex) and is not this row's: C6 refuses it. + * Local-runs `readLocalRuns` / `localRunsStand` (`record-recognisers.mjs`): `none` and `probe — REASON` are the + * closed set. + * @returns {{ key: string, why: string, line: string }[]} — empty when the body is not a record, or when every line reads. */ export function recordKeyedLineRefusals(body) { const text = String(body ?? ''); if (!CONTRACT_REVIEW_HEADING_MARKER.test(text)) return []; + const rows = []; + if (HEAD_SHA_KEY_ANYWHERE.test(text) && contractReviewHeadSha(text) === null) + rows.push({ key: 'Head-sha', line: headShaKeyLine(text), why: '`contractReviewHeadSha` reads NO head here — a `Head-sha` key is present, but no line below the `## Contract review` heading carries it plainly with a hex span of its own (a blockquoted, bulleted, fenced or pre-heading line, the key folded into the span, a bare hex value, a lowercase key and a sub-floor span all read as nothing). Every reader would file this record as a review of NO head.' }); const runs = readLocalRuns(text); - if (localRunsStand(runs)) return []; - return [{ key: 'Local-runs', line: runs.line, why: '`readLocalRuns` reads the line and cannot grade its value — `none` and `probe — REASON` are the closed set, and a probe with no reason names no deviation.' }]; + if (!localRunsStand(runs)) + rows.push({ key: 'Local-runs', line: runs.line, why: '`readLocalRuns` reads the line and cannot grade its value — `none` and `probe — REASON` are the closed set, and a probe with no reason names no deviation.' }); + return rows; } /** The refusal a caller reads, from `recordKeyedLineRefusals`' rows. */ @@ -2083,9 +2106,9 @@ export function recordLineRefusalText(rows) { return ( `post-stamped: REFUSED — ${rows.length} keyed line(s) in this \`## Contract review\` record cannot be read by the checker that owns them. Nothing was written.\n` + rows.map((r, i) => ` ${i + 1}. [${r.key}] ${r.why}\n line: ${r.line}\n write: ${RECORD_KEY_SPELLINGS[r.key]}`).join('\n') + - '\n\n `readLocalRuns` (`record-recognisers.mjs`) is imported HERE, so this IS the reading the record would get later —\n' + - ' on your own record, before the write. A record without the line is not re-judged; only a line that is present and\n' + - ' unreadable is refused. ⛔ No flag turns it off.' + '\n\n `contractReviewHeadSha` (`check-half-states.mjs`) and `readLocalRuns` (`record-recognisers.mjs`) are imported HERE,\n' + + ' so this IS the reading the record would get later — on your own record, before the write. A record without the\n' + + ' line is not re-judged; only a line that is present and unreadable is refused. ⛔ No flag turns it off.' ); } @@ -4064,7 +4087,7 @@ const SELF_TEST_BATTERIES = Object.freeze({ 'the two positions: a declaration renders as bare digits, and the patrol reads digits': 24, 'the shared rule: this tool and H56 cannot come to disagree': 6, 'the keyed lines: a claim\'s exact-value fields, judged by the readers that own them': 30, - 'the record line: a `## Contract review` record\'s `Local-runs:` value, judged when present — an absent line is not re-judged': 9, + 'the record line: a `## Contract review` record\'s `Head-sha:` and `Local-runs:` lines, judged when present — an absent line is not re-judged': 18, 'the vacant body: a VACANT seat post\'s ledger holds pointers, never obligations': 27, 'the decision facets: a decision analysis carries its marker, and a delta, a record or a quotation is not one': 22, 'the relay transport: the same act as one op, the comment found on the board, the exit register kept apart': 13, @@ -5274,7 +5297,7 @@ export function selfTest() { } // ── the record line ────────────────────────────────────────────────────── - battery('the record line: a `## Contract review` record\'s `Local-runs:` value, judged when present — an absent line is not re-judged'); + battery('the record line: a `## Contract review` record\'s `Head-sha:` and `Local-runs:` lines, judged when present — an absent line is not re-judged'); { const RECORD = (...lines) => ['## Contract review', '', `Served-tier: \`${CONTRACT_REVIEW_TIER_NAME}\``, 'Head-sha: `0123456789abcdef0123456789abcdef01234567`', ...lines, '', '**VERDICT: PASS**'].join('\n'); const rkeys = (body) => recordKeyedLineRefusals(body).map((r) => r.key).join(); @@ -5286,7 +5309,20 @@ export function selfTest() { t('the refusal prints the spelling that reads, names the owner, and says absence is not re-judged', (() => { const text = recordLineRefusalText(recordKeyedLineRefusals(RECORD('Local-runs: probe'))); return text.includes(RECORD_KEY_SPELLINGS['Local-runs']) && text.includes('readLocalRuns') && text.includes('not re-judged') && text.includes('Nothing was written'); })()); t('⛔ NOT a record: a claim or a report carrying a `Local-runs:` line is untouched — the reader runs on the record\'s heading only', rkeys('Claim: x\nLocal-runs: probe') === '' && rkeys('os-dev-report\n\nLocal-runs: probe') === ''); t('⛔ a `###` sub-heading is not the record marker, exactly as the locator reads it', rkeys('### Contract review\nLocal-runs: probe') === ''); - t('structural: the CLI runs this on `--comment` only, and the reader is imported from the record\'s own file, ⛔ never restated', /const record = options\.mode === 'comment' \? recordKeyedLineRefusals\(rendered\.body\) : \[\];/u.test(stampSource) && /import \{[^}]*\breadLocalRuns\b[^}]*\} from '\.\/record-recognisers\.mjs';/u.test(stampSource) && new RegExp('function\\s+(readLocalRuns|localRunsStand)\\b').test(stampSource) === false); + // The `Head-sha:` line — read by the one reader every guard reaches the head through, refused here when it is present and + // reads null. The shapes are the ones the line-form reader's review probed as a sha the author cited rather than reviewed. + const SHA_WITH = (headLines, ...lines) => ['## Contract review', '', `Served-tier: \`${CONTRACT_REVIEW_TIER_NAME}\``, ...headLines, ...lines, '', '**VERDICT: PASS**'].join('\n'); + const SHA_HEAD = '0123456789abcdef0123456789abcdef01234567'; + t('⛔ a `Head-sha:` line that is ONLY blockquoted is refused — the reader takes no quoted line, so this record would name no head', rkeys(SHA_WITH([`> Head-sha: \`${SHA_HEAD}\``])) === 'Head-sha'); + t('⛔ …and ONLY bulleted, ONLY fenced, or ONLY before the heading — each reads null and is refused', rkeys(SHA_WITH([`- Head-sha: \`${SHA_HEAD}\``])) === 'Head-sha' && rkeys(SHA_WITH(['```', `Head-sha: \`${SHA_HEAD}\``, '```'])) === 'Head-sha' && rkeys(`Head-sha: \`${SHA_HEAD}\`\n\n${SHA_WITH([])}`) === 'Head-sha'); + t('⛔ the key folded INTO the span, a bare hex value and a lowercase key are refused — each names no head to every reader', rkeys(SHA_WITH([`\`Head-sha: ${SHA_HEAD}\``])) === 'Head-sha' && rkeys(SHA_WITH([`Head-sha: ${SHA_HEAD}`])) === 'Head-sha' && rkeys(SHA_WITH([`head-sha: \`${SHA_HEAD}\``])) === 'Head-sha'); + t('⭐ a quoted line ABOVE the record\'s own passes — the reader skips the quotation and reads the line below it', rkeys(SHA_WITH([`> Head-sha: \`${'f'.repeat(40)}\``, `Head-sha: \`${SHA_HEAD}\``])) === ''); + t('⛔ ABSENT is NOT re-judged for this key either: a record with no `Head-sha` key at all passes untouched — the retired-dialect records carry none', rkeys(SHA_WITH([])) === ''); + t('⛔ a fully QUOTED record, and a code-indented (four spaces or tab) line alone, are refused — a quoted heading opens no scope and an indented line is code', rkeys(SHA_WITH([`Head-sha: \`${SHA_HEAD}\``]).split('\n').map((l) => `> ${l}`).join('\n')) === 'Head-sha' && rkeys(SHA_WITH([` Head-sha: \`${SHA_HEAD}\``])) === 'Head-sha' && rkeys(SHA_WITH([`\tHead-sha: \`${SHA_HEAD}\``])) === 'Head-sha'); + t('both lines unreadable ⇒ both rows, `Head-sha` first', rkeys(SHA_WITH([`> Head-sha: \`${SHA_HEAD}\``], 'Local-runs: probe')) === 'Head-sha,Local-runs'); + t('the `Head-sha` refusal prints the spelling that reads, names the owner, and quotes the offending line', (() => { const rows = recordKeyedLineRefusals(SHA_WITH([`> Head-sha: \`${SHA_HEAD}\``])); const text = recordLineRefusalText(rows); return text.includes(RECORD_KEY_SPELLINGS['Head-sha']) && text.includes('contractReviewHeadSha') && rows[0].line.includes('> Head-sha'); })()); + t('⛔ NOT a record: a claim or a report carrying a quoted `Head-sha:` line is untouched — the reader runs on the record\'s heading only', rkeys(`Claim: x\n> Head-sha: \`${SHA_HEAD}\``) === '' && rkeys(`os-dev-report\n\n> Head-sha: \`${SHA_HEAD}\``) === ''); + t('structural: the CLI runs this on `--comment` only, and BOTH readers are imported from the files that own them, ⛔ never restated', /const record = options\.mode === 'comment' \? recordKeyedLineRefusals\(rendered\.body\) : \[\];/u.test(stampSource) && /import \{[^}]*\breadLocalRuns\b[^}]*\} from '\.\/record-recognisers\.mjs';/u.test(stampSource) && /import \{[^}]*\bcontractReviewHeadSha\b[^}]*\} from '\.\/check-half-states\.mjs';/u.test(stampSource) && new RegExp('function\\s+(readLocalRuns|localRunsStand|contractReviewHeadSha)\\b').test(stampSource) === false); } // ── the vacant seat body (#23396, ruling 6162084315 letter A) ────────────── diff --git a/scripts/pm/record-recognisers.mjs b/scripts/pm/record-recognisers.mjs index 8ed4979a2b9..8f8eb3d85e9 100644 --- a/scripts/pm/record-recognisers.mjs +++ b/scripts/pm/record-recognisers.mjs @@ -2070,7 +2070,7 @@ export function locateReviewOfRecord(pair, carriedHead = null) { // stopped registering. const SELF_TEST_BATTERIES = Object.freeze({ 'the locator: the newest `## Contract review` on this head, on EITHER thread (#17302)': 19, - '#18141 · #27979: the head sha sits in a span of ITS OWN on the `Head-sha:` line — the key-in-span spelling and a span in the prose name no head': 10, + '#18141 · #27979: the head sha sits in a span of ITS OWN on the `Head-sha:` line — the key-in-span spelling and a span in the prose name no head': 14, 'the `Served-tier:` reading — the tier that SERVED the record (#17915 · #18060 · #18174)': 27, '⭐ the 2026-09-20 ruling: a pure-regeneration head move KEEPS the record, decided on the COMMITTED trees': 22, '#18042: the copyable record TEMPLATE — the one machine-read artefact with nothing to copy': 29, @@ -2185,7 +2185,7 @@ export async function selfTest() { `**Director seat adoption record** — the verdict on \`${HEAD_9AF9}\` below is adopted verbatim.\n\n---\n\n## Contract review\n\n- **Reviewed-by:** \`${RECORD_SESSION}\``, ].map((body, i) => ({ id: 3310 + i, created_at: '2026-09-01T08:52:00Z', body })); t('⛔ the RETIRED dialects — the head in the heading (two spellings), on the first body line, in the adoption sentence — read ABSENT: a record names its head on the `Head-sha:` line or not at all', LEGACY.every((row) => locateReviewOfRecord(pairOf({ prComments: [row] })).state === 'absent')); - t('a blockquoted heading still reads — H51 tolerates the `>` a seat writes without meaning it', locateReviewOfRecord(pairOf({ prComments: [{ ...RECORD_ON_9AF9, body: RECORD_ON_9AF9.body.replace(/^## /, '> ## ') }] })).state === 'found'); + t('⛔ #28024: a blockquoted heading is still the record MARKER (H51 tolerates the `>`), but it opens no head scope — the record names no head and reads ABSENT', locateReviewOfRecord(pairOf({ prComments: [{ ...RECORD_ON_9AF9, body: RECORD_ON_9AF9.body.replace(/^## /, '> ## ') }] })).state === 'absent'); t('⛔ a `###` sub-heading is not the marker', locateReviewOfRecord(pairOf({ prComments: [{ ...RECORD_ON_9AF9, body: RECORD_ON_9AF9.body.replace(/^## /, '### ') }] })).state === 'absent'); t('⛔ the heading mentioned inside a paragraph is not the marker', locateReviewOfRecord(pairOf({ prComments: [{ ...RECORD_ON_9AF9, body: RECORD_ON_9AF9.body.replace(/^## /, 'see the ## ') }] })).state === 'absent'); const DEV_REPORT = { id: 3303, created_at: '2026-09-01T08:40:00Z', body: `os-dev-report\n\n\`\`\`json\n{ "issue": 13657, "pr": "https://github.com/o/r/pull/13864", "summary": "landed at \`${HEAD_9AF9}\`" }\n\`\`\`` }; @@ -2232,7 +2232,20 @@ export async function selfTest() { const deltaOn = (headSha, where) => locateReviewOfRecord(pairOf({ headSha, ...(where === 'card' ? { cardComments: [DELTA] } : { prComments: [DELTA] }) })); t('⛔ #27979: a delta record on head A whose prose names head B is ABSENT for B — on the PR thread and on the card alike', deltaOn(DELTA_B, 'PR').state === 'absent' && deltaOn(DELTA_B, 'card').state === 'absent'); t('⭐ #27979: …and FOUND for A, naming the full sha its `Head-sha:` line carries', (() => { const r = deltaOn(DELTA_A, 'PR'); return r.state === 'found' && r.sha === DELTA_A && r.id === 6516042954; })()); - t('⛔ #27979: the FIRST `Head-sha:` line decides — a second one lower down (a quoted record) is prose to the reader', locateReviewOfRecord(pairOf({ headSha: DELTA_B, prComments: [{ ...DELTA, body: `${DELTA.body}\n\n> Head-sha: \`${DELTA_B}\`` }] })).state === 'absent' && contractReviewHeadMatch(`Head-sha: \`${DELTA_B}\`\nHead-sha: \`${DELTA_A}\``, DELTA_B) === DELTA_B); + t('⛔ #27979: the FIRST `Head-sha:` line below the heading decides — a second one lower down (a quoted record) is prose to the reader', locateReviewOfRecord(pairOf({ headSha: DELTA_B, prComments: [{ ...DELTA, body: `${DELTA.body}\n\n> Head-sha: \`${DELTA_B}\`` }] })).state === 'absent' && contractReviewHeadMatch(`## Contract review\nHead-sha: \`${DELTA_B}\`\nHead-sha: \`${DELTA_A}\``, DELTA_B) === DELTA_B); + // ⛔ #28024: the shapes the #28023 review probed — a `Head-sha:` line naming head B that the author CITED rather than wrote, + // sitting ABOVE the record's own line: blockquoted, bulleted, fenced, or placed before the heading. The first-line-anywhere + // reader took each as the record's head (found at B, absent at A); now each is prose and the record's own line decides. + const CITED_LEADS = [[`> Head-sha: \`${DELTA_B}\``], [`- Head-sha: \`${DELTA_B}\``], ['```', `Head-sha: \`${DELTA_B}\``, '```']]; + const QUOTED_ABOVE = (lead) => ({ id: 6516042955, created_at: '2026-10-30T05:53:38Z', body: ['## Contract review', '', ...lead, `Head-sha: \`${DELTA_A}\``, 'Local-runs: none', '', `Reviewed-by: \`${RECORD_SESSION}\``, '', '**VERDICT: PASS**'].join('\n') }); + t('⛔ #28024: a blockquoted, bulleted or fenced `Head-sha:` line ABOVE the record\'s own is ABSENT for the head it names — on the PR thread and on the card alike', CITED_LEADS.every((lead) => locateReviewOfRecord(pairOf({ headSha: DELTA_B, prComments: [QUOTED_ABOVE(lead)] })).state === 'absent' && locateReviewOfRecord(pairOf({ headSha: DELTA_B, cardComments: [QUOTED_ABOVE(lead)] })).state === 'absent')); + t('⭐ #28024: …and each record is FOUND for the head its OWN line names, with that line\'s full sha', CITED_LEADS.every((lead) => { const r = locateReviewOfRecord(pairOf({ headSha: DELTA_A, prComments: [QUOTED_ABOVE(lead)] })); return r.state === 'found' && r.sha === DELTA_A && r.id === 6516042955; })); + const PRE_HEADING = { ...QUOTED_ABOVE([]), body: `Head-sha: \`${DELTA_B}\`\n\n${QUOTED_ABOVE([]).body}` }; + // ⛔ A fully QUOTED signed record followed by a plain prose line opening `Head-sha:`: the quoted heading opens no scope, + // so the plain line is not read against the quoted signature — absent for B, and absent for A (the quoted line is quoted). + const QUOTED_RECORD = { id: 6516042956, created_at: '2026-10-30T05:53:38Z', body: QUOTED_ABOVE([]).body.split('\n').map((l) => `> ${l}`).join('\n') + `\n\nHead-sha: \`${DELTA_B}\` is the head the PR has since moved to.` }; + t('⛔ #28024: a QUOTED record followed by a plain `Head-sha:` prose line is ABSENT for both heads — a blockquoted heading opens no scope', locateReviewOfRecord(pairOf({ headSha: DELTA_B, prComments: [QUOTED_RECORD] })).state === 'absent' && locateReviewOfRecord(pairOf({ headSha: DELTA_A, prComments: [QUOTED_RECORD] })).state === 'absent'); + t('⛔ #28024: a `Head-sha:` line BEFORE the `## Contract review` heading is not the record\'s — absent for the head it names, found for the one below the heading', locateReviewOfRecord(pairOf({ headSha: DELTA_B, prComments: [PRE_HEADING] })).state === 'absent' && locateReviewOfRecord(pairOf({ headSha: DELTA_A, prComments: [PRE_HEADING] })).state === 'found'); t('⭐ a correct record beside a refused one is FOUND, in either arrival order', locateReviewOfRecord(pairOf({ prComments: [KEYED_ROW, NEWER_RECORD] })).state === 'found' && locateReviewOfRecord(pairOf({ prComments: [RECORD(HEAD_9AF9, undefined, '2026-09-01T08:45:00Z', 3404), KEYED_ROW] })).state === 'found'); t('…and the locator chooses the CORRECT one — a refused spelling is never chosen over it, and never chosen at all', locateReviewOfRecord(pairOf({ prComments: [KEYED_ROW, NEWER_RECORD] })).id === 3403 && locateReviewOfRecord(pairOf({ prComments: [RECORD(HEAD_9AF9, undefined, '2026-09-01T08:45:00Z', 3404), KEYED_ROW] })).id === 3404); t('the template and the reader name ONE spelling — and the printed template still round-trips', contractReviewHeadMatch(contractReviewTemplateLines({ headSha: HEAD_9AF9 }).join('\n'), HEAD_9AF9) === HEAD_9AF9);