5.5 KiB
Design — CLAUDE.global.md rename + project-scope CLAUDE.md
- Date: 2026-07-12
- Flow: /ship-feature
- Contract:
.claude/tasks/contracts/2026-07-12-claude-global-md-rename-2342.md - Status: approved (design gate 2026-07-12)
Problem
The repo-root CLAUDE.md is the user-scope global memory, symlinked to
~/.claude/CLAUDE.md by link.sh. Because the filename CLAUDE.md is taken
by global content, this repo has no project-level CLAUDE.md — repo-only
instructions (# This repo only (claude-config)) ride the global file and
load in every project (~40 tok/session waste), and the rules/ maintenance
doctrine lives in rules/README.md, a user-scope rule whose
paths: ["rules/**"] glob over-matches any foreign project with a rules/
directory.
Decision
Rename the global source file and free the CLAUDE.md name for a real
project-scope file. Name arbitrated: CLAUDE.global.md
(CLAUDE.prod.md rejected — "prod" implies a deployment environment that
does not exist here).
This does NOT contradict BDR-021's rejected "split into 2 files" alternative: that rejection targeted splitting GLOBAL content into two synced files. Here the scopes are disjoint — zero synchronization between the two files.
Changes
1. File split
-
git mv CLAUDE.md CLAUDE.global.md(history preserved; verify withgit log --follow). -
CLAUDE.global.mddrops the# This repo only (claude-config)section (6 lines) and gains a scope header above the title:<!-- USER-SCOPE GLOBAL — deployed as ~/.claude/CLAUDE.md via link.sh symlink. Repo-specific instructions live in ./CLAUDE.md (project scope). -->Net ≈ 301 lines — under the 320 guard (BDR-062). Security, Architecture decisions, and the "Design work — full toolchain (tiered by scope)" heading stay byte-identical (BDR-021: hook quotes the heading verbatim).
-
New project-scope
CLAUDE.md(~25 lines), minimal structure (YAGNI — no empty template sections):- mirror scope header: project scope only; global doctrine is in
CLAUDE.global.md, deployed as~/.claude/CLAUDE.md; edit THAT file for cross-project rules; ## Health Stack—shellcheck *.sh hooks/*.sh lib/*.sh(moved verbatim);## rules/ maintenance— doctrine migrated fromrules/README.md: what belongs inrules/(one rule = one file = one concern), lazy-load semantics (paths:frontmatter → loads on matching file read; nopaths:→ session-start cost, keep always-on doctrine in CLAUDE.global.md), machine-owned files note (context7.md DELETED BY DESIGN, BDR-053), link to the docs page.
- mirror scope header: project scope only; global doctrine is in
2. link.sh
Mapping line changes: link <repo>/CLAUDE.global.md → ~/.claude/CLAUDE.md.
ln -sf in link_file() replaces the stale symlink cleanly. Post-condition:
readlink ~/.claude/CLAUDE.md = <repo>/CLAUDE.global.md.
3. Dependent scripts (surgical)
| File | Change |
|---|---|
hooks/session-start.sh:205-206 |
line-count guard reads CLAUDE.global.md; threshold 320 unchanged (BDR-062). Repo detection via readlink (line 81) already works post-rename. |
doctor.sh:251,277 |
size/token stats read CLAUDE.global.md. Symlink check (line 65) unchanged — link name ~/.claude/CLAUDE.md is stable. |
install-plugins.sh:33-41,66 |
GUARDED_CONFIGS KEEPS "CLAUDE.md" (graphify's installer targets that name — now the project file, still needs the drift guard) and ADDS "CLAUDE.global.md". Comment + mktemp error message updated. |
lib/doc-commit.sh:48 |
add CLAUDE.global.md to the doc-sync exclusion list (BDR-022 spirit: memory/config files are never doc-commit targets). |
4. rules/README.md
Slimmed to a 3-line pointer, frontmatter kept:
---
paths: ["rules/**"]
---
User-scope rules, deployed to `~/.claude/rules` by `link.sh`.
Maintenance doctrine (what belongs here, lazy-load semantics, machine-owned
files): see `CLAUDE.md` (project scope) at the claude-config repo root.
Over-match in foreign projects with a rules/ dir becomes harmless (~30 tok).
5. Docs
README.md, USAGE.md, MIGRATION.md: update only references meaning the
repo-root GLOBAL file. References to the ~/.claude/CLAUDE.md symlink name
and to the per-project CLAUDE.md concept are unchanged. templates/project-CLAUDE.md
unchanged (its "Global rules: ~/.claude/CLAUDE.md" line stays accurate).
Verification
shellcheckon every modified.sh(repo Health Stack).bash link.sh→readlink ~/.claude/CLAUDE.mdresolves toCLAUDE.global.md; no dangling link.bash doctor.sh→ symlink check green, stats read the new file.- Residual grep: no script reference to the repo-root global file under the
old name (
grep -rn 'CLAUDE\.md' *.sh hooks/*.sh lib/*.shreviewed). - Session-start guard smoke test: guard finds
CLAUDE.global.md, no fail-open on the old path. git log --follow CLAUDE.global.mdshows pre-rename history.
Constraints honored
- BDR-062: guard path follows the rename, threshold 320 untouched.
- BDR-021: Security/Architecture verbatim; design heading byte-identical.
- BDR-031: global file net-shrinks (−6 +2 lines); no re-inflation.
- LRN-044: all edits on resolved repo paths, never through
~/.claude/CLAUDE.md. - Gitflow: all commits on a
feature/*branch off develop; this spec is committed as the branch's first commit (never directly on develop).
Out of scope
- graphify-section extraction from the global file (separate suggestion, not requested here).
- Any content rewrite of the global doctrine beyond the section move and scope header.