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
44 changes: 32 additions & 12 deletions scripts/pm/check-half-states.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12687,8 +12687,16 @@ export const CONTRACT_REVIEW_HEAD_SHA_LINE = new RegExp(
* 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.
*
* An OPENING fence may carry an info string after the run (```md); a CLOSING
* fence may not — a run followed by anything but whitespace is content of the
* open fence, not its close (`FENCE_CLOSE_LINE`), as CommonMark renders it.
* Measured in the delta review of the fence-tracking reader (2026-10-30): a
* ``` followed by prose closed the fence here while the renderer kept it open,
* so a `Head-sha:` line after it was read; 0 of 713 live records carry one.
*/
const FENCE_LINE = /^[ ]{0,3}(`{3,}|~{3,})/;
const FENCE_CLOSE_LINE = /^[ ]{0,3}(`{3,}|~{3,})[ \t]*$/;

/**
* The head a contract-review record names — the span on the first `Head-sha:`
Expand All @@ -12702,14 +12710,17 @@ const FENCE_LINE = /^[ ]{0,3}(`{3,}|~{3,})/;
* 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
* before the first UNQUOTED, UNINDENTED 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; nor does a heading indented four spaces or a tab, which is an
* indented code block to the renderer, held to the same three-space bound as
* the key line itself; 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 on a
* line of its own (`FENCE_LINE`, `FENCE_CLOSE_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,
Expand All @@ -12729,15 +12740,18 @@ 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/)) {
if (fence !== null) {
const c = FENCE_CLOSE_LINE.exec(line);
if (c !== null && c[1][0] === fence.ch && c[1].length >= fence.len) fence = null;
continue;
}
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;
fence = { ch: f[1][0], len: f[1].length };
continue;
}
if (fence !== null) continue;
if (!belowHeading) {
belowHeading = CONTRACT_REVIEW_HEADING_MARKER.test(line) && !/^\s*>/.test(line);
belowHeading = CONTRACT_REVIEW_HEADING_MARKER.test(line) && /^[ ]{0,3}#/.test(line);
continue;
}
const m = CONTRACT_REVIEW_HEAD_SHA_LINE.exec(line);
Expand Down Expand Up @@ -33545,6 +33559,12 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
// ⛔ 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('|'));
// ⛔ A closing run with TRAILING TEXT is fence content, not a close (CommonMark): the fence stays open past it, so a
// `Head-sha:` line after such a line is still fenced; a bare run later closes it; whitespace after the run still closes.
t('⛔ record reader: a fence is closed only by a run on a line of its OWN — a run with trailing text is content, so the line after it stays fenced; a bare run later closes; trailing whitespace still closes', [contractReviewHeadSha(UNDER51('```', '``` not a close', `Head-sha: \`${HEAD51}\``)), contractReviewHeadSha(UNDER51('```', '``` not a close', `Head-sha: \`${HEAD51}\``, '```', `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51('~~~', '~~~ tail', `Head-sha: \`${HEAD51}\``, '~~~', `Head-sha: \`${OLDHEAD51}\``)), contractReviewHeadSha(UNDER51('```md', `Head-sha: \`${HEAD51}\``, '``` ', `Head-sha: \`${OLDHEAD51}\``))].join('|'), ['', OLDHEAD51, OLDHEAD51, OLDHEAD51].join('|'));
// ⛔ A heading indented as CODE (four spaces or a tab) opens no scope — the same three-space bound the key line is held
// to — while one indented up to three spaces does; the marker itself keeps its tolerance for the locator.
t('⛔ record reader: a `## Contract review` heading indented four spaces or a tab opens no scope, one indented three does; the marker still finds the indented one', [contractReviewHeadSha([' ## Contract review', `Head-sha: \`${HEAD51}\``].join('\n')), contractReviewHeadSha(['\t## Contract review', `Head-sha: \`${HEAD51}\``].join('\n')), contractReviewHeadSha([' ## Contract review', `Head-sha: \`${HEAD51}\``].join('\n')), latestContractReviewOnHead([cm51(1, [' ## Contract review', `Head-sha: \`${HEAD51}\``].join('\n'), T51)], HEAD51), CONTRACT_REVIEW_HEADING_MARKER.test(' ## Contract review')].join('|'), ['', '', HEAD51, '', 'true'].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,
Expand Down
26 changes: 18 additions & 8 deletions scripts/pm/post-stamped.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2060,17 +2060,20 @@ 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',
'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 (up to three spaces of indent) BELOW an unquoted, unindented `## Contract review` heading — not blockquoted, not bulleted, not inside a fence, not indented as code (four spaces or a tab), not before the heading, not under a quoted or code-indented 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;
/** PRESENCE only, case-blind: a `Head-sha` key opening a LINE, however decorated or placed — any indent, blockquote
* markers, a bullet or a list number, bold or backticks around the key — so a decorated, fenced, code-indented or
* pre-heading key line still counts as written. Consulted solely to tell "the author wrote the key and the reader
* cannot read it" (refused) from "no key at all" (not re-judged). ⛔ Not mid-sentence: a comment that quotes a record's
* heading and then names the key in prose wrote no key line, and was refused for it until 2026-10-30 (loudness only). */
const HEAD_SHA_KEY_LINE = /^[ \t]*(?:>[ \t]*)*(?:(?:[-*+]|\d+[.)])[ \t]+)?[`*]*head-sha[ \t`*]*:/im;

/** 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));
const hit = text.split(/\r?\n/).find((line) => HEAD_SHA_KEY_LINE.test(line));
return hit === undefined ? '(no single line carries the key)' : offendingSpan(hit.trim(), 160);
}

Expand All @@ -2081,7 +2084,8 @@ function headShaKeyLine(text) {
* 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
* through: the first plain `Head-sha:` line below an unquoted, unindented heading, outside any blockquote,
* bullet, fence or indented code. 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.
Expand All @@ -2093,8 +2097,8 @@ 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.' });
if (HEAD_SHA_KEY_LINE.test(text) && contractReviewHeadSha(text) === null)
rows.push({ key: 'Head-sha', line: headShaKeyLine(text), why: '`contractReviewHeadSha` reads NO head here — a `Head-sha` key opens a line, but no line below an unquoted, unindented `## Contract review` heading carries it plainly with a hex span of its own (a blockquoted, bulleted, fenced or pre-heading line, a code-indented line — four spaces or a tab — a line under a quoted or code-indented heading, 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))
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.' });
Expand Down Expand Up @@ -5319,6 +5323,12 @@ export function selfTest() {
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');
// ⛔ PRESENCE is a key LINE, however decorated, ⛔ never a mid-sentence mention: a record-shaped comment that names the key
// in prose wrote no line and is untouched; a key under a code-indented heading, or opening a numbered, doubly quoted
// or `+`-bulleted line, is a written line the reader refuses, and is refused here.
t('⭐ NOT a key line: a record-shaped comment that names `Head-sha:` mid-sentence, with no line opening on the key, is untouched — presence is a key LINE', rkeys('## Contract review\n\nThe seat\'s own record carries its Head-sha: line plainly, below the heading; see `Head-sha:` in the template.') === '' && rkeys(`> ## Contract review\n\nquoted above; the record\'s Head-sha: line is \`${SHA_HEAD}\` there`) === '');
t('⛔ …while a decorated key LINE still counts as written and is refused when the reader names no head: numbered, doubly quoted, `+`-bulleted, and one under a CODE-INDENTED heading', rkeys(SHA_WITH([`1. Head-sha: \`${SHA_HEAD}\``])) === 'Head-sha' && rkeys(SHA_WITH([`> > **Head-sha:** \`${SHA_HEAD}\``])) === 'Head-sha' && rkeys(SHA_WITH([`+ Head-sha: \`${SHA_HEAD}\``])) === 'Head-sha' && rkeys(` ## Contract review\nHead-sha: \`${SHA_HEAD}\``) === 'Head-sha');
t('the `Head-sha` refusal and the printed spelling NAME the two causes the reader added — a code-indented line and a line under a quoted or code-indented heading', (() => { const text = recordLineRefusalText(recordKeyedLineRefusals(SHA_WITH([`\tHead-sha: \`${SHA_HEAD}\``]))); const rule = RECORD_KEY_SPELLINGS['Head-sha']; return text.includes('a code-indented line — four spaces or a tab') && text.includes('a line under a quoted or code-indented heading') && rule.includes('not indented as code (four spaces or a tab)') && rule.includes('not under a quoted or code-indented heading') && rule.includes('BELOW an unquoted, unindented'); })());
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}\``) === '');
Expand Down
9 changes: 5 additions & 4 deletions scripts/pm/record-recognisers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1720,10 +1720,11 @@ export function contractReviewTemplateLines(values = {}) {
` the review owed by the \`Clause-②: yes\` line alone ⇒ ${TIER_DEFAULT_NAME} (--face declaration).`,
faceLine,
' An at-tier/total stamp control may precede it — 75/75, then the constant.',
' · Head-sha the head you reviewed, 7 to 40 hex in a span of ITS OWN, on THIS line: it is',
' the only place the head is read, so a sha in the heading or in the prose names',
' nothing; a span holding the key as well is not a sha, and the record then names',
' no head.',
' · Head-sha the head you reviewed, 7 to 40 hex in a span of ITS OWN, on THIS line, plain and',
' BELOW the heading: it is the only place the head is read, so a sha in the heading',
' or in the prose names nothing; a span holding the key as well is not a sha, and a',
' quoted, bulleted, fenced, code-indented or pre-heading line is prose — the record',
' then names no head.',
' · Local-runs `none` — the read-only shape held (the diff, the card and the head\'s check-runs;',
' nothing built, run or re-run locally); or `probe — REASON`, naming the ONE local run',
' the rule was broken for. A record without the line predates it and is not re-judged;',
Expand Down
Loading