Seventeen lifecycle automations the engine can't forget to run

Hooks

Hooks are the part of the engine that survives forgetting. A rule can be ignored and a skill can go uninvoked, but a hook is executed by the harness — not by Claude — so it fires whether or not anyone remembers it.

19 hooks across 7 events, configured in .claude/settings.json. setup.sh merges that file into ~/.claude/settings.json — the hooks key is engine territory and is replaced wholesale — so a hook wired anywhere else is wiped at the next setup. Wire new hooks here, never by hand in user settings: that is how the report and block hooks below sat unregistered until 2026-09-28.

Active hooks

EventMatcherWhat it does
SessionStart*Inject engine context — sync drift, bridge handoffs (session-engine-context.sh, kun only)
SessionStart*Nag if the machine's maintain heartbeat is stale or RED
SessionStart*Warn if product flow recordings or media library are stale, and when render caches pass 2 GB
SessionStart*Devices brief — disk, swap, open sessions, the kun-rc server (devices.sh --brief)
SessionStart*List the open report-an-issue queue across the databayt repos (session-start-reports.sh)
UserPromptSubmit—Block protocol: inject a named block's records and obligations (block-context.sh)
ConfigChange*Warn when vocabulary.json or the plugins drift from their sources (async)
PreToolUseBashBlock destructive commands — the Unforgivable list (block-destructive-bash.sh)
PreToolUseBashScrape guard — account-risk readers and the session vault (scrape-guard.sh)
PreToolUseBashFunnel guard — outreach sends stay behind their gate (funnel-guard.sh)
PreToolUseBashMedia guard — no video, image/audio over 2 MiB or Thmanyah font gets committed to any repo; media goes to the CDN (media-guard.sh)
PostToolUseWrite|EditPrettier the changed file — only in a repo with a prettier config (format-on-write.sh)
PostToolUseWrite|EditLog the touched file for the block guard (block-touch.sh)
PostToolUseBashRecord scrape yield (scrape-yield.sh, async)
PostToolUseBashRecord funnel yield (funnel-yield.sh, async)
PostToolUsemcp__figma__.*Count Figma MCP calls against the free plan's ~20/month and warn from the 15th (figma-quota.sh)
Stop*Append a timestamp to ~/.claude/session-log.txt
Stop*Block protocol: one nudge when 2+ code files in a block changed without record/docs updates (block-guard.sh)
Notification*macOS notification when Claude needs attention

Hook scripts live in .claude/hooks/ (each *.test.sh beside its hook) and setup.sh installs them to ~/.claude/hooks/. Each command tries the project copy first and falls back to ~/.claude/hooks/, so the fleet-wide hooks run in every repo while kun sessions run the working copy.

Configuration

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "f=\"${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive-bash.sh\"; [ -f \"$f\" ] || f=\"$HOME/.claude/hooks/block-destructive-bash.sh\"; bash \"$f\""
          }
        ]
      }
    ]
  }
}

Every hook ends in exit 0 (or swallows its own failure). Exit code 2 is the one that blocks: on PreToolUse it stops the tool call (that is how block-destructive-bash.sh works); any other non-zero code is reported as a hook error and the call goes ahead. A Stop hook blocks by printing {"decision": "block", "reason": …} (block-guard.sh).

Events

EventFiresUsed for
SessionStartNew session beginsContext injection, drift warnings
ConfigChangeConfig file changesRegeneration checks
PreToolUseBefore a tool runsValidation, guards, cleanup
PostToolUseAfter a tool completesFormatting, browser open
StopAgent finishes a turnLogging, protocol nudges
NotificationClaude pauses for inputDesktop alerts

Matchers

PatternMatches
Bash(pnpm dev)One specific bash command
BashEvery bash call
Write|EditMultiple tools, pipe-separated
*Everything (events without a tool)

Block protocol enforcement

The block protocol is hook-enforced rather than trusted to memory: block-context injects a block's records on prompt, and block-touch + block-guard raise a Stop nudge when two or more code files change without the matching record and docs updates.

Adding a hook

Edit .claude/settings.json — event, matcher, command. Keep the command idempotent and non-blocking unless blocking is the point.