Skip to content

Commit 47a8b58

Browse files
fix(pm): the record reader closes a fence only on a bare run and opens its scope only on an unindented heading; the writer refuses on a key line and names the new causes (#28103)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9D6oE9YXTxKNvir4NixYdUwr)_ domain:skills#2 · os-elon-musk · session_016WV4czoR81DDKSnGWnBYqA Fixes #28057 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:契约复核记录的 `Head-sha:` 读取器再收紧两处,写入侧的拒收更准、报错点名新原因。(1) 围栏代码只在「同字符、不短于开栏、整行只有符号」的一行上关闭;三个反引号后面跟了文字的行是围栏内容,不关栏。(2) `## Contract review` 题头缩进 4 个空格或 tab(渲染为代码块)不再打开读取范围,与 `Head-sha:` 行本身的 3 空格上限一致。(3) `post-stamped.mjs` 发帖前的 `Head-sha` 拒收改为只看「以该键开头的行」(任意缩进、引用、列表、粗体、反引号都算),不再因为正文句中提到 `Head-sha:` 就拒收。(4) 拒收文案与 `RECORD_KEY_SPELLINGS['Head-sha']` 点名两个新原因:代码缩进的行、引用或代码缩进题头之下的行。(5) `contract-review.md:33` 写明 `Head-sha:` 行在「题头之下」,行数不变、120 字节;`--template` 的说明同步。4 个文件。 - 为什么改:这是 PR #28042 delta 复核的 should-fix 4–8。两种残留写法在基线上同样会把引用或代码样式的行读成记录的 head;线上 713 条记录 0 例,属响亮度收尾。写入侧那条是误拒:引了题头、句中提到键名的普通评论被拒。无新门禁、无新拒收类。 - 风险与回滚:读取只收紧不放宽;写入侧拒收集缩到「有键行」,#28042 钉住的每种拒收写法仍拒。实测 2026-10-23 以来 11482 条评论、724 条带题头记录:新旧读法逐条相同(0 变),拒收逐条相同(0 变)。回滚即 revert。 - 先看:`check-half-states.mjs` 的 `FENCE_CLOSE_LINE` 与 `contractReviewHeadSha` 的两行改动;再看 `post-stamped.mjs` 的 `HEAD_SHA_KEY_LINE`。 - 两分钟验证:本 head 五个自测全部 exit 0:half-states 5113、recognisers 337、post-stamped 747、owed-reviews 65、guard 403;四条消融各恰红一条新钉。 - 需要你做的:无。本 PR 欠一次契约复审(席位安排);落地档以登记册为准(`.claude/**` ×1,本地 `--test` 读为 Tier S)。 ## What changes - **`scripts/pm/check-half-states.mjs`.** `FENCE_CLOSE_LINE` (new, module-private) admits only whitespace after the run; `contractReviewHeadSha` now opens a fence on `FENCE_LINE` (info string allowed) and closes it only on a `FENCE_CLOSE_LINE` of the same character with a run at least the opening length. The heading scope opens only when the marker line also matches `^[ ]{0,3}#` (no blockquote marker, no four-space or tab indent); `CONTRACT_REVIEW_HEADING_MARKER` itself is unchanged, so the locator, H43, owed-reviews, the guard and `recordKeyedLineRefusals` still see an indented or quoted heading as a record. Two pins in the H51 block: the trailing-text close (open stays open; a later bare run closes; trailing whitespace closes) and the indented heading (four spaces and a tab open nothing, three spaces does, the marker still finds it, `latestContractReviewOnHead` answers null). - **`scripts/pm/post-stamped.mjs`.** `HEAD_SHA_KEY_ANYWHERE` becomes `HEAD_SHA_KEY_LINE`: a line opening on the key after any indent, blockquote markers, a `-`/`*`/`+` bullet or a list number, bold or backticks, case-blind, `m` flag. Every refused shape the #28042 battery pins still reads as a key line; a mid-sentence mention does not. The refusal `why` and `RECORD_KEY_SPELLINGS['Head-sha']` name the code-indented line and the line under a quoted or code-indented heading. Three pins: the prose mention is untouched, decorated key lines (numbered, doubly quoted, `+`-bulleted, under a code-indented heading) are refused, the printed text names both causes. - **`scripts/pm/record-recognisers.mjs`.** The `--template` note for `Head-sha` says the line is plain and BELOW the heading, and lists the shapes that read as prose. One line added to the printed template help; no reader touched. - **`.claude/skills/pm-dispatch/references/contract-review.md`** (:33, line-neutral, 120 bytes): 「题头之下 `Head-sha:` 行独占码段」. ## Why The delta contract review of PR #28042 (record 6520969324) left should-fixes 4 to 8, filed as #28057 and triaged p3 (6522257381: loudness, no wrong head admitted). Measured on `origin/main` `549203d04` before the change: 1. `FENCE_LINE` matched a closing run with trailing text, so after ```` ``` ```` + ```` ``` not a close ```` the fence was read as closed and a `Head-sha:` line after it was read. CommonMark keeps the fence open. Same on base. 2. The scope opened on ` ## Contract review` (an indented code block to the renderer) while the key line is held to three spaces. Same on base; 0 live instances. 3. `HEAD_SHA_KEY_ANYWHERE` matched the key anywhere, so a record-shaped comment that quotes a heading at line start and names `Head-sha:` mid-sentence was refused with "a `Head-sha` key is present". Loudness only. 4. The refusal text and the spelling enumerated "blockquoted, bulleted, fenced or pre-heading" and not the two causes #28042 added. 5. `contract-review.md:33` named the line but not that it sits below the heading; the `--template` note said "on THIS line" only. No new refusal class and no new gate: the accept set of every reader is the same or narrower than base, and the write-time refusal fires on a strict subset of what it fired on before (a key LINE rather than the key anywhere). ## Verification - Self-tests on the committed head, exit captured before any pipe: `check-half-states --self-test` 0 (5113 cases), `record-recognisers --self-test` 0 (337), `post-stamped --self-test` 0 (747 across 26 batteries), `check-owed-reviews --self-test` 0 (65 in 9 batteries), `check-governed-queue-guard --self-test` 0 (403). - Ablation on the committed head, each leg restored with `git checkout` and `git status --porcelain` empty: (A) `FENCE_CLOSE_LINE` back to the bare `FENCE_LINE` shape: half-states 1 of 5113 red (the trailing-text pin), recognisers 0, post-stamped 0. (B) the scope-open test back to `!/^\s*>/`: half-states 1 red (the indented-heading pin), post-stamped 1 of 747 (the code-indented-heading limb). (C) `HEAD_SHA_KEY_LINE` back to `/head-sha[ \t`*]*:/i`: post-stamped 1 red (the mid-sentence pin). (D) the two new causes removed from the `why` and the spelling: post-stamped 1 red (the wording pin). No other pin moves in any leg. - Corpus: 11482 issue comments updated since 2026-10-23 (REST, 115 pages), 724 carrying the heading marker, driven through `contractReviewHeadSha` and `recordKeyedLineRefusals` in a worktree at this head and one at `origin/main` `6b125a7a`: 0 records read differently, 0 records refused differently, 0 differences in the refusal over all 11482 comments; the 2 nulls are the two no-key legacy records, the 1 refusal on both sides is the pre-existing `Local-runs` row on record 6517315440. - Skill text: `check-skill-line-ratchet` exit 0 (contract-review.md 60 lines, ceiling 60; line 33 is 120 bytes under `MAX_LINE_BYTES` 120), `check-skill-id-lint` exit 0 (105 files clean), `check-skills-token-ratchet` exit 0 (56 files within ceilings). - Gates: `dispatch-gates --commands` at the committed head `f619d317` derives 56 families; all 56 run in a second worktree (`pnpm install --frozen-lockfile --ignore-scripts` first), exit codes recorded; `--ran` reconciles 56 derived / 56 run / 0 NOT-MEASURED / 0 UNRUN. One first-pass exit 3 (`check:doc-formula-expressions`, no formula/lint dist) exits 0 after `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint`. No family red. No changeset derived: no published package is touched, `skip-changeset` rides the create. - Landing tier: `check-governed-merges.mjs --test` on the four paths answers Tier S (`.claude/**` ×1; the three `scripts/pm/**` paths are off the register); the register's reading at PR time is the seat's. ## Human Minimum Each item is one rule from [CONTRIBUTING.md › Human Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum), which links the AGENTS.md text that states it. - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; the derived lint families pass. - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (no published package: `skip-changeset`) - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. (not touched) - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English (the 维护者速读 block is the one Chinese channel). - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016WV4czoR81DDKSnGWnBYqA Co-authored-by: Claude <noreply@anthropic.com>
1 parent cbb6df1 commit 47a8b58

4 files changed

Lines changed: 56 additions & 25 deletions

File tree

‎.claude/skills/pm-dispatch/references/contract-review.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
- 新 head 即清;点名现 head 时 (b) 不起;条件满足由持有车道挂等复核标记或落交接行。
3131
- 逾 24 小时条件未满的 hold 列入分诊轮次记录(`check-owed-reviews.mjs` 标出)。
3232
- 复核记录 = 一条评论落 PR 或卡,席内与子代理同形;散文、dev 自评、`os-dev-report` 恒不算。
33-
- 同形 = `## Contract review` 题头、`Head-sha:` 行独占码段、①②③ 逐项、独立性对、PASS/FAIL。
33+
- 同形 = `## Contract review` 题头之下 `Head-sha:` 行独占码段、①②③逐项、独立性对、PASS/FAIL。
3434
- 同形含首行 `Served-tier:`:值写该面所定档的常量名 `CONTRACT_REVIEW_TIER` 或 `TIER_DEFAULT`;
3535
无此行不成裁决;模板 `node scripts/pm/record-recognisers.mjs --template`,申报面加 `--face declaration`。
3636
- 同形带 `Local-runs:` 行:`none` 或 `probe — REASON` 具名唯一探针;有行必校,缺行旧记录不改判。

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

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12687,8 +12687,16 @@ export const CONTRACT_REVIEW_HEAD_SHA_LINE = new RegExp(
1268712687
* open fence (a nested ``` inside a ```` block, a ~~~ inside a ``` block), and
1268812688
* reading past either would read fenced content as the record's line. An
1268912689
* unclosed fence runs to the end of the body, as it renders.
12690+
*
12691+
* An OPENING fence may carry an info string after the run (```md); a CLOSING
12692+
* fence may not — a run followed by anything but whitespace is content of the
12693+
* open fence, not its close (`FENCE_CLOSE_LINE`), as CommonMark renders it.
12694+
* Measured in the delta review of the fence-tracking reader (2026-10-30): a
12695+
* ``` followed by prose closed the fence here while the renderer kept it open,
12696+
* so a `Head-sha:` line after it was read; 0 of 713 live records carry one.
1269012697
*/
1269112698
const FENCE_LINE = /^[ ]{0,3}(`{3,}|~{3,})/;
12699+
const FENCE_CLOSE_LINE = /^[ ]{0,3}(`{3,}|~{3,})[ \t]*$/;
1269212700

1269312701
/**
1269412702
* The head a contract-review record names — the span on the first `Head-sha:`
@@ -12702,14 +12710,17 @@ const FENCE_LINE = /^[ ]{0,3}(`{3,}|~{3,})/;
1270212710
* ABOVE the record's own line — read as the head, so the record was found at
1270312711
* the cited head and absent at the real one. None is prose to a first-line
1270412712
* reader, and all four shapes are what a quotation looks like. So: lines
12705-
* before the first UNQUOTED heading-marker line are skipped — the heading is
12706-
* read with `CONTRACT_REVIEW_HEADING_MARKER`, but a blockquoted heading opens
12707-
* nothing here, because a fully quoted record (heading, head line and
12708-
* signature all under `>`) followed by a plain line opening `Head-sha:` would
12709-
* otherwise hand the quoted record's signature to the plain line's head; the
12710-
* locator still finds such a comment as a record, and it then names no head.
12711-
* A fenced line is skipped, fence boundaries included, with the fence closed
12712-
* only by its own character and length (`FENCE_LINE`); and
12713+
* before the first UNQUOTED, UNINDENTED heading-marker line are skipped — the
12714+
* heading is read with `CONTRACT_REVIEW_HEADING_MARKER`, but a blockquoted
12715+
* heading opens nothing here, because a fully quoted record (heading, head
12716+
* line and signature all under `>`) followed by a plain line opening
12717+
* `Head-sha:` would otherwise hand the quoted record's signature to the plain
12718+
* line's head; nor does a heading indented four spaces or a tab, which is an
12719+
* indented code block to the renderer, held to the same three-space bound as
12720+
* the key line itself; the locator still finds such a comment as a record,
12721+
* and it then names no head. A fenced line is skipped, fence boundaries
12722+
* included, with the fence closed only by its own character and length on a
12723+
* line of its own (`FENCE_LINE`, `FENCE_CLOSE_LINE`); and
1271312724
* `CONTRACT_REVIEW_HEAD_SHA_LINE` itself refuses the blockquote, the bullet
1271412725
* and an indented-code line. Among the lines that remain, the FIRST wins, as
1271512726
* `readServedTier` reads its key: a record is one comment and names one head,
@@ -12729,15 +12740,18 @@ export function contractReviewHeadSha(body) {
1272912740
let fence = null; // the OPEN fence — its character and run length — or null outside one
1273012741
let belowHeading = false;
1273112742
for (const line of String(body ?? '').split(/\r?\n/)) {
12743+
if (fence !== null) {
12744+
const c = FENCE_CLOSE_LINE.exec(line);
12745+
if (c !== null && c[1][0] === fence.ch && c[1].length >= fence.len) fence = null;
12746+
continue;
12747+
}
1273212748
const f = FENCE_LINE.exec(line);
1273312749
if (f !== null) {
12734-
if (fence === null) fence = { ch: f[1][0], len: f[1].length };
12735-
else if (f[1][0] === fence.ch && f[1].length >= fence.len) fence = null;
12750+
fence = { ch: f[1][0], len: f[1].length };
1273612751
continue;
1273712752
}
12738-
if (fence !== null) continue;
1273912753
if (!belowHeading) {
12740-
belowHeading = CONTRACT_REVIEW_HEADING_MARKER.test(line) && !/^\s*>/.test(line);
12754+
belowHeading = CONTRACT_REVIEW_HEADING_MARKER.test(line) && /^[ ]{0,3}#/.test(line);
1274112755
continue;
1274212756
}
1274312757
const m = CONTRACT_REVIEW_HEAD_SHA_LINE.exec(line);
@@ -33545,6 +33559,12 @@ Doubles as the fire's **write self-check** (step 0). \`201\` is not the reading.
3354533559
// ⛔ A fence closes only on its OWN character and a run at least as long: a nested ``` inside ````, a ~~~ inside ```,
3354633560
// are content, and a `Head-sha:` line between them stays fenced; a longer closing run closes; a never-closed one runs out.
3354733561
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('|'));
33562+
// ⛔ A closing run with TRAILING TEXT is fence content, not a close (CommonMark): the fence stays open past it, so a
33563+
// `Head-sha:` line after such a line is still fenced; a bare run later closes it; whitespace after the run still closes.
33564+
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('|'));
33565+
// ⛔ A heading indented as CODE (four spaces or a tab) opens no scope — the same three-space bound the key line is held
33566+
// to — while one indented up to three spaces does; the marker itself keeps its tolerance for the locator.
33567+
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('|'));
3354833568
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('|'), '|||');
3354933569
// ⛔ The shapes the review of the line-form reader probed (2026-10-30): a `Head-sha:` line naming ANOTHER head above the
3355033570
// record's own — placed before the heading, blockquoted, bulleted or fenced. Each is a citation, not the record's line,

‎scripts/pm/post-stamped.mjs‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,17 +2060,20 @@ export function keyedLineRefusalText(rows) {
20602060

20612061
/** What the record's owner accepts, printed BY the refusal so the fix is one line away. */
20622062
export const RECORD_KEY_SPELLINGS = Object.freeze({
2063-
'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',
2063+
'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',
20642064
'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',
20652065
});
20662066

2067-
/** PRESENCE only, case-blind: a `Head-sha` key anywhere in the body, however decorated or placed. Consulted solely to
2068-
* tell "the author wrote the key and the reader cannot read it" (refused) from "no key at all" (not re-judged). */
2069-
const HEAD_SHA_KEY_ANYWHERE = /head-sha[ \t`*]*:/i;
2067+
/** PRESENCE only, case-blind: a `Head-sha` key opening a LINE, however decorated or placed — any indent, blockquote
2068+
* markers, a bullet or a list number, bold or backticks around the key — so a decorated, fenced, code-indented or
2069+
* pre-heading key line still counts as written. Consulted solely to tell "the author wrote the key and the reader
2070+
* cannot read it" (refused) from "no key at all" (not re-judged). ⛔ Not mid-sentence: a comment that quotes a record's
2071+
* heading and then names the key in prose wrote no key line, and was refused for it until 2026-10-30 (loudness only). */
2072+
const HEAD_SHA_KEY_LINE = /^[ \t]*(?:>[ \t]*)*(?:(?:[-*+]|\d+[.)])[ \t]+)?[`*]*head-sha[ \t`*]*:/im;
20702073

20712074
/** The first line carrying a `Head-sha` key, however spelled — a LOCATOR for the writer's eye, ⛔ never the judgement. */
20722075
function headShaKeyLine(text) {
2073-
const hit = text.split(/\r?\n/).find((line) => HEAD_SHA_KEY_ANYWHERE.test(line));
2076+
const hit = text.split(/\r?\n/).find((line) => HEAD_SHA_KEY_LINE.test(line));
20742077
return hit === undefined ? '(no single line carries the key)' : offendingSpan(hit.trim(), 160);
20752078
}
20762079

@@ -2081,7 +2084,8 @@ function headShaKeyLine(text) {
20812084
* verdict over a line nobody had been asked to write. Scoped by the record's own heading marker, on `--comment`
20822085
* alone — a record IS a comment.
20832086
* Head-sha `contractReviewHeadSha` (`check-half-states.mjs`) is the ONE reader every guard reaches the record's head
2084-
* through: the first plain `Head-sha:` line below the heading, outside any blockquote, bullet or fence. A
2087+
* through: the first plain `Head-sha:` line below an unquoted, unindented heading, outside any blockquote,
2088+
* bullet, fence or indented code. A
20852089
* body that carries the key somewhere and reads `null` there would be filed by every reader as a review of
20862090
* NO head — absent on the owed list, refused at the queue — so it is refused here, before the write. The
20872091
* template's null-oid placeholder reads as a head (it is 40 hex) and is not this row's: C6 refuses it.
@@ -2093,8 +2097,8 @@ export function recordKeyedLineRefusals(body) {
20932097
const text = String(body ?? '');
20942098
if (!CONTRACT_REVIEW_HEADING_MARKER.test(text)) return [];
20952099
const rows = [];
2096-
if (HEAD_SHA_KEY_ANYWHERE.test(text) && contractReviewHeadSha(text) === null)
2097-
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.' });
2100+
if (HEAD_SHA_KEY_LINE.test(text) && contractReviewHeadSha(text) === null)
2101+
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.' });
20982102
const runs = readLocalRuns(text);
20992103
if (!localRunsStand(runs))
21002104
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.' });
@@ -5319,6 +5323,12 @@ export function selfTest() {
53195323
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}\``])) === '');
53205324
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([])) === '');
53215325
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');
5326+
// ⛔ PRESENCE is a key LINE, however decorated, ⛔ never a mid-sentence mention: a record-shaped comment that names the key
5327+
// in prose wrote no line and is untouched; a key under a code-indented heading, or opening a numbered, doubly quoted
5328+
// or `+`-bulleted line, is a written line the reader refuses, and is refused here.
5329+
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`) === '');
5330+
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');
5331+
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'); })());
53225332
t('both lines unreadable ⇒ both rows, `Head-sha` first', rkeys(SHA_WITH([`> Head-sha: \`${SHA_HEAD}\``], 'Local-runs: probe')) === 'Head-sha,Local-runs');
53235333
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'); })());
53245334
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}\``) === '');

