Skip to main content
TRW
Skip to content
TRWHooks — Automated Quality-Gate Enforcement

Hooks

Hooks are optional, additive client adapters. They can surface guidance, record telemetry, snapshot local state, or provide early gate feedback when a client exposes the necessary event. TRW ships 17 top-level scripts, but no client registers every script.

How hooks work

Clients expose different lifecycle events, payloads, trust controls, and blocking semantics. TRW installs only the adapters supported by the selected client profile; the behavior is intentionally not identical everywhere.

Most policy adapters fail open on infrastructure errors. A few adapters intentionally block on a policy result, and Cursor CLI marks its before-shell and before-MCP security gates as fail closed. Treat the client row below as the source of blocking posture, not a universal exit-code rule.

Client support is not uniform

TRW maps only the events each client actually exposes. A missing hook does not remove the protocol: instruction files and MCP tool middleware remain available across clients.

Claude CodeInspect
Project surface
.claude/settings.json
Default posture
Broad lifecycle set. Delivery and Stop adapters can intentionally block.
Cursor IDEInspect
Project surface
.cursor/hooks.json
Default posture
Eight native IDE events. Pre-tool guidance remains non-blocking.
Cursor CLIInspect
Project surface
.cursor/hooks.json
Default posture
Five CLI events. Before-shell and before-MCP gates fail closed.
Codex CLIInspect
Project surface
.codex/hooks.json
Default posture
Five hooks, opt-in through [features].hooks and subject to manual trust review.
GitHub Copilot CLIInspect
Project surface
.github/hooks/hooks.json
Default posture
Five events routed through a client-specific payload adapter.
Antigravity CLIInspect
Project surface
.antigravitycli/hooks.json
Default posture
Supported PreToolUse before-edit telemetry from the bundled distill channel.
OpenCodeInspect
Project surface
Instruction files + MCP
Default posture
No project hook set by default. Tool middleware carries the protocol.

Hook events

These are the behaviors registered by the bundled Claude Code template. Other clients use different names and smaller event sets; see the support matrix above.

Session

SessionStartInspect
When it fires
Claude session opens, resumes, clears, or returns from compaction
What TRW does
Emits protocol and recovery guidance. It asks the agent to call trw_session_start; it does not load memory itself.
UserPromptSubmitInspect
When it fires
Before a prompt when the inferred phase changes
What TRW does
Emits phase guidance and can inject bounded, keyword-matched local learnings.
PreCompact / PostCompactInspect
When it fires
Around context compaction
What TRW does
Snapshots local run state, then presents recovery guidance from that snapshot.
InstructionsLoadedInspect
When it fires
An instruction file is loaded
What TRW does
Records an observability event; it does not enforce policy.
SessionEndInspect
When it fires
A Claude session closes normally
What TRW does
Warns when delivery or reflection evidence is missing; never blocks.
StopInspect
When it fires
Claude attempts to stop
What TRW does
Can issue up to two delivery reminders, then allows exit.

Tools and subagents

PreToolUseInspect
When it fires
Before trw_deliver, and before Write, Edit, or MultiEdit
What TRW does
Provides an early build-evidence check before delivery, and checks an edit against the run’s recorded intent. The trw_deliver tool middleware remains authoritative; the intent check is advisory and tamper-evident, not an enforcement boundary.
PostToolUseInspect
When it fires
After Claude Write, Edit, or MultiEdit calls
What TRW does
Records file-modification telemetry for the active run, and records what the edit actually changed against that same intent.
SubagentStart / SubagentStopInspect
When it fires
A Claude subagent starts or finishes
What TRW does
Adds abbreviated context on start and records completion telemetry on stop.

Registered Claude Code set

The package ships 17 top-level hook scripts, including compatibility scripts that are not active by default. The bundled Claude template registers the subset below through .claude/settings.json. Presence in .claude/hooks/ alone does not mean a script will run.

session-start.shInspect
Event
SessionStart
Purpose
Emits startup, resume, clear, and compaction guidance
user-prompt-submit.shInspect
Event
UserPromptSubmit
Purpose
Emits phase-aware guidance and bounded local-learning candidates
pre-compact.shInspect
Event
PreCompact
Purpose
Writes a local pre-compaction state snapshot
post-compact.shInspect
Event
PostCompact
Purpose
Presents recovery guidance from the snapshot
instructions-loaded.shInspect
Event
InstructionsLoaded
Purpose
Records which instruction surface loaded
session-end.shInspect
Event
SessionEnd
Purpose
Advises when reflection or delivery evidence is missing
stop-ceremony.shInspect
Event
Stop
Purpose
Issues bounded delivery reminders before allowing exit
pre-tool-deliver-gate.shInspect
Event
PreToolUse
Purpose
Provides early feedback before trw_deliver reaches its authoritative gate
pre-tool-intent-guard.shInspect
Event
PreToolUse
Purpose
Checks a Write, Edit, or MultiEdit against the run’s recorded intent. Advisory and tamper-evident, not an enforcement boundary
post-tool-event.shInspect
Event
PostToolUse
Purpose
Records Write and Edit effects
post-tool-intent-check.shInspect
Event
PostToolUse
Purpose
Records what a Write, Edit, or MultiEdit actually changed against that same intent
subagent-start.shInspect
Event
SubagentStart
Purpose
Emits abbreviated TRW context for a spawned subagent
subagent-stop.shInspect
Event
SubagentStop
Purpose
Records subagent completion telemetry

