diff --git a/.gitignore b/.gitignore index c96fcfe..2fbc15d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ .code-warden-report.json +.code-warden/ .claude/ code-warden-v*.zip code-warden-*.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 6660a39..c93bedd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,27 +5,56 @@ Versions follow [Semantic Versioning](https://semver.org/). --- -## Unreleased - -- Added Codex config handling to `--hooks=codex`: the installer now enables - `[features].hooks = true` in `~/.codex/config.toml` and removes deprecated - `[features].codex_hooks` entries when present. -- Updated Codex hook health checks so registered hooks verify both hook script - paths and feature-flag enablement without making optional hooks mandatory for - base installs. -- Added tests for Codex config migration and hook feature enablement. -- Improved first-run onboarding in CLI help, install output, and quickstart docs - so users see `init -> doctor -> report -> optional hooks`. -- Added Codex hook repair guidance so doctor/verify can point partial hook setup - back to `code-warden hooks codex`. -- Added `code-warden verify ` as a public CLI wrapper for strict - per-runtime install and hook health checks. -- Clarified target-specific onboarding examples for CLI and direct installer - users. -- Added `code-warden smoke-npx` as a public CLI wrapper for clean-temp npm - package smoke testing. -- Hardened release preflight so tags fail before publish when the npm version - already exists, and documented trusted-publisher setup. +## v4.0.0 - 2026-06-10 + +**Enforcement layers: scope lock, command risk gate, audit ledger, corroborated receipts, baseline ratchet, git backstop, and lifecycle hooks.** + +Major version: the report's `session.scopeGate` field becomes an object when a +scope lock exists (was always a string), everything under `.code-warden/` is +agent-write-protected unconditionally, and hook registration expands to new +events. **Upgrade note: re-run `code-warden hooks claude` (and `hooks codex`) +after updating — old registrations keep working but miss NotebookEdit/command +coverage and all new events and gates.** + +### Scanner and hook hardening + +- Added six secret patterns: Anthropic (`sk-ant-`), Google (`AIza`), GitLab (`glpat-`), npm (`npm_`), Hugging Face (`hf_`), and JWT. +- All secret matches are now reported per file (`scanForAllSecrets`), not first-match-only. +- Hooks discover the governed project's own `codewarden.json` (walking up from the working directory, stopping at the `.git` boundary), so `lint.exclude_paths`, `secrets.allowlist`, and thresholds behave identically in hooks and CI. +- Claude write hooks now cover `Write|Edit|NotebookEdit`; new `warden-command-hook.js` scans `Bash|PowerShell` command strings for credentials. +- `pre_flight_trigger_lines` is wired as an "ask" gate on single changes over 150 lines; `human_checkpoint_files` and `exempt_from_blast_radius` are honestly documented as prompt-layer-only. + +### Git backstop and baseline ratchet + +- Added `code-warden hooks git`: a marker-managed per-repo pre-commit hook that runs staged-content lint and secrets scans (`git show :path`) with exclude/allowlist parity. `git commit --no-verify` bypasses it — documented, not hidden. `code-warden verify git` checks the installation. +- Added `report --write-baseline[=path]` and `report --baseline[=path]` ratchet mode: only NEW or WORSENED violations fail; legacy findings are counted separately ("N new / M legacy"). Secrets are fingerprinted by sha256 of the trimmed matched line — no raw secrets in baselines. SARIF carries fresh findings only. A missing baseline file is a hard error. +- Added a `baseline` input to the GitHub Action and brownfield adoption guidance to the CI template. + +### Scope Lock and Command Risk Gate + +- Added `code-warden scope set|add|remove|clear|status` managing `/.code-warden/scope.json` (`goal`, `enforce`, `filesIn`, `expansions[]` audit trail); `scope set --no-enforce` records scope without blocking. +- Write hooks (Claude `Write`/`Edit`/`NotebookEdit`, Codex `apply_patch`) deny out-of-scope edits while a scope is locked; the deny message tells the agent to ask the user to run `code-warden scope add `. Strictly opt-in — no scope file means no enforcement. +- Added a Command Risk Gate to both command hooks. Default blocked (deny): `rm_rf_root`, `rd_root`, `remove_item_root`, `git_reset_hard`, `git_push_force` (`--force-with-lease` exempt), `git_clean_force`, `git_history_rewrite`, `curl_pipe_shell`, `ps_web_pipe_iex`, `chmod_777_root`. Default high (ask on Claude; allow on Codex, which has no ask equivalent): `package_install` (bare `npm install`/`ci` allowed), `npm_publish`, `git_push`, `recursive_delete`, `remove_item_recurse`, `git_discard_changes`. Configurable via `risk_policy.command_rules` (id-based override/disable; user rules merge with defaults). +- Governance report: `session.scopeGate` reports a `{status: 'locked', goal, filesIn, enforce}` object when a scope exists (previously always the string `'session_only'`) — JSON consumers should handle both shapes. + +### Audit ledger, lifecycle hooks, and corroborated receipts + +- Added a PostToolUse audit ledger: `.code-warden/audit.jsonl` with a sha256 hash chain (GENESIS-anchored). Command targets are secret-redacted and truncated to 300 chars. Auto-on while a scope lock exists, else `audit.enabled` config (explicit `false` wins). Claude sessions only. +- `.code-warden/` governance artifacts are unconditionally write-protected from agents (both runtimes), even with no scope lock. +- Added a SessionStart hook that injects architecture context (shared `lib/context-discovery.js`, also used by `get-context.js`) and scope status. +- Added an opt-in Stop hook (`session.verify_on_stop`, default `false`) that blocks completion on FRESH lint/secret violations; loop-guarded via `stop_hook_active` and baseline-aware. +- Added `receipt --from-audit[=path] --out=`: prefills draft receipts from `scope.json`, architecture context, git branch/commit (`lib/git-info.js`), and ledger evidence with chain verification. A `complete` receipt with `audit.chainValid: false` fails validation. Receipt schema is additive — v1 receipts still validate. +- Generalized hook management across PreToolUse/PostToolUse/SessionStart/Stop (`lib/hook-events.js`); behavioral-test timeout raised to 60s. + +### Previously unreleased changes (since v3.4.0) + +- `--hooks=codex` now enables `[features].hooks = true` in `~/.codex/config.toml` and removes deprecated `[features].codex_hooks` entries; health checks verify both hook script paths and feature-flag enablement without making optional hooks mandatory. +- Added `code-warden verify ` and `code-warden smoke-npx` as public CLI wrappers; improved first-run onboarding (`init -> doctor -> report -> optional hooks`) and Codex hook repair guidance. +- Hardened release preflight so tags fail before publish when the npm version already exists; documented trusted-publisher setup. + +### New config keys + +- `lint.exclude_paths` and `secrets.allowlist` (now hook-honored), `risk_policy.command_rules`, `audit.enabled`, `session.verify_on_stop`. --- diff --git a/README.md b/README.md index c4b7ea1..41e587d 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Code-Warden Quality Gate - Version 3.4.0 + Version 4.0.0 MIT License - Claude Code PreToolUse Hooks + Claude Code Lifecycle Hooks AI Governance Enforced npm @@ -29,16 +29,17 @@ npx code-warden verify codex # or your target runtime npx code-warden report ``` -Optional hard hooks where supported: +Optional hard enforcement: ```bash -npx code-warden hooks claude -npx code-warden hooks codex +npx code-warden hooks claude # per-user lifecycle hooks +npx code-warden hooks codex # per-user, partial surfaces +npx code-warden hooks git # per-repo pre-commit backstop (run from the repo) ``` Use `doctor` after install or hook setup. It verifies installed skill manifests, -hook script paths, and runtime-specific hook config when hooks are registered. -When it finds partial hook setup, it prints the repair command to rerun. +hook script paths, and runtime-specific hook config, and prints the repair +command when it finds partial setup. ## Who This Is For @@ -50,42 +51,27 @@ If you run long Claude Code, Codex, or Cursor sessions — multi-file refactors, At its core, Code-Warden is a governance contract: - The agent states architecture context before acting. -- The agent declares scope and patch order before edits. +- The agent declares scope and patch order before edits — and the scope lock can mechanically enforce it. - The repo verifies file size, secrets, tests, install health, risk policy, runtime hooks, and receipt artifacts. -- The workflow keeps receipts, JSON, Markdown, SARIF, and release evidence outside chat memory. - -**Built for developers who:** -- Run long, high-autonomy AI coding sessions -- Let agents touch multiple files or whole modules -- Work across several projects at once -- Need CI-friendly verification without relying on chat memory -- Need an audit trail for what the agent was allowed to change -- Want hard blocking where the runtime supports it - -**Probably overkill if:** -- You only use AI for short snippets -- You manually review every one-file edit before it lands -- You do not need CI checks -- You are comfortable relying entirely on prompt instructions +- The workflow keeps receipts, audit ledgers, JSON, Markdown, SARIF, and release evidence outside chat memory. ## Prevents / Allows **Prevents** - Code before scope is declared -- Multi-file edits without a patch plan -- Files touched outside approved scope +- Writes outside a locked scope (denied at the hook layer) +- Destructive shell commands — force push, hard reset, recursive root deletes, pipe-to-shell (denied) - Oversized monolithic files -- Hardcoded API keys and credentials +- Hardcoded API keys and credentials — in files, commands, and staged commits - Completion claims without verification evidence +- Agent tampering with governance artifacts (`.code-warden/` is always write-protected) - Stale or broken agent installs -- Claude Code writes that violate hook policy **Allows** -- Normal development work -- Fast solo-founder iteration -- Existing agent workflows +- Normal development work and fast solo-founder iteration +- Existing agent workflows — every enforcement layer is opt-in +- Brownfield adoption — baseline ratchet gates only new violations - CI enforcement without chat memory -- Optional hard blocking only where supported ## Four Layers @@ -93,19 +79,35 @@ At its core, Code-Warden is a governance contract: Code-Warden Four Layers

+| Layer | What it does | Bypass honesty | +|-------|--------------|----------------| +| **1. Prompt governance** | SKILL.md gates: Scope Gate, Plan Gate, blast radius, drift signals | Instructions only — the agent is told, nothing blocks | +| **2. Runtime hooks** | Claude lifecycle hooks (full) and Codex PreToolUse hooks (partial) deny bad writes, out-of-scope edits, and risky commands before execution | Only where the runtime exposes hook surfaces | +| **3. Git backstop** | Per-repo pre-commit scans staged content for lint/secrets — any agent, any editor, any human | `git commit --no-verify` skips it | +| **4. CI** | `governance-report.js` / GitHub Action: deterministic gate plus JSON/Markdown/SARIF evidence | Catches everything that reaches a PR | + +Each layer narrows what the previous one can miss. None of them is a sandbox +or a security boundary against a malicious user — they govern the agent inside +the workflow you already use. + ## Compatibility | Runtime | Install | Skill Rules | Local Tools | CI | Hard Hooks | |---|---:|---:|---:|---:|---:| -| Claude Code | ✅ | ✅ | ✅ | ✅ | ✅ PreToolUse | +| Claude Code | ✅ | ✅ | ✅ | ✅ | ✅ Full lifecycle | | OpenAI Codex | ✅ | ✅ | ✅ | ✅ | ⚡ Partial | -| Cursor | ✅ | ✅ | ✅ | ✅ | — | -| Warp | ✅ | ✅ | ✅ | ✅ | — | -| Windsurf | ✅ flat rules | ✅ adapted | ✅ | ✅ | — | -| Generic Agents | ✅ | ✅ | ✅ | ✅ | — | +| Cursor | ✅ | ✅ | ✅ | ✅ | git backstop | +| Warp | ✅ | ✅ | ✅ | ✅ | git backstop | +| Windsurf | ✅ flat rules | ✅ adapted | ✅ | ✅ | git backstop | +| Generic Agents | ✅ | ✅ | ✅ | ✅ | git backstop | | GitHub Actions | — | — | ✅ | ✅ | — | -Claude Code gets full hard enforcement (blocks `Write`/`Edit` before the file system is touched). Codex gets partial enforcement: `apply_patch` and `Bash` calls are intercepted for secrets and estimated file size — the tool surfaces Codex exposes at `PreToolUse`. CI enforcement closes the remaining gap for both runtimes. +Claude Code gets full enforcement: PreToolUse gates on `Write`/`Edit`/`NotebookEdit` +and `Bash`/`PowerShell`, a PostToolUse audit ledger, SessionStart context +injection, and opt-in Stop verification. Codex gets partial enforcement: +`apply_patch` and `Bash` are the only hookable surfaces — no ask-tier +confirmations, no PostToolUse ledger. The git backstop and CI close the +remaining gap for every runtime. ## Why Not Just Prompt Better? @@ -115,76 +117,50 @@ You should prompt well. Code-Warden does not replace that. | Rule | Prompt-only | Code-Warden | |---|---|---| -| Keep files modular | Agent remembers | `warden-lint` checks files and directories | -| No hardcoded secrets | Agent remembers | `verify-secrets` scans locally and in CI | -| Stay inside scope | Agent declares scope | Scope Gate creates an explicit file contract | -| Verify before done | Agent claims it checked | `npm run ci` produces a deterministic result | -| Block unsafe writes | Not possible everywhere | Claude `PreToolUse` hooks deny `Write`/`Edit` before execution | - -Code-Warden is portable at the governance, installer, local-tooling, and CI layers. Hard pre-write blocking is currently Claude Code-specific because Claude exposes `PreToolUse` hooks. Other runtimes get all other layers. - -## What Code-Warden Is / Is Not - -**Code-Warden is:** -- A governance layer for AI coding agents -- A local verification toolkit -- A cross-runtime installer and health checker -- A CI-friendly policy gate -- An optional hard-enforcement layer for Claude Code (full) and Codex (partial) - -**Code-Warden is not:** -- A replacement for your coding agent -- A full development methodology like Superpowers -- A sandbox or security boundary against malicious users -- A guarantee that unsupported runtimes can block tool calls before execution - -> Code-Warden governs the agent inside the workflow you already use. +| Keep files modular | Agent remembers | `warden-lint` checks files; hooks block oversized writes | +| No hardcoded secrets | Agent remembers | Scanned in writes, commands, staged commits, and CI | +| Stay inside scope | Agent declares scope | Scope lock denies out-of-scope writes at the hook layer | +| Don't run destructive commands | Agent is careful | Command Risk Gate denies blocked-tier, asks on high-tier | +| Verify before done | Agent claims it checked | `npm run ci`, Stop verification, and receipts corroborated by a hash-chained audit ledger | ## Adoption Path -You do not need to install everything at once. Each layer adds value independently. +Each layer adds value independently. Start where the pain is. -1. **CI only** — add `warden-lint` and `verify-secrets` to GitHub Actions. No skill install required. +1. **CI only** — add the GitHub Action or `governance-report.js`. Brownfield? `report --write-baseline` first, gate on `--baseline`. 2. **Skill governance** — install Code-Warden into your AI runtime. Scope Gates, Plan Gates, and drift signals activate immediately. -3. **Hard enforcement** — enable hooks for pre-tool-use blocking. Claude Code: full (`Write`/`Edit`). Codex: partial (`apply_patch`/`Bash`). Requires step 2 first. - -Start where you have the most immediate pain. +3. **Git backstop** — `code-warden hooks git` for a per-repo pre-commit scan. Works for every runtime and human commits too. +4. **Runtime hooks** — `hooks claude` / `hooks codex` for pre-execution blocking. Requires step 2 first. +5. **Scope lock + audit** — `code-warden scope set` per governed session; close with a corroborated receipt. ## Install ```bash -npx code-warden init +npx code-warden init # or: npm install -g code-warden && code-warden init ``` -Or install globally: - -```bash -npm install -g code-warden -code-warden init -``` - -The installer scans for AI runtimes and deploys to all of them in one step. -Supports Claude Code, Cursor, Warp, OpenAI Codex, Windsurf, and generic agent runtimes. +The installer scans for AI runtimes and deploys to all of them in one step: +Claude Code, Cursor, Warp, OpenAI Codex, Windsurf, and generic agent runtimes. ### CLI commands ```bash -code-warden init # install to detected AI runtimes -code-warden report # generate governance report -code-warden report --format=md # Markdown output (pipe to PR summary) -code-warden report --format=sarif # SARIF output for Code Scanning -code-warden report --format=sarif --out=code-warden.sarif -code-warden receipt --template --out=code-warden-receipt.json -code-warden receipt --validate=code-warden-receipt.json -code-warden references README.md code-warden/tools/ +code-warden init # install to detected AI runtimes +code-warden report # governance report (--format=md|sarif, --out=) +code-warden report --write-baseline # record current violations as the ratchet floor +code-warden report --baseline # fail only NEW or WORSENED violations +code-warden scope set --goal="..." # lock session scope (--no-enforce to record only) +code-warden scope add|remove|clear|status # manage the scope lock +code-warden receipt --template --out= # draft governance receipt +code-warden receipt --from-audit --out= # receipt prefilled from the audit ledger +code-warden receipt --validate= +code-warden references # recommend governance references +code-warden doctor # verify source + install health +code-warden verify # strict health check (claude, codex, git, ...) +code-warden list # show detected runtimes +code-warden hooks claude|codex|git # install enforcement hooks +code-warden uninstall-hooks claude|codex|git code-warden smoke-npx --package=code-warden@latest -code-warden doctor # verify source + install health -code-warden verify codex # strict health check for one runtime -code-warden list # show detected runtimes -code-warden hooks claude # install Claude Code PreToolUse hooks -code-warden hooks codex # install Codex PreToolUse hooks (partial) -code-warden uninstall-hooks claude -code-warden uninstall-hooks codex ``` ## Invoke @@ -195,203 +171,172 @@ code-warden uninstall-hooks codex Or: `"load code-warden"`, `"new session"`, `"begin coding"`, `"governance check"`. -### Session Start Sequence -

Code-Warden Session Start Sequence

-## Optional Hard Enforcement (Hooks) +## Hard Enforcement (Hooks)

Code-Warden Hook Enforcement Flow

-### Claude Code — Full enforcement +### Claude Code — full lifecycle -```bash -node install.js --hooks=claude # install (requires Claude target installed first) -node install.js --uninstall-hooks=claude # remove -``` +`code-warden hooks claude` registers (per-user, `~/.claude/settings.json`): -Blocks `Write` and `Edit` before the file system is touched — if the resulting file would exceed the line limit or contain a hardcoded credential. +| Event | Hook | Policy | +|-------|------|--------| +| PreToolUse `Write\|Edit\|NotebookEdit` | lint, secrets, scope | Deny oversized files, hardcoded credentials, out-of-scope writes; ask past `pre_flight_trigger_lines` | +| PreToolUse `Bash\|PowerShell` | command | Deny credentials in commands; Command Risk Gate (deny blocked-tier, ask high-tier) | +| PostToolUse | audit | Append to the hash-chained audit ledger (never blocks) | +| SessionStart | session | Inject architecture context + scope status | +| Stop | stop | Opt-in (`session.verify_on_stop`): block completion on fresh lint/secret violations | -### OpenAI Codex — Partial enforcement +### OpenAI Codex — partial -```bash -node install.js --hooks=codex # install (requires Codex target installed first) -node install.js --uninstall-hooks=codex # remove -``` +`code-warden hooks codex` registers `apply_patch` (secrets, estimated size, +scope lock) and `Bash` (secrets, Command Risk Gate — blocked-tier denies only; +Codex has no ask equivalent, so high-tier allows silently). The installer +enables `[features].hooks = true` in `~/.codex/config.toml` and removes the +deprecated `codex_hooks` key. No PostToolUse surface exists, so there is no +Codex audit ledger. + +### Git backstop — any runtime + +`code-warden hooks git` installs a marker-managed pre-commit hook in the repo +at cwd (per-repo, unlike the per-user hooks above). It scans **staged content** +(`git show :path`) for file-length and secret violations with the same +exclude/allowlist config as CI. `git commit --no-verify` bypasses it — that is +git's escape hatch and Code-Warden documents it rather than pretending +otherwise. Check it with `code-warden verify git`. -The Codex hook installer writes `~/.codex/hooks.json` and enables the current -Codex feature flag in `~/.codex/config.toml`: +### Scope Lock -```toml -[features] -hooks = true +```bash +code-warden scope set --goal="Fix auth bug" src/ lib/utils.js +code-warden scope status +code-warden scope add src/middleware.js # user-approved expansion (audited) +code-warden scope clear ``` -If an older config contains `[features].codex_hooks`, the installer removes that -deprecated key while enabling `hooks`. Current Codex docs list `hooks` as the -stable lifecycle-hook feature flag. +Writes `/.code-warden/scope.json`. While locked, agent writes outside +the declared paths are denied and the agent is told to ask you to run +`code-warden scope add `. Expansions are recorded in `expansions[]`. +Strictly opt-in — no scope file, no enforcement. `.code-warden/` itself is +always write-protected from agents, lock or not. If the agent runs `scope add` +via the shell, the command is visible in your session and the expansion is +recorded — auditable, not impossible. -| Hook | Trigger | Policy | -|------|---------|--------| -| `warden-apply-patch-hook.js` | `apply_patch` | Blocks if added lines contain a credential or estimated result exceeds line limit | -| `warden-bash-hook.js` | `Bash` | Blocks if command contains a hardcoded credential | +### Command Risk Gate -Codex exposes `apply_patch` and `Bash` at `PreToolUse` — not `Write`/`Edit`. These are the available surfaces. CI enforcement closes the remaining gap. +Conservative defaults, two enforced tiers: -Doctor and `--verify-target=` validate hook script paths and Codex hook -feature enablement when hooks are registered, with repair guidance for partial -hook setup. +- **blocked (deny)**: `rm_rf_root`, `rd_root`, `remove_item_root`, `git_reset_hard`, `git_push_force` (`--force-with-lease` exempt), `git_clean_force`, `git_history_rewrite`, `curl_pipe_shell`, `ps_web_pipe_iex`, `chmod_777_root` +- **high (ask on Claude, allow on Codex)**: `package_install` (bare `npm install`/`ci` allowed), `npm_publish`, `git_push`, `recursive_delete`, `remove_item_recurse`, `git_discard_changes` -## Governance Evidence +Override per rule id via `risk_policy.command_rules` in `codewarden.json` — +replace a default, disable it with `"tier": "off"`, or add your own patterns. -Code-Warden produces a machine-readable governance report — verifiable evidence that checks ran and passed: +## Governance Evidence ```bash -node tools/governance-report.js . # writes .code-warden-report.json -node tools/governance-report.js . --format=md # Markdown table for PR summaries -node tools/governance-report.js . --format=sarif # SARIF for source-located findings -node tools/governance-report.js . --format=sarif --out=code-warden.sarif +code-warden report # .code-warden-report.json + summary +code-warden report --format=md # Markdown for $GITHUB_STEP_SUMMARY +code-warden report --format=sarif --out=code-warden.sarif ``` -The report covers file length, hardcoded credentials, behavioral tests, source integrity, and runtime hook status in a single pass. In CI, it pipes directly into `$GITHUB_STEP_SUMMARY` so every PR shows what was checked. +One pass covers file length, credentials, behavioral tests, source integrity, +risk policy, runtime hook status, and session governance (the report's +`scopeGate` is a `{status, goal, filesIn, enforce}` object when a scope lock +exists; the string `"session_only"` otherwise — handle both shapes). SARIF is +intentionally narrower: only source-located findings (`CW001`/`CW002`). -SARIF output is intentionally narrower than the JSON report: it includes only -findings with source locations (`CW001/max-file-length` and -`CW002/hardcoded-credential`). Behavioral tests, install health, runtime hook -state, and session governance remain in JSON/Markdown because they are -workflow evidence, not source-code findings. +### Audit ledger and corroborated receipts -Governance receipts cover the part reports cannot know by themselves: the -confirmed Scope Gate and Plan Gate. Generate a draft receipt before or during a -session, fill in the gate and final command evidence, then validate it: +While a scope lock exists (or `audit.enabled` is `true`), Claude sessions +append every governed tool call to `.code-warden/audit.jsonl` — sha256 +hash-chained from GENESIS, so any edit breaks every later line. Commands are +logged secret-redacted and truncated. Gitignore the ledger; receipts are the +durable artifact: ```bash -code-warden receipt --template --out=code-warden-receipt.json +code-warden receipt --from-audit --out=code-warden-receipt.json code-warden receipt --validate=code-warden-receipt.json ``` -Receipts deliberately start as drafts with `canProveCompliance: false`; they -become valid only when the required evidence is filled in. - -Reports also include risk policy evidence from `codewarden.json`. The default -policy marks read-only work as `low`, file edits as `medium`, -dependency/network/release operations as `high`, and destructive or -secret-bearing actions as `blocked`. +`--from-audit` prefills the draft from the scope lock, architecture context, +git branch/commit, and ledger evidence with chain verification. Receipts still +start as drafts — a human completes them — and a `complete` receipt over a +broken chain fails validation. -External evidence providers are recorded with scope and trust limits. Code -Scanning SARIF, secret scanning, dependency scans, artifact attestations, npm -provenance, and CI run links can support a governance claim, but they do not -replace Scope Gate, Plan Gate, or receipts. +### Baseline ratchet (brownfield adoption) -MCP servers are governed as tool-bearing integrations, not harmless context -sources. Before enabling one, Code-Warden expects an approval record covering -server source, version, transport, toolsets, credential scope, data egress, and -rollback. See [`code-warden/references/mcp-governance.md`](code-warden/references/mcp-governance.md). +```bash +npx code-warden report --write-baseline # freeze current debt as the floor +git add .code-warden-baseline.json && git commit -m "chore: code-warden baseline" +npx code-warden report --baseline # N new / M legacy; fails on new only +``` -Use `code-warden references ` to recommend the focused governance -references for touched paths. This is advisory loading, not hidden enforcement. +Baselined files fail again the moment they grow. Secrets are fingerprinted by +content hash — no raw secrets in the baseline. A missing baseline file is a +hard error, never a silent skip. ## CI Integration -Use Code-Warden as a GitHub Action: - ```yaml - name: Code-Warden Governance Gate - uses: Kodaxadev/Code-Warden@v3 + uses: Kodaxadev/Code-Warden@v4 with: path: . + baseline: .code-warden-baseline.json # optional ratchet mode + sarif: 'true' # optional Code Scanning upload ``` -The action writes `.code-warden-report.json`, appends a Markdown summary to the -workflow run, and uploads the report as an artifact by default. +SARIF upload needs `security-events: write` permission and goes through +`github/codeql-action/upload-sarif@v4`. The action writes +`.code-warden-report.json`, appends a Markdown summary, uploads the report +artifact, and fails the job when the gate fails. -Enable GitHub Code Scanning annotations with SARIF: +Prefer a pinned download? Fetch +`https://github.com/Kodaxadev/Code-Warden/releases/download/v4.0.0/code-warden-v4.0.0.zip` +and run `node /tools/governance-report.js .` — full template with both +options: [`code-warden/templates/ci/github-actions.yml`](code-warden/templates/ci/github-actions.yml) -```yaml -permissions: - contents: read - security-events: write - -steps: - - uses: actions/checkout@v6 - - name: Code-Warden Governance Gate - uses: Kodaxadev/Code-Warden@v3 - with: - path: . - sarif: 'true' -``` +## Upgrading to v4 -The action uploads SARIF through `github/codeql-action/upload-sarif@v4` and -still fails the job when the governance report fails. +**Re-run `code-warden hooks claude` (and `hooks codex`) after updating.** Old +registrations keep working but lack NotebookEdit/command coverage and all new +events and gates. Breaking changes for consumers: -Or download a pinned release directly: +- Report `session.scopeGate` is an object when a scope lock exists (was always a string). +- `.code-warden/` is agent-write-protected unconditionally. +- New config keys: `lint.exclude_paths`, `secrets.allowlist` (both hook-honored), `risk_policy.command_rules`, `audit.enabled`, `session.verify_on_stop`. -```yaml -- name: Install Code-Warden - run: | - curl -fsSL -o cw.zip \ - https://github.com/Kodaxadev/Code-Warden/releases/download/v3.4.0/code-warden-v3.4.0.zip - unzip -q cw.zip -d .code-warden-ci - -- name: Governance report - run: node .code-warden-ci/tools/governance-report.js . - -- name: Publish governance summary - if: always() - run: node .code-warden-ci/tools/governance-report.js . --format=md >> $GITHUB_STEP_SUMMARY - -- name: Upload governance artifact - if: always() - uses: actions/upload-artifact@v7 - with: - name: code-warden-report - path: .code-warden-report.json - retention-days: 90 -``` - -The pinned `v3.4.0` release-download path includes SARIF and `--out` support. - -Full template: [`code-warden/templates/ci/github-actions.yml`](code-warden/templates/ci/github-actions.yml) +See [`RELEASE_NOTES_v4.0.0.md`](RELEASE_NOTES_v4.0.0.md). ## Release Trust -Code-Warden releases are tag-driven. The release workflow verifies the package -version matches the pushed tag, runs the governance gate, performs an npm -publish dry run, publishes to npm through trusted publishing, creates a GitHub -release, and uploads the versioned zip asset. - -Trusted publishing uses GitHub Actions OIDC instead of a long-lived npm token -and lets npm attach provenance to public package publishes from public -repositories. +Releases are tag-driven: the workflow verifies the package version matches the +tag, runs the governance gate, dry-runs the publish, publishes to npm through +trusted publishing (GitHub Actions OIDC, npm provenance — no long-lived token), +creates the GitHub release, and uploads the versioned zip. ## File Structure | File | Purpose | |------|---------| | `SKILL.md` | Session gates, quick rules, drift signals, reference index | -| `CONFIGURE.md` | Tunable thresholds and team-size profiles | +| `CONFIGURE.md` | Tunable thresholds, scope lock, audit ledger, command rules | | `DECISIONS.md` | Architecture decision log | -| `references/planning-gates.md` | Scope Gate and Plan Gate contracts | -| `references/architecture.md` | Blueprint Rule, Re-injection, State Update | -| `references/safety.md` | Blast Radius, Patch-First, Zero-Trust, Dependency Freeze | -| `references/cognition.md` | Think Before Coding, Don't Guess Syntax, Human Checkpoint | -| `references/cleanup.md` | Tech Debt format, Test Contract, Decision Log | -| `references/anti-drift.md` | Anchor Check, Session Scoping, Drift Trigger | -| `references/operations.md` | Verification evidence, git hygiene, dependency control | -| `references/evidence-providers.md` | External scanners, provenance, attestations, CI evidence, trust limits | -| `references/research-and-fit.md` | Live research gate, stack fit, product-shape guardrails | -| `references/mcp-governance.md` | MCP approval, toolset scope, credentials, consent, audit evidence | -| `tools/lib/reference-selector.js` | Path-based governance reference recommendations | -| `tools/lib/risk-policy.js` | Risk tier defaults, config merge, and validation | -| `tools/receipt.js` | Governance receipt template and validation CLI | +| `references/` | Planning gates, architecture, safety, cognition, cleanup, anti-drift, operations, evidence providers, research-and-fit, MCP governance | +| `tools/` | Scanners, governance report, receipt/scope CLIs, hooks, shared libs | +| `tools/hooks/claude/`, `tools/hooks/codex/` | Runtime hook scripts | +| `tools/lib/` | Shared policy modules (config, baseline, command-risk, scope-store, audit-ledger, ...) | ## Version -v3.4.0 — See [`CHANGELOG.md`](CHANGELOG.md) for full changelog. +v4.0.0 — See [`CHANGELOG.md`](CHANGELOG.md) for full changelog. ## Author diff --git a/RELEASE_NOTES_v4.0.0.md b/RELEASE_NOTES_v4.0.0.md new file mode 100644 index 0000000..99440b0 --- /dev/null +++ b/RELEASE_NOTES_v4.0.0.md @@ -0,0 +1,129 @@ +# code-warden v4.0.0 + +Enforcement layers — scope lock, command risk gate, audit ledger, +corroborated receipts, baseline ratchet, and a git pre-commit backstop. + +## Upgrade note (read first) + +**Existing hook users MUST re-run `code-warden hooks claude` (and +`code-warden hooks codex`).** Old registrations keep working, but they lack +NotebookEdit and Bash/PowerShell command coverage and none of the new +events or gates (scope lock, command risk gate, audit ledger, SessionStart +context, Stop verification). + +Why this is a major version: + +- The governance report's `session.scopeGate` field becomes an object + (`{status, goal, filesIn, enforce}`) when a scope lock exists; it was + always a string before. JSON consumers should handle both shapes. +- Everything under `.code-warden/` is write-protected from agents + unconditionally — even with no scope lock. +- Hook registration expands from PreToolUse-only to + PreToolUse/PostToolUse/SessionStart/Stop. + +## What's new + +### Scope Lock (`code-warden scope`) + +`code-warden scope set --goal="..." ` writes +`/.code-warden/scope.json`. While it exists with `enforce: true`, +write hooks (Claude `Write`/`Edit`/`NotebookEdit`, Codex `apply_patch`) deny +edits outside the declared paths and tell the agent to ask the user to run +`code-warden scope add `. Expansions are appended to `expansions[]` +as an audit trail. Strictly opt-in: no scope file, no enforcement. +`--no-enforce` records scope without blocking. + +### Command Risk Gate + +Both command hooks classify shell commands against conservative defaults: + +- **blocked (denied)**: `rm_rf_root`, `rd_root`, `remove_item_root`, + `git_reset_hard`, `git_push_force` (`--force-with-lease` exempt), + `git_clean_force`, `git_history_rewrite`, `curl_pipe_shell`, + `ps_web_pipe_iex`, `chmod_777_root` +- **high (ask on Claude; allow on Codex — no ask equivalent there)**: + `package_install` (bare `npm install`/`ci` allowed), `npm_publish`, + `git_push`, `recursive_delete`, `remove_item_recurse`, + `git_discard_changes` + +Tune via `risk_policy.command_rules`: reuse a default `id` to replace it, +set `"tier": "off"` to disable it, or add new rules with your own patterns. + +### Audit ledger + corroborated receipts + +A PostToolUse hook appends one line per governed tool call to +`.code-warden/audit.jsonl`, sha256 hash-chained from GENESIS — editing, +reordering, or deleting any line breaks every later hash. Commands are +logged secret-redacted and truncated to 300 chars. Auto-on while a scope +lock exists; otherwise controlled by `audit.enabled` (explicit `false` +wins). Claude sessions only — Codex has no PostToolUse surface. + +`code-warden receipt --from-audit --out=` prefills a draft receipt +from the scope lock, architecture context, git branch/commit, and ledger +evidence with chain verification. A `complete` receipt with a broken chain +(`audit.chainValid: false`) fails validation. Schema is additive — v1 +receipts still validate. + +### Baseline ratchet for brownfield repos + +```bash +npx code-warden report --write-baseline # record current debt as the floor +git add .code-warden-baseline.json +npx code-warden report --baseline # fail only NEW or WORSENED violations +``` + +Legacy findings are counted separately ("N new / M legacy"). Secrets are +fingerprinted by sha256 of the trimmed matched line — baselines never store +raw secrets. SARIF carries fresh findings only. A missing baseline file is +a hard error. The GitHub Action gained a `baseline` input. + +### Git pre-commit backstop + +`code-warden hooks git` installs a marker-managed pre-commit hook (per-repo, +run from the repo — unlike the per-user claude/codex hooks) that scans +staged content (`git show :path`) for lint and secrets with full +exclude/allowlist parity. `git commit --no-verify` bypasses it — that is +documented honestly, not hidden. Verify with `code-warden verify git`. + +### Lifecycle hooks + +- **SessionStart** injects architecture context and scope status, so + sessions start governed instead of discovering rules mid-task. +- **Stop** (opt-in via `session.verify_on_stop`, default `false`) blocks + session completion while FRESH lint/secret violations exist. Loop-guarded + and baseline-aware — legacy debt never traps a session. + +### Scanner and hook hardening + +- Six new secret patterns: Anthropic (`sk-ant-`), Google (`AIza`), GitLab + (`glpat-`), npm (`npm_`), Hugging Face (`hf_`), JWT. All matches per file + are reported, not just the first. +- Hooks read the governed project's own `codewarden.json` (walking up from + cwd, stopping at the `.git` boundary), so `lint.exclude_paths`, + `secrets.allowlist`, and thresholds match CI behavior exactly. +- `pre_flight_trigger_lines` now asks for confirmation on single changes + over 150 lines. + +### New config keys + +`lint.exclude_paths`, `secrets.allowlist` (both now hook-honored), +`risk_policy.command_rules`, `audit.enabled`, `session.verify_on_stop`. + +## Honest limits + +- The agent can run `code-warden scope add` itself via the shell — but the + command is visible in the session and recorded in `expansions[]`. The + lock makes scope creep auditable, not impossible. +- Codex has no "ask" permission decision and no PostToolUse hook: high-tier + commands allow silently there, and the audit ledger is Claude-only. CI + and the git backstop close part of that gap. +- `git commit --no-verify` skips the pre-commit backstop by design. + +## Verification + +```bash +node tools/tests/run-all-tests.js +node tools/warden-lint.js tools +node tools/governance-report.js . +npx code-warden --version # 4.0.0 +``` diff --git a/action.yml b/action.yml index d7a05db..9c283e1 100644 --- a/action.yml +++ b/action.yml @@ -34,6 +34,14 @@ inputs: description: Path to a codewarden.json config file in the repo. Overrides built-in defaults. required: false default: '' + baseline: + description: >- + Path to a committed code-warden baseline file (write one locally with + `npx code-warden report --write-baseline`). When set and the file + exists, only NEW or WORSENED violations fail the gate; baselined legacy + findings are reported but excluded from SARIF and the result. + required: false + default: '' outputs: report-path: @@ -69,7 +77,11 @@ runs: if [ -n "${{ inputs.config }}" ] && [ -f "${{ inputs.config }}" ]; then CONFIG_FLAG="--config=${{ inputs.config }}" fi - node "${{ github.action_path }}/code-warden/tools/governance-report.js" "${{ inputs.path }}" $CONFIG_FLAG + BASELINE_FLAG="" + if [ -n "${{ inputs.baseline }}" ] && [ -f "${{ inputs.baseline }}" ]; then + BASELINE_FLAG="--baseline=${{ inputs.baseline }}" + fi + node "${{ github.action_path }}/code-warden/tools/governance-report.js" "${{ inputs.path }}" $CONFIG_FLAG $BASELINE_FLAG - name: Publish governance summary if: ${{ always() && inputs.summary == 'true' }} @@ -79,7 +91,11 @@ runs: if [ -n "${{ inputs.config }}" ] && [ -f "${{ inputs.config }}" ]; then CONFIG_FLAG="--config=${{ inputs.config }}" fi - node "${{ github.action_path }}/code-warden/tools/governance-report.js" "${{ inputs.path }}" --format=md $CONFIG_FLAG >> "$GITHUB_STEP_SUMMARY" || true + BASELINE_FLAG="" + if [ -n "${{ inputs.baseline }}" ] && [ -f "${{ inputs.baseline }}" ]; then + BASELINE_FLAG="--baseline=${{ inputs.baseline }}" + fi + node "${{ github.action_path }}/code-warden/tools/governance-report.js" "${{ inputs.path }}" --format=md $CONFIG_FLAG $BASELINE_FLAG >> "$GITHUB_STEP_SUMMARY" || true - name: Generate SARIF report if: ${{ always() && inputs.sarif == 'true' }} @@ -89,7 +105,11 @@ runs: if [ -n "${{ inputs.config }}" ] && [ -f "${{ inputs.config }}" ]; then CONFIG_FLAG="--config=${{ inputs.config }}" fi - node "${{ github.action_path }}/code-warden/tools/governance-report.js" "${{ inputs.path }}" --format=sarif $CONFIG_FLAG > "${{ steps.sarif-path.outputs.path }}" || true + BASELINE_FLAG="" + if [ -n "${{ inputs.baseline }}" ] && [ -f "${{ inputs.baseline }}" ]; then + BASELINE_FLAG="--baseline=${{ inputs.baseline }}" + fi + node "${{ github.action_path }}/code-warden/tools/governance-report.js" "${{ inputs.path }}" --format=sarif $CONFIG_FLAG $BASELINE_FLAG > "${{ steps.sarif-path.outputs.path }}" || true test -s "${{ steps.sarif-path.outputs.path }}" - name: Upload SARIF report diff --git a/code-warden/CONFIGURE.md b/code-warden/CONFIGURE.md index fea81ef..27045b0 100644 --- a/code-warden/CONFIGURE.md +++ b/code-warden/CONFIGURE.md @@ -28,14 +28,20 @@ Located at the root of the skill folder. Default configuration: } ``` -| Setting | Default | Rationale | -|---------|---------|-----------| -| `max_file_length` | 400 | Keeps files reviewable in a single pass. The `warden-lint.js` script enforces this. | -| `pre_flight_trigger_lines` | 150 | Forces a JSON manifest before large outputs. | -| `human_checkpoint_files` | 2 | Requires human `[AWAITING CONFIRMATION]` before modifying this many files simultaneously. | -| `exempt_from_blast_radius` | (list) | Skips strict rewriting rollback plans on these file directories. | -| `lint.exclude_paths` | `[]` | Path prefixes excluded from file-length checks. Use for docs, generated files, or vendored code (e.g. `["Documents/", "generated/"]`). | -| `secrets.allowlist` | `[]` | Path prefixes excluded from hardcoded-credential scanning. Use for files with known-safe localhost dev URLs or test fixtures (e.g. `["scripts/indexer.config.toml"]`). | +| Setting | Default | Enforced by | Rationale | +|---------|---------|-------------|-----------| +| `max_file_length` | 400 | hook + CI | Keeps files reviewable in a single pass. Enforced by `warden-lint.js`, the governance report, and the runtime hooks. | +| `pre_flight_trigger_lines` | 150 | hook | The Claude lint hook asks for confirmation (`permissionDecision: "ask"`) when a single Write/Edit change exceeds this many lines without breaching `max_file_length`. | +| `human_checkpoint_files` | 2 | prompt | Requires human `[AWAITING CONFIRMATION]` before modifying this many files simultaneously. Protocol rule only — no runtime hook or CI check enforces it. | +| `exempt_from_blast_radius` | (list) | prompt | Skips strict rewriting rollback plans on these file directories. Protocol rule only — no runtime hook or CI check enforces it. | +| `lint.exclude_paths` | `[]` | hook + CI | Path prefixes excluded from file-length checks. Use for docs, generated files, or vendored code (e.g. `["Documents/", "generated/"]`). | +| `secrets.allowlist` | `[]` | hook + CI | Path prefixes excluded from hardcoded-credential scanning. Use for files with known-safe localhost dev URLs or test fixtures (e.g. `["scripts/indexer.config.toml"]`). | +| `risk_policy.command_rules` | `[]` | hook | Per-rule overrides for the Command Risk Gate. Entries `{ "id", "pattern", "tier", "message" }` merge with the built-in defaults: reusing a default `id` replaces that rule, and `"tier": "off"` (or `"allow"`) disables it. `"blocked"` denies the shell command; `"high"` asks for confirmation (Claude) or allows silently (Codex has no ask equivalent). | +| `audit.enabled` | not set | hook | Audit ledger (Claude PostToolUse). Unset: the ledger is on only while a scope lock exists. `true`: always on for the project. `false`: always off, even with a scope lock. | +| `session.verify_on_stop` | `false` | hook | Opt-in Stop verification (Claude). When `true`, the Stop hook re-scans the project for fresh file-length/secret violations before the session may finish. Off by default - the hook is registered but inert. | +| `.code-warden/scope.json` (managed via `code-warden scope`) | not set | hook | Opt-in Scope Lock. When present with `"enforce": true`, write hooks (Claude Write/Edit/NotebookEdit, Codex apply_patch) deny edits outside the declared `filesIn` paths. No file means no enforcement. Everything under `.code-warden/` is write-protected from agents unconditionally - scope lock or not. | + +"Enforced by" legend: **hook** = runtime lifecycle hooks (PreToolUse gates, PostToolUse audit, SessionStart context, Stop verification), **CI** = `warden-lint.js` / `verify-secrets.js` / `governance-report.js`, **prompt** = governance protocol text only (the agent is instructed to comply, but nothing blocks it at runtime). --- @@ -45,3 +51,117 @@ Located at the root of the skill folder. Default configuration: 2. **Modify** the specific rule or threshold inside the JSON structural fields. 3. The executable tools (`tools/warden-lint.js`, etc.) read these limits dynamically so no Markdown files need to be edited to enforce limits. 4. **Log the change** in `DECISIONS.md` so your team knows why the default was overridden. + +## Scope Lock + +The Scope Lock mechanically enforces the Scope Gate's files-in contract. +Declare the goal and the paths the session may touch: + +``` +code-warden scope set --goal="Fix auth bug" src/ lib/utils.js +``` + +This writes `/.code-warden/scope.json`. While it exists, any agent +write outside the declared paths is denied at the hook layer. The agent sees: + +``` +[CodeWarden] Scope lock: docs/notes.md is outside the declared scope +(goal: Fix auth bug). Ask the user to approve expansion via: +code-warden scope add docs/notes.md +``` + +Expansions are appended to `expansions[]` in the scope file as an audit trail. +The scope file itself is self-protected: hooks deny agent edits to anything +under `.code-warden/`, even when `enforce` is `false`. + +Honest limits: if the agent runs `code-warden scope add` itself via the shell, +that command is visible in your session and the expansion is recorded in +`expansions[]` - the lock makes scope creep auditable, not impossible. +Analogously, `git commit --no-verify` bypasses the git pre-commit backstop. + +## Command Risk Gate Defaults + +The command hooks (Claude `Bash`/`PowerShell`, Codex `Bash`) classify commands +against these built-in rules. `blocked` denies; `high` asks for confirmation +on Claude and allows silently on Codex (no ask equivalent there). + +| Rule id | Tier | Matches | +|---------|------|---------| +| `rm_rf_root` | blocked | `rm -rf` of `/`, `~`, `.`, `..`, `*`, `.git`, or a drive root | +| `rd_root` | blocked | `rd`/`rmdir /s` of a drive root | +| `remove_item_root` | blocked | `Remove-Item -Recurse -Force` of a critical root path | +| `git_reset_hard` | blocked | `git reset --hard` | +| `git_push_force` | blocked | `git push --force`/`-f` (`--force-with-lease` exempt) | +| `git_clean_force` | blocked | `git clean -f` | +| `git_history_rewrite` | blocked | `git filter-branch` / `filter-repo` | +| `curl_pipe_shell` | blocked | `curl`/`wget` piped into a shell | +| `ps_web_pipe_iex` | blocked | `iwr`/`irm` piped into `Invoke-Expression` | +| `chmod_777_root` | blocked | `chmod -R 777 /` | +| `package_install` | high | Dependency add/remove/update (bare `npm install`/`ci` allowed) | +| `npm_publish` | high | `npm`/`pnpm`/`yarn`/`bun publish` | +| `git_push` | high | Any `git push` | +| `recursive_delete` | high | Any recursive delete (`rm -r`, `rd /s`) | +| `remove_item_recurse` | high | Any `Remove-Item -Recurse -Force` | +| `git_discard_changes` | high | `git checkout --` / `git restore` (working-tree discard) | + +Override in `risk_policy.command_rules`: reuse a default `id` to replace its +pattern/tier/message, set `"tier": "off"` (or `"allow"`) to disable it, or add +new rules with your own `pattern`. Invalid user patterns are skipped with a +warning — a broken regex never widens or traps the gate. + +## Baseline Ratchet (brownfield repos) + +``` +npx code-warden report --write-baseline +git add .code-warden-baseline.json +npx code-warden report --baseline +``` + +With `--baseline`, only NEW or WORSENED violations fail; legacy findings are +counted separately. A baselined file fails again when it grows past its +recorded line count. Secrets are fingerprinted by sha256 of the trimmed +matched line - the baseline never stores raw secret text. A missing baseline +file is a hard error. The git pre-commit backstop and the Stop hook are also +baseline-aware. + +## Audit Ledger + +Claude sessions append one line per governed tool call (Write/Edit/ +NotebookEdit/Bash/PowerShell) to `/.code-warden/audit.jsonl` via a +PostToolUse hook. Each entry is chained to the previous one: +`hash = sha256(prev + canonical entry JSON)`, with the first entry chained to +`GENESIS` - editing, reordering, or deleting any line breaks every later +hash, and `code-warden receipt --from-audit` reports the first broken line. + +Enablement: on automatically while a scope lock exists; force it with +`"audit": { "enabled": true }` or disable it entirely with +`"audit": { "enabled": false }` (explicit `false` wins over a scope lock). + +Shell commands are logged secret-redacted (`[REDACTED: