docs: CLAUDE.md becomes a pointer to AGENTS.md — the inlined never-miss digest leaves - #17168
Conversation
…ss digest leaves CLAUDE.md restated four never-miss rules (claim-first, worktree-first, the stash / name-matched-kill ban, and the releases/ ban) with their hooks — a second instruction set to keep in sync with AGENTS.md, which already carries every one of them. Each of the four was verified to survive in AGENTS.md by its own sentence before deletion, and the hooks in .claude/hooks/ are untouched: they keep enforcing what they enforce. The line ratchet's CLAUDE.md ceiling is lowered 41 -> 5 in the same commit. The gate is green either way (slack only nudges past 120 lines), but 36 lines of headroom on a file that exists to inline must-never-miss rules is exactly the budget a future digest would re-accrete into — the growth that entry exists to price. Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ Co-authored-by: Claude <noreply@anthropic.com>
维护者速读 — PR #17168(#17162,skills 席终稿,2026-09-09T14:28Z)改了什么 — objectstack 根 为什么改 — 两套指令集的代价是漂移:摘要落后于原文,而只读 风险与代价(含回滚) — 已知代价:Claude Code 会话自动加载的是 席位意见 — 接受。本席在分支头复核:五句原话在; 你要做的 — 看一眼那五行的口吻;CI 全绿后人工合并,或由 os-zhuang / hotlong 批准后本席入队(受管面,席位不翻 ready、不入队、不挂 auto-merge)。 Generated by Claude Code |
Fixes #17162
Program anchor: #17161 (that card stays open — this repo is one of several).
Maintainer direct instruction, 2026-09-09, verbatim and untranslated:
What landed
CLAUDE.mdgoes 41 lines to 5: a title and one bold sentence namingAGENTS.mdas the singlesource of truth to read before the first edit. The four inlined never-miss rules leave the file.
.claude/hooks/**is untouched — the hooks keep enforcing worktree-first, the stash ban and theprocess-kill ban exactly as before.
Each of the four was verified to survive in
AGENTS.mdby its own sentence, not by a wordhit, before deletion:
CLAUDE.mdAGENTS.mdAGENTS.md:393— "Claim the issue BEFORE you write any code." Every agent here shares one GitHub identity, so the assignee field is only a presence bit; the identity record is theClaim:comment… (runs to :412, and carries the dispatch/seat split and the ⛔ never-reassign clause the digest compressed)AGENTS.md:217— Prime Directive 11. "Worktree-first — never edit on the sharedmaincheckout." …Before your first file edit, be in a dedicated worktree on a feature branch… (names both guard hooks andOS_ALLOW_MAIN_EDITS=1)git stashbanAGENTS.md:318— "⛔git stashis the sharpest thing the worktree does NOT isolate — never run a baregit stash push/pop." (namesguard-shared-stash.sh,OS_ALLOW_STASH=1, and the collision-free replacements)AGENTS.md:528— "⛔ One process table per container: kill only a PID you recorded, never a name" (guard-process-kill.sh)content/docs/releases/AGENTS.md:679— Documentation Guardrails row,content/docs/releases/· RELEASE-OWNED · "❌ Never edit in a code PR." Release notes are written centrally at release time… your PR's input is its changesetNothing was moved into
AGENTS.md: all five sentences were already there, each strictly longerthan the digest's version of it.
AGENTS.mdis not in this diff.The card's premise for the gate change is FALSIFIED — measured, not argued
The card and its dispatch both state that
scripts/pm/check-governed-prose.mjs"countsCLAUDE.mdas one of two instruction surfaces that must name all five governed surfaces", so apointer file would red it and the gate must adapt. It does not read
CLAUDE.mdat all.Its
PROSE_SURFACESholdsAGENTS.md(Prime Directive #14) and.claude/skills/pm-dispatch/SKILL.md.CLAUDE.mdappears in that gate only as a registerentry — one of the five surfaces those two files must NAME. The gate's own verdict line says
so, and it is byte-identical before and after this PR:
Three ablation legs on the committed tree, each self-restoring, each with its on-disk landing
proven before the gate ran and
git diff HEADproven empty after:`skills/**`from theAGENTS.mdPD#14 region (occurrences 2 → 1)skills/**— the register governs it and this prose under-claims"CLAUDE.mdreplaced by prose with no pointer (occurrences of the stringAGENTS.mdin it: 0)CLAUDE.mddeleted entirelyCLAUDE.mdSo the ruled adaptation has nothing to attach to, and the ruled self-test pair cannot be
written: "pointer form ⇒ green" and "prose without a pointer ⇒ red" are the same run of a gate
that reads neither file.
Worse, taking the ruling literally would BREAK a gate.
scripts/pm/dispatch-gates.mjspins theseparation, with a comment saying exactly what to do when a premise like this one is wrong:
⇒
scripts/pm/check-governed-prose.mjsis not in this diff. The ruling's intent holdsuntouched and unweakened: the five-surface enumeration stays enforced on the two files that
actually carry it, and
CLAUDE.mdstays a registered governed surface both of them must name.The one gate that DID need this PR: the line ratchet
scripts/pm/check-skill-line-ratchet.mjspinsCLAUDE.mdat a ceiling of 41 with headroom 0.Shrinking the file to 5 leaves 36 lines of slack. The gate is green either way — its slack nudge
only fires past 120 lines — but 36 lines of budget on the one file that existed to inline
must-never-miss rules is precisely what a future digest re-accretes into. Lowered 41 → 5, the
ratchet's own convention ("a ceiling may be LOWERED by any PR that shrinks its file — lowering is
always legitimate and encouraged", headroom 0 on every entry). This is a LOWERING and needs no
ruling of its own; the maintainer ruling is quoted in the entry because it is what emptied the
file.
Every other reader of
CLAUDE.md, measuredgrep -rn CLAUDE.md scripts .github .claude— no gate, hook or workflow requires prose in it:scripts/pm/check-governed-merges.mjs— the governed register. Keeps listingCLAUDE.md, as required.scripts/check-required-contexts.mjs—CLAUDE.mdis in the derivation population but not inINSTRUCTION_SURFACES; it enters that set "by naming [a registered context], never by sitting next to one that does", and it names none, before or after.scripts/check-agent-test-spelling.mjs— corpus membership only (LOOSE_FILE_NAMES); scans for test-command spellings, of which the new file has none.scripts/ci/select-gate-families.sh— maps the path to theagent-configfamily; unaffected..claude/hooks/guard-main-checkout-bash.sh,check-governed-queue-guard.mjs,check-doc-frontmatter.mjs— comments and self-test fixtures only.Governed terminal
Draft PR, maintainer merge.
skip-changeset: nothing published moves —CLAUDE.mdis arepo-root instruction file and
scripts/pm/**is repo tooling; neither is in any package'sfiles[].维护者速读(草稿)
改了什么 — 根
CLAUDE.md从 41 行缩到 5 行,只剩一句话:AGENTS.md是唯一真相源,动手前先读它。原先内联的四条「绝不能漏」规则(认领优先、worktree-first、禁
git stash/ 禁按名杀进程、代码 PR 不改 release notes)全部离开该文件——它们本来就一字不落地住在
AGENTS.md,本 PR 逐条核验过。.claude/hooks/**一行未动,三个守卫照常拦截。附带把行数棘轮里CLAUDE.md的上限从 41 降到 5。为什么改 — 维护者裁决:「claude.md 是不是直接让他阅读 agents.md 即可,没必要维护两套」。两套指令
集的代价是漂移:摘要版会落后于
AGENTS.md,而只读CLAUDE.md的 agent 拿到的就是那份落后的摘要。指针不会落后。
风险与代价(含回滚) — 已知代价(程序卡 #17161 已记录一次):Claude Code 会话启动自动加载
CLAUDE.md,AGENTS.md只能经指针到达,所以跳过指针的会话在第一个 hook 触发前手里没有规则。托底的是 hooks——worktree、stash、杀进程三条最贵的规则由 PreToolUse 守卫机械强制,不依赖任何一个 agent 读没
读文件。回滚成本为零:两个文件、一次 revert,无生成物、无 changeset、无发布面。
席位意见 — (留空,待评审席位定稿)
你要做的 — 只需确认两件事,然后人工合并这个 draft PR:① 那句指针的措辞是否是你要的口吻;②
本 PR 没有按裁决第 4 条去改
check-governed-prose.mjs——因为实测证明那条裁决的前提不成立(该门禁根本不读
CLAUDE.md,删掉整个文件它照样绿),而照裁决字面去改会让check:pm-dispatch-gates变红。证据在上面的消融表里。如果你要的是另一种处置,说一声。
Acceptance notes
Gates — all green at
41329812, the final commit.Re-derived on the actual diff with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) atmerge commit
41329812: 33 commands, all run, all exit 0. The dispatch's 34-command leadincluded
pnpm check:pm-governed-prose; it drops out because the re-derivation sees the realdiff, which does not touch that gate. Ran it anyway, before and after — green both times, same
verdict line.
pnpm check:pm-dispatch-gateswas run detached with its exit captured to a fileand waited on in the foreground, per the dispatch.
Shallow checkout.
git rev-parse --is-shallow-repositoryistruehere. No ancestry verdictin this PR rests on a negative
merge-base --is-ancestor; theAGENTS.mdsurvival checks arecontent greps on the working tree at
origin/main, which a shallow checkout answers exactly.origin/mainmerged in.f801e7d7(#17142) landed mid-run and touchedscripts/pm/dispatch-gates.mjs, the derivation tool itself, which the tool reported as a STALETREE warning. Merged and re-derived on the current tool: the 33-command list is byte-identical to
the pre-merge one.
Out of scope, noted, not filed. Six script/agent comments cite
CLAUDE.mdas the place arule is stated —
scripts/check-vendor-version-stamps.mjs:226,scripts/check-docs-single-h1.mjs:109and
:117,scripts/import-prerequisite.mjs:12and:382,scripts/pm/ci-failure.mjs:215,.claude/agents/os-dev.md:35,.claude/workflows/docs-accuracy-audit.js:41. After this PR thoserules are stated in
AGENTS.mdinstead. Not filed and not fixed here:CLAUDE.mdstill existsand points at
AGENTS.md, so every one of those citations still routes a reader to the rule —one hop longer, never a dead end. Who picks this up: the sibling sub-issues of #17161 do the same
rewrite in
objectuiandcloud, and whoever lands the last of them is the natural place tore-point the citations across all repos in one pass. No gate reads them.
Generated by Claude Code
Generated by Claude Code