Repository navigation
docs(agents,dogfood): point both ablation instruction surfaces at scripts/ablation-replace.mjs beside its sibling preflight - #18725
Conversation
…ipts/ablation-replace.mjs beside its sibling preflight The two documents that route an agent to `scripts/ablation-dist-preflight.mjs` now also route it to `scripts/ablation-replace.mjs`, in the order an ablation runs them: mutate on disk through the anchor-verifying helper, then prove the mutation reached `dist/` with the preflight, then read the run. `.claude/agents/os-dev.md` stays line-neutral (403 / 403): the new pointer sits under the mutate-then-build line of the ablation step; it is paid for by deleting the 干净收尾 opener that restated the header's two-delivery rule and the following bullet's ordering. `packages/qa/dogfood/README.md` gains one sentence in step 4 of "Adding a golden test", beside the existing preflight sentence. Adoption-style pointer only: not a mandatory path, no gate, no migration of existing ablation scripts. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
📓 Docs Drift Check
What this run could not see
Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
维护者速读(终稿)skills 席 改了什么 — 两处把 agent 指向姊妹工具 为什么改 — 新工具若无人被指向,只有碰巧 grep 过 风险与代价(含回滚) — 两行指向,不设必经路径、不加门禁、不迁移旧脚本(迁移集实测为空);行数中性、线宽 ≤120 B、line ratchet 绿;门禁 48 族绿、5 族需构建产物留给 CI。回滚 = revert 一个提交。顺带一件已结的:#18226 的选项 A(在字节纪律里点名 席位意见 — 荐合。把已落地的正确形态写进 dev 每次都读的那份定义,是最小改动。 你要做的(一个动作) — Approve 本 PR(或直接人工合并);席位随后按裁定 C 落地。 Generated by Claude Code |
…this file (objectstack-ai#19167) Fixes objectstack-ai#18698 Clause-②: no One clause in `.claude/agents/os-dev.md`, a governed Tier S surface (`.claude/**`): landing is the skills seat's contract-tier review of record through the queue. This PR stops at draft. ## What changed `.claude/agents/os-dev.md`, Definition-of-done list, right after the trailer line — today `:284`, kept byte-identical because `scripts/check-commit-card-trailers.mjs` cites that sentence verbatim and its self-test holds the citation to the file. New `:285` (120 B): > - harness 归属提醒凭其优先级句让位本文件;harness 自写含模型名 trailer 只报,⛔ 不仿不改史。 It restates the governing text, `AGENTS.md` :440–:444 (quoted with the two angle-bracket placeholders spelled out in words, because GitHub mutates body bytes): > **Commit message:** an agent commit ends with the model-free trailer pair `Claude-Session: https://claude.ai/code/session_ID` and `Co-authored-by: Claude (noreply at anthropic.com)`, and the pre-push hook refuses a model identifier in that pair; no model identifier lands in a PR title or body, a comment, a changeset, a doc or a code comment. Two exemptions: a harness-written `Co-Authored-By` trailer (REPORTING: not declared a deviation; landed history is not rewritten) and a verbatim maintainer ruling preserved as a quotation block. The precedence sentence the clause relies on is in the harness attribution reminder itself — read in this session, verbatim: 「the user's own instructions about these lines, such as a CLAUDE.md or memory rule, take precedence over this reminder」. `CLAUDE.md → AGENTS.md` is that instruction, so the reminder defers; the clause restates AGENTS.md, never the reminder. ## Ruling — director record on the card (batch objectstack-ai#154 item 5, maintainer 「同意」 2026-09-18T04:56Z), operative part verbatim > - `.claude/agents/os-dev.md:283` gains the one clause that would have saved the escalation: **the harness attribution reminder yields to this file by its own precedence sentence; a trailer the harness itself writes with a model name is `AGENTS.md`'s exemption — report it, ⛔ do not imitate it, ⛔ do not rewrite history.** Line-neutral; on the os-dev.md serial queue behind objectstack-ai#18599, in one round with objectstack-ai#18699 as triage suggested. > - ⛔ **B** — contradicts `AGENTS.md:442` and the hook, and lets a reminder that changes without a PR drive landed history in four repositories. > - `Clause-②: no`; `skip-changeset`; governed surfaces ⇒ human merge by the approver. Readings, not identifiers: the ruling's `:283` reads `:284` on today's `main` (the file moved when PR objectstack-ai#19038 landed at 2026-09-18T23:52Z); the serial 「behind objectstack-ai#18599, with objectstack-ai#18699」 is discharged — both landed before this PR (PR objectstack-ai#18725 and PR objectstack-ai#18898). The landing route is the later Tier S ruling (the seat's contract-tier review of record through the queue), superseding the last bullet's human merge. ## Line budget — ratchet row `['.claude/agents/os-dev.md', 403]` | reading | before (`5d0ee8f`) | after (`9612c8d`) | |---|---:|---:| | lines | 403 | 403 | | widest line | 120 B | 120 B | | new clause | — | 120 B at `:285` | | diff | — | +1 / −1, one file | **Payment (line-neutral):** the removed bullet is the former `:285` 「标题与散文用英文(见 AGENTS.md);引用的中文裁决保持原文不译,改写引文就是改写裁决。」 — a true duplicate that cited its own source. Its content survives verbatim in `AGENTS.md` § Communication: `:25` 「**GitHub 产物一律使用英文**:issue 与 PR 的标题、正文、评论。」 and `:26–:27` 「**引用中文裁决时保持原文、不翻译**,即使承载它的 issue/PR 正文通篇是英文——改写引文就是改写裁决。」; os-dev.md `:19` already binds AGENTS.md before the first edit. No re-wrap bought the line and no ceiling moved. Measured before choosing: zero gate scripts pin any phrase of the removed line (grep over `scripts/`, `.github/`, `.claude/skills/`, `package.json`); the only pinned sentence in this list is `:284`, which is untouched (`check-commit-card-trailers` self-test: 「✓ every sentence inside the corner brackets is verbatim in the cited rules file」). The clause names no model and no card. Frontmatter untouched (`model: opus` line unchanged). The wording was measured down from the seat's 157 B suggestion; the three-verb prohibition without objects follows the file's own register (`:303` 「⛔ 不挂不摘不等」). ## Verification — all at `9612c8d`, every exit code captured before any pipe Gate list derived in the worktree with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (change set: `.claude/agents/os-dev.md` only, 2 changed lines; 19 commands — identical to the dispatch's 19) and reconciled with `--ran`: 「✓ dispatch-gates --ran: 19 derived famil(ies) accounted for — 19 run, 0 NOT-MEASURED」. | command | exit | the gate's own verdict line | |---|---:|---| | `node scripts/check-closing-keyword-parity.mjs` | 0 | check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8991 tracked file(s), all registered). | | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | ✓ 40 assertions, 5 mutations of the shipped parsers each driven to red. | | `node scripts/check-comment-mask-corpus.mjs` | 0 | ✓ comment-mask corpus sweep: 6894 files, 0 disagree, 0 unparseable. | | `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 | ✓ check-governed-queue-guard self-test: 261 cases pass. | | `node scripts/pm/check-harness-current.mjs --self-test` | 0 | check-harness-current --self-test: all 26 cases passed. | | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | 0 | ✓ 14 predicate(s) on a statically determinable field layer judged clean; 6 skipped as undeterminable. (First run refused with exit 3 PREREQUISITE NOT MET — `@objectstack/formula` and `@objectstack/lint` unbuilt in a fresh worktree; built both under the verify lock, `VERDICT command-exit 0`, then this reading.) | | `pnpm check:agent-model-declared` | 0 | ✓ 1 agent definition(s) under .claude/agents/ all declare a model — os-dev.md → opus. | | `pnpm check:agent-test-spelling` | 0 | ✓ 0 violations — 516 file(s) · 9159 bare `--` token(s) · 13 separator(s) JUDGED. | | `pnpm check:commit-card-trailers` | 0 | ✓ check-commit-card-trailers self-test: 81 cases pass (incl. 「every sentence inside the corner brackets is verbatim in the cited rules file」). | | `pnpm check:cross-package-test-inputs` | 0 | OK: 29 package(s) read outside themselves, all declared. | | `pnpm check:doc-authoring` | 0 | ✓ doc authoring guard: 402 files clean — no bare metadata literals (+ the three sibling verdict lines, all ✓). | | `pnpm check:driver-memory-census` | 0 | check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live. | | `pnpm check:nul-bytes` | 0 | check-nul-bytes: OK (scanned 8984 text file(s); no raw ASCII control bytes). | | `pnpm check:pm-governed-merges` | 0 | ✓ check-governed-merges --self-test: 392 assertions. | | `pnpm check:pm-skill-id-lint` | 0 | ✓ check-skill-id-lint: 27 file(s) clean (pattern /#[0-9]{3,}/g). | | `pnpm check:pm-skill-ratchet` | 0 | ✓ .claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0). ✓ widest table row is 0 bytes (pin 0). | | `pnpm check:refd-timer-probe` | 0 | OK: 6889 source file(s) swept; the probe is read in the approved module and nowhere else. | | `pnpm check:skill-frame-sync` | 0 | ✓ the one declared copy of the decision frame is internally coherent; 74 markdown files scanned for undeclared copies. | | `pnpm check:watch-hint-literal` | 0 | ✓ 71 declaration(s) across 4 rostered name(s), every one an array of quoted literals. | | `pnpm check:pm-settings-deny-roster` (extra — the derivation flagged its roster as living under `.claude`) | 0 | ✓ 17 content-write tool(s) declared = enforced in .claude/settings.json. | Manual readings on the file: `wc -l` 403; `awk` widest 120 B; `grep -naP` for raw control bytes: no output (exit 1); `grep -c` of the removed line's text: 0. Pre-push hook on the push: 「✓ check:commit-card-trailers: 1 commit message(s) on this push carry no card relation and no model identifier in the trailer pair.」 Package tests / typecheck: none owed — the diff touches no package (no ① closure, no ② suite); the two-package build above was a gate prerequisite only. Repo-wide `pnpm lint` is CI's run. ## Acceptance notes - **Live instance of the clause, reported, not a deviation.** This session's harness attribution reminder asked for a model-bearing co-author trailer and a different PR footer block. Per the reminder's own precedence sentence and `AGENTS.md` :440, the commit carries the model-free pair and this body carries the session-URL footer; nothing was rewritten. - noted, not filed: `check:doc-formula-expressions` exits 3 (PREREQUISITE NOT MET) in any fresh worktree until `@objectstack/formula` and `@objectstack/lint` are built — the gate says so itself and names the fix; by design, not a defect. 承接者:无. - Context on the card that this PR does not act on (the devx seat's evidence): CI runs `check-commit-card-trailers` as `--self-test` only, so the pre-push hook is the only leg that refuses a model-bearing trailer; a session without `.githooks/` installed pushes one unrefused. Listed in the report's `out_of_scope_findings` as a class-(b) candidate with dedupe words for the triage seat; no card is opened here. - Out of scope, untouched: `AGENTS.md`, every `SKILL.md`, the hook, CI. objectstack-ai#18599 and objectstack-ai#18699 remain landed history; objectui#9441 is the ui lane's twin of the ruling. ## 维护者速读(草稿) - **改了什么**:`.claude/agents/os-dev.md` 的 Definition of done 列表,在 trailer 那一行之后新增一条(120 B):harness 的归属提醒按它自己的优先级句让位于本文件;harness 自己写入的含模型名 trailer 是 AGENTS.md 的例外——只报告,不仿写、不改历史。为守住 403 行上限,删掉了一条原文照抄 AGENTS.md「沟通」节的重复条目(GitHub 产物用英文;引用中文裁决不翻译),该内容在 AGENTS.md :25–:27 原样保留。 - **为什么改**:每一次派发,dev 都同时收到两份指令——仓库契约(model-free trailer)与 harness 注入的归属提醒(含模型名)。已有 dev 因此写不出 commit、只能升级上报。答案其实早就写在 AGENTS.md 里,也写在提醒自己的优先级句里;这一条把答案放到 dev 真正读的那一行旁边,省掉下一次升级。 - **风险与代价(含回滚)**:零机制变化——不动 hook、不动 CI、不动 frontmatter;行数 403/403、最宽 120 B 不变,19 个派生门禁全绿。风险只在措辞:若你觉得「只报,⛔ 不仿不改史」过于压缩,可在同一行内换词(须 ≤ 120 B)。回滚 = revert 这一个 commit(1 行换 1 行)。 - **席位意见**: - **你要做的**:无需你点击合并——本 PR 在 `.claude/**`(Tier S),由 skills 席位按合约档复核记录经队列落地。若对措辞有意见,在本 PR 留一句即可。 --- _Generated by [Claude Code](https://claude.ai/code/session_01W5y9kRg1YtYaMQYExVLRc2)_ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #18599
Clause-②: no
Governed surface (
.claude/**): draft,skip-changeset, an authorized approval lands it and thedomain:skillsseat lands it from there. File surface:.claude/agents/os-dev.md+packages/qa/dogfood/README.mdonly. Headfa55c9d0ad, basef6189a43f9(origin/mainat worktree creation; the card's readings were taken onab1d35925and 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 thesed -i/perl -ifamily that silently failed in two recorded ablations (2026-08-20, a-0run 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 siblingscripts/ablation-dist-preflight.mjsdo exist — so the helper could only be found by greppingscripts/, 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 reacheddist/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 outInserted, 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):
「
-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 「干净收尾」:
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 -naPover the C0/DEL range) on both files: clean.packages/qa/dogfood/README.md— one sentenceStep 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: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)ab1d35925f6189a43f9fa55c9d0adgit grep -lI 'ablation-replace'd0b8ec2aafafter it).claude/agents/os-dev.md,packages/qa/dogfood/README.md,scripts/ablation-replace.mjsgit grep -lI 'ablation-dist-preflight'git grep -cI 'dogfood'os-dev.md / README.mdgit grep -cI 'ablation'on.claude/skills/dogfood-verification/SKILL.mdPer-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.mdcarries no control-character orcheck:nul-bytessentence at all (its onlyperl -imention 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 inopen_questionsfor 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/objectstackfrom the worktree, no hand-fed paths (change set vs merge basef6189a43f: the two files; committed 2, working tree 0, untracked 0). 53 commands; reconciled with--rancarrying:: exit Nper 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 NUMBERneeds 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 pm(half-states): H19 founds every verdict on the set it judges #18654 ran it) :: exit 0, 100 s, onfa55c9d0adThe 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, speccheck: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/formulaand@objectstack/lintnot built),pnpm check:dts-closure,pnpm check:dual-build-cjs-loads(101 packages withoutdist/),pnpm check:lean-entry-closure(packages/objectql/distabsent),pnpm check:sourcemap-no-sources-content. Their only remedy is a whole-repopnpm build, not proportionate to a prose-only change; declared, not hidden.① / ②: the only package touched is
@objectstack/dogfood(private, nofiles[], scriptstypecheck+testonly); the README is an input to neither (the soleREADMEmention underpackages/qa/dogfood/testis a comment inrls-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/dogfoodis private with nofiles[]— nothing published moves.Acceptance notes
git grep -lI 'ablation-replace'→ 1」 toab1d35925, but that sha itself answers 0 — the helper landed ind0b8ec2aaf(feat(scripts): an anchor-verifying ablation replacement helper, and the census that shaped it #18598) after it; the reading was evidently taken on a tree that already carried the merge. The premise is unaffected: basef6189a43f9answers 1 (the helper alone). 承接者:无 — recorded here and in the report.ablation-dist-preflightanswers 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). 承接者:无.tail --pidrider in 干净收尾 (named by PR docs(agents): the PR body is the dev's one body write — a later correction is the seat's PATCH, and the Clause-② line rides the create #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