chore(docs): trim derivable content from CLAUDE.md and lazy-load the task-completion protocol #587

Closed
opened 2026-07-25 11:23:27 +02:00 by timothy · 2 comments
Owner

Ad-hoc work from a session context/config checkup (/doctor). Recorded after the fact; the change is on chore/claudemd-trim-lazy-skill.

What and why

CLAUDE.md is loaded into context at the start of every session, so anything in it that a session could reconstruct from the codebase is paid for on every turn. The checkup found ~2.5k chars of that, plus one task-specific workflow that only matters when an issue is being closed.

Cut — derivable from the repo:

  • the ### Project Layout table (what ls shows; roles are evident from the project names)
  • the dotnet build / dotnet run invocations (standard for the toolchain). The non-obvious docker build -f docker/Dockerfile … line is kept.
  • the Language / Media / Functional C# bullets (already stated by the csproj files and Directory.Packages.props)

Migrated to lazy loading:

  • the 7 mandatory completion steps and the ## Closing record template move to .claude/skills/closing-an-issue/SKILL.md. Only its one-line description stays resident; the body loads when the skill (or /done) is invoked.

Deliberately kept in CLAUDE.md: the whole ## Done-when / merge-consent block. It is safety-critical and describes hook behaviour (pretooluse-merge-consent.sh, prepush-donewhen.sh) that fires whether or not a skill happened to be loaded — exactly the kind of rule that must not move somewhere it might not be loaded when it matters. Also kept: the ### Key Files pointers, the UI/Blazor-removal history, ## Conventions, and ## Project Boundaries.

CLAUDE.md 13,306 → 10,801 chars (~625 est. tokens saved per session).

Scope note

The rest of the checkup touched only local, non-repo state and is out of scope for this issue: disabling two unused project MCP servers in the gitignored .claude/settings.local.json, and a cleanup of this project's Claude memory directory (4 superseded memories deleted — including one that still documented promotion via the dead media-servers bumblebee stack — plus index compression, 17.5 KB → 11.5 KB).

