Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Block merges in CI

vidi verify exits non-zero when the gate fails. That exit code is the enforcement. CI needs nothing else from vidi.

The job

# .github/workflows/vidi.yml
name: Review coverage

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  verify:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v7
        with:
          # The gate reads .vidi/*.jsonl out of the checkout. A shallow clone is
          # fine, the ledger is committed files, not git history.
          fetch-depth: 1

      - name: Install Rust (stable)
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable

      - name: Install vidi
        run: cargo install --git https://github.com/graze-ai/vidi vidi-cli --locked

      - name: Verify review coverage
        run: vidi verify

Nothing here needs a token, a login, or a network call. verify is offline and read-only: it scans the working tree, reads the committed ledger, and compares.

Getting the binary

vidi-cli is not published to crates.io and there are no prebuilt release binaries yet, so CI has to build it from source. Which source depends on where the workflow lives:

Where the workflow runsInstall step
Another repository, gating its own codecargo install --git https://github.com/graze-ai/vidi vidi-cli --locked
This repository, gating itselfcargo install --path crates/vidi-cli --locked

vidi-cli is the package name; the binary it installs is vidi. Both forms put it on PATH, which a bare cargo build would not.

Pin the version you gate on — --git … --tag v0.1.0 — rather than tracking the default branch, or a change upstream can turn a green build red without a commit of yours. --locked uses the lockfile committed to vidi rather than resolving fresh dependencies, so the build is reproducible.

Expect this step to dominate the run: it compiles the tree-sitter grammars from C, which takes minutes. actions-rust-lang/setup-rust-toolchain caches the cargo registry and build artifacts between runs, so only the first one pays full price.

Exit codes

ExitVerdictMeaning
0PASSa policy exists and every requirement is met
0NEUTRALno .vidi/policy.toml, and nothing broken
1FAILUREsomething is stale, orphaned, or unmet — or a ledger line would not decode
2the command refused: a broken policy, bad arguments, a failed scan
141stdout closed early (128+SIGPIPE, e.g. vidi queue | head)

Only 1 is a gate failure. A 2 means vidi never got as far as forming a verdict, so nothing was checked. Do not read it as a review backlog.

NEUTRAL passes: an unconfigured repository does not fail its own builds. See Turn on the gate.

Use verify, not status

vidi status prints the same verdict word and always exits 0. A CI step running vidi status prints FAILURE on every build and passes anyway.

Explaining a failure in the log

verify names the failing units but not why they fail. Running vidi explain on failure turns the log into something a contributor can act on without reproducing anything locally:

      - name: Verify review coverage
        run: vidi verify

      - name: Explain the failure
        if: failure()
        run: vidi explain

explain is read-only, and renders each failure as category → what failed → why → what to do.

Annotating the diff

vidi report --sarif emits SARIF 2.1.0, which GitHub renders as inline annotations on the changed lines:

      - name: Review-coverage annotations
        if: always()
        run: vidi report --sarif > vidi.sarif

      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: vidi.sarif

Use if: always() on both steps, so the annotations still appear when verify failed, which is exactly when they are useful.

Three rules can appear: vidi/requirement-unmet, vidi/stale, and vidi/orphan. A passing repository emits a valid document with an empty results array, which is the healthy case, not a broken one:

$ vidi report --sarif
{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","runs":[{"results":[],"tool":{"driver":{"informationUri":"https://vidivouch.com","name":"vidi","version":"0.1.2"}}}],"version":"2.1.0"}

There is no vidi/rejected-ledger-line result. A partially-decoded ledger refuses before any report is assembled, so it exits 1 with no SARIF artifact at all. The gate still fails closed; it just fails earlier and without annotations.

Machine-readable results

For a custom check-run or a dashboard, vidi verify --json emits the gate object directly:

$ vidi verify --json
{ "vidi": "0.1.2", "schema": "vidi.gate/v1", "gate": { "checkRun": "failure", "exit": 1, "policyPresent": true, "stale": 1, "orphan": 0, "requirementFailures": 0, "defaultShortfalls": 0, "rejectedLedgerLines": 0, "reviewed": 0, "total": 5456 } }

checkRun is success / failure / neutral — the GitHub check-run vocabulary, so it can be forwarded to the Checks API unchanged.

This is a different schema from vidi json (the full report, vidi.json/v1.0). The gate object carries its own schema id precisely so a consumer never validates one against the other’s schema.

Check the exit code before reading the stream. A ledger line that will not decode refuses before any report is assembled, so nothing is emitted: --json exits 1 with empty stdout and the rejected lines on stderr, and --sarif produces no artifact. The gate still fails closed, just earlier and without output.

Branch protection

Make the verify job a required status check in your branch-protection rules. Because an unconfigured repo returns NEUTRAL and exit 0, you can require the check before writing a policy and it will simply pass — then start gating by adding .vidi/policy.toml, with no CI change.

Deleting or corrupting the ledger does not quietly disable the gate, every ambiguity resolves toward failure. See Fail-closed by default.

Other CI systems

Nothing above is GitHub-specific except the SARIF upload. The whole gate is one command:

vidi verify

vidi push reads $GITHUB_REPOSITORY, $GITHUB_SHA, $GITHUB_REF_NAME and $CI_COMMIT_REF_NAME when they are set, and falls back to the git remote and HEAD otherwise, so the hosted lane works anywhere too. See Share reviews across a team.

Next

Review a pull request