Commit b3e437d
docs(agents,dogfood): point both ablation instruction surfaces at scripts/ablation-replace.mjs beside its sibling preflight (#18725)
Fixes #18599
Clause-②: no
Governed surface (`.claude/**`): draft, `skip-changeset`, an authorized
approval lands it and the `domain:skills` seat lands it from there. File
surface: `.claude/agents/os-dev.md` + `packages/qa/dogfood/README.md`
only. Head `fa55c9d0ad`, base `f6189a43f9` (`origin/main` at worktree
creation; the card's readings were taken on `ab1d35925` and re-read here
on the tip). Line-neutral on the governed file: **403 / 403**, widest
line 120 B before and after, zero lines over the ratchet's 120-byte cap.
## The defect
PR #18598 (card #18226) landed `scripts/ablation-replace.mjs` — the
on-disk mutation step of an ablation: replace through an anchor that
must hit, with the write and the restore verified against the disk
rather than an exit code, in place of the `sed -i` / `perl -i` family
that silently failed in two recorded ablations (2026-08-20, a `-0` run
whose `$/` interpolated into a NUL byte; 2026-09-14, an argument
swallowed and nothing written, exit 0). No instruction surface pointed
at it, while the two that route an agent to its sibling
`scripts/ablation-dist-preflight.mjs` do exist — so the helper could
only be found by grepping `scripts/`, which is the memory-dependent
shape #18226 was filed against. This PR adds the pointer to exactly
those two surfaces, in the order an ablation runs the two tools: mutate
on disk through the replace helper, then prove the mutation reached
`dist/` with the preflight, then read the run. Adoption-style only: not
a mandatory path, no gate, no migration of existing ablation scripts
(#18226's triage 5707288884 set that floor; the migration set measured
empty in #18598's census).
## `.claude/agents/os-dev.md` — one line in, one true duplicate out
Inserted, new :254 (119 B), directly under the mutate-then-build line of
the ablation step (:253 「每一腿(变异与还原)都是:改动 → 证明它真落到了磁盘 → …」, 108 B) and
above the preflight line (old :254, now :255, 113 B):
> - 落盘走 `node scripts/ablation-replace.mjs`,不走 `-i`
家族:锚点必须命中,写入与还原按磁盘核验。
「`-i` 家族」 is the name the file's existing :258 already gives the trap
(`sed`、`perl -i`、`str.replace`、`re.sub` 零命中也 exit 0). The line is a
routing line in the register of the preflight line beside it: one
instrument, the reason in the same breath, no 「必须」, no gate.
Paid by deleting old :319 (72 B), the opening bullet of 「干净收尾」:
> - 报告落两次,GitHub 优先:卡片评论在前,终报消息在后。
What it lost: nothing the file does not state elsewhere, twice. Its
three claims — two deliveries, GitHub first, comment before terminal
message — are the header's :17 (「报告交付两次,GitHub 优先:先作 issue 评论,…」) and
:18 (「…再作为终报消息…」), and the very next bullet of the same section (now
:320, 「终报消息之前,把同一段 JSON 发成 issue 评论,…」) restates the ordering together
with the marker. The section now opens on that bullet.
Why a payment and not a fold: measured on this tree, of the file's 284
adjacent bullet pairs ZERO merge under the 120-byte cap (smallest merged
133 B), and in the ablation block :253–:264 ZERO of 11 (smallest 162 B);
the block's total slack is 249 B spread over lines holding 0–61 B each,
none of which fits a 35-byte script path plus its reason. Re-wrap
funding is refused by the ratchet rule in any case.
Whole file: 403 → 403 lines; widest line 120 B before and after;
control-byte scan (`grep -naP` over the C0/DEL range) on both files:
clean.
## `packages/qa/dogfood/README.md` — one sentence
Step 4 of 「Adding a golden test」, placed before the existing preflight
sentence (mutate, then prove `dist/`); the helper's usage placeholders
are written as words here because the platform sanitizer eats tag-shaped
fragments in a PR body — in the file they are the helper's own
spellings:
> Make the revert itself through `node scripts/ablation-replace.mjs
--file PATH --anchor TEXT --replacement TEXT -- CMD` rather than `sed
-i` / `perl -i`: the anchor must hit, the write and the restore are
verified against the disk, and a mutation that did not land exits
non-zero instead of handing you a green run.
Non-governed, no line budget, the README's English register. Net +4
lines in that paragraph.
## Verification by the card's own instrument (tree
`objectstack-ai/objectstack`)
| reading | card @ `ab1d35925` | base `f6189a43f9` | head `fa55c9d0ad` |
|---|---|---|---|
| `git grep -lI 'ablation-replace'` | 1 as reported (that sha itself
answers 0 — the helper landed in `d0b8ec2aaf` after it) | 1 (the helper
itself) | **3** — `.claude/agents/os-dev.md`,
`packages/qa/dogfood/README.md`, `scripts/ablation-replace.mjs` |
| `git grep -lI 'ablation-dist-preflight'` | 7 | 8 (the card's 7 plus
the helper's own header, which names its sibling) | 8, unchanged |
| control `git grep -cI 'dogfood'` os-dev.md / README.md | 1 / 6 | 1 / 6
| 1 / 6, unchanged |
| `git grep -cI 'ablation'` on
`.claude/skills/dogfood-verification/SKILL.md` | 0 | 0 | 0 — the third
routing document named in #18598's report does not exist; not touched |
Per-file hits at head: `ablation-replace` — os-dev.md 1, README.md 1,
the helper 14; `ablation-dist-preflight` — os-dev.md 1, README.md 1.
## #18226 option A — a reading, not implemented here
Option A (「在字节纪律里加一条具名陷阱」: name the `perl -0` + `$/`-interpolates-to-NUL
trap in the byte-discipline rules) was **routed, not ruled, and has not
landed**. #18226's triage 5707288884 routed it to the skills seat (「选项
A(在字节纪律里加一条具名陷阱)要走 skills 席,⛔ 不在本卡内落地」), the claim 5710594253 repeated
the boundary, and PR #18598 landed option C (the helper) only. On this
tree: `AGENTS.md` carries no control-character or `check:nul-bytes`
sentence at all (its only `perl -i` mention is :224, the Bash-guard
write shapes); `os-dev.md` :387–:390 (字节与 sanitizer 纪律) covers writing
control characters as escapes and the harm of a raw NUL, and :258 covers
mode ② (nothing written, exit 0) — neither names mode ① (`-0` + `$/`).
Left untouched here (this card is the pointer only) and reported in
`open_questions` for the seat to queue.
## Gates (worktree at `fa55c9d0ad`; every exit code captured by
redirect-then-`$?`, never through a pipe)
Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` from the worktree, no hand-fed paths (change
set vs merge base `f6189a43f`: the two files; committed 2, working tree
0, untracked 0). 53 commands; reconciled with `--ran` carrying `:: exit
N` per line: **53 derived, 48 run, 5 NOT-MEASURED, 0 UNRUN**.
Named by the dispatch:
- `pnpm check:pm-skill-ratchet` :: exit 0 — 「.claude/agents/os-dev.md is
403 lines (ceiling 403; headroom 0)」 · 「widest table row is 0 bytes (pin
0; headroom 0)」
- `pnpm check:nul-bytes` :: exit 0 — 「OK (scanned 8814 text file(s) … no
raw ASCII control bytes)」
- `pnpm check:agent-model-declared` :: exit 0 — 「1 agent definition(s)
under .claude/agents/ all declare a model」
- `pnpm check:pm-governed-merges` :: exit 0 (the post-merge audit).
`node scripts/pm/check-governed-merges.mjs --pr NUMBER` needs this PR's
number, so it runs after creation; its verdict (expected GOVERNED, exit
3, which is a verdict and not a failure) is recorded in the report
comment on the card.
- `pnpm lint` (repo-wide, `eslint . --no-inline-config`, as PR #18654
ran it) :: exit 0, 100 s, on `fa55c9d0ad`
The other 43 that ran, all exit 0: `check-ci-filter-parity`,
`check-closing-keyword-parity` (+ `--self-test`),
`check-comment-mask-adoption` (+ `--self-test`),
`check-comment-mask-corpus`, `check-keyed-text-bounds` (+
`--self-test`), `check-platform-object-tenancy-census` (+
`--self-test`), `check-plugin-teardown-shape` (+ `--self-test`),
`check-registry-log-declared` (+ `--self-test`),
`check-rest-log-spy-declared` (+ `--self-test`),
`check-system-context-census` (+ `--self-test`),
`check-undeclared-dep-imports` (+ `--self-test`),
`docs-audit/check-affected-docs`, `docs-audit/check-drift-comment`,
`pm/check-governed-queue-guard --self-test`, `pm/check-harness-current
--self-test`, spec `check:empty-state` · `check:liveness` ·
`check:strictness-ledger` · `check:variant-docs`,
`check:agent-test-spelling`, `check:commit-card-trailers`,
`check:cross-package-test-inputs`, `check:doc-authoring`,
`check:driver-memory-census`, `check:org-identifier`,
`check:page-declaration-shape`, `check:pm-skill-id-lint`,
`check:published-files`, `check:refd-timer-probe`,
`check:skill-frame-sync`, `check:slot-lookup`,
`check:test-source-alias`, `check:tier-file-adoption`,
`check:type-source-resolution`, `check:watch-hint-literal`.
NOT MEASURED (exit 3, `PREREQUISITE NOT MET` — each loads BUILT
artifacts and refuses on this unbuilt worktree; the diff touches no
source, config or manifest, so their verdict is CI's on its correctly
built tree): `pnpm --filter @objectstack/lint run
check:doc-formula-expressions` (`@objectstack/formula` and
`@objectstack/lint` not built), `pnpm check:dts-closure`, `pnpm
check:dual-build-cjs-loads` (101 packages without `dist/`), `pnpm
check:lean-entry-closure` (`packages/objectql/dist` absent), `pnpm
check:sourcemap-no-sources-content`. Their only remedy is a whole-repo
`pnpm build`, not proportionate to a prose-only change; declared, not
hidden.
① / ②: the only package touched is `@objectstack/dogfood` (private, no
`files[]`, scripts `typecheck` + `test` only); the README is an input to
neither (the sole `README` mention under `packages/qa/dogfood/test` is a
comment in `rls-fixture.dogfood.test.ts:29`), so no closure build and no
package test is owed — a declared narrowing.
`skip-changeset`: `.claude/**` is on the fast lane and
`@objectstack/dogfood` is private with no `files[]` — nothing published
moves.
## Acceptance notes
- noted, not filed: the card attributes 「`git grep -lI
'ablation-replace'` → 1」 to `ab1d35925`, but that sha itself answers 0 —
the helper landed in `d0b8ec2aaf` (#18598) after it; the reading was
evidently taken on a tree that already carried the merge. The premise is
unaffected: base `f6189a43f9` answers 1 (the helper alone). 承接者:无 —
recorded here and in the report.
- noted, not filed: `ablation-dist-preflight` answers 8 on the tip, not
the card's 7 — the eighth is the helper's own header cross-referencing
its sibling, which is the intended composition (the two tools prove
different halves and say so). 承接者:无.
- Rider not taken: the pending `tail --pid` rider in 干净收尾 (named by PR
#18659) stays for the next os-dev.md PR; this PR's deletion in that
block is the duplicate opener, not a slot for it.
## 维护者速读(草稿)
**改了什么** — 只动两处文档。`.claude/agents/os-dev.md`(行数不变 403 / 403)在消融步骤里加一行,把
dev 指向 PR #18598 新落地的锚点核验替换助手 `scripts/ablation-replace.mjs`,与已有的
`ablation-dist-preflight.mjs` 指向并排、按消融实际执行顺序(先落盘变异、再证明到达
`dist/`、再读结果);付账是删掉「干净收尾」节开头一条与文件头部及同节下一条重复的「报告落两次」句。`packages/qa/dogfood/README.md`
的「Adding a golden test」第 4 步加一句同样的指向。
**为什么改** — 这个助手落地了,却没有任何一份指令文档指向它;而指向其姊妹工具的文档有两份。一个采纳式工具若无人被指向,只有碰巧 grep
过 `scripts/` 的人会用到 —— 这正是卡 #18226 要消灭的「靠记性」。⛔
不做成必经路径、不加门禁、不批量迁移旧消融脚本(#18226 分诊已定这条地板)。
**风险与代价(含回滚)** — 零代码、零
changeset、零门禁改动;唯一的内容损失是被删的那条重复句,它的三个主张在文件头部(:17–:18)与同节下一条(:320)各有原文。回滚
= revert 本 PR,无其它文件牵连。
**席位意见** — (留空,席位定稿)
**你要做的** — 读新 :254 一行与被删 :319 一行;批准即由席位落地。若不同意付账形态,点名另一条重复句,席位代改。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent f9a9e76 commit b3e437d
2 files changed
Lines changed: 6 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
251 | 251 | | |
252 | 252 | | |
253 | 253 | | |
| 254 | + | |
254 | 255 | | |
255 | 256 | | |
256 | 257 | | |
| |||
316 | 317 | | |
317 | 318 | | |
318 | 319 | | |
319 | | - | |
320 | 320 | | |
321 | 321 | | |
322 | 322 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
50 | 54 | | |
51 | 55 | | |
52 | 56 | | |
| |||
0 commit comments