Session reviewer

How repeated corrections become hooks: the reviewer, its thresholds, and the PR it opens.

The session reviewer closes the loop on hook authoring. The corrections you give Claude during a session — “never force-push”, “use uv, not pip”, “you weakened that test” — are exactly the rules a hook should enforce, but writing the hook by hand is friction you skip in the moment. The reviewer notices those corrections for you. When a session ends, it reads the transcript, finds the durable corrections and the hooks that misfired, and once a pattern proves itself, opens a pull request that adds or fixes a hook. You review the PR like any other.

It is on by default after uvx capt-hook init. This page explains the path from a session to a PR, what it takes to run, and the knobs that decide what crosses the bar.

How it works

The reviewer runs entirely on your machine, riding captain-hook’s own event dispatch:

  1. The plugin’s hook wiring runs capt-hook run <Event> for every lifecycle event, and the reviewer rides that dispatch natively — the async SessionStart and SessionEnd passes fire the enrollment-gated reviewer, and the throttled sweep rides Stop. There is no dedicated reviewer hook entry to wire or break. The pass detaches into the background and returns immediately, so a utility command like claude update never blocks on it, and a non-interactive claude -p session end is skipped — it carries no corrections worth mining.
  2. The background pass resolves the repo, checks that you are watching it, then scans the ended transcript for two things: durable corrections you gave, and hooks that fired when they should not have.
  3. Each finding is judged by an LLM for whether it states a durable, enforceable rule.
  4. A judged finding becomes a candidate. The reviewer tracks it across sessions and only acts once it clears its thresholds.
  5. An eligible candidate spawns a headless brain (claude -p running the scanning-sessions skill) inside the repo, which drafts the hook, proves it with uvx capt-hook test, and opens one pull request carrying the verbatim evidence.

Candidates come in two kinds. A create candidate turns a recurring correction into a new hook. A fix candidate amends a hook that misfired, adding a regression test that reproduces the misfire. Fix candidates clear the bar faster — a hook firing when it should not is a concrete, actionable defect.

Turn it on

uvx capt-hook init arms the reviewer for the current repo. To arm a repo without re-running init, enable it directly:

uvx capt-hook review enable

Both paths set up what the reviewer needs:

  • the captain-hook plugin registered in .claude/settings.json — its event wiring carries the reviewer along with every other hook, and the brain loads the plugin’s scanning-sessions and authoring-hooks skills directly;
  • a per-machine watching flag for this repo.

The plugin owns the event wiring; review enable writes no hook entries of its own. Watching is stored once per machine, and the reviewer enrolls a repo the first time it runs in it — a plugin-wired clone is watched from its first session end with no manual step. review enable arms a repo immediately instead of waiting for that first session; review disable is the durable opt-out, which auto-enrollment never overrides.

Prerequisites

The reviewer shells out to two CLIs, so both must be authenticated:

  • claude — the judge and the PR-drafting brain both call it. Log in with claude once.
  • gh — the brain opens the PR with it. Authenticate with push and pull-request scope (gh auth login).

The repo also needs a git remote; the reviewer keys candidates to the normalized origin.

What crosses the bar

A correction does not become a PR the moment you say it. It has to recur, survive an LLM verdict, and clear thresholds — so a one-off aside never turns into noise. Every threshold is an environment variable with the HOOKS_REVIEW_ prefix:

Setting Default What it gates
HOOKS_REVIEW_MIN_SESSIONS 3 Distinct sessions a correction must recur across before a create candidate is eligible.
HOOKS_REVIEW_MIN_DAYS 2 Days a create candidate must span before it is eligible.
HOOKS_REVIEW_MIN_SESSIONS_FIX 2 Sessions a hook-misfire fix candidate must recur across.
HOOKS_REVIEW_JUDGE_TIER medium LLM tier that judges each candidate (small, medium, large).
HOOKS_REVIEW_MIN_JUDGE_CONFIDENCE 0.6 Minimum judge confidence for a candidate to count.
HOOKS_REVIEW_MAX_OPEN_PRS 2 Open reviewer PRs allowed per repo at once.
HOOKS_REVIEW_BRAIN_MAX_BUDGET_USD 5.0 Spend cap on the PR-drafting brain per session.
HOOKS_REVIEW_BRAIN_MAX_TURNS 80 Turn cap on the PR-drafting brain.