Hook registration

This excerpt uses the same project-root-safe command path as the bundled Claude template. Each client has its own schema; do not copy this JSON into Cursor, Codex, Copilot, or Antigravity configuration.

Claude Code example (.claude/settings.json)
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [{
          "type": "command",
          "command": "sh \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh\"",
          "timeout": 5000
        }]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "mcp__trw__trw_deliver",
        "hooks": [{
          "type": "command",
          "command": "sh \"$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-deliver-gate.sh\"",
          "timeout": 5000
        }]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [{
          "type": "command",
          "command": "sh \"$CLAUDE_PROJECT_DIR/.claude/hooks/stop-ceremony.sh\"",
          "timeout": 10000
        }]
      }
    ]
  }
}

The matcher field filters when the hook fires. An empty string matches all events of that type. A specific value like mcp__trw__trw_deliver matches only that tool call. The full template registers separate SessionStart entries for startup, resume, compact, and clear. The abbreviated example shows only startup.

Custom hooks

You can add your own hooks alongside TRW's registered set. This non-blocking Claude Code example is intentionally client-specific; use your client's schema elsewhere.

adding a custom PostToolUse hook
// In .claude/settings.json, add to the PostToolUse array:
{
  "matcher": "Bash",
  "hooks": [{
    "type": "command",
    "command": "sh \"$CLAUDE_PROJECT_DIR/.claude/hooks/my-bash-audit.sh\"",
    "timeout": 3000
  }]
}

A blocking hook needs to distinguish an intentional policy denial from an infrastructure failure. An unconditional trap 'exit 0' EXIT would erase an intentional exit 2. The bundled shell gates use an explicit flag instead:

fail open on errors, preserve an intentional block
#!/bin/sh
set -e
intentional_exit=0
trap '[ "$intentional_exit" = "1" ] || exit 0' EXIT

if policy_is_unmet; then
  echo "BLOCKED: explain the required action" >&2
  intentional_exit=1
  exit 2
fi

exit 0

Phase-change suppression

user-prompt-submit.sh avoids repeating phase guidance when the inferred phase has not changed. This keeps repeated prompts in one phase quiet.

The hook caches the last emitted phase in .trw/context/last_ups_phase. It emits again when the phase changes, when there is no active run, or after session-start logic clears the cache.

Condition
Phase unchanged since last emission
Hook fires?
No - suppressed
Condition
Phase changed
Hook fires?
Yes - emits and updates cache
Condition
Phase is none (no active run)
Hook fires?
Yes - session-start reminder
Condition
Phase is done
Hook fires?
No - silent

Contextual learning injection

In clients that register user-prompt-submit.sh, a phase-change event can scan recent local learning entries and add a bounded set of keyword matches to hook output. This lightweight adapter is separate from the ranked trw_recall tool and should not be described as equivalent retrieval.

injection pipeline
1. Extract bounded keywords from the user's prompt
2. Scan recent files in .trw/learnings/entries by keyword match
3. Score each result; keep entries where score ≥ auto_recall_min_score (default 0.7)
4. Emit up to auto_recall_max_results matches within the configured token cap
5. Session dedup: skip learnings already injected this session
6. 500ms timeout guard - if search exceeds limit, inject nothing (fail-open)

Troubleshooting

Expected hook does not fire

Cause: The selected client may expose a smaller event set, require opt-in, or require trust approval.

Fix: Check the client row above, then inspect its generated config instead of assuming the Claude layout.

Hook script is present but inactive

Cause: Bundled compatibility scripts are not necessarily registered by the selected client.

Fix: Confirm that the client config contains an event entry pointing to that script.

A blocking adapter allows an operation after an error

Cause: Most policy adapters intentionally fail open on infrastructure errors.

Fix: Inspect the MCP tool result: the server-side tool or middleware gate is the authoritative decision.

Next steps

Next

Hooks wrap the lifecycle with guardrails. Troubleshooting and configuration help when you need to tune or debug that behavior.