Skip to content

Commit b3e437d

Browse files
os-justinclaude
andauthored
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

File tree

‎.claude/agents/os-dev.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ model: opus
251251
- 那份绿给一条可能永远失败不了的断言背书,对 CI 永久隐形(CI 构建正确,那边永远绿)。
252252
- 消融本就为证明新门禁能失败时,这份假绿读作门禁没触发,于是有人去弱化一条好门禁。
253253
- 每一腿(变异与还原)都是:改动 → 证明它真落到了磁盘 → `pnpm --filter <pkg> build`。
254+
- 落盘走 `node scripts/ablation-replace.mjs`,不走 `-i` 家族:锚点必须命中,写入与还原按磁盘核验。
254255
- 再证明它到达了 `dist/`,才读运行结果:`node scripts/ablation-dist-preflight.mjs <pkg> '<marker>'`。
255256
- 删守卫的消融以及每个还原腿,用 `--absent`。
256257
- ⛔ 还原腿不可跳过:留在 `dist/` 里的 marker 会让变异代码对该树之后的每次运行继续生效。
@@ -316,7 +317,6 @@ model: opus
316317

317318
## 干净收尾 —— 报告是你的终局动作
318319

319-
- 报告落两次,GitHub 优先:卡片评论在前,终报消息在后。
320320
- 终报消息之前,把同一段 JSON 发成 issue 评论,首行单独一行、就是字面纯文本 `os-dev-report`。
321321
- 全文 ⛔ 不出现 HTML 注释:sanitizer 落库后吃短尖括号片段,连反引号里的也吃。
322322
- 被吃掉标记的评论对 PM 扫描不可见;HTML 注释形不是等价写法,是坏写法。

‎packages/qa/dogfood/README.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ the generic verifier cannot auto-derive.
4646
green-on-the-bug test is not a gate — and an ablation run against a stale `dist/`
4747
is exactly that, silently: it certifies an assertion that may never be able to
4848
fail, and no later CI run can expose it (CI builds correctly, so it stays green
49-
there forever). Prove the mutation actually reached the built artifact before
49+
there forever). Make the revert itself through
50+
`node scripts/ablation-replace.mjs --file <path> --anchor <text> --replacement <text> -- <cmd>`
51+
rather than `sed -i` / `perl -i`: the anchor must hit, the write and the restore are
52+
verified against the disk, and a mutation that did not land exits non-zero instead of
53+
handing you a green run. Prove the mutation actually reached the built artifact before
5054
trusting the colour: `node scripts/ablation-dist-preflight.mjs <pkg> '<marker>'`
5155
(`--absent` when the revert deletes a guard rather than adding something
5256
identifiable) exits non-zero unless it does.

0 commit comments

Comments
 (0)