Skip to content

Guard command

Muhammet Şafak edited this page Jul 26, 2026 · 3 revisions

guard — declarative policy gate

commitbrief guard evaluates a review's actionable findings against a declarative policy (.commitbrief/policy.yml) and exits non-zero when the policy is breached. It is a richer alternative to the single --fail-on=<severity> threshold — a per-severity budget — and is aimed at gating high-volume, often AI-authored pull requests at scale. It pairs with the MCP server: an agent self-reviews via the review tool, and guard enforces the bar in CI.

Introduced in v1.10.0 (ADR-0029). Opt-in and additive — it changes nothing about the existing commands or --fail-on.

The policy file

.commitbrief/policy.yml — the gate is opt-in: with no file, guard errors (it never silently passes). Unknown keys are rejected so a typo can't disable the gate.

version: 1
thresholds:        # maximum findings allowed per severity
  critical: 0
  high: 0
  medium: 5
  low: ~           # ~ (or omitted) = unlimited
total: 20          # optional overall cap on actionable findings

Severities are the canonical five (critical, high, medium, low, info). A severity that is absent — or set to ~ — is unlimited.

Sharing the policy with your team

CommitBrief adds .commitbrief/ to your .gitignore the first time it writes a cache entry, which also ignores the policy file. To version-control the gate, add a negation below that line:

.commitbrief/
!.commitbrief/policy.yml

The baseline (.commitbrief/baseline.json) is deliberately not shared — it is per-developer, so it can never hide a finding from CI or a reviewer.

Two modes

Run-mode (default) reviews the diff and evaluates the result. It reuses the exact review pipeline (runReview) — diff acquisition, filtering, the pre-send guard + secret scan, cost preflight, cache, the flaky pre-pass, and signal control — with no --fail-on set, because the policy decides the verdict.

commitbrief guard                  # staged diff (default)
commitbrief guard --unstaged       # working tree
commitbrief guard --diff main...HEAD

Provider/model/flaky knobs come from the persistent flags (--provider, --model, --no-flaky), as do the path filters (--file / --dir / --exclude-file / --exclude-dir) and the commit filters (--author / --committer / --start-date / --end-date / --text) — guard forwards them into the shared review seam, which otherwise resets the global flag state.

# Gate only the backend package.
commitbrief guard --diff main...HEAD --dir internal/api

# Gate this sprint's work by one author.
commitbrief guard --author alice --start-date 2026-07-01

A commit filter replaces the scope, so combining it with --unstaged is an error — same rule, same message, as on a review.

Consume-mode (--from-json) evaluates a review you already produced — no provider call. Ideal for gating an agent's self-review (the MCP review tool's JSON) cheaply in CI:

commitbrief --json --staged > review.json
commitbrief guard --from-json review.json
commitbrief guard --from-json -        # read the JSON from stdin

What it counts

guard evaluates the findings that survive baseline + inline suppression (signal control) — exactly the set that commitbrief --json emits and that a human reviewer sees. Baselined and suppressed findings do not count toward the gate.

Output & exit codes

Exit Meaning
0 pass — every threshold (and the total cap) is within budget
non-zero blocked — a threshold was exceeded, or the policy/review could not be loaded or parsed

A load/parse failure blocks deliberately: a merge gate must not pass when it cannot prove the change is within policy.

By default guard prints a human summary (the verdict, each breached severity, and the per-severity counts). With --json it emits a machine verdict:

{
  "passed": false,
  "counts": { "critical": 1, "high": 0, "medium": 8, "low": 3, "info": 0 },
  "total": 12,
  "violations": [
    { "severity": "critical", "allowed": 0, "actual": 1 },
    { "severity": "medium", "allowed": 5, "actual": 8 }
  ]
}

Sandbox-rerun is suppressed

Run-mode drives the review through the same runReviewForMCP seam commitbrief mcp uses, so if you've configured review.sandbox_rerun and review.sandbox_command (see Review: sandbox-rerun confirmation), guard never runs it — unconditionally, regardless of flag or config, with no override. The flaky detector's static findings are still evaluated against the policy as usual; only the empirical rerun confirmation is skipped, so a flagged test is gated at the static-only confidence level. This is deliberate: guard is meant to run non-interactively (often in CI), and a policy gate must not be the thing that arms unattended code execution. See ADR-0033 §6 for the full rationale.

guard vs --fail-on

--fail-on=<severity> is a single all-or-nothing threshold (fail if any finding meets or exceeds one level). guard is a declarative, version-controlled, per-severity budget that can also consume a prior review's JSON. They are independent — use either or both.

Flags

Flag Default Meaning
--policy <path> .commitbrief/policy.yml policy file (resolved relative to the repo root)
--from-json <file|-> consume-mode: evaluate a prior schema-v1 review instead of running one
--unstaged false run-mode: review the working tree instead of the staged index
--diff <range> run-mode: review an arbitrary git diff range (repeatable)

Limits

Rule-id-scoped allow/deny lists are not supported: findings carry no stable rule identifier (they are free-form model output), so a name-based list would be unreliable. That awaits a finding rule-id field.

See also

Clone this wiki locally