Reference
Hooks.
The 24 hooks are the structural enforcement layer of the baseline. Each runs at a tool boundary or lifecycle event, outside Claude's control.
§ I
Why hooks
The baseline ships 24 hooks wired into Claude Code's PreToolUse, PostToolUse, UserPromptSubmit, and lifecycle events. They form the part of the constitution that cannot be overridden by memory or prompt text alone.
A hook runs outside Claude's tool boundary. When a hook blocks a write, Claude never executes the tool call. This matters because Claude cannot forge a consent marker (written by consent_gate_grant on the UserPromptSubmit path) by invoking a tool: the block happens before Claude gets the chance. Memory is helpful, prompts are helpful, hooks are load-bearing.
§ II
Hooks by tool boundary
Most hooks run on Write, Edit, MultiEdit, or Bash before the tool fires. A small set runs on the surrounding session lifecycle.
| Event | Hooks |
|---|---|
| PreToolUse · Bash | destructive_cmd_guard, git_commit_guard, gitignore_leak_guard, process_lifecycle_guard |
| PreToolUse · Edit / Write / MultiEdit | setup_guard, env_guard, git_commit_guard, spec_approval_guard, swarm_approval_guard, epic_approval_guard, verify_pass_guard, track_guard, artifact_template_guard, plantuml_syntax_guard, spec_diagram_presence_guard, spec_design_calls_guard, swarm_boundary_guard, tdd_order_guard |
| PostToolUse · Edit / Write / MultiEdit | lint_runner, test_runner |
| UserPromptSubmit | consent_gate_grant |
| SessionStart | memory_session_start |
| Stop | memory_stop, harness_continuation |
| PreCompact | memory_pre_compact |
§ III
What each hook enforces
The constitution maps each hook to the article it backs. Modifying or disabling a hook requires user approval and a corresponding amendment in seed.md.
| Hook | Article | Behavior |
|---|---|---|
| setup_guard | III | Advisory reminder when project is unconfigured (rate-limited 10 minutes). Does not block. |
| destructive_cmd_guard | VII | Hard-block catastrophic commands; ask risky. |
| git_commit_guard | IV (gate C), VII | Require fresh consent for git commit and block forbidden flags. Enforce the branch model declared in git.workflow_model so commits never land on the wrong branch: under direct-to-main a feature-branch commit is blocked with a fix-it hint, and swarm worktrees are exempt. Gate writes to the commit-consent marker. A commit that closes backlog work has to record that closure in the same commit, so a finished item's done-stamp can't be left uncommitted. |
| env_guard | VII | Block writes to .env* files (allows .env.example). |
| spec_approval_guard | IV (gate A) | Validate fresh marker before allowing approval-token writes; block self-approval inside spec markdown. |
| swarm_approval_guard | IV (gate B) | Validate fresh marker before allowing swarm-approval writes. |
| epic_approval_guard | IV (§18.9) | Allow an epic-state approved: true transition only when the matching persistent spec_approvals/<slug>.approval token exists; non-transition writes pass through. |
| verify_pass_guard | V, VI | Block writing PASS to verify artifacts when truth source says FAIL. |
| track_guard | IV | Enforce per-track DAG ordering for workflow artifacts. |
| artifact_template_guard | IV | Block artifact writes missing required sections. |
| plantuml_syntax_guard | IV (phase 4) | Advisory by default (no JVM): notes that PlantUML fences in docs/specs/*.md are unvalidated at write time. Runs the strict java -checkonly parse only when project.json sets plantuml.strict_syntax_check to true; otherwise validate on demand with /spec-lint. |
| spec_diagram_presence_guard | IV (phase 4) | Block specs missing required diagram kinds. |
| spec_design_calls_guard | X.2 | Block specs whose write_set intersects tdd.ui_globs from omitting a populated ## Design calls section. |
| swarm_boundary_guard | IV (phase 6c) | Enforce write-set discipline in shared isolation mode. |
| tdd_order_guard | VI.4 | Require a test before a new source file lands. |
| gitignore_leak_guard | VII | Blocks a git commit that stages a path meant to stay ignored, such as a secret (.env) or generated state (.claude/state/). It checks staged paths against the baseline must-ignore set offline with git check-ignore and names whatever leaks, and warns when a should-be-ignored path exists but is not yet covered. Secrets and build noise cannot slip into history by accident, and it never touches the network. |
| process_lifecycle_guard | IX | Advisory. Surfaces relevant memory entries (e.g. landmines.md → lsof-port-kill-takes-firefox-with-it, conventions.md → dev-server-ownership) verbatim before any kill, lsof, or serve Bash. Never blocks. |
| lint_runner | VI | Run lint.cmd on code changes (guide mode until configured). |
| test_runner | VI | Run test.cmd on code changes (guide mode until configured). |
| memory_session_start | III, IX | Inject memory index and resume snapshot at session start. |
| memory_stop | IX | Auto-extract memory candidates each turn-end. |
| memory_pre_compact | IX | Capture resume snapshot before context compaction. |
| harness_continuation | V | Stop-hook safety net that re-fires Skill(harness) only when the loop exited mid-flow with a fresh marker; silent otherwise. Never writes consent. |
| consent_gate_grant | IV (gates A / B / C), VII (push gate) | Detect the four slash commands (/approve-spec, /approve-swarm, /grant-commit, /grant-push) in user input and write the gate-specific consent marker. Runs outside Claude's tool boundary. |
§ IV
Why consent gates are unforgeable
Each gate is a slash command the user types. Three are workflow-phase gates (/approve-spec, /approve-swarm, /grant-commit). The fourth, /grant-push, is a runtime gate for protected-branch pushes (Article VII). UserPromptSubmit runs outside Claude's tool boundary, so Claude has no way to write the marker.
The consent_gate_grant hook runs on UserPromptSubmit, before Claude sees the prompt. It writes a short-lived marker that the matching PreToolUse guard validates. Markers expire after two minutes by default (consent.gate_marker_ttl_seconds) and are deleted on use, so the same gate cannot be replayed.
The PreToolUse guard then allows Claude's slash-command-body write of the approval token only if the marker is present, fresh, and slug-matched. The marker is single-use and deleted on the allowed write.
§ V
Modifying or disabling a hook
Disabling, modifying, or bypassing a hook requires explicit user approval and a corresponding amendment in seed.md (the genesis prompt). The order of precedence is seed.md > CLAUDE.md > implementation.
Hooks are the implementation layer: they cannot be loosened by a memory entry or a CLAUDE.md amendment alone.
For a one-shot exception inside a workflow, use the exceptions array in .claude/state/workflow.json, written only by /triage. Exceptions are surfaced in every phase log so the audit trail records which phases ran and which were waived.