Skip to content

docs: reduce CLAUDE.md to a pointer at AGENTS.md (objectui#8840) - #8848

Draft
os-justin wants to merge 1 commit into
mainfrom
claude/issue-8840-claude-md-pointer
Draft

docs: reduce CLAUDE.md to a pointer at AGENTS.md (objectui#8840)#8848
os-justin wants to merge 1 commit into
mainfrom
claude/issue-8840-claude-md-pointer

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #8840

Part of the program card objectstack-ai/objectstack#17161 — one CLAUDE.md shape per repo, landed one repo per sub-issue.

Maintainer direct instruction (2026-09-09), 原文照录、不翻译:

「首先要明确 hotcrm 是元数据应用,平台功能应该在 objectstack中开发。 其次 claude.md 是不是直接让他阅读 agents.md 即可,没必要维护两套。所有仓库都有类似的问题」

What this does

CLAUDE.md: 25 lines to 3. It now carries exactly one proposition — AGENTS.md is the source of truth, read it before your first edit. The two inlined rules (worktree-first, never git stash), their hook roster and the closing digest line leave the file. .claude/hooks/** is untouched: guard-main-checkout.sh, guard-main-checkout-bash.sh and guard-shared-stash.sh keep enforcing both rules exactly as before, and the hook self-test workflow is unchanged.

Both deleted rules verified present in AGENTS.md before deletion

Grep-verified in AGENTS.md 〈9. Operational Rules〉, paragraph 多 agent 协作纪律. Anchoring sentences, quoted (the worktree command example is elided at the ellipsis — it contains a placeholder in a shape GitHub's body sanitizer eats, see AGENTS.md 〈GitHub 会改写你写进 issue/PR 正文的字节〉):

worktree-firstAGENTS.md:249:

必须一个任务一个 git worktree(…)做物理隔离 —— 这是强制而非「首选」。共享的 main checkout 不是可用退路 […] 一个 PreToolUse 钩子(.claude/hooks/guard-main-checkout.sh)强制此规则 […](确属非任务的临时改动用 OS_ALLOW_MAIN_EDITS=1 放行)

never git stashAGENTS.md:250 and AGENTS.md:258:

绝不 git stash —— stash 栈不在上一条的 worktree 隔离范围内。 git stash 把栈存在共享 .git 目录里的 refs/stash,每个 worktree 共用同一个 LIFO 栈

一个 PreToolUse 钩子(.claude/hooks/guard-shared-stash.sh)强制此规则 […] 确知栈只属于你时用 OS_ALLOW_STASH=1 放行;改了钩子就重跑 .claude/hooks/guard-shared-stash.selftest.sh

Both rules, both hook names, both override variables and the self-test instruction survive there. One element does not, and it is not a rule: the name guard-main-checkout-bash.sh and the clause "the same writes as Bash". AGENTS.md:249 names guard-main-checkout.sh only and enumerates Edit/Write/NotebookEdit. The rule it enforces survives in full and is stated there as 强制而非「首选」, independent of which tool performs the write; what is missing is one half of an enforcement roster, and ruling 1 removes the roster from CLAUDE.md in any case. Recorded under Acceptance notes rather than fixed here — this PR's declared file surface is CLAUDE.md, and AGENTS.md is itself governed.

Gates re-run — none required prose in CLAUDE.md

Measured, not assumed. Every exit code captured before any pipe.

gate exit verdict line
check-governed-queue-guard.mjs --self-test 0 OK check-governed-queue-guard self-test: 132 cases pass
check-governed-queue-guard.mjs --test CLAUDE.md 3 ⛔ GOVERNED — 1 of 1 path(s) are on a governed surface: CLAUDE.md x1 — the repo-root Claude instruction file
check-doc-links.mjs 0 Links are valid across 17 scan roots.
check-shell-escape-residue.mjs 0 OK (5/5 root(s) resolved -- … CLAUDE.md: 1 file(s), 0 fence(s) …)
check-bash32-floor.mjs 0 13 tracked shell file(s) under scripts/**, .claude/hooks/**, e2e/** name no bash 4+ construct …
check-upstream-port-parity.mjs 0 3 ported file(s) match objectstack-ai/objectstack modulo their declared divergences
check-control-bytes.mjs 0 OK (scanned 7041 tracked text file(s); skipped 85 binary)
check-changeset-presence.mjs 0 No source or published contract of a released package changed in this range, so no changeset is owed.

What each one actually wants of this file:

  • check-governed-queue-guard.mjs — registers CLAUDE.md as a governed surface ({ id: 'claude-md', exact: 'CLAUDE.md', … }, check-governed-queue-guard.mjs:232). Unchanged, and it still answers GOVERNED for this diff. That is the ruling's requirement that it keep doing so, met.
  • check-doc-links.mjs — the row is { path: 'CLAUDE.md', rule: 'disk' } (:696): it resolves the links the file carries. The pointer carries exactly one, ./AGENTS.md, and it resolves.
  • check-shell-escape-residue.mjs — the row is { spec: 'CLAUDE.md', kind: 'file', minFiles: 1 } (:260): a file-existence floor, not a prose requirement. Fence count is 0 after — and was already 0 on the base commit e9d92120 (git show e9d92120:CLAUDE.md | grep -c '```' returns 0), so this gate's reading of the file is byte-for-byte the same verdict before and after.
  • check-bash32-floor.mjsPOPULATION_ROOTS = ['scripts/**', '.claude/hooks/**', 'e2e/**'] (:286). CLAUDE.md appears in this file only inside a header comment describing a sibling gate's population (:52). This gate never opens CLAUDE.md.
  • check-upstream-port-parity.mjs — see the falsified assumption below.
  • check-changeset-presence.mjs — objectui has no skip-changeset label, and none was applied. This gate's own 「no changeset owed」 verdict is the declaration.

No gate was adapted, because none required prose here. A pointer file has no fences, no shell, and one link that resolves — the expected reading, now measured.

PM mechanism assumption A: falsified

The dispatch asked whether check-upstream-port-parity.mjs pins CLAUDE.md parity with objectstack's copy, and whether the pointer form would then need pinning on both sides. It does not, and it would not:

  • Its ledger is scripts/upstream-port-pin.json, and the ported set is exactly three files, printed by the gate itself: scripts/pm/check-half-states.mjs, scripts/invoked-as.mjs, .claude/hooks/guard-main-checkout.selftest.sh. CLAUDE.md is not among them.
  • The two CLAUDE hits in that ledger are CLAUDE_PROJECT_DIR (an environment variable inside the hook self-test matrix) and one prose comment — neither is CLAUDE.md.
  • Root CLAUDE.md occurs in the gate script only at :120, in a header comment enumerating this repo's governed surfaces; the gate imports GOVERNED_SURFACES from check-governed-queue-guard.mjs so that its --rewrite-governed-file path refuses to rewrite a governed file. That is a refusal, not a parity pin.

⇒ objectstack's parallel sub-issue and this one are independent on this axis; neither needs to pin the other's pointer wording.

Ablation — the file-existence floor is live, not a phantom check

Run from the committed state (7dd3aaa1), one leg each, both proven on disk, trap … EXIT INT TERM armed with an absolute path before the mutation:

HEAD blob for CLAUDE.md: 5961a39659af021ff51ee7c9caffcf2c7e1dfee4
on-disk proof (mutation): CLAUDE.md absent; git status ->
 D CLAUDE.md
MUTATION EXIT=1
❌  check-shell-escape-residue: a declared root did not resolve
    - CLAUDE.md (scan root, declared file) does not exist
restore proof: blob 5961a39659af021ff51ee7c9caffcf2c7e1dfee4 == HEAD blob; git diff HEAD empty
RESTORE EXIT=0
✅  check-shell-escape-residue: OK (5/5 root(s) resolved -- … CLAUDE.md: 1 file(s), 0 fence(s) …)

Direction predicted before running: turns red. Observed: red, naming the file. So the green above means "the declared root resolved and was scanned", not "nothing was looked at" — which is the whole question a pointer file raises for that gate. Restore verified by blob-hash equality against the HEAD blob and by an empty git diff HEAD, not by an exit code. No test file was left behind.

维护者速读(草稿)

改了什么 —— CLAUDE.md 从 25 行缩到 3 行,只剩一句话:AGENTS.md 是唯一事实源,动手改任何文件之前先读它。原先内联的两条规则(一任务一 worktree、绝不 git stash)连同它们的钩子清单一起删除。AGENTS.md.claude/hooks/**、任何门禁脚本都没有改动。

为什么改 —— 同一套规程此前维护两份:AGENTS.md 里的原文,和 CLAUDE.md 里的摘要。两份就会漂移,而漂移的方向是固定的 —— 摘要落后于原文,读摘要的 agent 拿到的是过期规程,并且看不出它过期。指针不会漂移:它不复述任何规则,所以没有任何东西可以过期。这也正是维护者裁定的那一句:「没必要维护两套」。

风险与代价(含回滚) —— 已知代价,如实说明:Claude Code 只自动加载 CLAUDE.md,AGENTS.md 要靠这条指针才被读到,所以一个跳过指针的会话在钩子响之前手上没有任何规则。兜底是钩子本身 —— guard-main-checkout.shguard-main-checkout-bash.shguard-shared-stash.sh 三个 PreToolUse 钩子这次一个字都没动,写共享 checkout 和 git stash 照样在工具调用那一刻被拦下,不依赖任何人读过文档。回滚成本近似为零:单文件、单次提交(7dd3aaa1),git revert 即可还原原文,没有任何生成物、锁文件或已发布内容随之变动。

席位意见 ——

你要做的 —— 这是受管面(CLAUDE.md 在本仓五项受管面之列),PR 停在 draft 等你合并;你的那次合并动作本身就是审核记录。只需确认一件事:缩到这一句是否就是你要的形态。若认为该保留「钩子会兜底」这类提示,请直说,我把它写进 AGENTS.md(而不是写回 CLAUDE.md)。

Acceptance notes

  • Governed terminal respected. CLAUDE.md is on this repo's five-item governed register. This PR stays a draft: not flipped ready, not enqueued, no auto-merge armed, no approving review submitted from this seat.
  • Noted, not filedAGENTS.md:249 names guard-main-checkout.sh and enumerates Edit/Write/NotebookEdit, but never names guard-main-checkout-bash.sh, which guards the same writes performed through Bash. After this PR no document in the repo names that hook (.github/workflows/hook-selftests.yml names it in a comment, as a hook, not as coverage prose). The rule survives intact and is stated as mandatory regardless of tool, and the hook keeps firing, so this is an incomplete enforcement roster rather than a lost rule or a reproducible defect — outside all three filing classes. Carrier named: the program card [program] CLAUDE.md is a pointer to AGENTS.md in every repo — no second instruction set to maintain (maintainer 2026-09-09) objectstack#17161, whose sibling repos are reducing the same digests this week.
  • Noted, not filed.github/workflows/hook-selftests.yml:8 says the two guards stand behind "the rule both CLAUDE.md files state as binding … (see those files' '⛔' sections)". Those sections are gone from this one, and objectstack's sub-issue removes them from the other, so the citation goes stale on both sides in the same program. It is a comment in a workflow, reads nothing, and gates nothing; repairing it here would put a file outside this PR's declared surface into a governed-terminal PR. Same carrier: [program] CLAUDE.md is a pointer to AGENTS.md in every repo — no second instruction set to maintain (maintainer 2026-09-09) objectstack#17161.
  • Noted, not filed — the dispatch's serial-constraint line states objectui has no scripts/pm/. It does: scripts/pm/check-half-states.mjs, one file, the ported half-state patrol. The operative conclusion is unaffected — there is no scripts/pm/os-verify-lock.sh in this repo, so no verify lock was owed, and this change is docs-only in any case.
  • Local scope. No package is touched, so no build closure and no package test suite is owed; repo-wide pnpm lint / pnpm test are CI's. No gate script was edited, so no gate's own suite is owed either. The four scripts/__tests__/* files that mention CLAUDE.md build it as a fixture in a temp directory (FIXTURE_ROOTS), and none reads the real root file.

Generated by Claude Code

CLAUDE.md restated worktree-first and the stash ban with their hook
roster. Both rules live in AGENTS.md 9. Operational Rules (paragraph
多 agent 协作纪律) and in .claude/hooks/, so the restatement was a second
instruction set that can lag the first. It now says one thing: AGENTS.md
is the source of truth, read it before your first edit.

No gate required prose here: the shell-escape-residue row is a
file-existence floor (0 fences before and after), check-doc-links only
resolves the links the file carries, check-bash32-floor never reaches it,
check-upstream-port-parity pins three ported files and CLAUDE.md is not
among them, and check-governed-queue-guard still lists it as governed.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 9, 2026
@os-justin os-justin added the domain:skills label Sep 9, 2026 — with Claude

Copy link
Copy Markdown
Collaborator Author

维护者速读 — objectui PR #8848(objectui#8840,skills 席终稿,2026-09-09T14:08Z)

改了什么 — objectui 根 CLAUDE.md 从 25 行缩到 3 行:标题加一句「AGENTS.md 是本仓唯一事实源,第一次改文件之前先读它」。原先内联的两条规则(一任务一 worktree、绝不 git stash)连同钩子清单一起删除;AGENTS.md.claude/hooks/**、门禁脚本一字未动。这是你「claude.md 直接让他阅读 agents.md 即可,没必要维护两套」那条裁决在 objectui 的落地,程序父单 objectstack#17161。

为什么改 — 同一套规程此前两份:AGENTS.md 原文与 CLAUDE.md 摘要;摘要只会落后于原文,而读摘要的 agent 看不出它过期。指针不复述规则,没有可过期的东西。

风险与代价(含回滚) — 已知代价:Claude Code 只自动加载 CLAUDE.md,AGENTS.md 靠这句指针才被读到;兜底是三个 PreToolUse 钩子原样在,写共享检出与 git stash 照样在工具调用那一刻被拦。回滚 = revert 单文件单提交。

席位意见 — 接受。本席在分支头复核:diff +1/−23 一文件;两条被删规则各自的原句在 AGENTS.md :249 / :250(钩子与 override 在 :258)都在;受管判据 exit 3;changeset 门禁「无需 changeset」本席重跑;merge-tree 干净;CI 23 绿 / 3 跳过 / 3 在跑(14:07Z,下次巡检再读)。dev 实测本席派发词里的一条假设为假(check-upstream-port-parity 根本不读 CLAUDE.md),照实报告,没有为迎合派发词硬改门禁——这是对的。两条顺手发现(AGENTS.md :249 的钩子清单没点名 guard-main-checkout-bash.sh;hook-selftests.yml:8 注释引用了两个 CLAUDE.md 的 ⛔ 段落,程序落地后两边都过时)已记到父单 #17161,不单开卡。

你要做的 — 看一眼那三行是否就是你要的形态;CI 全绿后人工合并,或由 os-zhuang / hotlong 批准后本席入队(受管面,席位不翻 ready、不入队、不挂 auto-merge)。


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation domain:skills needs-user-decision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLAUDE.md becomes a pointer to AGENTS.md — the two inlined rules and their hook pointers leave

2 participants