‎scripts/pm/record-recognisers.mjs‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,10 +1720,11 @@ export function contractReviewTemplateLines(values = {}) {
17201720
` the review owed by the \`Clause-②: yes\` line alone ⇒ ${TIER_DEFAULT_NAME} (--face declaration).`,
17211721
faceLine,
17221722
' An at-tier/total stamp control may precede it — 75/75, then the constant.',
1723-
' · Head-sha the head you reviewed, 7 to 40 hex in a span of ITS OWN, on THIS line: it is',
1724-
' the only place the head is read, so a sha in the heading or in the prose names',
1725-
' nothing; a span holding the key as well is not a sha, and the record then names',
1726-
' no head.',
1723+
' · Head-sha the head you reviewed, 7 to 40 hex in a span of ITS OWN, on THIS line, plain and',
1724+
' BELOW the heading: it is the only place the head is read, so a sha in the heading',
1725+
' or in the prose names nothing; a span holding the key as well is not a sha, and a',
1726+
' quoted, bulleted, fenced, code-indented or pre-heading line is prose — the record',
1727+
' then names no head.',
17271728
' · Local-runs `none` — the read-only shape held (the diff, the card and the head\'s check-runs;',
17281729
' nothing built, run or re-run locally); or `probe — REASON`, naming the ONE local run',
17291730
' the rule was broken for. A record without the line predates it and is not re-judged;',

0 commit comments

Comments
 (0)