codereview-loop 0.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,502 @@
1
+ Metadata-Version: 2.4
2
+ Name: codereview-loop
3
+ Version: 0.13.0
4
+ Summary: Multi-model code review loop: one agent builds, independent models review, a deterministic gate decides.
5
+ Author: Superstar Telecom LLC
6
+ License: MIT
7
+ Project-URL: Homepage, https://gitlab.com/superstartelecom/codereview
8
+ Project-URL: Issues, https://gitlab.com/superstartelecom/codereview/-/issues
9
+ Project-URL: Security, https://gitlab.com/superstartelecom/codereview/-/blob/master/SECURITY.md
10
+ Keywords: code-review,multi-agent,llm,ci,quality-gate
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Dynamic: license-file
20
+
21
+ # CodeReview
22
+
23
+ **One agent builds. Independent models review. A deterministic gate decides.**
24
+
25
+ CodeReview is a multi-model review gate for AI-written code: a panel of models from different providers reviews every change, cross-checks each other's findings, and issues a recorded pass/block verdict — so the model that wrote the code is never the only one that checked it.
26
+
27
+ CodeReview runs your change past a panel of independent AI reviewers — different providers, different model families — instead of a single model. Findings are cross-examined across families, executable claims are tested by running probes against the code, and the verdict is written to a verifiable record you keep. It ships as a CLI and an MCP server, so your coding agents can submit their own work to the gate before it merges. You bring your own API keys and assign models per role; your code goes to the providers you chose, nowhere else.
28
+
29
+ The default `strict` gate is deliberately unforgiving. In registered confirmation testing it caught every seeded defect (recall 1.00, 7 of 7; Wilson 95% [0.646, 1.000]) at 0.583 merged blocking precision (Wilson 95% [0.320, 0.807]) — and it blocked 94% of clean changes (32 of 34; Wilson 95% [0.809, 0.984]). That's the trade, stated plainly: strict is a high-recall gate for code you can't afford to get wrong, with a documented waiver path for the blocks you choose to accept. (Registered confirmation record: bench/generation-v2/RESULTS-2026-11-05.md — counting attempt 1, which FAILED its clean-change threshold; the `strict` figures are the paired row scored on that run's records.)
30
+
31
+ ```
32
+ builder ──diff──▶ deterministic checks ──▶ reviewer A (correctness/security)
33
+ (secrets, injection, │
34
+ data-loss, your tests) ▼
35
+ reviewer B (operations/reliability)
36
+ │
37
+ ┌───── gate ◀───────┘
38
+ │ PASS / BLOCKED / INCONCLUSIVE
39
+ ▼
40
+ fix or rebut ──▶ verification round ──▶ PASS only when
41
+ every reviewer agrees
42
+ the block is resolved
43
+ ```
44
+
45
+ ## Why
46
+
47
+ - **Self-review is not review.** The model that wrote the code shares its blind spots. Independent providers catch different things.
48
+ - **LLM opinions are not a gate.** Findings are structured, merged across reviewers (consensus raises confidence, never lowers severity), and the pass/block decision is made by deterministic rules over those findings.
49
+ - **Alerts about the loop itself can't flood you** — the same dedup discipline CodeReview checks for in your code is part of its own design brief (see the `alert-no-guard` rule, born from a real incident: an hourly checker that emailed on every run while the same violation persisted for weeks).
50
+
51
+
52
+ ## If something goes wrong
53
+
54
+ 1. `codereview --version`, then re-run the failing command.
55
+ 2. `codereview support-bundle` writes one redacted zip. Read it, then attach it to an issue (templates in `.github/ISSUE_TEMPLATE/`). Run records contain excerpts of reviewed code; `--no-records` leaves them out.
56
+ 3. `codereview report` shows what it would send and sends only after you confirm.
57
+ 4. A crash prints one line (`codereview: internal error ...`), writes a redacted `codereview-crash-*.json` note and exits **2**, never 0 or 1. `--debug` adds the redacted traceback.
58
+ 5. Security problems: see `SECURITY.md`; do not open a public issue.
59
+
60
+ **Privacy.** Feedback is opt-in. Nothing leaves your machine because of these features unless you run `codereview report` and confirm, or turn on the optional anonymous crash ping with `codereview telemetry enable` (off by default). Exactly what each sends: `PRIVACY.md`.
61
+
62
+ ## Quick start
63
+
64
+ ```bash
65
+ pip install codereview-loop # or: pip install -e . from a clone
66
+
67
+ export GEMINI_API_KEY=... # any two providers are a good start
68
+ export OPENAI_API_KEY=...
69
+
70
+ # Review the staged diff in the current repo
71
+ codereview --repo . --staged --provider gemini --provider openai
72
+
73
+ # Review a patch file, with your test suite as a first-class input
74
+ codereview --patch change.diff \
75
+ --provider gemini --provider xai \
76
+ --test-command "python -m pytest -q" \
77
+ --context "Adds retry logic to the payment webhook handler"
78
+
79
+ echo $? # 0 = PASS, 1 = BLOCKED, 2 = INCONCLUSIVE
80
+ ```
81
+
82
+ The report lands in `codereview-out/` as Markdown (for humans) + JSON (for tooling).
83
+
84
+ ## Intake — from issue report to solution plan
85
+
86
+ Everything above starts from a code change that already exists. Intake starts from the problem:
87
+
88
+ ```bash
89
+ codereview intake issue-report.md \
90
+ --provider gemini --provider nvidia \
91
+ --context src/alerts.py --repo .
92
+ ```
93
+
94
+ Solver models (a separate seat type that answers in Markdown, not JSON) read the issue report plus any context files and the repo's file listing, and each writes a solution plan in a fixed shape: diagnosis steps in order, ranked hypotheses — each with the test that settles it — a numbered fix plan, prevention, risks and rollback, and closure criteria a verifier can run. The first solver that answers becomes `solution.md`; the others are kept as alternates next to it, with an `intake.json` manifest recording the report's hash, what was sent, and every truncation. Reports and context are secret-redacted and size-capped before anything leaves the machine.
95
+
96
+ Intake writes no code and gates nothing: the builder reads `solution.md`, implements it, and the review loop above gates the resulting change. Exit 0 means a plan was produced; exit 2 means every solver failed.
97
+
98
+ ## Account pools — when a free quota dies, the next account reviews
99
+
100
+ Repeat a seat to give it several accounts:
101
+
102
+ ```bash
103
+ export GEMINI_API_KEY=... # account 1
104
+ export GEMINI_API_KEY_2=... # account 2
105
+ codereview --repo . --provider gemini --provider gemini --provider nvidia
106
+ ```
107
+
108
+ Accounts are tried in order; a quota, rate-limit, 5xx, or dead-key error rotates the seat to the next account. Every report records **which account served and each rotation** (from → to → error) — fallback is visible in the run history, never silently substituted. If every account fails, the seat degrades honestly and the gate says so. Custom specs sharing a name pool the same way (one account per spec, labelled by its key env).
109
+
110
+ ## Multi-step workflows
111
+
112
+ ```bash
113
+ codereview workflow --file workflow.json --repo . --out wf-out
114
+ ```
115
+
116
+ `workflow.json`: `{"steps": [{"id": "db", "title": "Add the schema", "agent_cmd": "…", "test_commands": ["…"]}]}`. Steps run in order; each step's change goes through the full review loop; on PASS the change is **committed as the step's approval record** and the workflow advances to the next step. State persists in `wf-out/workflow-state.json` — rerun to resume (approved steps are skipped), touch `wf-out/STOP` to pause between steps, and record owner waivers under the state's `waivers` map (`{step_id: [finding_id, …]}`).
117
+
118
+ **Working-tree isolation:** a step that is starting fresh requires a **clean working tree** — commit or stash local edits first; the engine stops with a clear message rather than reviewing or committing changes that are not the step's. (A resumed step keeps its own in-progress changes.) The approval commit is verified byte-for-byte against the diff the reviewers approved; if the tree changed after the final review round, the commit is refused. The commit itself is assembled in a **temporary Git index**, so a refusal never alters your real index — staged work you had before the run is exactly where you left it.
119
+
120
+ ## GitHub pull requests
121
+
122
+ ```bash
123
+ codereview pr --repo OWNER/REPO --number 123 # read-only review
124
+ codereview pr --pr https://github.com/OWNER/REPO/pull/123 --post
125
+ ```
126
+
127
+ Fetches the PR's metadata + unified diff from the GitHub API and reviews it in place. Read-only works without a token on public repos. `--post` (needs `GITHUB_TOKEN` with repo access) posts the report as a PR review comment — COMMENT by default; `--request-changes` escalates a blocked gate to REQUEST_CHANGES; the bot never auto-approves — and sets a `codereview` commit status on the PR head (success / failure / pending — `pending` is the inconclusive gate: the review reached no verdict, and the status description says so explicitly rather than leaving a silent wait). Re-run with `--verify-previous report.json` after a push to run the verification round against the new diff.
128
+
129
+ ## The loop driver — the full workflow in one command
130
+
131
+ ```bash
132
+ codereview loop --repo . \
133
+ --builder-cmd "your-coding-agent-command" \
134
+ --provider gemini --provider nvidia --max-rounds 3
135
+ ```
136
+
137
+ 1. Reviewers review the working-tree diff. **PASS → the workflow advances** (exit 0).
138
+ 2. On BLOCKED, the findings are written to `$CODEREVIEW_PACKET` (JSON) and your builder command — the coding agent — is invoked. It edits the working tree and writes its **work report** (per finding: what changed, with test evidence) to `$CODEREVIEW_RESPONSE`.
139
+ 3. A verification round checks the fixes against the new diff. PASS advances; BLOCKED loops back to the builder.
140
+ 4. After `--max-rounds` builder rounds without a PASS, the loop stops and hands back to a human. A round that cannot complete (reviewer outage) stops the loop as INCONCLUSIVE — infrastructure problems are never blamed on the builder.
141
+
142
+ Per-round reports land in `round-1/`, `round-2/`, … and `loop-result.json` summarizes the run (`advanced: true/false`).
143
+
144
+ The loop's surface is deliberately narrower than the gate command's (P6-08): it takes `--provider` (or the environment / your saved setup), `--range`/`--staged` for the diff it starts from, `--waive`, `--test-command`, `--allow-major` and `--strict` — and no `--seat-meta`, `--policy` or `--role`, because it drives its own rounds against the working tree as the builder edits it; the policy-driven surfaces (tiering, hooks, gate profile, role assignment) belong to the gate command. For the same reason its per-round records are *working* records: they carry no committed range/head, so they are not Form B chain records and cannot be release-eligible through `record-verify`. For a release record, run the gate command over a committed range.
145
+
146
+ ## The verification round
147
+
148
+ A blocked change is fixed — or rebutted with evidence — and re-checked:
149
+
150
+ ```bash
151
+ codereview --patch fix.diff \
152
+ --verify-previous codereview-out/codereview-20261103-150000.json \
153
+ --builder-response @response.md \
154
+ --provider gemini --provider openai
155
+ ```
156
+
157
+ A prior blocker clears **only when every successful reviewer agrees** it is fixed (with evidence in the new diff) or was invalid. A split verdict keeps the block. The builder can never approve their own work — that rule is the product.
158
+
159
+ ## Providers
160
+
161
+ ### First run — you set up your models
162
+
163
+ CodeReview ships **no default panel**: we don't choose the LLM for you. On first open, run:
164
+
165
+ ```bash
166
+ codereview setup
167
+ ```
168
+
169
+ The wizard walks you through it: pick one or more providers, see which environment variable must hold **your** key for each (keys are never typed into, echoed by, or stored in the config — it references env var names only), then assign a model per role (correctness, operations, verifier, tech-lead, solver), confirming each one. Your choices are saved to `~/.config/codereview/config.toml` (XDG; override with `$CODEREVIEW_CONFIG`) as *your* defaults, and every later run uses them — explicit `--provider`/`--role` flags and `$CODEREVIEW_PROVIDERS` still win per run. Re-run `codereview setup` any time to change them. With no setup and no flags, a run stops and tells you how to set up — it never quietly substitutes a vendor-chosen panel. (An interactive first review launches the wizard for you.)
170
+
171
+ The model names in the table below are the registry's *suggestions* — what the wizard offers for you to confirm or replace — not decisions the product makes.
172
+
173
+ Any two (or more) independent models, from any major vendor. Presets (each is also usable as `preset:model` to pick a specific model):
174
+
175
+ | Preset | Default model | Your key in |
176
+ |---|---|---|
177
+ | `openai` | gpt-5-mini | `OPENAI_API_KEY` |
178
+ | `gemini` | gemini-2.5-flash | `GEMINI_API_KEY` |
179
+ | `anthropic` | claude-sonnet-4-5 | `ANTHROPIC_API_KEY` |
180
+ | `deepseek` | deepseek-chat | `DEEPSEEK_API_KEY` |
181
+ | `minimax` | MiniMax-M2 | `MINIMAX_API_KEY` |
182
+ | `moonshot` | kimi-k2-0711-preview | `MOONSHOT_API_KEY` |
183
+ | `nvidia` | nvidia/nemotron-3.5-lightning-30b-a3b | `NVIDIA_API_KEY` |
184
+ | `xai` | grok-4-mini | `XAI_API_KEY` |
185
+ | `mistral` | mistral-large-latest | `MISTRAL_API_KEY` |
186
+ | `openrouter` | anthropic/claude-sonnet-4.5 | `OPENROUTER_API_KEY` |
187
+
188
+ Any other OpenAI-compatible endpoint works too, either fully custom:
189
+
190
+ ```
191
+ --provider "mine|openai-compatible|https://api.example.com/v1|model-id|MY_KEY_ENV"
192
+ ```
193
+
194
+ or via the generic escape hatch `--provider "openai-compatible:https://api.example.com/v1|model-id|MY_KEY_ENV"`. (Anthropic's native Messages API and Gemini's native API are wired directly; everything else rides the OpenAI-compatible shape most vendors now expose.)
195
+
196
+ Keys come from environment variables only — bring your own key for every provider; nothing is stored or logged. Secrets in the diff are redacted before anything is sent to a provider: the redaction is pattern-based best-effort, covering known token formats (cloud and provider API keys, private-key blocks, a bare private-key body line that carries the key's DER/OpenSSH start, credential-bearing URLs, and keyword-named assignments) — it is not a guarantee. The boundary, stated plainly: encoded forms of a key (base64, hex, URL-encoded, line-wrapped, case-transformed) are not caught, and neither are the later body lines of a headerless key fragment — only the line carrying the key's start is a recognisable shape. The diff is treated as untrusted data — reviewers are instructed to ignore instructions embedded in code and comments.
197
+
198
+ ### Pick the model for each role
199
+
200
+ Reviewing isn't one job, and one model doesn't have to do all of it. `--role <role>=<provider-spec>` assigns a provider/model per role; the role's seat joins the panel, and the assignment (seat, provider, model, vendor family) is written into the run record. Roles: `correctness` and `operations` (the two review lenses — the assigned seat serves that lens), `verifier` (the verification-round panel), `tech-lead` (the dispute adjudicator), and `solver` (intake mode's plan writer, via `codereview intake --role solver=...`). Roles you assign in `codereview setup` are your standing defaults; `--role` on a run overrides them per role for that run.
201
+
202
+ ```bash
203
+ # Strong models on the lenses, a cheap high-volume model as Tech Lead:
204
+ codereview --repo . \
205
+ --provider anthropic --provider openai \
206
+ --role tech-lead=deepseek:deepseek-reasoner
207
+
208
+ # Kimi on correctness, MiniMax on operations:
209
+ codereview --repo . \
210
+ --role correctness=moonshot --role operations=minimax
211
+ ```
212
+
213
+ Cost guidance: the Tech Lead and other high-volume seats are called once per dispute or round, so they are the natural home for the inexpensive, high-quality seats — DeepSeek, MiniMax, and Moonshot (Kimi) are first-class presets for exactly this. One independence rule is enforced by the record: each seat carries its vendor *family* from the registry (a seat built from `moonshot` is family `moonshot` whatever you name it), and the Tech Lead's family must differ from the families that raised and refuted a finding — pick the Tech Lead from a different vendor than your two reviewers.
214
+
215
+ ### Agent seats — any agent as a reviewer
216
+
217
+ A panel seat doesn't have to be a model API. Any local agent CLI can review (or solve), riding on the subscription you already have for it — no vendor API key involved:
218
+
219
+ ```bash
220
+ codereview --repo . \
221
+ --provider gemini \
222
+ --provider "agent:claude:sh examples/agent-seat-claude.sh" \
223
+ --provider "agent:grok:<your grok bot command>"
224
+ ```
225
+
226
+ The contract is the one the builder already uses: the prompt (diff + instructions) is written to a file named by `$CODEREVIEW_PROMPT` and piped to the command's stdin; whatever the agent prints on stdout is its verdict. Claude Code, Codex, Gemini CLI, a Grok bot, or your own wrapper script all fit the same seat. The command line is split like a shell line (POSIX rules) and run **without a shell** — metacharacters are literal, so anything needing pipes or redirection goes in a wrapper script (as in the example). Repeating an agent name creates distinct seats (`claude`, `claude-2`, ...), because each spec is a different agent, not another account of one seat; an agent seat may not share a name with an API seat — that configuration is rejected loudly rather than merged. A seat that exits non-zero, times out (900s for reviewers, the intake deadline for solvers), or prints nothing is recorded as failed in the report — degraded honestly, never silently replaced. Agent commands are operator-supplied configuration, like `--builder-cmd`; they are never read from the repo or the diff under review.
227
+
228
+ **The agent-seat boundary, plainly.** CodeReview runs each agent in a fresh scratch directory containing only the prompt file, with an allow-listed environment: your provider API keys are **not** passed to agent processes (only PATH/HOME/locale/proxy basics, plus any names you opt in via `CODEREVIEW_AGENT_ENV`). The seat's process group is killed when the call ends — timeout or not — and on POSIX its file writes are capped at 8 MB per file (on Windows only the direct child is killed). A seat that doesn't need login state in HOME can shed that exposure too: `agent:claude[home=private]:...` points HOME at the scratch directory. What CodeReview cannot do is govern the agent itself: a seated agent runs with **its own permissions**, and the redaction guarantee covers the prompt CodeReview sends — not files the agent chooses to read on its own. Seat only agents you would trust at your keyboard, keep their tools restricted (the example wrapper runs Claude Code with `--tools ""` — the brief is self-contained, so a reviewer seat needs no tools at all), and remember that the diff under review is untrusted input to a tool-enabled process.
229
+
230
+ ## Evidence runner — tests and scanners, executed and hashed
231
+
232
+ Reviewers judge a diff; they cannot run anything. The evidence runner is the deterministic counterpart: it executes your configured commands (tests, lint, SAST) and security adapters against the change and stores a hashed evidence record in the run JSON.
233
+
234
+ ```bash
235
+ # Attach evidence to a review:
236
+ codereview --repo . --range main...HEAD --provider gemini \
237
+ --provider nvidia --evidence-config evidence.json
238
+
239
+ # Or run evidence standalone:
240
+ codereview evidence --repo . --config evidence.json
241
+ # Verify the commands a builder's report claims were run:
242
+ codereview evidence --repo . --claims builder-report.md
243
+ ```
244
+
245
+ The config (see `examples/evidence.json`) lists `commands` (run in order; required unless marked optional), `adapters` (`gitleaks`, `semgrep`, `dependency-audit` — each skipped cleanly as `unavailable` when its tool is not installed, never a crash), and a `require` list of names policy demands be present — a required name with no configured item is recorded as `missing`. The shape deliberately matches the policy file's `[evidence]` table, so a TOML policy's evidence section drops in unchanged.
246
+
247
+ **Gate rule.** Required evidence that fails, times out, errors, or is missing **blocks** the gate. Required evidence that is merely *unavailable* (tool not installed) makes the run **inconclusive** — the machine could not produce the evidence, which is not the builder's failure. Optional evidence is recorded and never moves the gate.
248
+
249
+ **Builder-claim verification.** Reports routinely say "ran the tests — passed". The claims mode extracts the claimed commands a work report actually commits to (a backtick-quoted command with an outcome on the same line, or a `Tests:`/`Ran:`/`Command:` line with its outcome in the tail), re-runs each one in the sandbox, and compares: a claim the re-run contradicts is recorded as **downgraded**, with both the claimed and actual outcomes. Commands mentioned without a claimed outcome are not claims and are ignored, never guessed at.
250
+
251
+ **Sandbox (the threat model in one paragraph).** Evidence commands execute code from the tree under review — untrusted code — so they run confined: in a throwaway copy of the tree (`.git` excluded, deleted afterwards, so a hostile command cannot rewrite the checkout you are about to merge), with an allow-listed environment — your provider API keys, tokens, and SSH agent **never** reach an evidence process, and HOME/TMPDIR point inside the sandbox so credential files are unreachable through them; with no network by default (proxy variables stripped, and a Linux network namespace where `unshare` is available — the record states which isolation actually applied, and an item must opt in with `"allow_network": true` to run unrestricted). One boundary on the "never reach" claim: it is the namespace mode that enforces it — in the `env-only` fallback (no `unshare`), a sandboxed command running as your uid can read the environment of other same-user processes (your shell, IDE, other agents) via `/proc/<pid>/environ`, so keys another process holds are reachable there; the run warns and records the exposure when that is live, and `env-only` is not suitable for untrusted trees (THREAT-MODEL residual 7); and under resource limits (wall-clock timeout enforced by killing the process group, CPU and per-file-size rlimits, and a per-stream cap on captured output). Every item's record carries its exit code, duration, and the sha256 of its output; only a redacted tail is stored, so a scanner that prints the secret it found cannot smuggle it into the record. Commands and adapter choices come from you (the config you point the CLI at), never from the diff. The full threat model — every control, the trust boundaries, and the residual risks (no filesystem isolation; memory deliberately uncapped) stated plainly — is `THREAT-MODEL.md` at the repository root.
252
+
253
+ ## Release records — preflight, fallbacks, verification
254
+
255
+ A gate verdict you intend to *publish* (a release record) has to survive its own panel being flaky. Three mechanisms keep that honest:
256
+
257
+ - **Preflight.** `codereview doctor [--provider ...] [--fallback lens=spec]` probes every seat — primaries and fallbacks — with the *exact review request shape* (a toy probe can lie: a JSON-mode provider may reject a non-review-shaped request the real review would never send) and prints per-seat health as JSON with an error class — a dead quota or a missing key shows up *before* a run, not inside a wasted record.
258
+ - **Fallback seats.** Provider failures are classified (daily quota and auth errors are never retried; per-minute rate limits and 5xx/overloads get bounded backoff — every attempt is recorded). If a lens's primary seat still fails, a configured fallback serves it: `--fallback correctness=openai`. The substitution is printed on the report and in the gate reasons, never silent.
259
+ - **Machine-checked eligibility.** Every reviewer report records its seat's vendor **family** and pricing **tier** (`--seat-meta gemini:google:paid`). `codereview record-verify <record-dir> --repo .` then decides whether a record may be called clean: gate PASS, not degraded, tests passed, every lens served, at least two distinct vendor families, the reviewed range's patch hash unchanged, and no later commits except the one filing the record. Relatedly, `--private` refuses seats tagged with a free tier — free tiers may train on submitted code, so they don't review private repositories.
260
+ - **Fixture scope, declared and machine-checked.** Deliberately-buggy test *data* (a benchmark corpus, scripted seats' canned responses) is not product code, but silently skipping it would be worse than reviewing it. Instead, a versioned **fixture manifest** (`fixtures-manifest.json`: every fixture path, why it is intentionally vulnerable, expected finding categories, a policy version) can be declared with `--fixture-manifest`. The review still runs over the full patch; findings belonging to declared fixture data are classified in the record as **expected fixture findings** — evidence and counts preserved — and do not block the gate. The record carries the manifest's path, content hash, policy version, and classified paths, and `record-verify` recomputes all of it against the manifest committed at the recorded head: a missing, stale, or mismatched manifest fails verification. Ordinary reviews without a declared scope scan fixtures exactly like product code. A manifest entry may also declare an `expected_finding_count`: findings on that path *beyond* the count are not classified — they stay in the record as product findings, with the excess noted.
261
+ - **Coverage accounting (v0.9.3).** Every review record carries, per file and per lens, which batches covered it — derived from the actual batching the run used. `record-verify`'s `coverage_complete` check fails a record whose own accounting shows a file/lens omission (a run with omissions is also marked degraded at review time); records that predate coverage accounting report the check as not applicable rather than failing retroactively.
262
+ - **Evidence-grounded findings (v0.9.3).** A model finding must carry an `evidence_quote` copied exactly from the diff text its reviewer received; the quote is validated deterministically against that batch. A finding that fails validation is not dropped: it goes through an automatic **re-ground pass** — the raising seat (or another family's) is shown the exact file section and must confirm with a valid quote or withdraw. Confirmed findings stand (marked re-grounded), withdrawn findings stay in the record as `withdrawn_findings` with the seat's reason, and a finding no seat could re-ground stays visible and makes the gate INCONCLUSIVE — a human decides it.
263
+ - **Cross-family confirmation (v0.9.3).** A blocker/major raised by a single vendor family is put to a seat of a *different* family that served the run, with the cited file's section as evidence. Agreement leaves it blocking. A refutation counts only with a quote that validates against that section; a refuted finding becomes **disputed**: it leaves the blocking set, is listed in `disputed_findings`, and the gate may not auto-PASS — the verdict is INCONCLUSIVE and the human gate decides. A refutation without a valid quote changes nothing.
264
+ - **Round-structured records — Form B (v0.9.3).** A release record may also be a *chain*: one complete-coverage full review, then verification rounds over the fix diffs. `codereview record-verify --chain <round-1-dir> <verify-dir> [...] --repo .` checks the whole chain: ranges contiguous from base to HEAD with patch hashes recomputed, every round non-degraded with two families, round 1's coverage complete, every round-1 blocker/major cleared on the record by a verification round, final gate PASS with tests passing and the final head current. The adopted convergence budget (at most 3 verification rounds, non-increasing new-blocker counts) is reported as WARN lines — a process flag for the owner — not as an eligibility failure.
265
+ - **Tier attestation (v0.9.3).** Records carry the run's own `private` declaration (from `--private`) alongside each seat's tier. `record-verify`'s `tier_policy` check fails exactly one conjunction: a `private=true` record with a *successful* free-tier seat. Public runs attest `private=false` and are unaffected; records without a declaration report the check as not applicable.
266
+ - **Content-addressed precedent (v0.9.3).** Re-litigating a settled rebuttal every round wastes the panel; trusting prose dispositions would let a builder talk the gate down. The middle path is a **precedent store** (`precedents.json`, passed with `--precedents`): each entry keys a finding fingerprint + cited path to the cited file's git blob at disposition time. A matching finding is classified **standing** — listed in `standing_findings` with its repeat count and disposition link, non-blocking — *only while that file's blob is unchanged*; any edit retires the precedent automatically and the finding is adjudicated fresh. `record-verify` recomputes the store hash and every link against the recorded head, so a stale or forged link fails.
267
+
268
+ ## Dispositions and the open-dispute inventory
269
+
270
+ A refutation is not a bug to be solved: it is a dispute to be
271
+ dispositioned. Until a human ruling exists, a finding whose
272
+ refutation is `disputed` or `refutation_weak` stays open. The
273
+ tooling here makes that state computable instead of
274
+ hand-maintained (the inventory used to be re-derived by reading
275
+ every record by hand, repeatedly).
276
+
277
+ codereview disputes [--repo DIR] [--store PATH] [--json]
278
+ codereview disputes --record entry.json [--store PATH]
279
+ codereview disputes --check --span A..B [--inventory FILE]
280
+
281
+ - **Computed inventory.** `codereview disputes` scans every
282
+ `review-*/` record (JSON plus evidence sidecars), collects the
283
+ findings whose dispute is not closed in the disposition store,
284
+ and prints them grouped by record with fingerprints, severity,
285
+ lens, raiser/refuter families, and the authority-affecting tag
286
+ below. `--json` emits the same inventory for tooling. Run
287
+ summaries also carry an `open_disputes` count and fingerprint
288
+ list, so a finished review names what it leaves open.
289
+ - **Append-only store.** `dispositions.jsonl` at the repository
290
+ root holds one JSON entry per dispositioned finding — nothing
291
+ is edited or deleted. An entry carries the finding
292
+ fingerprint; a `type` (`sustain_refutation`, `sustain_finding`,
293
+ `split`, `fixed` — which requires the fixing `commit` —
294
+ `retired_archived_subject`, `retired_out_of_scope`,
295
+ `superseded`, `downgraded`); the `decider` (`owner`,
296
+ `tech_lead`, or `probe`); a `date`; and the reasoning-quality
297
+ fields `reason_check` (`verified` / `unverified` /
298
+ `contradicted`), `reason_corrected`, and `basis_evidence`
299
+ (sha256 hashes of the evidence the reasoning was checked
300
+ against) — a ruling whose mechanism was wrong is a good
301
+ outcome and a bad precedent, so the record keeps both. A
302
+ fingerprint can be dispositioned only once, unless the new
303
+ entry is marked `override` and names the `supersedes` hash of
304
+ the entry it replaces; `codereview disputes --record` validates
305
+ all of this on append.
306
+ - **Verification consults the store.** `record-verify`
307
+ automatically loads `dispositions.jsonl` from the repository
308
+ root (or `--dispositions PATH`): a disputed finding with a
309
+ store entry counts as dispositioned, and the verdict names the
310
+ entries consumed. When no store exists, verification behaves
311
+ exactly as it always has.
312
+ - **Freshness check (the release-flow gate).** Before a release
313
+ is cut, run `codereview disputes --check --span <base>..<head>`.
314
+ It exits non-zero and lists every dispute ID in records
315
+ touching the span that is neither dispositioned in the store
316
+ nor present in a caller-supplied inventory file
317
+ (`--inventory inv.json`, e.g. a fresh `disputes --json`
318
+ snapshot): the release's dispute state is either ruled,
319
+ listed, or the release waits. The check is a command plus this
320
+ documentation — it is deliberately not wired into any
321
+ automated release script.
322
+
323
+ ### Authority-affecting findings
324
+
325
+ The older "protected class" is now defined deterministically. A
326
+ finding is **authority-affecting** if it could change:
327
+
328
+ - **who decides** — role/seat assignment, the Tech Lead, or the
329
+ adjudication path;
330
+ - **whether independence holds** — family attribution;
331
+ - **whether a precedent or decision is valid** — precedent keys,
332
+ the decision cache, verdict records as subjects;
333
+ - **whether eligibility passes** — coverage, quorum, finder
334
+ confirmation, record-verify, gate logic;
335
+ - **what leaves the machine** — redaction, tier policy,
336
+ `--private`, public record surfaces.
337
+
338
+ Explicitly **not** authority-affecting: report rendering and
339
+ prompt display truncation (storage is full-text; the public view
340
+ is display-only). The tagger applies this definition over a
341
+ finding's title, category, and file path; a manual override list
342
+ (`{"authority": [...], "not_authority": [...]}` JSON) always
343
+ wins. The disputes inventory prints the tag and the dimensions
344
+ matched, so a tag is auditable rather than asserted.
345
+
346
+ ### Quote anchoring and `locate`
347
+
348
+ Reviewers cite line numbers in two numberings — new-side blob
349
+ lines and diff-relative indices — and nothing used to say which.
350
+ Findings now carry `line_basis` (`blob` or `diff`) whenever the
351
+ cited line and quote determine exactly one numbering;
352
+ deterministic-scan findings are always `blob`. To resolve a
353
+ citation back to the reviewed commit:
354
+
355
+ codereview locate <finding-id> --record <record.json|dir>
356
+
357
+ `locate` takes the finding's validated quote (the evidence
358
+ sidecar's full text when the record's display copy is truncated)
359
+ and prints every blob line at the reviewed commit whose text
360
+ matches it, plus whether the cited line itself resolves. Legacy
361
+ findings without `line_basis` — or records without a head — still
362
+ locate by quote alone.
363
+
364
+ ## Benchmark — prove it, don't pitch it
365
+
366
+ CodeReview's claim is measured, not asserted. The benchmark (Epic A) runs named panel configurations over a labelled corpus of diffs and scores them against ground truth:
367
+
368
+ ```bash
369
+ codereview bench run --config panel-gemini-nvidia --split public
370
+ codereview bench report # full report; --public excludes the holdout split
371
+ ```
372
+
373
+ Corpus v0 ships in `bench/corpus/`: 36 cases (29 defect cases with 31 ground-truth defects, 7 clean controls; 27 public / 9 holdout), mixing authored defect injections (secrets, injection, auth regressions, payment bugs, races) with cases converted from this repo's own run records. The headline metric is the **escaped-defect rate**: defects no finding blocked on — unseen, or reported only as a minor/nit so the gate would have shipped them. Reports also carry recall, precision, clean-case BLOCKED and any-finding rates, cost per verdict, and p50/p95 latency; `bench report --max-escaped-rate R` works as a CI regression gate. Scripted replay seats (`oracle` / `blind` / `noisy` configs) run the whole harness deterministically with zero provider calls — their scores validate the harness, not any model. Definitions, corpus provenance, and the growth path to 50–100 cases live in `bench/README.md`. Anything public quotes the public split only: holdout records are dropped before scoring in `--public` reports, so no holdout case can leak into a claim.
374
+
375
+ ## SARIF export — findings where your other tools live
376
+
377
+ Any saved record exports as a SARIF 2.1.0 log (Epic D, ticket D1) — the format GitHub code scanning and most static-analysis surfaces ingest:
378
+
379
+ ```bash
380
+ codereview sarif review-out --out results.sarif # or omit --out for stdout
381
+ ```
382
+
383
+ The export carries the record's **open** findings: the merged findings, a verification record's still-open `remaining`, and the human-decision lists (disputed, refutation-weak, escalated), each result tagged with its adjudication state. Resolved or classified-away findings (cleared, withdrawn, fixture, precedent-standing) stay in the record, not in the alert list. Deterministic-scan findings keep their scan rule ids (`eval-exec`, …) with the scan's own descriptions; model findings group under `model-<category>` rules. Severities map blocker → `error`, major → `warning`, minor/nit → `note`; results carry the record's finding id as their fingerprint, the cited line as the region snippet, and the recommendation in the result properties. The gate and patch hash ride in the run's property bag.
384
+
385
+ ## Policy: supported platforms, severity rubric, disputes
386
+
387
+ **Supported platforms.** The project supports POSIX operating systems with Python 3.11 or newer:
388
+
389
+ ```toml
390
+ supported = { os = ["posix"], python = ">=3.11" }
391
+ ```
392
+
393
+ A finding that depends only on an unsupported platform (for example Windows drive-letter path behaviour) is auto-labelled `out-of-scope (documented)` in the record: it stays visible in `out_of_scope_findings`, with the reason recorded, and it never blocks the gate. A finding that also claims impact on a supported platform is never labelled.
394
+
395
+ **Severity rubric.** A `blocker` or `major` requires a demonstrated, reachable defect with impact in a supported configuration, and the finding's evidence must cite the reachable path — the input, state, or call sequence that reaches the defect. Claims that cannot be traced to a reachable path, or that depend on unsupported platforms, on operator-supplied input being malicious, or on hypothetical future seat kinds, are `minor` or `nit` at most. The rubric is stated in the reviewer prompts; any label the rubric drives is recorded on the finding with its reason — severities are never silently downgraded.
396
+
397
+ **Dispute ladder and the Tech Lead.** When one vendor family refutes another family's blocking finding with quote evidence, the refutation is first graded for *adequacy*: a refutation whose quote merely repeats the line the finding itself cites carries no counter-evidence and is recorded as `refutation-weak` — a routing state, not a verdict. Every refuted finding (disputed or refutation-weak) then goes to the **Tech Lead**: a seat from a family that neither raised nor refuted the finding, which makes the *final* decision — there is no majority vote, and the project owner is the appeal, not the routine judge. The Tech Lead receives the claim, the cited code region, both positions with their quotes, and any builder-probe results, and decides one of:
398
+
399
+ - `sustain_finding` — the defect stands (the Tech Lead may *downgrade* its severity under the rubric above, never raise it);
400
+ - `sustain_refutation` — the claim is wrong and the refutation stands;
401
+ - `escalate` — the evidence is genuinely insufficient, or the panel degraded, and the case goes to the owner as an appeal/exception, with the reason recorded.
402
+
403
+ A sustain decision counts only with a quote that validates against the cited region. The Tech Lead may first send a case back for a **builder probe** (`needs_probe`) and then decide on the combined evidence; a probe can only rebut, never decide alone. The decision, the Tech Lead's family and tier, and all positions are stored on the record, and a sustained refutation or a rubric downgrade is precedent-eligible (it feeds the precedent store below). A seat can be designated for the role with `--tech-lead`; a designation whose family raised or refuted the finding is rejected and substituted — loudly, on the record, never silently accepted. When no independent Tech Lead served, the finding keeps its disputed/refutation-weak state for the human gate, and a run with such findings cannot auto-PASS.
404
+
405
+ **Precedents.** A dispositioned finding can be stored as a precedent keyed to content, not names: file-level entries hold the cited file's git blob at disposition time, and an entry may additionally key to the *enclosing function's* normalized AST hash (plus its direct callees' hashes). A function-level precedent survives unrelated edits elsewhere in the file; an edit inside the judged function — or one of its callees — re-opens the finding for fresh adjudication. When the function cannot be resolved, matching falls back to the file blob. Standing precedents are shown in the record, never hidden.
406
+
407
+ ## Gate semantics
408
+
409
+ | Gate | Meaning |
410
+ |---|---|
411
+ | `PASS` | No blocking findings (blockers, and majors by default; `--allow-major` relaxes majors to advisory), and no finding waiting on a human-decision state |
412
+ | `BLOCKED` | A blocking finding stands — from a model reviewer, a deterministic rule, or a failing test command |
413
+ | `INCONCLUSIVE` | Reviewers unavailable, or (with `--strict`) the review was degraded/truncated — a human decides. Also the verdict when a finding is **disputed** (refuted cross-family with quote evidence) or could not be **re-grounded**: neither state auto-clears or auto-blocks; both route to the human gate |
414
+
415
+ Provider failure degrades the review (noted in the report); it never silently passes a change, and in `--strict` mode it blocks the pass.
416
+
417
+ ## The verdict record — schema v1.0.0
418
+
419
+ Every surface — CLI, MCP server (`run_gate` / `get_verdict`), GitHub adapter, agent skills, the loop/workflow drivers — calls the same core, and the core emits **one versioned JSON verdict record** (`codereview-out/*.json`, next to the Markdown report). The contract is locked as **schema v1.0.0**: the definition and the stdlib-only validator live in `src/codereview/record_schema.py` (`RECORD_SCHEMA`, `validate_record`), the core validates every record at emission and again before it is written to disk, and golden records for all three verdicts live in `tests/fixtures/record_schema/`.
420
+
421
+ A v1 record carries, alongside the historical fields (the `schema: "codereview.report/1"` tag is unchanged; `schema_version: "1.0.0"` rides next to it):
422
+
423
+ - **Verdict, in both spellings:** `gate` (legacy lowercase: `pass` / `blocked` / `inconclusive`) and `verdict` (uppercase: `PASS` / `BLOCKED` / `INCONCLUSIVE`) — they must agree. **Exit/status semantics are stable:** CLI exit code **0 = PASS, 1 = BLOCKED, 2 = INCONCLUSIVE** (2 is also the code for a run that could not produce a record at all); the MCP `run_gate` payload returns the same verdict plus `exit_code`, and `get_verdict` reads the same record back.
424
+ - **MCP server threading limit:** `codereview mcp serve` handles one request at a time — a `run_gate` call runs to completion before the next frame is read, so a `notifications/cancelled` sent mid-run cannot interrupt it; the notification is read only after the run finishes. Long runs should be sized with the client's own timeout in mind.
425
+ - **What PASS means — and does not.** The record's `verdict_semantics` section states it in the record itself: a PASS means *no actionable issue was found under the checks that ran*. It is **not a correctness guarantee**.
426
+ - **Checks that did not run are visible:** `checks` lists every check in exactly one of `run` / `not_run` / `unavailable` (attempted, but produced no result — e.g. every reviewer seat errored). A PASS over a partial check set reads as one.
427
+ - **Findings with evidence:** each finding carries its severity, category, file/line, evidence quote, and (when stamped) its `evidence_class` — E0 deterministic, E2 probe-confirmed, E1 corroborated, E3 single-source. Absent means unstamped, never silently E0.
428
+ - **Panel composition and health:** `panel` lists each seat (provider, lens, vendor family, tier, status), the distinct families, and the degraded state; `degraded` + `degraded_causes` on the record say *why* a run degraded. Degraded-provider behaviour is never substituted silently — fallbacks are recorded per seat as before.
429
+ - **Scope, provenance, identity:** `scope` (files, patch hash, git `range`/`head` when the caller recorded them), `provenance` (generator + product version + patch hash), and `record_identity` — a content-hash `id` stamped at emission, plus the record's file name stamped by the writer (the full path travels outside the record, e.g. as the MCP payload's `record_path`).
430
+ - **Cost and duration:** `duration_ms` is the core's wall clock for the round. `cost` is explicit rather than absent: `{"status": "not_available", ...}` — core providers return review text only and report no usage to the core, so the core invents no figure. (The benchmark wrapper estimates cost from its own seat prices, outside this record, under its separate `codereview.benchresult/1` schema.)
431
+ - **Waivers:** `waivers` lists the waivers the caller recorded for the round, verbatim.
432
+
433
+ Known, documented gaps (in the schema, not hidden): the **verification round** recomputes no per-file coverage and collects no evidence/policy sections — its record lists `coverage` / `evidence` / `policy` under `checks.not_run`, and its `waivers` is always empty (verification waivers live in the loop/workflow state). The **loop summary** record (`mode: "loop"`) is a container of verdict records, not itself a v1 verdict record. Records written before v1.0.0 carry the legacy tag without `schema_version` — they are legacy records, not corrupt ones, and `record-verify`'s eligibility checks (a separate concern from schema validation) judge them by the standard of their era.
434
+
435
+ ## Gate profiles
436
+
437
+ The gate's admission rule for blocking findings is a **profile**: set it per project in `.codereview.toml` (`[gate] profile`) or per run with `--gate-profile` (the flag wins over the file; the file wins over the default). Every record names the effective profile and where it came from in its `gate_profile` section.
438
+
439
+ - **`strict`** — the default, and the historical behaviour: every blocking-severity finding blocks, whoever raised it. Stated plainly: on our registered benchmark the default gate blocks most clean changes (32 of 34 clean cases under `strict`); it favours recall over precision. Use a waiver, or the opt-in `corroborated` profile below, if that trade-off is unworkable for a change.
440
+ - **`balanced`** — policy **B1** (per-lens corroboration): a blocking finding whose merged sources are *solely the operations lens* is demoted to a visible minor unless a correctness-lens seat or the deterministic layer also reported the same defect — the gate's merge is the live grouping for "same defect". Demote, never delete: the finding stays in the record with its demotion and reason (`profile_demotion`), and correctness-lens and deterministic findings are never gated by the rule. Every merged finding also carries evidence-class metadata — E0 (deterministic source), E1 (corroborated across distinct families), E3 (single-source, unverified); E2 (probe-confirmed) is assigned only when a probe result already rides on the finding, because probes run later, in adjudication. The classes are metadata only under `strict` and `balanced`: they change no verdict except through B1.
441
+ - **`corroborated`** — policy **B2** (evidence-class blocking, all lenses; opt-in; **its registered confirmation ran on 2026-11-06 and FAILED its primary threshold — dev evidence only**): a blocking finding may block only with evidence class E0, E2, or E1. A single-source (E3) major is sent to a seat of another vendor family for confirmation with a validated deciding quote: confirmed promotes it to E1, refuted demotes it, and anything else leaves it a visible, counted, non-blocking **unverified major**. A blocking model finding must also carry a validated quote and a concrete `trigger` (the input, state, or call sequence that makes the code misbehave); missing or vague caps it at minor. If the corroborating family is unavailable the run is marked degraded. The profile requires at least two distinct vendor families able to serve the correctness lens — single-family and single-seat setups keep `strict`. B2 runs with its own versioned reviewer prompt (intent-relative review, severity calibration examples, the `trigger` field); the prompt version and hash ride in the record's `gate_profile` section, alongside the evidence-class counts and confirmation outcomes.
442
+ - **`quiet`** — *not shipped, by decision.* The original design's third profile (blanket corroboration for every lens) was tested by replay and rejected: it demoted three real single-seat majors (a destructive health-check delete, an SSRF fetch with no timeout, an unbounded response body). Naming it — on the command line, in a policy file, or in the API — is an error.
443
+
444
+ Where B1 comes from, stated as replay rather than as a live result: in the step-4 replay over the stored baseline run (public split; exploration happened on that split only), B1 held blocking recall at 29/34 on the adjudication-enlarged ground truth (24/24 on the originally labelled defects), reached merged blocking precision 1.000 excluding unknowns (0.967 including them), and cut clean-case BLOCKED from 2/5 to 1/5. Live grouping is the merge identity, not the replay's adjudicated semantic grouping, so live behaviour sits between `strict` and that row. Baseline v2's registered confirmation (2026-11-05, attempt 1) then measured B1 live: precision 0.875 and recall 7/7 on the defect stratum, but clean-case BLOCKED 22/34 — B1 was **not** confirmed, and the default stays `strict`. The follow-up is policy B2 above, developed on the consumed sets as dev data; its registered confirmation ran on 2026-11-06 on fresh sealed sets and **FAILED** on blocking-tier recall (7/24 = 0.292 against the ≥ 0.60 threshold; clean-BLOCKED passed at 6/64), so B2 is not confirmed either, and the default stays `strict`. The baseline and its required caveat: bench/BASELINE-2026-11-04.md.
445
+
446
+ ## Project policy — `.codereview.toml`
447
+
448
+ A repository can carry its own review contract in `.codereview.toml` at its root (the review command discovers it there; `--policy PATH` overrides). The policy is the *project's* rules — distinct from your per-user setup, which only chooses your models. The gate enforces it on every review, and the record lists every rule that applied and every violation found. A blocking violation turns a PASS into BLOCKED; an `advisory` path-rule violation is recorded only.
449
+
450
+ **Discovered-policy trust.** A policy the command *discovers* in the repository under review comes from the tree itself, so it is honoured only where it can strengthen a review: panel minimums, evidence requirements, path `require_*` rules, strict waiver terms. Its `[hooks]`, a weaker-than-`strict` gate profile, `advisory` path rules, waiver leniency, `[diversity]` degrade/exclusion and `[risk]` tiering are ignored — dropped to defaults before the run, named in the record's `policy.ignored_keys`, and counted in a stderr warning. To honour a repository's policy in full, pass `--trust-repo-policy`; a policy you name yourself with `--policy` is always honoured in full.
451
+
452
+ ```toml
453
+ [panel]
454
+ min_seats = 2 # successful seats required
455
+ min_families = 2 # distinct vendor families required
456
+ required_families = ["google"] # families that must serve
457
+ require_lenses = ["correctness", "operations"]
458
+
459
+ [evidence]
460
+ require_tests_pass = true # tests must run and pass
461
+
462
+ [gate]
463
+ profile = "strict" # gate profile: "strict",
464
+ # "balanced" (policy B1) or
465
+ # "corroborated" (policy B2 —
466
+ # see Gate profiles above)
467
+
468
+ [[paths]] # extra rules for sensitive paths
469
+ match = "auth/**"
470
+ require_full_panel = true # every configured seat must serve
471
+ require_tests_pass = true
472
+
473
+ [waivers]
474
+ require_reason = true # a waiver must say why
475
+ max_age_days = 30 # ... and it expires
476
+ ```
477
+
478
+ **Lifecycle hooks.** A `[hooks]` table may declare shell commands (`pre_run`, `post_verdict`, `on_blocked`) that fire around a run. Every hook receives the event name and the record path in the `CODEREVIEW_HOOK_EVENT` / `CODEREVIEW_HOOK_RECORD` environment variables. The values are also the positional parameters `$1` / `$2` *of the hook command string itself*: a command written as a shell string — `"/path/hook.sh \"$1\" \"$2\""` — forwards them to the script, while a bare script path (`"/path/hook.sh"`) is run with no arguments and must read the environment variables instead. Hooks run sandboxed, are recorded in the record's `hooks` section, and can never change the verdict.
479
+
480
+ Every key is optional; omitted keys take the defaults (no requirement). An invalid file stops the run with a line-referenced error — a broken policy is never silently ignored. Check a file and see the effective policy with defaults filled in:
481
+
482
+ ```console
483
+ $ codereview policy check
484
+ ```
485
+
486
+ ## Deterministic checks (no model required)
487
+
488
+ Hardcoded secrets · AWS keys · private keys · `eval`/`exec` · `DROP TABLE` · string-built SQL · `DELETE` without `WHERE` · shell execution · alerts/notifications with no dedup/cooldown/state guard · disabled tests · debug leftovers · your own test commands as blockers.
489
+
490
+ ## Status
491
+
492
+ v0.4 — the core loop (review → gate → verify), the loop driver, account pools, the multi-step workflow engine, and GitHub PR integration are complete, unit-tested, and live-verified against real providers. On the roadmap: a hosted dashboard with team policies and history, notifications, reviewer calibration stats, and more provider adapters. See [ROADMAP.md](ROADMAP.md).
493
+
494
+ ## Honest limits
495
+
496
+ - **Diff-only review can false-positive** when correctness depends on code outside the hunk (helpers, state semantics). The verification round — builder rebuttal with quoted evidence, reviewers must agree — is the designed correction, and full-file context modes are on the roadmap.
497
+ - **Reviewers are models.** They miss things and occasionally invent them. The deterministic layer and your tests are not optional extras; they are half the gate.
498
+ - **A degraded review is labeled, never hidden.** If a provider is down, the report says so; `--strict` turns degradation into INCONCLUSIVE instead of a pass.
499
+
500
+ ## License
501
+
502
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,44 @@
1
+ codereview/__init__.py,sha256=PoTKgL3YpodzysxhF4y214TMcufu_7RABbz5fYOltnY,208
2
+ codereview/__main__.py,sha256=8_vZmM-d7CCqWntQvLX7CJO1AloOGE2lj-d6uWHLz9Q,95
3
+ codereview/acceptance.py,sha256=JDzK5EzSEAcUWYAu_HwjCamLqZslLAzv5BWomctoLiU,27205
4
+ codereview/agentsmd.py,sha256=qZCyX0czGc-sg2qf1LLM7rziy0bpRJi-tEc25j7yPs8,10240
5
+ codereview/bench.py,sha256=fmhIvGBLf-mMJnx12xf2snImGHVHznl-S9UnwJp0eXM,38528
6
+ codereview/claims.py,sha256=QSqsYg1K8fojV622ZeRA4ZYZU4kOYqirjzx5RgrFOvA,8124
7
+ codereview/cli.py,sha256=53_1CBhgynVWKk_YFwmle6hV9T8pa2L_MBPmebsNaLw,133980
8
+ codereview/core.py,sha256=qX-XcDQS42GTpJHldq5_rYYRE0sMeSNrThHICNE2GKY,417989
9
+ codereview/crash.py,sha256=i82W0EKED17HsLtoBQJh3wsz4ouufK5PtNfQYWBl0rU,7691
10
+ codereview/discovery.py,sha256=dugW0gUJKydbsnlRsR2S4ErKntD7Q-RJVqfJh1x8990,11939
11
+ codereview/disputes.py,sha256=NSWzU-wO38tQr-VBse70WvJOhTgQIKcZGga7qaFEMu4,29775
12
+ codereview/diversity.py,sha256=A2kuyKEl03s-L5C8vto6cucWnfnb8sR8e8c_H5lHVPI,8154
13
+ codereview/evidence.py,sha256=p6gq9SKSaiLXo0DgkU19jXjnPbQqtiO8HVzmmZupQ0E,43360
14
+ codereview/feedback.py,sha256=8DPjhEvpaQU18CBrqNZergzpW4COic5UPXBfF_ONPSA,15447
15
+ codereview/github.py,sha256=mBpSWfULJAPzQMkK1OSDhA-U9xDQCNO6SglZljQDYKE,11906
16
+ codereview/hardening.py,sha256=EmMtc2GtoFcd9rBAN6dBJeidLpvC6D11Co98-jPySWs,1559
17
+ codereview/herdrhost.py,sha256=3mlI0YP0ATcnOwe0fo9n2v6-GTQ_XCPHiw1epkv5DJA,15792
18
+ codereview/hooks.py,sha256=0PLD2SmMofkQMs5Gb_QuHtoekah_pN9DAQPPjzHlPkU,11106
19
+ codereview/intake.py,sha256=cWNdco05PHDp-wB9hDVLZejwfegkwJ54r6LFDy2JV8w,9633
20
+ codereview/mcp_tools.py,sha256=AD4O41x0xe7iQruk3wQ3iE8sUXfDavyNsvIhm4rO07Q,18137
21
+ codereview/mcp_transport.py,sha256=K_vSO6ggIZZzaIKHl6uEaONm8bioN5VFMrMN0AXPRNE,11128
22
+ codereview/notify.py,sha256=NDWrpNqfiQ-o075_t5w6CKk9x2XH4F_R2OEMC4SnwYQ,9292
23
+ codereview/pilot.py,sha256=_SZ7iTyM9ouYVI4mcwu3YRgLlUL6K_5d70_thVVBwJo,23721
24
+ codereview/policy.py,sha256=HqAq6IVXj-eEqDoD7TJ1IxqgWWmjodw8iFj23VSkun8,48090
25
+ codereview/probe.py,sha256=5gsYKvbYXyrjl2IcNYAdSPgHjrqLLCEDFqXSeI00Szo,22485
26
+ codereview/providers.py,sha256=bUC9Q3EyjB5pFBEyA81LrSKtvzgm06Wk_cimcWMEQo0,72624
27
+ codereview/record_schema.py,sha256=-R9v9OwL-8P0b46RbnJdUpenDKRUibdqVevTlYnyrkU,40073
28
+ codereview/registry.py,sha256=Aujh_8-Xxx32VNGuloyyMyTMRjBfJHqk_qwY9gL4hkA,11979
29
+ codereview/risk.py,sha256=97O3pnVHoziHiPegBuhTy8Q5y7LkatPxJnckJ6Q1pa8,13926
30
+ codereview/sarif.py,sha256=27ImwgpMZPFjJxgAaROfxCa2rPJ2BQr3I8yt2NVynWk,12384
31
+ codereview/seatstate.py,sha256=ceU-LS6Kge5wE4sDRRlde8fjSkI6x2kEOaAIx_VxECY,17386
32
+ codereview/setup_wizard.py,sha256=W1uf2HCw2N9apDn_lg5PCpUD9V8fqEWQl6vuo8F4_XY,15628
33
+ codereview/support.py,sha256=mmPZgZiEJZitCU0ajl3qAI-c8pzIt_p2Mb3IoUFsrwU,12542
34
+ codereview/techlead.py,sha256=h0w-CL5BNvj1YlOu2Q-bGsuERnCit0D3r7lIwk2wG8M,45312
35
+ codereview/tiering.py,sha256=Pe3MRdxzLanN9qRB7uYX4q134WhgrY5ahVN5IWZk_kI,10970
36
+ codereview/userconfig.py,sha256=FllP5PduM6Api34DATNAlaRdLJH6tLMcVtMLBdVquDc,12214
37
+ codereview_loop-0.13.0.dist-info/licenses/LICENSE,sha256=SG_GFUP2WVBLmAwgmho0F5Dz6FsfKW3uWWbCvnGBGAs,1078
38
+ reviewloop/__init__.py,sha256=HMg8ucYnr8NvoZt-Wi6UnUoZZRHo9pflAibIu36NsQE,288
39
+ reviewloop/__main__.py,sha256=8226rkRBQiRMkmLPl_lbMZuhWqAobmydmn729TxRjU4,157
40
+ codereview_loop-0.13.0.dist-info/METADATA,sha256=70aJ2bFcKZf6ulR4KOlVnmzPbx_4z_Tj2_tEYDXTctc,54323
41
+ codereview_loop-0.13.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
42
+ codereview_loop-0.13.0.dist-info/entry_points.txt,sha256=Eg4SFvgaadV465Z6XQptU13CReP266l-sHMR_bsAC3I,100
43
+ codereview_loop-0.13.0.dist-info/top_level.txt,sha256=P3yTLTH0YPWZM2LOuNy2Z_p-nQp7LTI1YjNLsh1nzKQ,22
44
+ codereview_loop-0.13.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ codereview = codereview.cli:console_main
3
+ reviewloop = codereview.cli:console_main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Superstar Telecom LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ codereview
2
+ reviewloop
reviewloop/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Backwards-compatible alias — ReviewLoop was renamed CodeReview
2
+ in v0.5.0. New code should import `codereview`."""
3
+
4
+ from codereview import * # noqa: F401,F403
5
+ from codereview import cli, core, github, providers # noqa: F401
6
+ from codereview import VERSION, __version__ # noqa: F401