CLAUDE.md · diff
git:20260930.479f431 to git:20261002.1f8ff96
5 added, 5 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` — 5 subagents, ALL read-only (`ccf-codebase-analyzer`, `ccf-best-practice-researcher`, `ccf-spec-writer`, `ccf-spec-checker`, `ccf-scope-checker`). 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 — nudges the model toward the `ccf:plan` skill once per session in plan mode, session-start, updatespec-nudge — advisory Stop nudges, auto-verify — an opt-in `--auto-verify` Stop hook that BLOCKS via `decision:"block"` to drive a single verify step (`/ccf:check` then `/ccf:updatespec`) when a task is in-review and code changed this session, completion-evidence — an opt-in `--completion-evidence` advisory Stop hook (also needs `TYPESAFE_API_KEY`) that sends the in-review task's criteria + diff to Jev and prints the unmet criteria, and explore-guide-inject, which on `SubagentStart` (matcher `Explore`) injects a language-agnostic LSP/Grep/Glob exploration directive into the built-in `Explore` subagent) sharing `lib/io.mjs` (+ `lib/freshness.mjs`, `lib/plan.mjs`, `lib/review-trace.mjs`, `lib/git-trace.mjs`, `lib/verify-trace.mjs`, `lib/verify-chain.mjs`, `lib/explore-guide.mjs`, `lib/archive.mjs`, `lib/plan-trigger.mjs`, `lib/jev-client.mjs`, `lib/completion-evidence.mjs`, `lib/slice-check.mjs`, `lib/finding-verify.mjs`, `lib/worktree-preflight.mjs`, `lib/worktree-wave.mjs`).
- - `scripts/*.mjs` — 6 CLIs (no event invokes them): `jev-slice-check.mjs` (advisory Jev dependency check; needs `TYPESAFE_API_KEY`), `jev-verify-findings.mjs` (Jev annotates each `FAIL:` of a `/ccf:check` report; same key), `plan-waves.mjs` (the wave split), `worktree-preflight.mjs` (read-only pre-merge check of a wave), `integrate-wave.mjs` (merges a wave, tests after each merge) and `archive-plan.mjs`, which retires a fully-closed iteration out of `PLAN.md` into `ARCHIVE.md`. File-MUTATING actions belong here, never in a hook — see `@.claude/rules/architecture.md`.
+ - `hooks/*.mjs` + `hooks/hooks.json` + `hooks/lib/` — 7 hooks (plan-mode-guard, plan-skill-inject — nudges the model toward the `ccf:plan` skill once per session in plan mode, session-start, updatespec-nudge — advisory Stop nudges, auto-verify — an opt-in `--auto-verify` Stop hook that BLOCKS via `decision:"block"` to drive a single verify step (`/ccf:check` then `/ccf:updatespec`) when a task is in-review and code changed this session, completion-evidence — an opt-in `--completion-evidence` advisory Stop hook (also needs `TYPESAFE_API_KEY`) that sends the in-review task's criteria + diff to Jev and prints the unmet criteria, and explore-guide-inject, which on `SubagentStart` (matcher `Explore`) injects a language-agnostic LSP/Grep/Glob exploration directive into the built-in `Explore` subagent) sharing `lib/io.mjs` (+ `lib/freshness.mjs`, `lib/plan.mjs`, `lib/review-trace.mjs`, `lib/git-trace.mjs`, `lib/verify-trace.mjs`, `lib/verify-chain.mjs`, `lib/explore-guide.mjs`, `lib/archive.mjs`, `lib/plan-trigger.mjs`, `lib/jev-client.mjs`, `lib/completion-evidence.mjs`, `lib/slice-check.mjs`, `lib/backtest-{corpus,metrics}.mjs`, `lib/finding-verify.mjs`, `lib/worktree-preflight.mjs`, `lib/worktree-wave.mjs`).
+ - `scripts/*.mjs` — 9 CLIs (no event invokes them): `jev-slice-check.mjs` (advisory Jev dependency check; needs `TYPESAFE_API_KEY`), `jev-backtest.mjs` (Jev recall backtest; same key), `jev-verify-findings.mjs` (Jev annotates each `FAIL:` of a `/ccf:check` report; same key), `plan-waves.mjs` (the wave split), `worktree-preflight.mjs` (read-only pre-merge check of a wave), `integrate-wave.mjs` (merges a wave, tests after each merge), `archive-plan.mjs` (closed iteration → `ARCHIVE.md`), `prune-archive.mjs` (old archived task files) and `memory-audit.mjs` (memory consolidation gate). File-MUTATING actions belong here, never in a hook — see `@.claude/rules/architecture.md`.
- `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`.
- 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 (documented, not yet observed) a plugin skill body — NOT in markdown 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` (max depth 5). **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 (a number written here and not re-measured has drifted before — four times in a row for this same figure). 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 one genuinely lazy rule is `.claude/rules/prompt-standard.md` — it carries `paths:`, is deliberately NOT `@import`ed, and is reachable by PATH from `.claude/rules/coding-conventions.md`, 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. second-review-scope + worktree-safety (063-066) closed and archived: `ccf-scope-checker` runs beside `ccf-spec-checker` in `/ccf:check`, `slice-check` is fail-closed, `scripts/worktree-preflight.mjs` checks a wave read-only. Open follow-ups from `archive/task-064-*`: Jev backtest (step 2), `integrate-wave --apply` (4), plan-step opt-in (5); a paid `claude plugin eval` run.
+ **Live iterations in `PLAN.md`:** lifecycle-cleanup (070-074) and skills-and-refuter (075-080; 075-077 done). Archived before it: 063-066 (second-review-scope + worktree-safety) and the Jev backtest (067-069): recall 92.3% / FPR 65-70% (13+/60-), under the 95% bar, so Jev stays advisory-only. Open follow-ups: plan-step opt-in (`archive/task-064-*` section 5); a paid `claude plugin eval` run.
**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 `ARCHIVE.md`'s "Residual risk carried forward from the bulk-closes" section before planning anything.** Two bulk-closes happened by explicit user command, so a `done` row in the archive does NOT imply its gate was observed. Still **UN-OBSERVED** from there: the real `agent_type` in a `SubagentStart` payload, both remaining opt-in toggles (`--auto-verify`, `--dual-channel-stop` — neither has been seen running; both stay OFF in the shipped `hooks.json`), and every part of task 044. (`--enforce-tests`'s `SubagentStop` payload-shape question is now moot: the hook it gated, `implementer-verify-gate.mjs`, was retired along with `ccf-implementer` — see the architecture note below. Task 040's post-reload `/compact` wording and `--hard-block` are moot too: `context-guard.mjs` was retired.)
**Architecture simplification (this session, coding-subagent + multi-step-verify retirement, done directly by explicit user command, no `/ccf:plan` ceremony):** retired the writer agent `ccf-implementer` and the debugging command `/ccf:fix` + its agent `ccf-debugger`. Reason: for one task at a time a coding subagent was slower than the main session. **Reversed 2026-09-30 by owner command:** `/ccf:cook` spawns writers again, one per task of a wave, only in isolated worktrees; the 5 CCF agents stay read-only. Also collapsed the multi-step verify chain: `/ccf:plan` and `/ccf:init` no longer run a mandatory plan-time `ccf-spec-checker` premortem-review loop before presenting the plan, and `/ccf:cook`'s batch-verify phase no longer chains `ccf-spec-checker` + `/code-review` + `/simplify` + re-gate; a single `/ccf:check` now stands between implementing and `/ccf:updatespec` marking a task `done`. `/code-review` remains a good optional extra, never a mandatory gate. Every prompt/hook/template/rule file referencing the retired pieces was swept and updated in the same pass; see `git log` for the exact diff. **Also retired by explicit user command (2026-09-29): the `/compact` nudge** (`context-guard.mjs` + `lib/context-usage.mjs`, `io.mjs#emitPromptWarning`, `plan.mjs#findHintTask`); CCF no longer measures context usage at all, so do not re-add a compact suggestion without the user asking.
**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`. A task reaches `in-review` once merged (`/ccf:cook`) or implemented in-session; only `/ccf:updatespec` writes `done`, after `/ccf:check` passes. Counts: **4 cmd / 5 agent (all read-only) / 7 hook / 2 skill / 6 script** — the real files under `commands/`, `agents/`, `hooks/`, `skills/`, `scripts/` are the source of truth.
+ **Task-status lifecycle**: `todo → in-progress → in-review → done`. A task reaches `in-review` once merged (`/ccf:cook`) or implemented in-session; only `/ccf:updatespec` writes `done`, after `/ccf:check` passes. Counts: **4 cmd / 5 agent (all read-only) / 7 hook / 2 skill / 9 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. `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.
+ **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.