Done-when

  • Build & test (.NET) green on the PR — all 8 jobs green at e678e665; image push correctly skipped for a docs-only change
  • adversarial review passed — cold review of the full diff, #588 (comment) (Review-verdict: MERGEABLE @ e678e665); the load-bearing check was reference integrity, since 7 places outside CLAUDE.md cite "CLAUDE.md → Task Completion Protocol" and the heading is retained
  • no doc updates triggered — verified against the docs-update table in CLAUDE.md → Conventions (no convention, route, /api/* endpoint, or decision changed)
Ad-hoc work from a session context/config checkup (`/doctor`). Recorded after the fact; the change is on `chore/claudemd-trim-lazy-skill`. ## What and why `CLAUDE.md` is loaded into context at the start of every session, so anything in it that a session could reconstruct from the codebase is paid for on every turn. The checkup found ~2.5k chars of that, plus one task-specific workflow that only matters when an issue is being closed. **Cut — derivable from the repo:** - the `### Project Layout` table (what `ls` shows; roles are evident from the project names) - the `dotnet build` / `dotnet run` invocations (standard for the toolchain). The non-obvious `docker build -f docker/Dockerfile …` line is kept. - the Language / Media / Functional C# bullets (already stated by the csproj files and `Directory.Packages.props`) **Migrated to lazy loading:** - the 7 mandatory completion steps and the `## Closing record` template move to `.claude/skills/closing-an-issue/SKILL.md`. Only its one-line description stays resident; the body loads when the skill (or `/done`) is invoked. **Deliberately kept in `CLAUDE.md`:** the whole `## Done-when` / merge-consent block. It is safety-critical and describes hook behaviour (`pretooluse-merge-consent.sh`, `prepush-donewhen.sh`) that fires whether or not a skill happened to be loaded — exactly the kind of rule that must not move somewhere it might not be loaded when it matters. Also kept: the `### Key Files` pointers, the UI/Blazor-removal history, `## Conventions`, and `## Project Boundaries`. `CLAUDE.md` 13,306 → 10,801 chars (~625 est. tokens saved per session). ## Scope note The rest of the checkup touched only local, non-repo state and is out of scope for this issue: disabling two unused project MCP servers in the gitignored `.claude/settings.local.json`, and a cleanup of this project's Claude memory directory (4 superseded memories deleted — including one that still documented promotion via the dead `media-servers` bumblebee stack — plus index compression, 17.5 KB → 11.5 KB). ## Done-when - [x] `Build & test (.NET)` green on the PR — all 8 jobs green at `e678e665`; image push correctly skipped for a docs-only change - [x] adversarial review passed — cold review of the full diff, https://gitea.tblindustries.be/timothy/ersatztv/pulls/588#issuecomment-15884 (`Review-verdict: MERGEABLE @ e678e665`); the load-bearing check was reference integrity, since 7 places outside `CLAUDE.md` cite "CLAUDE.md → Task Completion Protocol" and the heading is retained - [x] no doc updates triggered — verified against the `docs-update` table in `CLAUDE.md` → Conventions (no convention, route, `/api/*` endpoint, or decision changed)
timothy added the ad-hoc label 2026-07-25 11:23:27 +02:00
timothy added the priority: low label 2026-07-25 12:09:02 +02:00
Author
Owner

Drive-by from a parallel session (#436/#440/#583/#592): the H12 qualification audit flagged this as the only OPEN issue with no priority: label, so I added priority: low per process.issue-qualification-audit — an unlabeled issue is invisible to scripts/select-queue.sh.

Not claiming or touching the work: it reads as in-flight (change already on chore/claudemd-trim-lazy-skill), and the tier is a queue-visibility fix, not a judgment on the change. Relabel freely if low is wrong.

One thing worth knowing, since your trim landed in CLAUDE.md while I was mid-session: I picked up the new closing-an-issue skill and used it for four closing records today, and the lazy-load worked exactly as intended — the resident one-line description was enough to know to invoke it. The kept-resident decision on the ## Done-when / merge-consent block also proved correct in practice: the H6/H10 gate auto-granted four merges from derived state today, and that only works because the convention is loaded before any skill is.

Drive-by from a parallel session (#436/#440/#583/#592): the H12 qualification audit flagged this as the only OPEN issue with no `priority:` label, so I added `priority: low` per `process.issue-qualification-audit` — an unlabeled issue is invisible to `scripts/select-queue.sh`. Not claiming or touching the work: it reads as in-flight (change already on `chore/claudemd-trim-lazy-skill`), and the tier is a queue-visibility fix, not a judgment on the change. Relabel freely if `low` is wrong. One thing worth knowing, since your trim landed in `CLAUDE.md` while I was mid-session: I picked up the new `closing-an-issue` skill and used it for four closing records today, and the lazy-load worked exactly as intended — the resident one-line description was enough to know to invoke it. The kept-resident decision on the `## Done-when` / merge-consent block also proved correct in practice: the H6/H10 gate auto-granted four merges from derived state today, and that only works because the convention is loaded before any skill is.
Author
Owner

Closing record

Outcome: Merged as #588 (merge commit 0952078c, branch deleted). CLAUDE.md 13,306 → 10,801 chars; the 7 completion steps and the ## Closing record template now live in .claude/skills/closing-an-issue/SKILL.md, loaded on invocation instead of on every session start. ~625 est. tokens off each session's resident context.

Root cause: Not a bug fix, but the accumulation has an explanation worth recording: CLAUDE.md grew by addition across many sessions with nothing ever pruning it, and no step in the workflow asks "could a session derive this from the repo?" The docs-update table enforces adding documentation when a convention changes; there is no corresponding pressure to remove content that the codebase already teaches.

Decisions/conventions changed: None. No docs/decisions.md key added or superseded — this only relocates existing guidance, and the ## Task Completion Protocol heading is retained so every external cross-reference still resolves.

Reusable knowledge: When trimming or relocating CLAUDE.md sections, the load-bearing check is reference integrity, not word count: seven places outside the file (docs/README.md, docs/decisions/release-ci-governance.md ×3, two handoff docs, and the ask message inside .claude/hooks/pretooluse-merge-consent.sh) cite "CLAUDE.md → Task Completion Protocol" by section name. Keeping the heading while moving its body preserves all of them; deleting the heading would have silently broken a hook's own guidance text. Grep for the section name across docs/, scripts/, .gitea/, .claude/ and .husky/ before removing any CLAUDE.md heading. The complementary rule: never migrate a rule whose enforcement is a hook — the ## Done-when / H6 / H10 block must stay resident because the hook fires whether or not a skill was loaded.

Verification: All 8 CI jobs green at e678e665 (Build & test (.NET), EF migration integrity, functional E2E, API-docs-in-sync, formatting, and the three PR Gates); image push correctly skipped as docs-only. Cold review of the full diff posted at pulls/588#issuecomment-15884 with Review-verdict: MERGEABLE @ e678e665. Post-merge, origin/main's CLAUDE.md was confirmed byte-identical to the reviewed version.

Deferred: None for this issue. Noted but out of scope: docs/decisions.md and two handoff docs mention the ## Closing record template, which now lives in the skill — the retained pointer line and the skill's description keep it findable, so no doc edit was warranted.

Docs updated: None required. Checked against the docs-update table in CLAUDE.md → Conventions: no route migrated, added or redirected; no /api/* endpoint changed; no SPA screen convention touched; no decision established or reversed. The Docs update reminder CI gate passed.

Out-of-repo changes from the same session (recorded here for traceability; nothing to merge): two unused project MCP servers (nuget, docker-mcp — zero calls across 400 sessions) disabled in the gitignored .claude/settings.local.json, and a cleanup of this project's Claude memory directory — 4 superseded memories deleted, 11 cross-links repaired, 4 truncated descriptions rewritten, index compressed 17.5 KB → 11.5 KB. The most useful deletion was a memory that still documented prod promotion via the dead media-servers bumblebee stack, which would have misdirected a future session now that prod runs on jazz.

## Closing record **Outcome:** Merged as https://gitea.tblindustries.be/timothy/ersatztv/pulls/588 (merge commit `0952078c`, branch deleted). `CLAUDE.md` 13,306 → 10,801 chars; the 7 completion steps and the `## Closing record` template now live in `.claude/skills/closing-an-issue/SKILL.md`, loaded on invocation instead of on every session start. ~625 est. tokens off each session's resident context. **Root cause:** Not a bug fix, but the accumulation has an explanation worth recording: `CLAUDE.md` grew by addition across many sessions with nothing ever pruning it, and no step in the workflow asks "could a session derive this from the repo?" The docs-update table enforces *adding* documentation when a convention changes; there is no corresponding pressure to remove content that the codebase already teaches. **Decisions/conventions changed:** None. No `docs/decisions.md` key added or superseded — this only relocates existing guidance, and the `## Task Completion Protocol` heading is retained so every external cross-reference still resolves. **Reusable knowledge:** When trimming or relocating `CLAUDE.md` sections, the load-bearing check is *reference integrity, not word count*: seven places outside the file (`docs/README.md`, `docs/decisions/release-ci-governance.md` ×3, two handoff docs, and the `ask` message inside `.claude/hooks/pretooluse-merge-consent.sh`) cite "CLAUDE.md → Task Completion Protocol" by section name. Keeping the heading while moving its body preserves all of them; deleting the heading would have silently broken a hook's own guidance text. Grep for the section name across `docs/`, `scripts/`, `.gitea/`, `.claude/` and `.husky/` before removing any `CLAUDE.md` heading. The complementary rule: never migrate a rule whose enforcement is a hook — the `## Done-when` / H6 / H10 block must stay resident because the hook fires whether or not a skill was loaded. **Verification:** All 8 CI jobs green at `e678e665` (`Build & test (.NET)`, EF migration integrity, functional E2E, API-docs-in-sync, formatting, and the three PR Gates); image push correctly skipped as docs-only. Cold review of the full diff posted at pulls/588#issuecomment-15884 with `Review-verdict: MERGEABLE @ e678e665`. Post-merge, `origin/main`'s `CLAUDE.md` was confirmed byte-identical to the reviewed version. **Deferred:** None for this issue. Noted but out of scope: `docs/decisions.md` and two handoff docs mention the `## Closing record` template, which now lives in the skill — the retained pointer line and the skill's description keep it findable, so no doc edit was warranted. **Docs updated:** None required. Checked against the docs-update table in `CLAUDE.md` → Conventions: no route migrated, added or redirected; no `/api/*` endpoint changed; no SPA screen convention touched; no decision established or reversed. The `Docs update reminder` CI gate passed. **Out-of-repo changes from the same session** (recorded here for traceability; nothing to merge): two unused project MCP servers (`nuget`, `docker-mcp` — zero calls across 400 sessions) disabled in the gitignored `.claude/settings.local.json`, and a cleanup of this project's Claude memory directory — 4 superseded memories deleted, 11 cross-links repaired, 4 truncated descriptions rewritten, index compressed 17.5 KB → 11.5 KB. The most useful deletion was a memory that still documented prod promotion via the dead `media-servers` bumblebee stack, which would have misdirected a future session now that prod runs on jazz.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: timothy/ersatztv#587