Repository navigation
Commit 1831836
fix(pm): read a record's Head-sha line only below its heading, refuse an unreadable one at write time, and name the line in the rules (#28042)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DHcz5tQsg7ziuDgABaqcfbH)_
domain:skills#2 · os-elon-musk · session_016WV4czYNVYrGQqbs51hpvv
Fixes #28024
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:契约复核记录「审的是哪个 head」现在只从它自己 `## Contract review`
题头**之下**、第一条不带引用块、不带列表符、不在代码围栏里的 `Head-sha:` 行读;题头之前、引用、列表、围栏里的
`Head-sha:` 行一律当散文。写入侧 `post-stamped.mjs` 在发评论前就拒收「带 `Head-sha` 键但读器读不出
head」的记录。规则文 `contract-review.md:33` 点名这一行。4 个文件,+141/−32,无新门禁。
- 为什么改:PR #28023 之后读器取的是全文第一条 `Head-sha:` 键行,且容忍引用块和列表符。于是一条引用别人记录的 `>
Head-sha: B` 若排在自己那行之上,记录就被读成「审过 B」(复核探针实测:在 B 找到、在真 head 缺席)。这是三条
should-fix 里唯一能放进错 head 的一条,另两条是让作者早点知道。
- 风险与回滚:只收紧不放宽。实测 2026-10-23 至 30 的 709 条题头记录:707 条本来就是题头下的裸行,2 条无
`Head-sha` 键(已知旧写法,PR 已合并),0 条在新读法下读数改变。读不出的记录读为「无
head」,补发一条正确的记录即可;写入侧的拒收会在发出前就说明哪一行坏了。回滚即 revert。
- 先看:`scripts/pm/check-half-states.mjs` 的 `contractReviewHeadSha` 与
`CONTRACT_REVIEW_HEAD_SHA_LINE`(所有读者只经这一个函数取
head);`scripts/pm/post-stamped.mjs` 的
`recordKeyedLineRefusals`(导入同一读器,不重拼);`.claude/skills/pm-dispatch/references/contract-review.md:33`。
- 两分钟验证:`node scripts/pm/check-half-states.mjs --self-test && node
scripts/pm/post-stamped.mjs --self-test`,预期两者 exit 0(5102 与 743
例),其中含「引用行在上、真行在下:在引用 head 缺席、在真 head 找到」的钉子。
- 需要你做的:无。本 PR 触及 `.claude/**`
受管规则文,欠一轮契约复核(席位安排,CONTRACT_REVIEW_TIER);落地档位以登记册为准(`check-governed-merges.mjs
--test` 读为 Tier S,`--pr` 在 PR 上再读一次)。
## What changes
Follow-up to PR #28023 (#27979), closing its review's should-fixes 1–3.
Triage ruling: edge 1 is the one that can admit a wrong head; edges 2
and 3 are loudness; no new gate.
- **Edge 1 — the reader** (`scripts/pm/check-half-states.mjs`).
`contractReviewHeadSha(body)` now returns the span on the FIRST
`Head-sha:` line BELOW the record's own `## Contract review` heading
(read with `CONTRACT_REVIEW_HEADING_MARKER`, blockquoted heading
included, exactly as the locator finds the record), skipping every line
inside a fenced code block (three or more backticks or tildes; an
unclosed fence runs to the end, as it renders).
`CONTRACT_REVIEW_HEAD_SHA_LINE` drops the blockquote and bullet
tolerances the `keyLineRegex` convention keeps for `Served-tier:`,
`Reviewed-by:` and `Local-runs:`: those values describe the record; this
line decides which head a PASS clears, and a quoted or bulleted
`Head-sha:` line is one the author cited rather than wrote. Bold and
backticks on the key, a trailing span on the line, and CRLF still read.
Every reader reaches the head through this one function —
`latestContractReviewOnHead` (H43), `locateReviewOfRecord`, owed-reviews
`recordReading`, the queue guard's `recordVerdict` — so they move
together; no other file changes a predicate.
- **Edge 3 — the write side** (`scripts/pm/post-stamped.mjs`).
`recordKeyedLineRefusals` refuses a `## Contract review` comment that
carries a `Head-sha` key anywhere (case-blind presence test) for which
the imported `contractReviewHeadSha` reads `null`, before the write and
beside the existing `Local-runs:` row; a record with no key at all is
not re-judged (the two retired-dialect records carry none).
`RECORD_KEY_SPELLINGS` prints the accepted shape; the refusal names the
owner. Imported from `check-half-states.mjs`, which the file already
imports at module scope — no new edge, no cycle (both self-tests that
load the graph pass).
- **Edge 2 — the rules text**
(`.claude/skills/pm-dispatch/references/contract-review.md:33`). 「head
sha 独占码段」 becomes 「`Head-sha:` 行独占码段」. 60 lines before and after; the
line is 117 bytes under the 120-byte cap.
- **Pins.** H51 battery (`check-half-states.mjs`): the four probe shapes
from the #28023 review — a `Head-sha:` line naming another head placed
before the heading, blockquoted, bulleted, fenced, each above the
record's own — read absent at the cited head and found at the real one;
a quoted, bulleted or fenced line alone, a line with no heading above
it, and a line inside an unclosed fence name no head; tilde fence and
CRLF. Locator battery (`record-recognisers.mjs`, floor 10 to 13): the
same shapes through `locateReviewOfRecord` on the PR thread and the
card. `post-stamped.mjs` battery (floor 9 to 17): each unreadable shape
refused, the quoted-above-real shape passes, absent not re-judged, both
rows together, the structural import pin now covers both readers.
## Tests
Self-tests on the committed head `fd3f1f3e`, exit codes captured before
any pipe:
| Command | Exit | Cases |
|:--|:--|:--|
| `node scripts/pm/check-half-states.mjs --self-test` | 0 | 5102 (5089
on the base of #28023) |
| `node scripts/pm/record-recognisers.mjs --self-test` | 0 | 336 |
| `node scripts/pm/post-stamped.mjs --self-test` | 0 | 743 across 26
batteries |
| `node scripts/pm/check-owed-reviews.mjs --self-test` | 0 | 65 in 9
batteries |
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 | 403
|
Ablations, one per new leg, through `scripts/ablation-replace.mjs`
(anchor hit 1 to 0, blob hash moved, restored to the HEAD blob with `git
diff HEAD` empty on every leg):
| Leg | Mutation | Red pins |
|:--|:--|:--|
| heading scope | `let belowHeading = true` | half-states 3 of 5102,
recognisers 1 of 336 (the pre-heading pin), post-stamped 1 of 743 |
| blockquote / bullet refusal | `(?:>[ \t]*)?(?:[-*][ \t]+)?` restored
into the key regex | half-states 3, recognisers 2, post-stamped 4 |
| fence skip | `if (fenced && false) continue` | half-states 4,
recognisers 2, post-stamped 1 |
| write-time refusal | `if (false && …)` in `recordKeyedLineRefusals` |
post-stamped 5 of 743 |
A first attempt at the heading-scope leg mutated the assignment inside
the loop (`belowHeading = true; continue`), which also skipped the first
line and so stayed green; it was discarded and re-run as the initialiser
mutation above. Both runs are in the report.
## Acceptance notes
- **Measured before changing the accept set** (REST
`issues/comments?since=2026-10-23`, 113 pages, 11,236 comments, at
`origin/main` `e1dde1a5c`): 709 bodies with a line-initial `## Contract
review`; 707 carry their first readable `Head-sha:` line plain and below
the heading; 2 carry no `Head-sha` key (6360650327 on PR #25399,
6397040906 on PR #25774 — the retired dialects, both PRs merged); 0
blockquoted, bulleted, bold-key, backticked-key, fenced or pre-heading
first lines; 0 blockquoted headings; 1 body with more than one
`Head-sha` key line (the #28023 review record, whose prose names the key
mid-sentence; its first line is the real one). 0 records read
differently under the new reader.
- `keyLineRegex` still tolerates blockquote and bullet for
`Served-tier:`, `Reviewed-by:` and `Local-runs:`; only the head line is
stricter, for the reason in its docblock. Not changed here.
- The template's null-oid placeholder is 40 hex and reads as a head, so
`post-stamped` does not refuse a pasted template; C6 refuses it at the
locator, as before.
- Scope: exactly the four files the claim names (record-recognisers.mjs
for pins only). No production predicate outside `check-half-states.mjs`
and `post-stamped.mjs` moved.
- `skip-changeset`: `scripts/pm/**` and `.claude/**` only, nothing
published.
## Verification
Local gates on the committed head `fd3f1f3e`, from a second worktree at
the same commit (`pnpm install --frozen-lockfile --ignore-scripts`, then
`turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` for the formula gate), exit codes captured
before any pipe:
- `node scripts/pm/dispatch-gates.mjs --commands` derived 56 families
for the 4 changed paths (+141 / −32, under the human-merge threshold).
- All 56 ran and exited 0 — among them `check:pm-half-states`,
`check:pm-post-stamped`, `check:pm-record-recognisers` (recognisers +
owed-reviews + mailbox-open + fleet-roster self-tests),
`check:pm-governed-merges`, `check:pm-dispatch-gates`,
`check:pm-skill-id-lint`, `check:pm-skill-ratchet` (the line budget and
the 120-byte line cap), `check:skill-frame-sync`,
`check:commit-card-trailers`, `check:nul-bytes`,
`check:scripts-symbol-anchors`.
- `node scripts/pm/dispatch-gates.mjs --ran ran.txt`: 56 derived, 56
run, 0 NOT-MEASURED, 0 UNRUN (exit 0).
- `pnpm lint` (the repo-wide ESLint sweep) is CI-owned and was not run
locally; read it off the PR check-runs.
- Landing tier: `node scripts/pm/check-governed-merges.mjs --test` on
the four paths answers Tier S (`.claude/**` ×1, the three
`scripts/pm/**` paths not on the register); the seat reads `--pr` on
this PR.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4czYNVYrGQqbs51hpvv)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2f2c6b9 commit 1831836
4 files changed
Lines changed: 169 additions & 33 deletions
File tree
- .claude/skills/pm-dispatch/references
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
| 33 | + | |
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12641 | 12641 | | |
12642 | 12642 | | |
12643 | 12643 | | |
12644 | | - | |
12645 | | - | |
12646 | | - | |
| 12644 | + | |
| 12645 | + | |
| 12646 | + | |
| 12647 | + | |
| 12648 | + | |
| 12649 | + | |
| 12650 | + | |
| 12651 | + | |
| 12652 | + | |
| 12653 | + | |
| 12654 | + | |
| 12655 | + | |
| 12656 | + | |
| 12657 | + | |
| 12658 | + | |
12647 | 12659 | | |
12648 | 12660 | | |
12649 | 12661 | | |
| |||
12652 | 12664 | | |
12653 | 12665 | | |
12654 | 12666 | | |
12655 | | - | |
| 12667 | + | |
| 12668 | + | |
| 12669 | + | |
12656 | 12670 | | |
12657 | 12671 | | |
12658 | | - | |
| 12672 | + | |
12659 | 12673 | | |
12660 | 12674 | | |
12661 | 12675 | | |
12662 | | - | |
12663 | | - | |
12664 | | - | |
12665 | | - | |
12666 | | - | |
12667 | | - | |
12668 | | - | |
12669 | | - | |
| 12676 | + | |
| 12677 | + | |
| 12678 | + | |
| 12679 | + | |
| 12680 | + | |
| 12681 | + | |
| 12682 | + | |
| 12683 | + | |
| 12684 | + | |
| 12685 | + | |
| 12686 | + | |
| 12687 | + | |
| 12688 | + | |
| 12689 | + | |
| 12690 | + | |
| 12691 | + | |
| 12692 | + | |
| 12693 | + | |
| 12694 | + | |
| 12695 | + | |
| 12696 | + | |
| 12697 | + | |
| 12698 | + | |
| 12699 | + | |
| 12700 | + | |
| 12701 | + | |
| 12702 | + | |
| 12703 | + | |
| 12704 | + | |
| 12705 | + | |
| 12706 | + | |
| 12707 | + | |
| 12708 | + | |
| 12709 | + | |
| 12710 | + | |
| 12711 | + | |
| 12712 | + | |
| 12713 | + | |
| 12714 | + | |
| 12715 | + | |
| 12716 | + | |
| 12717 | + | |
| 12718 | + | |
| 12719 | + | |
12670 | 12720 | | |
12671 | 12721 | | |
| 12722 | + | |
| 12723 | + | |
12672 | 12724 | | |
| 12725 | + | |
| 12726 | + | |
| 12727 | + | |
| 12728 | + | |
| 12729 | + | |
| 12730 | + | |
| 12731 | + | |
| 12732 | + | |
| 12733 | + | |
| 12734 | + | |
| 12735 | + | |
12673 | 12736 | | |
12674 | 12737 | | |
12675 | 12738 | | |
| |||
33452 | 33515 | | |
33453 | 33516 | | |
33454 | 33517 | | |
33455 | | - | |
33456 | | - | |
| 33518 | + | |
| 33519 | + | |
| 33520 | + | |
| 33521 | + | |
| 33522 | + | |
| 33523 | + | |
| 33524 | + | |
| 33525 | + | |
| 33526 | + | |
| 33527 | + | |
| 33528 | + | |
| 33529 | + | |
| 33530 | + | |
| 33531 | + | |
| 33532 | + | |
| 33533 | + | |
| 33534 | + | |
| 33535 | + | |
| 33536 | + | |
| 33537 | + | |
| 33538 | + | |
| 33539 | + | |
| 33540 | + | |
| 33541 | + | |
| 33542 | + | |
| 33543 | + | |
33457 | 33544 | | |
33458 | 33545 | | |
33459 | 33546 | | |
| |||
0 commit comments