bro

bro self guard

Declarative prompt contributions β€” nudges conditioned on worktree state, session markers, or a judge veto.

Guards are one-line nudges the hook evaluates at session-start, prompt-submit, post-tool, and stop. A guard fires when its when clause β€” event name, match keys, state probes, optional judge veto β€” holds, and contributes its say line to the hook's output. Guards never block and never grant: a guard line is context, not a gate.

Declarations come from guard.defs in bro.config.json (project-owned) and from connectors' guards() contributions β€” first name wins, so a config def shadows a builtin. guard.enabled: false silences the mechanism; guard.maxPerEvent caps the lines one event emits; when.budget caps how often one guard fires.

CommandWhat it does
bro self guard list [--json]Every collected guard: name, source, events, budget, validation state
bro self guard test <name> [--event E] [--prompt T] [--session ID]Dry-run one guard against live state β€” per-clause verdicts, then FIRE/SKIP and the rendered line. Read-only: the fired set and verdict journal are untouched

The when clause

KeyMatch
when.onHook events the guard may fire on
when.state.diff.changed / diff.withoutGlobs over git status (a rename counts both paths)
when.state.branchBranch-name glob
when.state.armedSession-gate aspects armed this session
when.state.existsRepo-relative paths that must exist
when.state.probesEngine-registered named probes (spec-drift, …) with their own args
when.judgeAsk the judge facade β€” a veto suppresses the guard; abstention (no judge, low confidence, decide error) doesn't
when.budgetFired-set cap β€” how many times the guard may fire

One builtin ships β€” test-coverage-on-stop nudges at stop when src/** moved and no test file did. Bro's own repo config adds retro-on-stop, spec-drift, and vendor-boundary defs as living examples.

On this page