From a4ee7e1790c03259a2df3f172e3f0eafb63d9068 Mon Sep 17 00:00:00 2001 From: Bastien Chanot Date: Mon, 13 Jul 2026 00:06:21 +0200 Subject: [PATCH] =?UTF-8?q?docs(spec):=20CLAUDE.global.md=20rename=20?= =?UTF-8?q?=E2=80=94=20design=20+=20implementation=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-07-13-claude-global-md-rename.md | 365 ++++++++++++++++++ ...26-07-12-claude-global-md-rename-design.md | 125 ++++++ 2 files changed, 490 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-13-claude-global-md-rename.md create mode 100644 docs/superpowers/specs/2026-07-12-claude-global-md-rename-design.md diff --git a/docs/superpowers/plans/2026-07-13-claude-global-md-rename.md b/docs/superpowers/plans/2026-07-13-claude-global-md-rename.md new file mode 100644 index 0000000..48648fa --- /dev/null +++ b/docs/superpowers/plans/2026-07-13-claude-global-md-rename.md @@ -0,0 +1,365 @@ +# CLAUDE.global.md Rename Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Rename the repo-root global memory to `CLAUDE.global.md` and free the `CLAUDE.md` name for a real project-scope file, following every dependent script and doc. + +**Architecture:** One atomic file-split task (git mv + both file contents + link.sh retarget, so no commit leaves the tree incoherent), then a script-followers task, then docs, then a verification sweep. Spec: `docs/superpowers/specs/2026-07-12-claude-global-md-rename-design.md`. Contract: `.claude/tasks/contracts/2026-07-12-claude-global-md-rename-2342.md`. + +**Tech Stack:** bash (shellcheck-clean), markdown, git (gitflow via `~/.claude/lib/gitflow.sh`). + +## Global Constraints + +- BDR-062: line-count guard threshold stays **320**; only its path changes. +- BDR-021: `## Security`, `# Architecture decisions` content and the heading `## Design work — full toolchain (tiered by scope)` stay **byte-identical** in CLAUDE.global.md. +- BDR-031: global file must NOT grow — expected net: 305 → 301 lines (−7 tail section incl. leading blank, +3 header incl. trailing blank). +- LRN-044: edit repo paths only (`/home/bchanot/Documents/claude/...`), never through `~/.claude/CLAUDE.md`. +- Gitflow: every commit lands on `feature/claude-global-md-rename` (created Task 1). Never commit on develop. +- Shell code: ≤80 cols, shellcheck-clean (`shellcheck *.sh hooks/*.sh lib/*.sh`). +- Deployed-state note: between Task 2's `git mv` and its `bash link.sh` step, `~/.claude/CLAUDE.md` dangles — Task 2 must run to completion without interruption. + +--- + +### Task 1: Feature branch + commit spec and plan + +**Files:** +- Commit: `docs/superpowers/specs/2026-07-12-claude-global-md-rename-design.md` (exists) +- Commit: `docs/superpowers/plans/2026-07-13-claude-global-md-rename.md` (this file) + +**Interfaces:** +- Produces: branch `feature/claude-global-md-rename` off develop — all later tasks commit here. + +- [ ] **Step 1: Create the branch via the gitflow lib (never by hand)** + +Run: `bash "$HOME/.claude/lib/gitflow.sh" start feature claude-global-md-rename` +Expected: branch created off develop; `git branch --show-current` → `feature/claude-global-md-rename` + +- [ ] **Step 2: Commit the two docs** + +```bash +git add docs/superpowers/specs/2026-07-12-claude-global-md-rename-design.md \ + docs/superpowers/plans/2026-07-13-claude-global-md-rename.md +git commit -m "docs(spec): CLAUDE.global.md rename — design + implementation plan" +``` + +Expected: clean commit; `git status` shows the two files gone from untracked. +NOTE: `settings.json` is dirty with session-scoped plugin toggles — do NOT stage it in any task. + +--- + +### Task 2: Atomic file split (git mv + contents + link.sh) + +**Files:** +- Rename: `CLAUDE.md` → `CLAUDE.global.md` (git mv) +- Modify: `CLAUDE.global.md` (add header, drop tail section) +- Create: `CLAUDE.md` (project scope, new content below) +- Modify: `link.sh:20` + +**Interfaces:** +- Produces: `CLAUDE.global.md` = global memory (Task 3 scripts point here); `CLAUDE.md` = project memory (stays graphify's / GUARDED_CONFIGS' target name). + +- [ ] **Step 1: git mv** + +```bash +cd /home/bchanot/Documents/claude +git mv CLAUDE.md CLAUDE.global.md +``` + +- [ ] **Step 2: Add the scope header to CLAUDE.global.md** + +Insert at the very top, ABOVE `# Global coding preferences`: + +```markdown + + +``` + +- [ ] **Step 3: Remove the repo-only tail section from CLAUDE.global.md** + +Delete exactly these final lines (and the blank line before `# This repo only`): + +```markdown + +# This repo only (claude-config) + +Apply when working directory = the claude-config repo itself. + +## Health Stack +- shell: `shellcheck *.sh hooks/*.sh lib/*.sh` +``` + +The file now ends with the graphify section's last line: +`- After editing code → \`graphify update .\` (AST-only, free).` + +- [ ] **Step 4: Create the project-scope CLAUDE.md** + +Full content: + +```markdown + + +# claude-config — project instructions + +## Health Stack +- shell: `shellcheck *.sh hooks/*.sh lib/*.sh` + +## rules/ maintenance + +Modular instruction files loaded by Claude Code alongside the global memory. +`rules/` is symlinked to `~/.claude/rules` by `link.sh` (user scope, ALL +projects). One rule = one file = one concern. + +A rule WITH `paths:` YAML frontmatter (glob list) loads lazily — only when +Claude reads a file matching a glob; a rule WITHOUT it loads at session +start, same cost as the global memory. Extract from CLAUDE.global.md only +what can be path-scoped (the token win) or what is generated; always-on +doctrine stays in CLAUDE.global.md. `paths:` globs match against the +CURRENT project's tree — a broad glob (e.g. `rules/**`) can fire in foreign +projects; keep rule bodies tiny. +Docs: https://code.claude.com/docs/en/memory.md#path-specific-rules + +Machine-owned: `rules/context7.md` is DELETED BY DESIGN (BDR-053, +2026-07-06) — `ctx7 setup --claude --cli` still writes it, but +install-plugins.sh STEP ctx7 purges it right after; the find-docs skill is +the single ctx7 surface. If it reappears (manual `ctx7 setup`), delete it +or re-run `make plugin`. +``` + +- [ ] **Step 5: Retarget link.sh** + +`link.sh:20` — change: + +```bash +link_file "$REPO/CLAUDE.md" "$CLAUDE/CLAUDE.md" +``` + +to: + +```bash +link_file "$REPO/CLAUDE.global.md" "$CLAUDE/CLAUDE.md" +``` + +- [ ] **Step 6: Run link.sh and verify the symlink** + +```bash +bash link.sh +readlink "$HOME/.claude/CLAUDE.md" +``` + +Expected: `✅ 1 symlink(s) updated in ~/.claude/` (ln -sf replaces the stale +link) and readlink prints `/home/bchanot/Documents/claude/CLAUDE.global.md`. + +- [ ] **Step 7: Stage, then verify sizes and byte-identity of protected content** + +```bash +wc -l CLAUDE.global.md # expected: 301 (≤ 320, BDR-062 margin) +git add CLAUDE.global.md CLAUDE.md link.sh +git diff -M --cached --stat # rename CLAUDE.md→CLAUDE.global.md + new CLAUDE.md + link.sh +git diff -M --cached -- CLAUDE.global.md +``` + +Expected: rename detected (similarity ~97%); exactly TWO hunks on +CLAUDE.global.md (header insertion at top, tail-section deletion) — nothing +else. `## Security`, `# Architecture decisions`, `## Design work — full +toolchain (tiered by scope)` untouched. + +- [ ] **Step 8: shellcheck + commit** + +```bash +shellcheck link.sh +git commit -m "feat(memory): split user-scope global (CLAUDE.global.md) from project CLAUDE.md" +``` + +--- + +### Task 3: Point dependent scripts at CLAUDE.global.md + +**Files:** +- Modify: `hooks/session-start.sh:202-211` +- Modify: `doctor.sh:244,251,277` +- Modify: `install-plugins.sh:33-41,65-68` +- Modify: `lib/doc-commit.sh:44-50` + +**Interfaces:** +- Consumes: `CLAUDE.global.md` from Task 2. +- Produces: guard/stats/exclusions used by Task 5's verification sweep. + +- [ ] **Step 1: session-start.sh — line-count guard follows the file (BDR-062)** + +Replace lines 202-211: + +```bash +# CLAUDE.global.md line-count guard (anti-regression). BDR-062 supersedes +# BDR-031's 275 target: 305 is the assumed reality (extraction done at +# job1; further compression costs clarity > token gain) — warn past 320. +if [ -n "$REPO_DIR" ] && [ -f "$REPO_DIR/CLAUDE.global.md" ]; then + _claude_lines=$(wc -l < "$REPO_DIR/CLAUDE.global.md") + if [ "$_claude_lines" -gt 320 ]; then + _cmd_warn="CLAUDE.global.md ${_claude_lines}L (>320) — density pass" + printf "│ ⚠️ %-44s│\n" "${_cmd_warn:0:44}" + unset _cmd_warn + fi + unset _claude_lines +fi +``` + +(Behavior guard: without this change the check would silently measure the +NEW 30-line project CLAUDE.md and never warn again — fail-open.) + +- [ ] **Step 2: doctor.sh — stats read the global file** + +Line 244 comment: `# The passive footprint (CLAUDE.md + skill descriptions` +→ `# The passive footprint (CLAUDE.global.md + skill descriptions`. + +Line 251: + +```bash +CLAUDE_MD_CHARS=$(wc -c < "$REPO/CLAUDE.global.md" 2>/dev/null || echo 0) +``` + +Line 277 (keep the `~` column aligned — 4 spaces after the colon): + +```bash +echo " CLAUDE.global.md: ~${CLAUDE_MD_TOKENS}t" +``` + +Lines 32, 52, 65 (symlink-NAME references `~/.claude/CLAUDE.md`) unchanged. + +- [ ] **Step 3: install-plugins.sh — guard both memory files** + +Line 36: `These 3 files` → `These 4 files`. After line 40 (`— anything the +installer should add…`), append one comment line, then replace line 41: + +```bash +# CLAUDE.md = project memory (graphify's rewrite target); CLAUDE.global.md +# = user-scope global memory (deployed as ~/.claude/CLAUDE.md). +GUARDED_CONFIGS=("CLAUDE.md" "CLAUDE.global.md" ".claude/settings.json" + "settings.json") +``` + +Lines 65-68 err message: + +```bash + err "Config guard could not be created (mktemp failed) — refusing to run" \ + "unguarded: CLAUDE.md/CLAUDE.global.md/.claude/settings.json/settings.json" \ + "could be silently rewritten by the installer. Fix mktemp/TMPDIR and retry." +``` + +- [ ] **Step 4: lib/doc-commit.sh — exclude the global file (BDR-022)** + +Comment (line ~44-45): `or a CLAUDE.md (root or nested)` → `or a CLAUDE.md / +CLAUDE.global.md memory file (root or nested)`. Case patterns: + +```bash +_forbidden_path() { + case "$1" in + .claude | .claude/* | */.claude/* | CLAUDE.md | */CLAUDE.md | \ + CLAUDE.global.md | */CLAUDE.global.md) return 0 ;; + *) return 1 ;; + esac +} +``` + +- [ ] **Step 5: Lint + smoke test + commit** + +```bash +shellcheck hooks/session-start.sh doctor.sh install-plugins.sh lib/doc-commit.sh +bash doctor.sh | sed -n '/Token budget/,/────/p' # shows "CLAUDE.global.md: ~Nt", N≈3600 +git add hooks/session-start.sh doctor.sh install-plugins.sh lib/doc-commit.sh +git commit -m "feat(memory): guards, doctor stats and doc-commit exclusions follow CLAUDE.global.md" +``` + +--- + +### Task 4: rules/README.md pointer + README tree + +**Files:** +- Rewrite: `rules/README.md` +- Modify: `README.md:16` + +- [ ] **Step 1: Rewrite rules/README.md (full new content)** + +```markdown +--- +paths: ["rules/**"] +--- + +# rules/ + +User-scope rules, deployed to `~/.claude/rules` by `link.sh`. +Maintenance doctrine (what belongs here, lazy-load `paths:` semantics, +machine-owned files): see `CLAUDE.md` (project scope) at the repo root. +``` + +- [ ] **Step 2: README.md tree — show both memory files** + +Replace line 16: + +``` +├── CLAUDE.md # Global coding preferences (style, rules, workflow) +``` + +with: + +``` +├── CLAUDE.global.md # Global coding preferences — deployed as ~/.claude/CLAUDE.md +├── CLAUDE.md # Project-scope instructions (this repo only) +``` + +Lines 29, 100, 152 (per-project CLAUDE.md concept) and 189 (symlink name) +unchanged. USAGE.md / MIGRATION.md / update-all.sh audited: zero references +to the repo-root global file — no edits (contract criterion 10 satisfied +by verification). + +- [ ] **Step 3: Commit** + +```bash +git add rules/README.md README.md +git commit -m "docs: slim rules/README to a pointer; README tree lists both memory files" +``` + +--- + +### Task 5: Verification sweep (no new code) + +**Files:** none modified (fixes only if a check fails — then loop the owning task). + +- [ ] **Step 1: link.sh idempotence** + +Run: `bash link.sh` +Expected: `✅ All symlinks already up to date.` + +- [ ] **Step 2: doctor.sh green** + +Run: `bash doctor.sh` +Expected: `~/.claude/CLAUDE.md` symlink PASS (resolves inside repo); token +stats line reads `CLAUDE.global.md:`; no new FAIL vs pre-change baseline. + +- [ ] **Step 3: Residual reference grep** + +```bash +grep -rn '\$REPO/CLAUDE\.md\|\$REPO_DIR/CLAUDE\.md' -- *.sh hooks lib || echo CLEAN +grep -rn 'CLAUDE\.md' -- *.sh hooks/*.sh lib/*.sh +``` + +First: `CLEAN`. Second — every hit must be one of: link.sh `$CLAUDE/CLAUDE.md` +(dst name), session-start.sh `readlink "$HOME/.claude/CLAUDE.md"`, doctor.sh +`check_symlink "CLAUDE.md"` + name comments, install-plugins.sh +`"CLAUDE.md"` guard entry + comments, doc-commit.sh case patterns, +update-all.sh graphify comment (targets the project file — accurate). + +- [ ] **Step 4: History + guard smoke** + +```bash +git log --follow --oneline CLAUDE.global.md | tail -3 # pre-rename commits +awk '/line-count guard/,/^fi$/' hooks/session-start.sh | grep -c 'CLAUDE\.global\.md' # ≥ 2 +wc -l CLAUDE.global.md CLAUDE.md # 301 and ~31 +``` + +- [ ] **Step 5: Full Health Stack** + +Run: `shellcheck *.sh hooks/*.sh lib/*.sh` +Expected: exit 0, no findings on modified files. diff --git a/docs/superpowers/specs/2026-07-12-claude-global-md-rename-design.md b/docs/superpowers/specs/2026-07-12-claude-global-md-rename-design.md new file mode 100644 index 0000000..1f1c938 --- /dev/null +++ b/docs/superpowers/specs/2026-07-12-claude-global-md-rename-design.md @@ -0,0 +1,125 @@ +# 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 with + `git log --follow`). +- `CLAUDE.global.md` drops the `# This repo only (claude-config)` section + (6 lines) and gains a scope header above the title: + + ```markdown + + ``` + + 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 from `rules/README.md`: + what belongs in `rules/` (one rule = one file = one concern), lazy-load + semantics (`paths:` frontmatter → loads on matching file read; no + `paths:` → 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. + +### 2. link.sh + +Mapping line changes: link `/CLAUDE.global.md` → `~/.claude/CLAUDE.md`. +`ln -sf` in `link_file()` replaces the stale symlink cleanly. Post-condition: +`readlink ~/.claude/CLAUDE.md` = `/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: + +```markdown +--- +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 + +1. `shellcheck` on every modified `.sh` (repo Health Stack). +2. `bash link.sh` → `readlink ~/.claude/CLAUDE.md` resolves to + `CLAUDE.global.md`; no dangling link. +3. `bash doctor.sh` → symlink check green, stats read the new file. +4. Residual grep: no script reference to the repo-root global file under the + old name (`grep -rn 'CLAUDE\.md' *.sh hooks/*.sh lib/*.sh` reviewed). +5. Session-start guard smoke test: guard finds `CLAUDE.global.md`, no + fail-open on the old path. +6. `git log --follow CLAUDE.global.md` shows 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.