CLAUDE.md · diff

git:20261007.e712f87 to git:20261019.3485d5e

2 added, 3 removed. Audit A to A.

# CCF — Claude Context First (plugin source)
> Managed by **CCF**. This project IS the source code of the CCF plugin — not an app with a DB/API/frontend.
> **Parallel by waves**: tasks code proves independent run at once, each in its own isolated worktree; everything else waits.
> Ground every decision about Claude Code's schema/contract in the official docs (via Context7) before writing.
> Keep this spec always fresh with `/ccf:updatespec`.
## What this is
CCF is a **Claude Code plugin** that imposes a context-first, spec-driven, wave-parallel workflow. It has NO application runtime (no server, DB, API, UI). The entire "product" is the artifacts Claude Code loads: commands (markdown prompts), agents/subagents (markdown), hooks (`.mjs` scripts run by `node`), templates for `/ccf:init` to instantiate, plus distribution manifests. Users install via a marketplace, then run `/ccf:*` (e.g. `/ccf:plan`).
## Repo layout
- **git init at the root** (`D:/projects/ccf`). The root holds `CLAUDE.md`, `.claude/`, `package.json`, `tsconfig.json`, `bin/`, `README.md`, `LICENSE`, `.claude-plugin/marketplace.json`.
- `plugins/ccf/` — the plugin itself. `.claude-plugin/plugin.json` is the manifest (ONLY the manifest goes in `.claude-plugin/`); the component directories live at the **plugin root**:
- `commands/*.md` — 4 slash commands (`init`, `check`, `updatespec`, `cook`; invoked via the plugin namespace, e.g. `/ccf:check`).
- `agents/*.md` — 6 subagents, ALL read-only (`ccf-codebase-analyzer`, `ccf-best-practice-researcher`, `ccf-spec-writer`, `ccf-spec-checker`, `ccf-scope-checker`, `ccf-finding-refuter`). The only writers are `/ccf:cook`'s built-in `general-purpose` task agents, each in an `isolation: "worktree"` checkout.
- `skills/*/SKILL.md` — 2 skills: `plan` (user-facing workflow, typed as `/ccf:plan`, the 5th slash command by name) and internal `grill-me`, the shared interview engine (`user-invocable: false`; hidden from the `/` menu).
- `hooks/*.mjs` + `hooks/hooks.json` + `hooks/lib/` — 7 hooks (plan-mode-guard, plan-skill-inject, session-start, updatespec-nudge, auto-verify, completion-evidence, explore-guide-inject) sharing `lib/io.mjs` and the pure helpers in `hooks/lib/` (list the directory; an enumeration here drifted).
- - `hooks/register.tsx` + `sprite.ts` + `ui/` + `types/` + `tests/*.test.tsx` — the UI mod (Rem dock, CCF band/board/wave map).
- `scripts/*.mjs` — 12 CLIs (no event invokes them): `jev-slice-check.mjs`, `jev-backtest.mjs`, `jev-verify-findings.mjs`, `plan-waves.mjs`, `worktree-preflight.mjs`, `scope-diff.mjs` (files outside `Files to touch`), `integrate-wave.mjs`, `archive-plan.mjs`, `prune-archive.mjs`, `memory-audit.mjs`, `spec-budget.mjs` and `eval-changelog.mjs`.
- `templates/{root,backend,frontend}/**` — `*.tmpl` files with `{{...}}` placeholders for `/ccf:init` to instantiate.
- `.mcp.json` — bundles 2 remote MCP servers (microsoft-learn, context7).
- `bin/ccf-bootstrap.mjs` — the npx entry; only shells out to the `claude plugin` CLI, writes no files itself.
## Core invariants (read before editing)
- - Hooks are **no-build, no-dependency, Windows-clean** `.mjs` run directly with `node` (Node ≥ 18). Do NOT add a dependency, do NOT add a build step. See `@.claude/rules/hooks.md`. Sole exception: the UI mod's `.ts`/`.tsx`, run by the Claude Code mod engine, not `node`.
+ - Hooks are **no-build, no-dependency, Windows-clean** `.mjs` run directly with `node` (Node ≥ 18). Do NOT add a dependency, do NOT add a build step. See `@.claude/rules/hooks.md`.
- Components (command/agent/template) are **markdown prompts**, not executable code. Editing the content = changing Claude's behavior. See `@.claude/rules/components.md`.
- `${CLAUDE_PLUGIN_ROOT}` expands in `hooks[].command`, `mcpServers` and a plugin skill body (observed 2026-10-02), NOT in a skill's `references/` files nor in frontmatter.
- Every `CLAUDE.md` (including ones CCF generates for other projects) must be **< 200 lines AND < 12KB, whichever binds first**, pushing detail into `.claude/rules/*` via `@import` (at most four hops). **Measure both, because a line count alone is gameable:** this file passed "< 200 lines" at 38 lines while weighing 25KB, since a single `## Current plan` paragraph had grown to 21KB on one line. Official guidance calls `CLAUDE.md` a cheat sheet of about two screens, not documentation; 200 lines of readable prose lands near 12KB, so a file over that is documentation no matter how few newlines it contains. Check with `wc -lc CLAUDE.md`, not by eye.
- **Everything `CLAUDE.md` `@import`s is loaded EVERY session — budget it as one total.** Run `wc -c CLAUDE.md .claude/rules/*.md` before adding prose anywhere in that set, and again as the LAST step of any task that touches it (an unmeasured figure here drifted four times in a row). The verified paid total is no longer restated here; it lives in `.claude/rules/prompt-standard.md`'s machine-readable `<!-- ccf-budget: paid=NNNNN -->` label, which `.claude/tests/context-budget.test.mjs` asserts against a real measurement on every run.
- - **An `@import` loads a rule unconditionally and VOIDS its `paths:` frontmatter.** Observed live in the session that wrote `prompt-standard.md`: `hooks.md` arrived in full at session start, before any file under `plugins/ccf/hooks/**` was read. The docs describe the two mechanisms separately and never say how they combine, so this observation is the ruling. Consequences: `.claude/rules/hooks.md` DOES load every session despite carrying `paths:`, and the only way to make that scope real is to delete its `@import` line below, not to add more `paths:` patterns. The genuinely lazy rules are `.claude/rules/prompt-standard.md` and `.claude/rules/ui-mod.md`: each carries `paths:`, is deliberately NOT `@import`ed, and is reachable by PATH from a paid file, so each costs 0 per session; see the `ccf-budget` label in `.claude/rules/prompt-standard.md` (referenced by path in the bullet above, not restated here) for the exact paid figure.
+ - **An `@import` loads a rule unconditionally and VOIDS its `paths:` frontmatter.** Observed live in the session that wrote `prompt-standard.md`: `hooks.md` arrived in full at session start, before any file under `plugins/ccf/hooks/**` was read. The docs describe the two mechanisms separately and never say how they combine, so this observation is the ruling. Consequences: `.claude/rules/hooks.md` DOES load every session despite carrying `paths:`, and the only way to make that scope real is to delete its `@import` line below, not to add more `paths:` patterns. The genuinely lazy rule is `.claude/rules/prompt-standard.md`: it carries `paths:`, is deliberately NOT `@import`ed, and is reachable by PATH from a paid file, so it costs 0 per session; see the `ccf-budget` label in `.claude/rules/prompt-standard.md` (referenced by path in the bullet above, not restated here) for the exact paid figure.
## Rules (imported — keep this file < 200 lines)
@.claude/rules/architecture.md
@.claude/rules/components.md
@.claude/rules/hooks.md
@.claude/rules/coding-conventions.md
@.claude/rules/testing.md
@.claude/rules/tooling.md
@.claude/rules/git-workflow.md
## Current plan
Live queue: `.claude/plan/PLAN.md` — the CURRENT iteration only. Closed history + postmortems: `.claude/plan/ARCHIVE.md`, with their task files in `.claude/plan/archive/`. Those two files are the premortem anchor source; read them together. Do NOT let a closed row sit in `PLAN.md` — `lib/plan.mjs` counts it as live work.
**Live iteration in `PLAN.md`:** none. waves-tab (088, v0.21.0: press a wave row to open a Waves tab) closed `done` by owner command with its live dock unobserved (PENDING.md R10 waived), after a clean `/ccf:check` rerun. Archived 2026-10-05 by a third owner-command bulk-close (no `/ccf:check`; PENDING.md R1-R9 waived, not observed): jev-inline-gate (087) and lifecycle-cleanup (070-074). Earlier: fincon-feedback (081-086: `FAIL:` by `repro:`, `accepted` + `PENDING.md`, UI runtime evidence, `spec-budget.mjs`, `eval-changelog.mjs`; open WARNs in its `## Closed` note), skills-and-refuter (075-080; open WARNs in its `## Closed` note) and the Jev backtest (067-069): recall 92.3% / FPR 65-70%, under the 95% bar, so Jev stays advisory-only. Open follow-ups: plan-step opt-in (`archive/task-064-*` section 5); the paid eval runs (`PENDING.md` A1, A2).
**Standing machine latches now guarding this repo:** `.claude/tests/context-budget.test.mjs` (the paid-context label — `updatespec.md`'s Closing re-measures it after every spec edit), the `FAIL:` marker asserts in `verify-chain.test.mjs`, and the `PLAN.md.tmpl` preamble latch in `archive.test.mjs` (verified RED on a bug reproduction before being trusted — `testing.md`'s "a latch never seen RED proves nothing" lesson).
**Read `.claude/plan/PENDING.md` before planning anything:** its open `risk`/`action` rows (seeded from `ARCHIVE.md`'s "Residual risk carried forward from the bulk-closes") are what is still UN-OBSERVED, because two bulk-closes by user command mean a `done` row in the archive does NOT imply its gate was observed.
**Retired by owner command (history in `ARCHIVE.md` + `git log`):** `ccf-implementer` (writers returned 2026-09-30 only as `/ccf:cook` worktree agents), `/ccf:fix` + `ccf-debugger`, the plan-time premortem loop and the multi-gate verify chain (one `/ccf:check` gates `done`; `/code-review` is optional), and the `/compact` nudge (`context-guard.mjs`): do not re-add a compact suggestion unless the user asks.
**Deferred but already grounded**: task 042, an ack-vs-finished detector for a background spawn. A background-spawned agent returns an instant `"Async agent launched successfully."` ack instead of its report, and `is_error` is `undefined` in every observed case, so it cannot serve as a done-ness signal. Buildable; deliberately not built yet.
**Standing debt**: `.claude/rules/hooks.md` at ~24KB (measured after plan-skill-inject) is the largest per-session cost, and the proposal to split it by event is still open. Note the `@import` finding above before "fixing" it with `paths:`: that frontmatter buys nothing while the import line stays.
**Task-status lifecycle**: `todo → in-progress → in-review → done` or `accepted` (closed with an open `risk` row in `PENDING.md`). A task reaches `in-review` once merged (`/ccf:cook`) or implemented in-session; only `/ccf:updatespec` writes `done`/`accepted`, after `/ccf:check` passes. Counts: **4 cmd / 6 agent (all read-only) / 7 hook / 2 skill / 12 script** — the real files under `commands/`, `agents/`, `hooks/`, `skills/`, `scripts/` are the source of truth.
**Archive retirement is now deterministic-detected, human-applied.** `scripts/archive-plan.mjs` performs the retirement (`--apply`; default previews); `updatespec-nudge` clause D detects a fully-closed iteration and prints the command; clause E nudges `prune-archive.mjs` (keeps the newest 10 iterations' task files). `lib/archive.mjs` holds the shared decision, grouping iterations **by position** (`## Origin` to the next `## Origin`) because their section headings do not share a name.
When you need a new change, enter plan mode and run `/ccf:plan`; then `/ccf:cook` runs it in waves: one worktree agent per task, preflight + merged-result tests before the next wave.