The Go gopher as a curly-haired Renaissance schemer in an embroidered doublet, arms spread in triumph over two silver goblets of wine, a wheel of cheese and a dagger, on a rocky clifftop above the sea.
> vizzini_

AI code review that never takes the model's word for it

Vizzini packs your diff into token-sized chunks, asks any model behind a LiteLLM proxy for findings, and checks every answer against a CUE schema before it reaches you. If an answer fails, the error goes back to the model to fix. Written in pure Go as a static binary, ready to embed in mrman or run offline in a microVM.

Get StartedHow It WorksGitHub →
git clone https://github.com/infrashift/vizzini-pr-reviewer && cd vizzini-pr-reviewer && make buildNeeds Go 1.25+. Not released yet: the pr-review CLI and pkg/reviewer API work today, and a first tag is coming soon. Try it from source.
what the model sees  ·  annotated diff
### internal/store/users.go (modified)
@@ -41,6 +41,7 @@ func (s *Store) FindUser(ctx context.Context, name string) (*User, error) {
41 41       if name == "" {
42 42           return nil, ErrEmptyName
43 43       }
44    -     row := s.db.QueryRowContext(ctx, "SELECT id, name FROM users WHERE name = $1", name)
   44 +     q := "SELECT id, name FROM users WHERE name = '" + name + "'"
   45 +     row := s.db.QueryRowContext(ctx, q)
45 46       var u User
46 47       return &u, row.Scan(&u.ID, &u.Name)
what you get  ·  validated finding
{
  "summary": "FindUser now builds its SQL by string concatenation instead of a bound parameter.",
  "risk_score": 92,
  "findings": [
    {
      "file": "internal/store/users.go",
      "start_line": 44,
      "end_line": 45,
      "severity": "Critical",
      "category": "Security",
      "title": "SQL injection via string-built query",
      "body": "name is concatenated into the query, so a name like ' OR '1'='1 returns arbitrary rows. The previous version bound it as $1.",
      "suggestion": "row := s.db.QueryRowContext(ctx, \"SELECT id, name FROM users WHERE name = $1\", name)"
    }
  ]
}

Left: the diff exactly as Vizzini renders it for the prompt, with old and new line numbers so findings point at real lines. Right: a review that passes schema/review.cue. Both are generated by Vizzini's own code.

Why Vizzini?

⚖

Every Answer Checked

Responses are unified with closed CUE definitions. A "High" where the schema says "Major", an end_line before its start_line, or an invented field is rejected, and the error goes back to the model to fix.

✂

Token-Aware Chunking

Diffs are packed into chunks within a tiktoken budget. Large files split only between hunks, never in the middle of one, and the token counts are exact, not estimated.

📍

Line-Anchored Findings

Every line in a hunk is labeled with its old and new line number, so the model points at lines that exist instead of guessing at offsets.

🔌

Any Model, One Proxy

Speaks the OpenAI chat API to your internal LiteLLM proxy in JSON mode. Retries transient failures with backoff, and never retries a client error.

📦

Pure Go, Static Binary

CGO_ENABLED=0 everywhere. Drop it into a minimal UBI9 image or a QEMU microVM with no libc dependency.

🔒

Offline by Design

Tokenizer encodings are pinned by checksum and pre-seeded, so token counting never needs the network. The test suite blocks network access to prove it.

Where it's headed

Each milestone is tracked as an issue and lands as its own tested commit.

Engine coreDiff parsing, chunking, CUE schema, LiteLLM client
Done
CLI & Go APIdescribe · review · improve
Done
Policy gateExit 2 on Critical/Major
Done
Lint + AIgolangci-lint findings in the prompt
Done
Risk scoringBlast radius & auto-approve
Done
OKF groundingCite your design decisions
Done

Never go up against a CUE schema when JSON is on the line

The model can be as clever as it likes. Vizzini only accepts answers that unify with the schema, and anything else goes back to the model to fix before you ever see it.

Review SchemaHow It Works