Overview
vg scan answers how far behind is the stack? Vibgrate Review answers a
different question about the same repository:
Did this change move the system toward its declared architecture, weaken a security control, or create an implication the author did not account for?
It answers it locally, from the Vibgrate Graph, with deterministic
scanners and a policy-owned decision. The result is a signed, versioned receipt
(vg.review.receipt.v1) that can stay on disk or be pushed to
Vibgrate Cloud and shown commit by commit.
AI coding agents write changes faster than anyone can judge their architectural consequences. Review is the layer that reads the change the way a senior reviewer would — before the pull request, not after the incident.
Usage
vg review # the working tree + index, vs HEAD
vg review --base origin/main # merge-base of HEAD and the base branch
vg review explain arch:<rule>:<path> # the evidence behind one finding
vg review findings-from-diff # deterministic graph/policy findings only
vg review propose blast:<node_id> --model forge --json
vg review propose arch:<rule>:<path> --model forge --json --findings findings.json
vg review verify review-receipt.json # check a receipt offline
Review reads the code map. If there isn't one, vg review builds it first and
shows a progress bar; if the map has drifted from the working tree, it refreshes
it incrementally. Pass --no-auto-build to turn that off — then a missing or
stale map exits 6, never 0.
The first run in a repository with no review policy also writes a starter one,
seeded with the layering shape the repository already exhibits, and tells you it
did. It goes into the project config: a new .vibgrate/config.yml when there is
no config yet, or a review block added to an existing .vibgrate/config.yml
or vibgrate.config.json. A .ts or .js config is never rewritten, so
those repositories get .vibgrate/review.toml instead. The policy is written
after the review, so it never lands in the change set being reviewed; it is
advisory, so it cannot turn a green build red; and --base runs skip it, so a
pull request review never writes to the tree. Pass --no-setup to turn that
off too.
Options
| Flag | Default | Description |
|---|---|---|
--base <ref> | — | Review HEAD against the merge-base with <ref> |
--format <fmt> | text | text, json (the receipt), sarif (security findings only), md |
-o, --out <file> | — | Write the formatted result to a file |
--push | off | Send the receipt to Vibgrate Cloud (needs a DSN) |
--dsn <dsn> | — | DSN token for --push (or use VIBGRATE_DSN / vg login) |
--strict | off | Fail the command when --push fails |
--fail-on <level> | from config | Gate CI on this decision: none, fail, or needs_review. Omit to use the review policy; gating is off unless enforcement is enforced |
--explain | off | Add local-model explanations. Requires a local model; fails closed (exit 6) without one |
--include-spans | off | Include evidence line ranges in the pushed receipt |
--include-snippets | off | Include capped source snippets in the pushed receipt |
--no-auto-build | — | Don't build or refresh the code map; exit 6 when it is missing |
--no-setup | — | Don't write a starter review policy |
--sign-key <file> | $VG_ATTEST_KEY, else .vibgrate/attest-key.pem | Ed25519 private key to sign the receipt with — the same key vg build --attest and vg evidence use. Minted on first use if absent (keep it, gitignore it) |
--no-sign | — | Leave the receipt unsigned (signature: null) |
--write-context | off | Write .vibgrate/review-context.md — committed agent memory of what Review found |
--inject-context [file] | off | Update the managed review block inside an instructions file (default CLAUDE.md) |
--offline (global) | off | Never touch the network — no push, no model fetch |
vg review explain
vg review explain <finding-id>
vg review explain arch:<rule>:<path> --base origin/main
vg review explain blast:<node_id>
Prints the evidence behind one finding from the current change: the remediation, its alignment and confidence, whether it is protected, which layer produced it, and each piece of evidence — the graph edge with its source span, and the policy rules it violates. Finding ids are the stable blast:{node_id} / blast:{path}:{name} and arch:{rule}:{path} keys, not rank-local labels.
vg review findings-from-diff
vg review findings-from-diff
vg review findings-from-diff --base origin/main
vg review findings-from-diff --diff pr.patch --format json
The same deterministic scanners vg review runs, printed as the
vg.review.findings.v1 document — blast-radius facts from the code graph for
changed symbols, plus architecture-policy and security-control findings. No hosted
model. --diff reads a unified diff (- is stdin). JSON also includes a
publishable array:
| Field | Blast-radius | Architecture-policy |
|---|---|---|
kind | correctness (top-level only) | correctness (top-level only) |
id / finding_key | blast:{node_id} or blast:{path}:{name} | arch:{rule}:{path} |
source | scanner | scanner |
producer / scanner_kind | blast_radius | architecture |
| severity | low or medium | low, medium, or high — never critical from version lag |
receipts | existing capsule verify: / scan: / attest: ids when those facts already exist | same |
Suggested-fix on those rows is an honest skip (null / skipped_no_patch).
Each run also writes .vibgrate/review-propose-handoff.json so
vg review propose can resolve those ids without a second findings loop.
This path does not post a check run or a review comment.
vg review propose
vg review propose blast:<node_id> --model forge --json
vg review propose blast:<node_id> --model forge --json --base origin/main
vg review propose arch:<rule>:<path> --model forge --json --findings findings.json
vg review propose arch:<rule>:<path> --model relay:<slug> --json
Attaches a PatchIR dry-run to one finding id via the VG Code agent loop — there
is no second Review runtime. Lookup is the current change set, then
--findings JSON (a findings document or a review receipt), then the last-run
.vibgrate/review-propose-handoff.json. Pass the same --base /
--in-place / --diff as findings-from-diff when you want that change set
explicitly. --model is required:
| Shape | Meaning | Example |
|---|---|---|
relay:<slug> | Hosted Review on Vibgrate Relay | --model relay:hosted-coder |
spark · flow · forge | Local Code Mode | --model forge |
A bare slug is invalid — propose never calls a backend in that case. Dry-run
unless you pass --apply --yes on a topic branch. It never writes the
default branch. The same model-id shapes are used by vg code and
vg models. This command does not post a check run or a review comment.
vg review verify
vg review verify review-receipt.json
vg review verify review-receipt.json --pub trust-root.pem
Checks a receipt offline — no Vibgrate account, no network. It recomputes
digests.receipt over the document it holds and verifies the detached Ed25519
signature over that digest. The states are honest and never fabricate a pass:
| State | Exit | Meaning |
|---|---|---|
verified | 0 | Signature valid against the key pinned with --pub, and the digest matches |
unverified | 2 | Intact, but trust is not established — the signer is not pinned, or the receipt is unsigned |
failed | 1 | The digest no longer matches the contents, or the signature does not verify (altered after signing, or a different key) |
Every receipt carries the signer's public key, so integrity can be checked
without any setup; who signed it is established only by pinning that key.
Pass --json for the machine-readable result.
Decisions
Policy owns the decision, and it is the only layer that writes one. Findings — from the scanners, or from the optional local model — have nowhere to put a verdict.
| Decision | Exit (gated) | Meaning |
|---|---|---|
pass | 0 | No material delta, or every finding is target-aligned |
fail | 2 | An unresolved protected finding, or a high-severity finding above the confidence threshold |
needs_review | 2 with --fail-on needs_review, else 0 | Findings a human should look at |
undetermined | 2 with --fail-on needs_review, else 0 | Not enough evidence to decide — deliberately not a pass |
Gating is opt-in, exactly as it is for vg scan: without --fail-on or
enforcement: enforced in the review policy, every decision is
reported, written to the receipt, and the process exits 0. A gate failure
exits 2 — the CLI's gate-failed code, shared with vg scan --fail-on — and
never 1, which across the CLI means the command itself broke. A missing code
map, policy, or model exits 6, which is deliberately never read as a pass.
You get needs_review or undetermined instead of a fake pass when the system
does not have enough peers or tests to judge the change. That is the point: a
green tick nobody earned is worse than an honest "I could not tell".
Protected findings
An unguarded entrypoint, a removed guard, a validated taint flow, and a
known-vulnerable dependency are protected. While one is unresolved, policy
cannot emit pass — not through an approved exception, not through low
confidence, not through the quick path, and not through anything a model says.
Turn a rule off in the review policy (review.protected) if it does not apply
to your repository. That is the only way to stop it gating.
Configuration
The review policy is the review block of the project config — .vibgrate/config.yml
or vibgrate.config.json, which take the same settings:
# .vibgrate/config.yml
review:
enforcement: advisory # advisory | enforced
failOn: fail # none | fail | needs_review
targetPattern: layered # the architecture you say you want
protected:
unguardedEntrypoint: true
knownVulnerableDependency: true
validatedTaint: true
Read from the trusted base branch when --base is given, so a pull request
cannot weaken the policy applied to itself. The same goes for the team files in
.vibgrate/review/ (ignore.md, policy.md, merge.md, checks/*.md):
with --base they come from the base branch, and a change's edits to them apply
after it merges. If the base has no policy, Review uses the defaults, never the
change's own copy. It is read as data: a review block
in a .ts or .js config is never run, so keep it in YAML or JSON.
Repositories that already have .vibgrate/review.toml keep working: it is read
whenever the config has no review block. Its keys are the snake_case forms of
the ones above (fail_on, target_pattern, [review.protected]).
vg doctor tells you when a review block makes review.toml redundant.
Declaring targetPattern is what turns a layering observation into a
regression. Without it, a dependency that skips a tier is reported as a medium
finding about the repository's own majority — because a majority is not a
decision, and Review will not treat it as one.
In CI
The same binary, the same policy, the same receipt schema as on a laptop.
vg review --base "origin/$BASE_REF" --format json --out review-receipt.json
Review builds the map itself on a cold runner, so a separate vg build step is
optional. Keep one if you want the map build timed and cached as its own CI step.
Set VG_ATTEST_KEY from a CI secret so every receipt is signed by the same key
— then a downstream job, or an auditor, can pin its public half with
vg review verify --pub and get verified rather than unverified. Without
it the runner mints a throwaway key: the receipt is still signed and
tamper-evident, but the signer cannot be recognised across runs.
Start with the default --fail-on fail so only a fail stops the build, and
move to --fail-on needs_review once a repository has opted into enforcement.
--format sarif uploads the security findings to GitHub code scanning.
Architectural findings stay on the receipt and the check summary on purpose:
code scanning is a security surface, and filling it with layering observations
trains reviewers to ignore it.
Privacy
Source stays on your machine. --push sends the receipt — decisions, claims,
evidence ids, paths, digests, versions — never the analysis capsule and never
source text. Line ranges are opt-in with --include-spans; snippets are a
second, explicit opt-in with --include-snippets, and are capped. With no DSN,
the receipt stays local.
What it does not claim
- It does not prove code is secure, correct, or free of vulnerabilities.
- It does not replace Semgrep, CodeQL, a compiler, or your tests.
- It does not govern AI-system fairness or model risk — that is a different category.
- Absence of findings is not a certification.
Related
- vg scan — stack currency, the other half of the picture
- vg code — the VG Code loop
vg review proposereuses - vg models — Code Modes (
spark/flow/forge) - CI integration — wiring the gate into your pipeline