Set them in your Claude Code settings.json env block, or export them in the shell that ends the session.

Watch the queue

uvx capt-hook status shows everything the reviewer is tracking — every correction, how close each one is to a PR, and what that PR would do — grouped by lifecycle stage:

uvx capt-hook status
captain-hook  ·  github.com/you/app    [watching]    PR slots 1/2

WATCHING   building toward the bar
  #3  fix     sessions █░ 1/2
      would fix hooks.style:nudge_1 (.claude/hooks/style.py): stop firing on its own output
  #1  create  sessions ██░ 2/3   days ██ 2/2
      would add a hook: "block force-push to main"

ELIGIBLE   a PR opens next session
  #2  create  ready  ·  sessions ███ 3/3   days ██ 2/2
      would add a hook: "run the test suite before every commit"

PR OPEN   pull request awaiting your review
  #4  create  https://github.com/you/app/pull/214   ·   4d open
      would add a hook: "use uv instead of pip"

ACCEPTED   PR merged
  #5  create  https://github.com/you/app/pull/208
      would add a hook: "use loguru instead of print"

The same view lives under the reviewer namespace as uvx capt-hook review status. Read it top to bottom as a funnel — a correction climbs from WATCHING to a merged hook.

The header

The top line is the repo the reviewer keys candidates to, whether you are [watching] it, and the open-PR slots in use (1/2 here). At the cap, eligible candidates wait for a slot before a new PR opens. An unwatched repo says so and tells you to run uvx capt-hook review enable.

The stages

Each candidate sits in exactly one stage:

  • Watching — tracked, still under the bar. The bars show judge-accepted progress toward the thresholds: distinct sessions and distinct days for a create candidate, a lower bar for a fix. A correction has to recur and survive an LLM verdict before it counts, so a one-off aside never climbs. See the thresholds for the exact gates.
  • Eligible — over the bar and watched, with a free PR slot. The next session that ends drafts and opens the PR; you do nothing.
  • PR open — the reviewer opened a pull request, shown with its URL and age. Review it like any other.
  • Accepted / rejected / stale — a merged PR accepts the candidate, a closed PR rejects it, and a PR left open past the staleness cutoff goes stale and frees its slot.

Each row

A row reads #id kind progress, then the one-sentence summary of what its PR would do — the verdict the judge wrote when it accepted the correction. A create candidate adds a new hook; a fix candidate amends an attributed hook that misfired and adds a regression test. Pull the verbatim evidence and the full thresholds for one candidate with uvx capt-hook review show <id>.

Refresh and manual passes

status prints the last-synced PR state immediately, then refreshes open PRs against GitHub in the background and updates the frame when gh answers. Skip the network round-trip with --no-sync:

uvx capt-hook status --no-sync

Corrections only land after a session ends with the reviewer watching the repo, so an empty queue usually means no session has closed yet. To backfill from a transcript you already have, run the ingest and judge passes by hand:

uvx capt-hook review scan --transcript path/to/session.jsonl
uvx capt-hook review triage

For one candidate, or to script against the queue, the narrower views print plain lines:

uvx capt-hook review list             # the repo's candidates and their status
uvx capt-hook review show <id>        # one candidate's row and threshold status
uvx capt-hook review threshold-check  # which candidates currently cross the bar
uvx capt-hook review sync-prs         # fold merged/closed PR states back into candidates

update moves a candidate’s status by hand — useful when you want to override the automatic flow.

Turn it off

The reviewer is opt-out per repo:

uvx capt-hook review disable          # stop watching this repo (candidates stay recorded)
uvx capt-hook init --no-review        # scaffold without arming the reviewer

disable stops new sessions from being reviewed; the candidates already recorded stay, so re-enabling later picks up where it left off.

Cost and privacy

The reviewer reads your transcripts locally and never uploads them — it sends only the specific correction text to the judge, and the brain runs in your own claude session. It does spend tokens: one judge call per new candidate at the configured tier, and, when a candidate is eligible, one brain session bounded by HOOKS_REVIEW_BRAIN_MAX_BUDGET_USD. The max_open_prs cap keeps it from flooding your repo with pull requests while you catch up.

For the kind of correction signal the reviewer mines — and a hook that points you back here when you give one — see Surface tracked corrections.