CLAUDE.md ยท diff

git:20260816.553494f to git:20260816.411968e

115 added, 230 removed. Audit C to A.

# Claude Power Pack
+ Claude Power Pack (CPP) is a Python 3.11+ repository of issue-driven workflow,
+ CI/CD, security, documentation, and project-management commands for Claude Code
+ and Codex. Command documents are the orchestration sources; Python and shell
+ helpers own deterministic behavior.
+
## Core Directives
- - **NEVER output API keys, passwords, connection strings, or `.env` file contents in responses.** The PostToolUse hook masks terminal output, but your response text is logged before masking applies.
- - **Use `make` targets for build/test/deploy operations.** Never run raw `uv run ruff` or `uv run pytest` directly. If a target is missing, add it to the Makefile.
- - **Progressive disclosure:** Do NOT auto-load documentation. Read topic-specific files from `docs/skills/` only when the task requires it.
- - **Python 3.11+, uv for dependencies.** Each component has its own `pyproject.toml`.
- - **When fixing errors, fix BOTH the application code AND the CI/CD process** (Makefile, Dockerfile, `.woodpecker.yml`). Never bypass quality gates.
- - Before debugging manually, run `make lint` and `make test` to surface known issues.
- - **A test that shells out to a real binary (`git`, `docker`, `gitleaks`, `jq`) MUST guard with `@pytest.mark.skipif(shutil.which("<tool>") is None, ...)`.** The Woodpecker `validate` container (uv:python3.11-slim) ships none of them, so an unguarded test errors the suite and turns CI red even though it passes locally (recurred #451, #489, #577, #716/#717 for `jq`). This is no longer prose alone: `scripts/check-test-binary-guards.py` enforces it (`make binary-guards-check`, part of `make verify`, asserted in `tests/test_test_binary_guards.py`), so the failure is now reproducible on a dev box that HAS git instead of only in CI (#602). It covers the indirect shape too - a test calling a module-level helper that shells out - but NOT a binary called from inside a separately-invoked script under test (e.g. a bash script that itself shells out to `jq`); that transitive case still needs a hand-added skipif. Rare intentional exception: `# binary-guard: allow <reason>` on the `def` or call line (deliberately NOT ruff's `# noqa:` namespace, which ruff itself warns on for an unknown code).
- - **A fixture that creates a NEGATIVE condition (a tool absent, a path missing, a capability unavailable) by CONSTRUCTING an environment rather than removing one named thing MUST assert the precondition before exercising the code under test.** `assert shutil.which("git", path=str(stub_path)) is None, "fixture must lack git"` is the shape (#695, #697). The reason is that indirect construction can succeed at something broader than intended - a test proving `--check` fails open without `git` emptied `PATH`, which also removed `ln`/`mkdir`/`readlink`/`bash`, so the script failed for unrelated reasons and the assertions STILL PASSED, because "nothing printed, exit code unchanged" is also what a correct fail-open produces. A fail-open or absence test is one of the few kinds whose passing state carries almost no information; the precondition guard is what separates a real pass from an unfalsifiable one, and it costs one line. Enforced by `scripts/check-negative-fixture-preconditions.py` (`make negative-fixture-check`, part of `make verify`, asserted in `tests/test_negative_fixture_preconditions.py`). Deliberately NOT covered, so the rule is not over-applied: a `PATH` PREPEND (`f"{bindir}:{env['PATH']}"`) adds without removing; `monkeypatch.delenv("X", raising=False)` removes exactly one named thing; and `assert not path.exists()` about what the code PRODUCED is an outcome assertion, not a fixture precondition. Rare intentional exception: `# negative-fixture: allow <reason>`.
- - **A fixture must never interpolate an absolute path it does not control (`sys.executable`, anything under pytest's `tmp_path`) into a value the code under test PATTERN-MATCHES.** The path carries the checkout's name into the match, so the assertion's outcome follows where the repo happens to live rather than what the fixture built. `tests/test_test_workers_cap.py` proved a `lint` step never receives the pytest worker cap by building its command from `sys.executable`; `is_test_step()` scans the whole command, so under a flow worktree named `...-issue-697-test-convention-...` the linter classified as a TEST step and the gate went red - meaning every `/flow:auto` run on an issue whose TITLE contains "test" failed on its own branch name, with a message (`assert '7' == 'unset'`) pointing at worker-cap resolution rather than at the collision (#704). Two corollaries, both measured: pytest's own `tmp_path` root is `/tmp/pytest-of-<user>/pytest-<n>/`, so an absolute path under it is no safer than the checkout path; and naming the matched variable in a command (`printf '%s' "$PYTEST_WORKERS"`) matches too. The fix is a value the fixture owns - steps run with `cwd=project_root`, so a RELATIVE name (`./interp`, symlinked to the interpreter) keeps every path out of the string. Pair it with the negative-fixture precondition guard above (`assert not ShellStep(step).is_test_step()`), which is what turns a recurrence into a failure that names its own cause. Not enforced by a script: the general shape (a fixture handing a scanner an input it did not mean to include) is not statically detectable, so this one is prose plus a characterization test (`tests/test_cicd_outcomes.py::TestStepGating::test_path_in_command_matches_by_design`) pinning that the broad scan is deliberate (#621) and must not be narrowed to dodge paths.
- - After any fix, verify through the full pipeline: `make verify`.
- - Use `/cpp:dockers` to check container status, health, and project linkages.
- - **Use single dashes (-) not em dashes (-)** in all markdown, comments, and documentation. Never generate Unicode em dashes (U+2014) or en dashes (U+2013).
- - **Never wrap a read-only command in `cd X && ...`.** Name the path instead of moving to it: `git -C <path> status`, an absolute path, or the tool's own path argument. A permission allow rule matches a command PREFIX, so `cd "$(git rev-parse --show-toplevel)" && grep -n foo bar.md` prompts even though `Bash(grep:*)` is allowlisted - the 2026-07-19 retro found EVERY safe-tier prompt in a 138-record census firing this way, with the matching rule already installed. This generalizes #581's bare-invocation discipline from the flow helpers to ordinary commands. The `cd` habit is a defense against the Bash tool's cwd drifting between calls, and that same drift caused the #590/#592 lane bugs and the #595 stale-grep trap - so prefer an explicit path there too, rather than trusting where the shell happens to be.
- - **In shared checkouts, characterize branch/repo state with ref-scoped reads only** - `git -C <repo-root>` with full refs, `git show <ref>:<path>`, `git diff <ref1> <ref2>`, `git cat-file -e <ref>:<path>` - never a bare relative pathspec (`git diff ... -- ui/`) and never a working-tree `grep`/file read standing in for a BRANCH's content (#659). The Bash cwd drifts between calls by design, and once it sits in a subdirectory every relative pathspec matches nothing: the empty diff is a SILENT wrong answer, indistinguishable from "no changes", so the failure produces confident false conclusions rather than errors - in the 2026-08-11 damage assessment four such reads agreed with each other and were all wrong, while the lone ref-scoped `git cat-file -e <ref>:<path>` dissented and was correct. Corollary: agreement between measurements sharing a broken assumption is not corroboration - when one measurement dissents from several, investigate the dissenter FIRST. This extends the `cd`-wrapping bullet above from command prefixes to pathspecs; #592/#614/#657 fixed the same disease inside the flow helpers (declared roots, `git -C`, full refs), but ad-hoc measurement commands have no helper guarding them - this directive is that guard.
- - **One inventory item per line in CLAUDE.md.** When adding to an inventory entry (the `scripts/` list, component feature lists, CI behavior lists), add a new sub-bullet - never append to an existing line. Git merges at line granularity, so packed single-line lists make every concurrent PR that touches them a manual merge conflict (#501). **The per-item HISTORY does not belong here** (#711): this file loads into every session, and it reached 2.4x Claude Code's ~40,000-char memory-file warning threshold because each fix appended its incident narrative to the always-loaded brief. A `scripts/` entry keeps ONE line naming what the script is; the why-it-exists narrative goes to `docs/scripts.md`, per-command detail to `docs/commands-reference.md`, and a command's own description is never restated here at all - every session already receives it in the skill listing.
+ - **NEVER output API keys, passwords, connection strings, or `.env` file contents in responses.** Output masking does not protect response text.
+ - **Use `make` targets for build/test/deploy operations.** If a needed target is missing, add it to the Makefile.
+ - **Progressive disclosure:** do not auto-load documentation; load the topic-specific source only when the task requires it.
+ - **Python 3.11+, uv for dependencies.** Each component owns its dependency configuration.
+ - **When fixing errors, fix BOTH the application code AND the CI/CD process.** Never bypass quality gates.
+ - Before debugging manually, run `make lint` and `make test`.
+ - A test that shells out to a real binary must use a `shutil.which` skip guard. `scripts/check-test-binary-guards.py` enforces the detailed contract.
+ - A fixture that constructs a NEGATIVE condition must assert that precondition before exercising the code. `scripts/check-negative-fixture-preconditions.py` owns the detectable contract.
+ - A pattern-matching fixture must not interpolate an absolute path it does not control; use a fixture-owned relative value and assert the intended classification.
+ - After any fix, verify through the full pipeline with `make verify`.
+ - Use `/cpp:dockers` for container status, health, and project linkages.
+ - Use single dashes (-), never Unicode em or en dashes, in markdown, comments, and documentation.
+ - Never wrap a read-only command in `cd X && ...`; use `git -C`, an absolute path, or the tool's path argument.
+ - Resolve edit paths from `git rev-parse --show-toplevel`; never hand-build an absolute worktree path (#486).
+ - In shared checkouts, characterize branch content with ref-scoped reads only; never substitute a working-tree read or cwd-relative pathspec for a branch measurement.
+ - Keep one inventory item per line. Per-item history belongs in [the script reference](docs/scripts.md) or [the command reference](docs/commands-reference.md), not this always-loaded file.
+ The detailed rationale, incidents, exceptions, and state transitions behind
+ these rules remain in [commands-reference.md](docs/commands-reference.md) and
+ [scripts.md](docs/scripts.md).
+
## Project Map
- Core components and their locations:
+ - `docs/skills/` - topic guidance loaded on demand.
+ - `docs/reference/CLAUDE_CODE_BEST_PRACTICES_FULL.md` - full best-practices guide.
+ - `docs/commands-reference.md` - command decisions, histories, and workflow detail.
+ - `docs/scripts.md` - script inventory and per-script behavioral history.
+ - `docs/agents/knowledge-lifecycle.md` - canonical completed-spec graduation policy.
+ - `ISSUE_DRIVEN_DEVELOPMENT.md` - issue-driven development source.
+ - `PROGRESSIVE_DISCLOSURE_GUIDE.md` - context-loading guidance.
+ - `MCP_TOKEN_AUDIT_CHECKLIST.md` - MCP context-efficiency checklist.
+ - `.specify/` - active specifications, plans, tasks, templates, and lifecycle ledger.
+ - `.claude/commands/` - canonical Claude command documents and detailed state machines.
+ - `.claude/skills/` - canonical topic-skill packages and provenance.
+ - `codex/skills/` - generated Codex command-skill mirrors; regenerate with `make codex-skills`.
+ - `scripts/` - deterministic helpers and repository checks.
+ - `lib/cicd/` - CI/CD configuration, runner, health, smoke, and deployment verification.
+ - `lib/security/` - deterministic secret, dependency, and policy scanning.
+ - `lib/creds/` - secret retrieval, injection, UI, and audit support.
+ - `lib/cpp_memory/` - fail-open local or federated friction-knowledge ledger.
+ - `templates/` - Makefile, workflow, permission, and container templates.
+ - `vendor/project_next/` - pinned project-next engine vendored from codex-power-pack.
+ - `.woodpecker.yml` - repository CI pipeline.
- - `docs/skills/` - Topic-focused best practices (~3K tokens each). Load on demand.
- - `docs/reference/CLAUDE_CODE_BEST_PRACTICES_FULL.md` - Complete guide (25K tokens). Load via `/cpp:load-best-practices`.
- - `ISSUE_DRIVEN_DEVELOPMENT.md` - IDD methodology
- - `PROGRESSIVE_DISCLOSURE_GUIDE.md` - Context optimization patterns
- - `MCP_TOKEN_AUDIT_CHECKLIST.md` - Token efficiency checklist
- - `.specify/` - Spec-Driven Development (specs, plans, tasks, templates)
- - `.mcp.json` - Client pointer for the external `second-opinion` MCP server (streamable-http `${SECOND_OPINION_URL:-http://127.0.0.1:8080}/mcp` - default localhost 8080, per-host override via the `SECOND_OPINION_URL` env var without editing the tracked file, #633; the same variable `mcp-evaluate/src/config.py` reads). Run the server from its own repo (https://github.com/cooneycw/mcp-second-opinion); CPP's `mcp-second-opinion` container + `aws-secrets-agent` sidecar (and the whole Docker MCP runtime) were retired in #469.
- - Browser automation - upstream `@playwright/mcp` registered via npx/stdio by `/cpp:init` (no container; the CPP `mcp-playwright-persistent` fork was retired in #423)
- - `extras/sequential-thinking/` - Optional: structured reasoning (stdio, npm)
- - `lib/creds/` - Secrets management (dotenv/AWS SM, FastAPI UI, audit logging)
- - `lib/security/` - Security scanning (native + external tools)
- - `lib/cicd/` - CI/CD framework detection, Makefile generation, health/smoke checks, deterministic runner, deployment strategies, Pydantic v2 config validation
- - `ProcessCheck` is a probe UNION, not a port (#620): an entry names exactly one of `port` (ss/lsof), `systemd_user_unit` (`systemctl --user is-active`), `systemd_unit` (`systemctl is-active`) or `pattern` (`pgrep -f`), so a service with no listening socket - a queue worker, a scheduler - can be described as what it is instead of being handed a fabricated port. Exactly-one rather than at-least-one is deliberate: several set would need a silent precedence order, and a field that quietly loses to another is the bug the union fixes. Before it, a port-less entry failed the WHOLE config, which took `verify --baseline` down with it - and since verification is fail-open, a repo with `deploy_verification.enabled: true` deployed green with no gate running at all
- - Health probes fail SOFT per entry (#620) - `CICDConfig.load` drops an individually-invalid `endpoints`/`processes`/`smoke_tests` entry with a `UserWarning` instead of raising, so one malformed probe cannot disable every other probe plus the gate; a structural error outside those lists still raises, having no smaller unit to fall back to
- - CPP dogfoods its own deploy verification (#603) - `.claude/cicd.yml` enables `deploy_verification` with a service-less smoke probe (`python3 -c "import lib.cicd"`), so Step 9 runs `verify --baseline` + `verify` around the #469 no-op deploy on every CPP flow run; and `tests/test_step9_verify_executes.py` EXTRACTS the documented Step-9 invocation pair from auto.md (section-scoped to Step 9 - the third file-global `uv run --project` mention is Step-6 prose, deliberately excluded; exact-pair count asserted, loud failure on doc-shape change) and EXECUTES it against a scaffolded temp project - a regression to the pre-#595 broken shape is a red test, not plausible prose, because execution is the assertion
- - `validate_file` reports unknown keys INSIDE a probe (#620), not just unknown top-level sections - every model is `extra="ignore"`, so `expect_status` for `expected_status` parses fine and leaves a 302 probe comparing against the default 200 with nothing to say so. The audit surface is `python -m lib.cicd validate`; load time stays permissive by design
- - `lib/cpp_memory/` - Common-memory client: a **pluggable** fail-open friction-knowledge ledger (issue #472) with three `/cpp:init`-selectable backends behind one `StoreBackend` interface:
- - `md` - best-effort local, no federation; subsumes `.claude/learnings.md`
- - `local-pg` - full SQL dedup, single box, `lib/cpp_memory/docker-compose.yml`
- - `remote-pg` - full dedup, fleet-federated Postgres, `scripts/memories-db-setup.sh`
- - Backend chosen via `CPP_MEMORIES_BACKEND` / step 8d; **federation is a surfaced per-tier property** (only remote-pg shares across VMs)
- - Holds *portable* CPP learnings/infra traps (bucket-2-plus) plus a dedup/rejection ledger and a learnings->GitHub-issue bridge (`--emit-issue-candidate` / `link-issue`, #463)
- - Sightings carry a `harness` tag (`claude`|`codex`) for multi-harness attribution and a `sightings_by_harness` query split (#557); the write/read contract codex-power-pack targets is `docs/contracts/friction-ledger-shared-store.md`
- - Consult-not-push; see `/self-improvement:memory`
- - `scripts/` - Shell utilities, one per sub-bullet (add new scripts as their own line, #501); per-script history lives in `docs/scripts.md` (#711):
- - prompt-context
- - worktree-remove - claim-aware worktree removal used by `/flow:auto` Step 7, `/flow:merge` and `/flow:cleanup`
- - gh-pr-merge - layout-aware PR squash-merge used by `/flow:auto` + `/flow:merge`
- - branch-protection - declared branch-protection posture as data (ADR 0004)
- - flow-stale-check - advisory early stale-base detector for `/flow:auto` Step 4/6 + `/flow:finish`
- - flow-worktree-guard - leaked-edit detector; `--strict` BLOCKS at both `/flow:auto` call sites
- - tool-risk-drift - shared permission-risk taxonomy guard (hard gate via `make tool-risk-check`)
- - flow-start-resolve - deterministic `/flow` Step-1 resolver + `--verify` gate
- - flow-live-driver-guard - advisory concurrent-session guard
- - flow-worktree-claim - cross-session OWNERSHIP claim on a flow worktree (a real `git worktree lock`)
- - flow-wave-registry - role -> address registry for multi-session flow waves
- - flow-wave-mailbox - the wave DELIVERY lane - `send`/`read`/`watch`/`list`
- - flow-wave-lexicon - reserved vocabulary for wave STATE TRANSITIONS
- - flow-wave-plan - deterministic wave planner; the ONE dependency parser shared with `/project:next`
- - flow-wave-residuals - executable residual candidate ledger, close-time human promotion gate, and issue-economy metrics
- - project-init - deterministic scaffold planning/apply engine with validated semantic checkpoints
- - flow-finish-gate - the deterministic quality-gate invocation as ONE audited helper
- - hooks
- - drift-detect
- - mcp-drift - orphaned Docker MCP detection + provenance-protected teardown
- - codex-skill-sync - single-source -> per-harness SKILL.md generator
- - eli5-vendor - guard for the canonical->vendored eli5-core link
- - eli5-core-drift - thin shim onto `eli5-vendor.py --upstream`
- - retired-surface-prune - teardown for GENERATED file surfaces CPP retired but left behind in HOME
- - measurement-shape-scan - transcript detector for the #659 cwd-relative measurement trap
- - speckit-tasks-to-issues
- - playwright-desk - lease-desk ledger
- - check-ignored-additions - advisory guard for a file a blanket-ignore rule silently swallowed
- - check-test-binary-guards - gate for the shell-out-binary guard directive
- - skills-check - read-only topic-skill provenance, reference, and managed-install parity gate
- - project-next-vendor - offline per-file pin check, advisory upstream drift check, and reviewed refresh for the vendored project-next engine
- - check-negative-fixture-preconditions - gate for the negative-fixture precondition directive
- - install-drift - read-only host check: installed helpers vs checkout, plus retired marketplace state
- - commands-mirror-sync - drift guard + refresher for out-of-repo command-surface mirrors
- - cpp-commands-link - user-scope command-surface symlinker (the canonical Tier 1)
- - sandbox-phase1-trial - ADR 0002 Phase 1 trial harness (historical record; epic abandoned in #553)
- - `templates/` - Makefile, workflow, container templates
- - CPP's own `.claude-plugin/` + `plugins/` marketplace lane was retired by issue #662 / ADR 0005. The tiered `/cpp:init` + `/cpp:update` symlink command surface returns as canonical in #663; existing caches migrate with `/plugin uninstall <family>@cpp`.
- - `codex/skills/` - Codex SKILL.md skills, the second harness surface (issue #555, companion to codex-power-pack epic cooneycw/codex-power-pack#64): generated `<family>-<cmd>/` skill dirs emitted from the same `.claude/commands/<family>/` single source by `scripts/codex-skill-sync.py`; codex-power-pack vendors this source (pull model, issue #556 / codex-power-pack#75) rather than receiving a push, and CPP's own currency is guarded by the explicit `codex-skills-check` step in `.woodpecker.yml`. Regenerate with `make codex-skills` after any command or referenced-script edit; `make codex-init` installs to `~/.codex/skills/`. Hand-curated skill dirs (no GENERATED marker) are never touched.
- - `codex/cpp-memory.md` - Hand-curated Codex `/cpp-memory` prompt (#433) for the common-memory harness, installed to `~/.codex/prompts/cpp-memory.md` by `scripts/install-memory-harness.sh`. Relocated here when the deprecated generated `codex/prompts/` flat surface (#446) was retired at the #556 cutover (superseded by `codex/skills/`, #555).
- - `.woodpecker.yml` - Woodpecker CI pipeline (secret-scan, lint, test, typecheck, codex-skills-check, eli5-vendor-check, eli5-upstream-drift (advisory), tool-risk-drift, Dockerfile lint)
+ Component contracts and retired-surface history are routed through
+ [the command reference](docs/commands-reference.md) and
+ [the script reference](docs/scripts.md); do not duplicate them here.
## Environment Variables
- - `CLAUDE_PROJECT` - Default project for `/project:next` from `~/Projects`. Set in `~/.bashrc`.
- - `FLOW_WORKTREE_BASE` - Optional worktree base override (issue #584, ADR 0003). Worktrees default to a VISIBLE sibling in the repo's parent dir (`../<repo>-<branch>`, issue #627); set this (host config, e.g. `~/.bashrc`) to relocate them to `$FLOW_WORKTREE_BASE/<repo>-<branch>` instead. Either way the run rides the git lane (`GIT_LANE=1`). Never set in shipped config beyond the visible-sibling default (PR #527 norm).
+ - `CLAUDE_PROJECT` selects the default `/project:next` repository under the user's projects directory.
+ - `FLOW_WORKTREE_BASE` optionally relocates visible sibling flow worktrees; never set it in shipped config.
+ - `SECOND_OPINION_URL` overrides the external second-opinion server base URL.
+ - `CPP_MEMORIES_BACKEND` selects the documented common-memory backend.
- ## MCP Servers and Secrets
+ See [commands-reference.md](docs/commands-reference.md) for precedence and host
+ installation details.
- CPP ships **no container runtime** as of #469. The Docker MCP runtime (the `mcp-second-opinion` server, the `aws-secrets-agent` sidecar, all `docker-compose*.yml` files, every `make docker-*` target, and the compose-based deploy path) was retired when `mcp-second-opinion` moved to its own external repo (https://github.com/cooneycw/mcp-second-opinion). The remaining MCP servers are stdio/http and need no CPP-built image.
+ ## MCP Servers and Secrets
- - **Second opinion (`/second-opinion:*`, `/evaluate:*`):** the server runs from its own external repo (localhost, or a Tailscale host). CPP consumes it as a client - the repo ships a root `.mcp.json` registering `second-opinion` as a streamable-http server at `${SECOND_OPINION_URL:-http://127.0.0.1:8080}/mcp` (#633): default localhost 8080, overridable per host by exporting `SECOND_OPINION_URL` with the base url (no `/mcp`) - or register at user scope (`claude mcp add second-opinion --transport http --url <url> --scope user`). Start the external server first; see `/cpp:init`.
- - **Browser automation:** upstream `@playwright/mcp` registered via npx/stdio by `/cpp:init` (no container; the CPP `mcp-playwright-persistent` fork was retired in #423).
- - **Secrets:** CPP stores no application secrets on disk and runs no secrets sidecar. The remaining AWS Secrets Manager consumers fetch **directly** via the AWS SDK/CLI: `essent-ai` (Woodpecker CI keys `WOODPECKER_URL` / `WOODPECKER_API_TOKEN`, consumed by `/flow:auto`, `woodpecker/bootstrap-secrets.py`, and `scripts/setup-woodpecker-cli.sh`; also holds `CPP_MEMORIES_DSN`, the common-memory Postgres DSN used by `lib/cpp_memory` for fleet-wide federation - reference the store host by its Tailscale address). Second-opinion LLM keys (`codex_llm_apikeys`) now live with the external server, not CPP. See `/secrets:*` and `lib/creds/`.
- - **Deploy:** `make deploy` is an informative no-op - CPP ships no deployable services.
- - **Secret scanning:** `make secret-scan` runs gitleaks locally (native binary or Docker fallback). Config in `.gitleaks.toml` with allowlists for doc/test false positives.
- - **Bootstrap checks:** `make bootstrap-check` verifies admin-only prerequisites in `.claude/bootstrap.yaml` (now just `jq`, since the Docker runtime prerequisites were retired).
- - **Woodpecker CI** runs on push/PR: secret-scan (gitleaks), lint, test, typecheck, and Dockerfile lint. The image-build / CVE-scan / SBOM / compose-policy / runtime-smoke stages were retired with the container runtime in #469.
- - **Drift detection:** `make drift-check` compares host-installed artifacts against repo templates and flags **orphaned Docker MCP servers** - a leftover container, `mcp-<name>:*` image, or `claude`/`codex mcp` registration from a retired server (e.g. a lingering `mcp-second-opinion` or `aws-secrets-agent`) - against the curated `.claude/deprecated-mcps.yaml` list of record (via `scripts/mcp-drift.py`). Since CPP now ships no compose file, the current service set is empty by absence, so a listed server still present on the host is treated as an orphan. Detection is curated-list driven so a user's own custom MCP registration is never flagged (the valid external `second-opinion` registration is intentionally not listed); a **running** container that shares a deprecated name but belongs to an external compose project (or runs a non-CPP image) is also auto-protected by provenance and never torn down (issue #520), so the live external `second-opinion` / `aws-secrets-agent` containers survive `/cpp:update`. Teardown is per-server, user-confirmed, and keeps a newest-image restore point unless prune-all is chosen (run `/cpp:update`, or `python3 scripts/mcp-drift.py --teardown <name>`). See `docs/HOST_MANAGED_ARTIFACTS.md` for full inventory.
- - **Reproducible builds:** the remaining container image references (the `mcp-evaluate` Dockerfile and the tool images in `.woodpecker.yml`) are pinned by version tag plus `@sha256:` digest, never `:latest`. `renovate.json` rotates the pinned digests on a weekly schedule so pinning never freezes security updates.
+ CPP ships no container runtime. It consumes the external second-opinion server
+ through `.mcp.json` and upstream Playwright MCP through `/cpp:init`. Remaining
+ AWS Secrets Manager consumers fetch directly through the SDK or CLI; CPP stores
+ no application secrets in this repository. Use `/secrets:*` for credentials and
+ `make secret-scan` for the deterministic leak check. Runtime retirement,
+ bootstrap, drift, and reproducible-image details live in
+ [commands-reference.md](docs/commands-reference.md).
## Commands Reference
- Every command's own description is resident in each session's skill listing, so
- this section carries only what is NOT derivable from it: the workflow rules, the
- non-command CLI surface, and the decisions behind delegated or retired families.
- The per-command detail and incident history moved to
- `docs/commands-reference.md` (issue #711) - nothing was dropped, only relocated.
-
- ### Workflow rules
-
- **Visible worktrees on the git lane (issue #627; native `EnterWorktree` #440
- superseded for the default):** `/flow:start` and `/flow:auto` create worktrees
- OUTSIDE the repo - a visible sibling of the repo in its parent dir
- (`../<repo>-<branch>`), or under `FLOW_WORKTREE_BASE` when set (#584) - via
- `git worktree add`, entered with `cd`. The run rides the git lane end-to-end
- (`GIT_LANE=1` always) and cleanup uses `git worktree remove` /
- `scripts/worktree-remove.sh`; the native `EnterWorktree`/`ExitWorktree` fresh
- lane is retired. CPP layers its gate policy on top: the `issue-<N>-<slug>` branch
- name (enforced by `flow-start-resolve.sh --verify`), the `/flow:eli5` necessity
- gate, the quality gates, and merge/cleanup discipline. The `/flow:*` commands
- live under the permanent source of truth at `.claude/commands/flow/*`.
- `/flow:repair` (`scripts/flow-helpers-install.sh`) installs the helper family to
- `~/.claude/scripts/`, the stable path the #581 allowlist rules match.
-
- **Worktree path-resolution rule (issue #486):** resolve every `Write`/`Edit` path
- from the active worktree root - `git rev-parse --show-toplevel` - or use a plain
- relative path from the session cwd; **never hand-build an absolute worktree
- path**, which has been observed to land the edit in the MAIN repo working tree
- instead of the worktree. `/flow:auto` Steps 4/6 run
- `scripts/flow-worktree-guard.sh --strict` - **blocking since #576**: exit 3 is a
- STOP, so the trap stops the run instead of being narrated past. Freshness
- downgrades keep it from crying wolf (#536, #573, #576).
-
- **Concurrent flow sessions (issue #597):** CPP encourages parallel `/flow`
- sessions, so a run stakes a **claim** on its checkout
- (`scripts/flow-worktree-claim.sh`, a real `git worktree lock`) during the Step-1
- verify gate, and three guards read it: Step 1 refuses to start on an issue
- another LIVE session holds (`CLAIM=held` -> `CONFIRM_REQUIRED=1`),
- `worktree-remove.sh` refuses (exit 4) to delete a worktree claimed by a live
- sibling, and Step 4 re-runs the #503 live-driver guard immediately before the
- first edit. Step 9 skips `make deploy` when `.claude/deploy.log` already records
- a SUCCESSFUL deploy of the current HEAD sha. An owner that is gone reads as
- `stale` and is taken over automatically; `--steal` is the documented break-glass.
-
- **Standalone skill extractions (issue #443):** skills with standalone value are
- extracted to their own public plugin repos, and improvement issues for an
- extracted skill are filed in THAT repo, not here (the learnings->issue bridge,
- #463, routes there too). CPP stays a consumer: it vendors the extracted skill's
- canonical core between marker comments and layers its /flow wiring outside them.
- First extraction: the `/flow:eli5` necessity gate ->
- https://github.com/cooneycw/eli5-gate (core markers `eli5-core:begin`/`end` in
- `.claude/commands/flow/eli5.md`), guarded on both sides by `scripts/eli5-vendor.py`
- (offline manifest, hard gate) and `scripts/eli5-core-drift.sh` (live fetch,
- fail-open). Reconcile drift by editing the canonical repo first, then
- `make eli5-revendor`.
-
- ### Non-command CLI surface
-
- - `python -m lib.cicd validate` - Validate .claude/cicd.yml with fix suggestions (Pydantic v2)
- - `python -m lib.cicd validate --schema` - Generate JSON Schema for IDE autocompletion
- - `python -m lib.cicd run --plan <name>` - Execute CI/CD plan deterministically (finish, check, deploy)
- - Test steps resolve `PYTEST_WORKERS` from step env, host `PYTEST_WORKERS`, then host `CPP_TEST_WORKERS`, and log the effective cap and source (#640)
- - `python -m lib.cicd verify --baseline` - Capture pre-deploy health/smoke baseline
- - `python -m lib.cicd verify` - Verify post-deploy against baseline (exit 1 = ROLLBACK)
- - Invocation contract for every `lib.cicd` call in a command doc: `PYTHONPATH="$CPP_DIR:$PYTHONPATH" uv run --project "$CPP_DIR" python -m lib.cicd ...` - `PYTHONPATH` names the PARENT of `lib/` (or `-m lib.cicd` cannot resolve) and `uv` supplies the pinned 3.11+ interpreter plus pydantic. Bare `python3` with `PYTHONPATH` pointed inside `lib/` fails on both counts; it silently disabled deploy verification for months (#430 fixed Step 6, #595 fixed the Step 9 / `/flow:deploy` / `/cicd:verify` verify calls, pinned by `tests/test_cicd_verify_invocation.py`). The same broken shape still rides ~40 non-`verify` `lib.cicd` lines in the `cicd`/`cpp`/`codex`/`project` families - a known latent bug awaiting its own sweep, not a covered case
- - `python -m lib.cicd.bootstrap check` - Check admin-only bootstrap dependencies (config: `.claude/bootstrap.yaml`)
-
- ### Family decisions and delegations
-
- - `/project:next` - Decision policy comes VERBATIM from the always-present codex-power-pack engine vendored under `vendor/project_next/` (#636/#723), with an offline per-file manifest pin and runtime contract-version check. CPP's thin `scripts/project-next.py` adapter adds native GitHub relationship confidence, planning-only Wayfinder routes, and one shared spec-lifecycle annotation without re-ranking or correcting the engine result; fixture dogfood is unconditional and a missing or broken vendored engine FAILS rather than falling back or skipping
-
- `/project:init` delegates config scaffolding (CLAUDE.md, skills, hooks) to Claude
- Code's native `/init` interview rather than hand-rolling a fixed template, then
- runs `/claude-md:lint` to overlay CPP's CI/CD governance directives. CPP keeps the
- zero-to-GitHub-repo orchestration native `/init` does not provide: directory +
- framework scaffold, `git init` and repo create/push, Makefile/CI wiring
- (`lib/cicd`), CPP toolkit install, and `.specify/` structure (epic #417 Phase A,
- mirrors the `/security` #438 and hooks #439 defer-the-commodity-half moves).
-
- - `/spec:adopt` - **(supported)** Install the official GitHub spec-kit CLI and scaffold it into the project (`specify init --here --ai claude`); then author with the `/speckit-*` skills and ship with `/flow:auto`. Turn `tasks.md` into GitHub issues with `scripts/speckit-tasks-to-issues.sh` (gh-CLI, no github-mcp-server). Per-project, always latest upstream. The `specify` CLI installs on first `/spec:adopt` use, or up front via `/cpp:init` / `/cpp:update`.
-
- **Spec-driven dev = official spec-kit plugin + `/flow:auto`.** CPP's home-grown pipeline (`/spec:create`, `/spec:sync`, `/spec:status`, `/spec:init`, backed by `lib/spec_bridge`) was **retired** in favor of upstream spec-kit (epic #417 Phase A, decision on #418).
-
- **Scaffolding backend:** GitHub Issues (driven by the `flow-auto` skillset) is the only supported scaffolding/issue backend. Wiki.js and Plane are out of scope and are not part of CPP.
-
- **Skills:** the `/skills:*` wrapper was retired (issue #437) - it is fully absorbed
- by the native Claude Code ecosystem. Use these instead:
- - `npx skills find|add|list|update <...>` - Discover/install/manage skills from [skills.sh](https://skills.sh/)
- - `/plugin` - Browse and install from the plugin marketplace
- - Auto-loading `.claude/skills` + `/reload-skills` - Project-local skills, no wrapper needed
+ Command descriptions are already present in each session's skill listing. Use
+ the smallest applicable workflow and load its command document for execution:
- **Installation:** the tiered `/cpp:init` install is CANONICAL (#663, on the
- marketplace retirement #662 / ADR 0005) - Tier 1 symlinks the command surface so
- the executed command text follows `git pull` atomically, and `/cpp:update` is the
- ONE update verb with no package cache to reconcile. Hosts still carrying the
- retired cached families should uninstall each with `/plugin uninstall <family>@cpp`.
- `/cpp:init` / `/cpp:update` also install and refresh the non-command infra; see
- `README.md`, `docs/HOST_MANAGED_ARTIFACTS.md` and `docs/commands-reference.md`.
+ - `/flow:*` - issue worktrees, planning gate, implementation, finish, merge, wave orchestration, and cleanup.
+ - `/project:init` - destination-first project creation and optional Wayfinder/spec handoff.
+ - `/project:next` - planning-aware next-action routing through the always-present vendored engine.
+ - `/spec:adopt` and `/speckit-*` - official spec-kit authoring; implementation routes to `/flow:auto`.
+ - `/cicd:*` - Makefile, container, pipeline, health, smoke, deploy, and infrastructure workflows.
+ - `/security:*` - deterministic scans; use native `/security-review` for semantic vulnerability review.
+ - `/documentation:*` - repository documentation, C4 diagrams, and presentations.
+ - `/claude-md:lint` - generated-project governance and compact knowledge-lifecycle guidance.
+ - `/cpp:*` - installation, updates, status, diagnostics, and on-demand references.
+ - `/self-improvement:*` - friction retrospectives and portable memory.
+ - `/second-opinion:*` and `/evaluate:*` - external multi-model evaluation.
+ - `/browser:session` and `/qa:test` - browser sessions and web QA.
+ - `/secrets:*` - masked credential management and validation.
- **Friction retro:** `/self-improvement:retro` runs the grill-me cycle over the
- always-on capture buffer (`scripts/friction-log.sh` -> `.claude/friction.jsonl`,
- woven into `/flow:auto` + `/flow:merge`); the local ledger is `.claude/learnings.md`
- and portable knowledge delegates to `/self-improvement:memory` (#433).
+ Detailed arguments, recovery states, delegation decisions, and incident history
+ live in [commands-reference.md](docs/commands-reference.md). The permanent
+ workflow sources live under `.claude/commands/`.
## Makefile Integration
- Flow commands use Makefile targets as the canonical build interface:
+ Makefile targets are the canonical build interface. Required local gates are:
- - `/flow:check` and `/flow:finish` use `python -m lib.cicd run --plan finish` (deterministic runner) with fallback to `make lint` / `make test` / `make typecheck` - the plan and the fallback carry the same targets the CI templates run (#617)
- - `/flow:deploy` uses `python -m lib.cicd run --plan deploy` (deterministic runner) with fallback to `make deploy` + post-deploy health/smoke
- - `/flow:auto` runs `make update_docs` after implement, verifies CI after merge, then `make deploy`
- - `/flow:doctor` reports which standard targets are available
- - Deploy metadata in `.claude/deploy.yaml` (optional, created manually when needed)
- - `mode: external` - deploy runs out of band (host timer / CI on origin/main); `/flow:auto` Step 9 and `/flow:deploy` skip the inline `make deploy` rather than staging a throwaway per-worktree compose stack (#535)
- - `compose_project_name: <name>` - pins `COMPOSE_PROJECT_NAME` so docker compose never derives it from a worktree/tmp directory basename and collides with fixed prod `container_name` values or the published port; defaults to the canonical primary-checkout name when unset. Read by `make deploy` (#535) AND emitted run-wide in the `flow-start-resolve` contract so every compose step in a flow worktree inherits it (#626)
- - Deploy history logged to `.claude/deploy.log`
- - Starter template at `templates/Makefile.example`
+ - `make lint` - lint source.
+ - `make test` - run the test suite.
+ - `make typecheck` - run static type checks.
+ - `make verify` - full pre-deploy verification, including persistent-context checks.
+ - `make skills-check` - validate topic-skill provenance and surfaces.
+ - `make project-next-check` - verify the vendored project-next hash contract.
+ - `make codex-skills` - regenerate mirrors after command-document changes.
+ Flow runner, deployment, baseline, and fallback behavior remains in
+ [commands-reference.md](docs/commands-reference.md); per-helper behavior remains
+ in [scripts.md](docs/scripts.md).
+
## Security
- Security is split into two complementary halves. **Semantic** code-vulnerability
- review - SQL injection, XSS, broken authorization, insecure credential handling -
- is handled by Claude Code's native **`/security-review`** command and its GitHub
- Action; CPP defers to it and does not duplicate it. CPP's `/security:*` commands
- and `lib/security` own the **deterministic** complement: secret scanning
- (gitleaks + native patterns), git-history secret scanning, dependency CVE audits
- (`pip-audit`, `npm audit`), and the blocking gate. See `lib/security/README.md`.
+ - Never print, commit, or expose secrets. The PostToolUse hook masks tool output but does not authorize reading credentials.
+ - Destructive commands remain subject to native safeguards and explicit user authorization.
+ - The PermissionRequest hook is an observe-only, fail-open permission-prompt census; it never decides permissions.
+ - Never allowlist file-dumpers or bare tool namespaces. Raw shipping actions remain excluded from the user-level flow allowlist.
+ - `/flow:finish` and `/flow:deploy` run the deterministic security scan; CRITICAL findings block and HIGH findings warn.
+ - Branch-protection posture is declared in `.claude/branch-protection.json`; use its Makefile check/apply targets.
+ - The pending-retro reminder is opt-in and advisory.
- - **Destructive commands** (force push to main, `rm -rf /`, disk formatting, etc.) are blocked by Claude Code's native destructive-git auto-blocking and OS sandbox - the custom PreToolUse dangerous-command hook was retired (issue #439) as redundant.
- - **PostToolUse hook** masks secrets in Bash/Read output (connection strings, API keys, env vars). Retained because native tooling blocks credential *reads* but does not *mask* secrets that surface in output.
- - **PermissionRequest hook** (`scripts/hook-permission-census.sh`) is an observe-only, fail-open permission-prompt census: it fires when a permission dialog is shown, derives a narrowest allow-rule candidate plus a risk tier (`READONLY-*`/`WRITE-LOCAL` -> allow candidate; `DESTRUCTIVE`/`CODE-EXEC`/net -> recorded but no candidate), and appends a `permission-prompt` record to the project's `.claude/friction.jsonl` for `/self-improvement:retro`. It captures the one friction class the model cannot observe (issue #482). Inherently-interactive tools (`AskUserQuestion`, `Skill`, `EnterPlanMode`, `ExitPlanMode`) are user-interaction dialogs, not permission friction, so they are skipped entirely rather than recorded as noise (issue #542). Registered user-level in `~/.claude/settings.json` (user-confirmed) by `/cpp:init` and `/cpp:update` Step 7.6 - never emits a permission decision.
- - **Never-allowlist policy for the census candidate** (issue #598): a SAFE tier is necessary but not sufficient for a `fix`. Two classes are withheld even when read-only, because `/self-improvement:retro` Step 4 tells the retro to TRUST the `fix` field - so a forbidden rule emitted there is a rule that gets installed. (1) **File-dumpers** (`cat`, `head`, `tail`, `less`, `more`, `tac`, `nl`, `strings`, `xxd`, `od`, `hexdump`, `base64`) - an allow rule defeats the PostToolUse masking hook on a secret file, and `head -20 .env` leaks exactly as `cat .env` does; `Bash(head:*)`/`Bash(tail:*)` were dropped from `templates/claude-settings-permissions.json` in the same change so the shipped allowlist and the census agree (the union-merge only ADDS, so a box that merged an earlier template must drop them by hand). (2) **Bare tool namespaces** - `Bash(git:*)` silently permits `git push` and `git reset --hard`, and was emitted whenever a flag preceded the verb (`git -C /path status`); the candidate is now withheld rather than widened, which is also the honest answer since allow rules match a command PREFIX and no narrow rule can match a flag-bearing invocation. The risk TIER is still recorded truthfully in both cases - only the rule is withheld. `NO_ALLOW_CANDIDATE` / `SUBCOMMAND_REQUIRED` in the hook, pinned by `tests/test_hook_permission_census.py`.
- - **SessionStart pending-retro reminder** (`scripts/hook-pending-retro.sh`) is an OPT-IN, fail-open, read-only reminder: when registered, it prints ONE advisory line at session open counting pending `.claude/friction.jsonl` signals (actionable vs the bulk permission-prompt census, separately) plus uncodified `Status: proposed` learnings, and points at `/self-improvement:retro`. It only SURFACES - never codifies, never blocks - and is silent when nothing is pending. Default OFF: deliberately NOT shipped in `.claude/hooks.json` (which `/cpp:init` copies into projects), so it never turns itself on; registered user-level in `~/.claude/settings.json` only on explicit opt-in (default N) by `/cpp:init` and `/cpp:update` Step 7.7 (issue #530).
- - **Second advisory: helper drift + retired marketplace state** (#622/#662) - the same hook runs `scripts/install-drift.sh --quiet`: it warns when installed `~/.claude/scripts/*.sh` helpers differ from the checkout and also names retired CPP cache families pending `/plugin uninstall <family>@cpp`. Session open matters for the helper half because learning that installed code is stale before acting avoids re-diagnosing an already-fixed bug and filing a duplicate, the flow:auto #65 failure that motivated #622. It is independent of the retro reminder, fail-open, and suppressible with `CPP_HOOK_SKIP_INSTALL_DRIFT=1`; stale helpers are real `drift`, while migration-only state is non-failing `skipped`.
- - **Hooks configured in** `.claude/hooks.json` (SessionStart staleness + PostToolUse project-level) and `~/.claude/settings.json` (PermissionRequest census + opt-in SessionStart pending-retro reminder, user-level); the masking hook ships through `.claude/hooks.json` and the host install path
- - `/flow:finish` and `/flow:deploy` run the deterministic security scan (`lib/security`) as a quality gate
- - CRITICAL findings block gates; HIGH findings produce warnings
- - Configure gating in `.claude/security.yml` (optional, created by `/security:scan` when needed)
- - For **semantic** code review (SQLi/XSS/authz/insecure handling), run native `/security-review` - not a CPP command
- - **Branch-protection posture** (issue #577, ADR 0004): `main` requires the Woodpecker PR pipeline as a required status check (`ci/woodpecker/pr/woodpecker`, `strict: true`); required reviews stay at 0 and `enforce_admins` stays off. The posture is declared in `.claude/branch-protection.json` and applied/checked with `scripts/branch-protection.sh` (`make branch-protection-check` / `-apply`). Reviews are 0 by choice: on a solo repo a review requirement forces `--admin` on every merge, and `--admin` bypasses the CI check at the same time - the stricter-sounding posture would enforce strictly less. `enforce_admins` is off so a check that never reports (skipped pipeline, renamed context) leaves the owner one documented break-glass (`gh-pr-merge.sh --admin`) instead of a permanently unmergeable PR; the automation is what is prevented from reaching for it.
- - **User-level flow allowlist** (`templates/claude-settings-permissions.json`) auto-approves the read-only git/gh plumbing that `/flow:*` runs, plus the audited flow helper-script family at its stable `~/.claude/scripts/` path (flow-start-resolve, flow-stale-check, flow-worktree-guard, flow-live-driver-guard, gh-pr-merge, worktree-remove - issue #581, invoked BARE so the prefix rules match); raw shipping actions (`git push`, `gh pr create`) and `cat` are deliberately excluded so gates and secret-read prompts stay intact. Merged via `/cpp:init` or `/cpp:update` Step 7.6; scripts re-linked by `/cpp:update` Step 5b; checked by `/flow:doctor`. Rationale: `templates/claude-settings-permissions.md`
+ The full hook, allowlist, branch-protection, scanning, and retired-runtime
+ contracts are maintained in [commands-reference.md](docs/commands-reference.md),
+ [scripts.md](docs/scripts.md), and `lib/security/README.md`.
## On-Demand Documentation
- Load topic-specific skills instead of the full guide (88-92% token savings):
+ Load topic-specific skills from `docs/skills/`, or use
+ `/cpp:load-best-practices` and `/cpp:load-mcp-docs` for the full references.
+ Do not copy detailed manuals or command state machines into this file.
- - Context efficiency, session management, MCP optimization, skills patterns
- - Hooks/automation, spec-driven dev, issue-driven dev, CLAUDE.md config
- - Code quality, Python packaging, CI/CD verification, documentation/diagrams
+ ## Knowledge Lifecycle
- **Commands:** `/cpp:load-best-practices` (full 25K guide), `/cpp:load-mcp-docs` (MCP server docs)
+ Specifications are temporary coordination artifacts. Before a completed spec
+ is removed, durable facts must graduate to code and behavioral tests, schemas,
+ nearby intent comments, ADRs, domain glossaries, runbooks, maintained docs, or
+ linked issues. Run the verified mapping process in the
+ [canonical knowledge-lifecycle reference](docs/agents/knowledge-lifecycle.md);
+ independently valuable contracts are retained with a named owner.
## Secrets Management
- Tiered: dotenv-global (`~/.config/claude-power-pack/secrets/`) -> env-file -> AWS Secrets Manager. Features: project identity (git-based), bundle API, secret injection (`creds run`), FastAPI web UI, audit logging, IAM isolation, output masking. Configure in `.claude/secrets.yml` (optional, created manually when needed).
+ Use the tiered credential interface documented by `/secrets:*` and
+ `lib/creds/`. Secret values never belong in responses, tracked files, command
+ arguments, or logs.
## Version
Current version: 7.4.0