Skip to main content
Glama

BlindReview MCP

ARCHIVED (2026-10-04). This was a research experiment and it is finished. The code still works, but it is not maintained.

Result in short:

  • v0.2–v0.3, blind-first vs ordinary review: the hypothesis was not confirmed. Spending extra compute (two passes) helped a little; blindness itself gave no advantage. The real problem we found was false interventions: on correct proposals the reviewer asked for changes about 61% of the time while catching ~100% of defects.

  • v0.4, Decision Judge with an Evidence Gate (the reviewer may only intervene with a mechanically confirmed claim: failing test, typecheck error or search hit): on 20 repo-snapshot cases with two cheap models it caught 95–100% of testable defects with 0% false interventions. But recall on untestable (architectural) defects fell to 8–20%. The harness gate itself barely changed verdicts; the effect came from the "prove it with tools" format. Our correct cases also turned out not to be tempting, so the baseline judge rarely intervened either (5–14%), and a follow-up set built to provoke nitpicks did not provoke them (8%). Verdict per the pre-registered criteria: inconclusive.

  • Takeaway (a hypothesis, not proven): reviewers stop over-intervening when they are given the rationale behind a decision and read-only tools to check claims. That is a prompt and context recipe, not a reason for a separate MCP server.

Details: docs/v0.4-evidence-gate.md, CHANGELOG.md, raw results in benchmark-results/. Total model spend for the whole study was about $1.

One independent, blind-first reviewer for the expensive decisions of AI coding agents.

Spend extra inference before expensive implementation. Independent first. Compare second.

CI

Problem

AI coding agents commit to the first plausible solution too early. When the decision is expensive (an architecture choice, a schema migration, a public API change, a dependency swap, a big refactor) a wrong first idea costs days of rework, data loss or an outage. Asking a second model to "review this plan" helps less than it should: once the reviewer reads the author's solution it gets anchored on its framing and mostly polishes it.

Related MCP server: tenth-man-mcp

Hypothesis

An independent reviewer that first reasons about the problem without seeing the author's solution, and only then compares it with the revealed proposal, catches fundamental errors that ordinary critique misses because of anchoring.

This repo is a research MVP to test that hypothesis cheaply, with an A/B harness (blind_first vs proposal_first).

What it is

An MCP server with one main tool, review_decision, that adds exactly one extra reviewer before an expensive decision:

Main agent ──► Phase 1: blind independent review (no proposal) ──► Phase 2: reveal proposal, compare ──► compact verdict
  1. Phase 1 (blind). The reviewer receives only objective, constraints, context, environment, evidence, decision_type, risk_level. The proposed_solution is physically absent from the request (enforced by types and tests). It produces an internal independent position: assumptions, several directions, failure modes, preferred solution, assumptions to test.

  2. Phase 2 (reveal). In the same session (Phase 1 messages + Phase 1 answer + reveal) the proposal is shown and compared against the reviewer's own position.

  3. Verdict. A compact typed JSON result (KEEP | MODIFY | REPLACE | INSUFFICIENT_EVIDENCE, risks, optional better alternative, the cheapest falsification probe, confidence). The Phase 1 position, chain-of-thought and transcript are not returned. Session state is discarded after the call.

What it is NOT

  • Not a swarm, not multi-agent debate, no voting, no rounds.

  • Not an autonomous team or an orchestration framework.

  • Not a replacement for your coding agent.

  • Not a generic "Sequential Thinking" clone.

Hard invariants: exactly 1 main agent + 1 reviewer; no recursive agents. Recursion is structurally impossible rather than guarded at runtime: the provider interface has no tools field, requests never carry tools, tool calls in a model answer are never executed (the answer is treated as unusable), and there is one call path to the model (ReviewSession). The reviewer therefore cannot call review_decision, spawn reviewers or delegate.

Blindness contract

The blind fields (objective, constraints, context, environment, evidence) are written by the calling agent, so the agent's plan can leak into them. The contract:

  • The tool and field descriptions tell the agent to describe the problem there and put the plan only in proposed_solution.

  • decision_type and risk_level are visible to Phase 1 by design.

  • A deterministic check measures how much of proposed_solution is already present in the blind fields: the share of its word 5-grams (shingles) that also occur there (containment; short proposals use n = their word count).

    • >= BLINDNESS_WARN_THRESHOLD (default 0.15): the review runs and meta.blindness_warning says the blind phase may have seen part of the plan.

    • >= BLINDNESS_LEAK_THRESHOLD (default 0.5): blind_first refuses with BLINDNESS_LEAK before any model call (no tokens spent). We refuse rather than silently downgrade to a non-blind review, so that a blind_first result always means the blind phase was (lexically) blind. proposal_first only gets the warning.

  • This catches copy-paste and close paraphrase only; a reworded plan in context is not detected.

Install

Requires Node.js >= 20.12 (Windows 11, macOS, Linux).

git clone https://github.com/Magnifico4625/blindreview-mcp.git
cd blindreview-mcp
npm ci
npm run build
npm test

Then configure (below) and start it (an MCP client normally starts it for you):

npm start

The server speaks MCP over stdio; it logs only to stderr. In MCP client configs launch node dist/src/server.js directly, not npm start (npm prints a banner to stdout, which corrupts the protocol stream).

Configuration (.env)

Invalid values (e.g. a REVIEWER_BASE_URL without http(s)://, a non-numeric limit, a typo in a boolean) fail at startup with an error naming the variable. There is no MAX_TOOL_CALLS: the reviewer has no tools, so there is nothing to budget.

Copy .env.example to .env in the repo root (copy .env.example .env on Windows cmd, cp .env.example .env elsewhere). The server loads <repo>/.env itself (no dependency, independent of the working directory the MCP client uses); variables passed by the MCP client's env block take precedence. Set BLINDREVIEW_ENV_FILE to use a different file.

Variable

Default

Meaning

REVIEWER_BASE_URL

https://api.openai.com/v1

Any OpenAI-compatible /chat/completions base URL

REVIEWER_API_KEY

–

API key (omit for local servers)

REVIEWER_MODEL

gpt-5-mini

Reviewer model id

REVIEWER_REASONING_EFFORT

–

low / medium / high; dropped automatically if the API rejects it

REVIEWER_REASONING_PARAM

auto

reasoning_effort (OpenAI/xAI) or reasoning_object (OpenRouter reasoning: {effort}); auto-detected from the URL

REVIEWER_MAX_TOKENS

4000

Max completion tokens per call

REVIEWER_TEMPERATURE

–

Optional 0..2; not sent when empty, dropped automatically if rejected

MAX_REVIEW_TOKENS

20000

Near-hard cap on cumulative tokens (prompt + completion) across all phases, see budget policy

BLINDNESS_LEAK_THRESHOLD

0.5

Proposal/blind-field 5-gram containment at which blind_first refuses (BLINDNESS_LEAK)

BLINDNESS_WARN_THRESHOLD

0.15

Containment at which meta.blindness_warning is set

REVIEW_TIMEOUT

120000

Timeout for the whole review in ms (AbortController)

TELEMETRY_ENABLED

false

Opt-in local JSONL telemetry. Booleans accept true/false, 1/0, yes/no, on/off; anything else is a startup error

TELEMETRY_PATH

./data/reviews.jsonl

Relative paths resolve against the repo root

Choosing a reviewer model

Use a strong model, ideally from a different family than your main agent (different blind spots). Examples:

Provider

REVIEWER_BASE_URL

REVIEWER_MODEL (example)

OpenAI

https://api.openai.com/v1

gpt-5-mini, gpt-5

OpenRouter

https://openrouter.ai/api/v1

xiaomi/mimo-v2.6-flash, openai/gpt-5-mini, any id from https://openrouter.ai/api/v1/models

xAI

https://api.x.ai/v1

a current Grok model id from the xAI console, e.g. grok-4

Ollama (local)

http://localhost:11434/v1

e.g. qwen3:14b (no key needed)

LM Studio (local)

http://localhost:1234/v1

the model id shown in LM Studio's server tab

The adapter asks for JSON (response_format: json_object) and, when servers reject parameters, retries once without them: reasoning_effort/reasoning is dropped, max_tokens becomes max_completion_tokens (newer OpenAI reasoning models), response_format is dropped. <think> blocks and Markdown fences are stripped before parsing.

Connecting to MCP clients

Use the absolute path to dist/src/server.js in your clone. Put secrets in .env (or in the client's env block).

Claude Code

claude mcp add blindreview -- node /abs/path/blindreview-mcp/dist/src/server.js

Cursor – .cursor/mcp.json in your project (or ~/.cursor/mcp.json globally):

{
  "mcpServers": {
    "blindreview": {
      "command": "node",
      "args": ["/abs/path/blindreview-mcp/dist/src/server.js"]
    }
  }
}

Claude Desktop – claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), same mcpServers shape as Cursor.

VS Code – .vscode/mcp.json:

{
  "servers": {
    "blindreview": {
      "type": "stdio",
      "command": "node",
      "args": ["/abs/path/blindreview-mcp/dist/src/server.js"]
    }
  }
}

Windows 11 example (escape backslashes in JSON; an absolute node.exe path avoids PATH issues in GUI apps):

{
  "mcpServers": {
    "blindreview": {
      "command": "C:\\Program Files\\nodejs\\node.exe",
      "args": ["C:\\Users\\you\\code\\blindreview-mcp\\dist\\src\\server.js"],
      "env": { "REVIEWER_API_KEY": "sk-..." }
    }
  }
}

Note: on Windows, MCP servers launched via npx usually need "command": "cmd", "args": ["/c", "npx", ...] because npx is a .cmd shim. This repo is not published to npm; launch it with node as above.

Tools

review_decision

Input:

{
  "objective": "string",
  "constraints": ["string"],
  "context": "string",
  "proposed_solution": "string",            // hidden from the reviewer until phase 2 (blind_first)
  "decision_type": "architecture|refactor|debugging|database|api|dependency|performance|security|implementation|other",
  "risk_level": "low|medium|high|critical",
  "review_mode": "blind_first|proposal_first", // optional, default blind_first; proposal_first = control (plain critique)
  "environment": "string",                   // optional
  "evidence": ["string"]                     // optional: logs, numbers, test output
}

Output example (real blind_first output for the v0 authz-cache-ttl case, lists truncated with "…"):

{
  "verdict": "MODIFY",
  "recommendation": "Keep the read-through Redis cache direction but drop the 15-minute TTL with no invalidation: replace it with a 2-minute TTL plus a Kafka consumer on 'permission.changed' that deletes affected keys. Do not cache negative decisions (denials) for write/admin actions — either bypass the cache for non-read actions or use a much shorter TTL. This preserves the ~78% hit rate at 2 minutes while making revocation lag bounded and demonstrably SOC 2 compliant.",
  "critical_assumptions": [
    "SOC 2 permits up to 15 minutes of stale grants for terminated employees — 'promptly' is almost certainly stricter than this.",
    "…"
  ],
  "material_risks": [
    "15-minute stale grant for a terminated employee directly violates the SOC 2 revocation requirement — the most likely reason this gets rejected in audit or causes a real incident.",
    "…"
  ],
  "falsification_probe": {
    "description": "Ask the compliance/owning team for the documented maximum acceptable revocation delay for terminated employees (SOC 2 control text or internal policy), and simultaneously run a 1-day shadow test: apply cache with 15-min TTL but log and compare every cached decision against a live permissions-service.check result.",
    "expected_signal": "If any stale-grant event exceeds the stated revocation SLA (likely <60s), the 15-min no-invalidation design is falsified; if the documented SLA is ≥15 minutes and zero stale-grant violations occur, KEEP becomes defensible."
  },
  "confidence": 0.85,
  "meta": {
    "review_id": "63218c5b…",
    "review_mode": "blind_first",
    "model": "xiaomi/mimo-v2.6-flash",
    "phases": 2,
    "usage": {
      "prompt_tokens": 2761,
      "completion_tokens": 1781,
      "total_tokens": 4542
    },
    "latency_ms": 33087
  }
}

Errors come back as an MCP tool error (isError: true) with {"error": {"code", "message"}}; codes: CONFIG_ERROR, INVALID_INPUT, PROVIDER_HTTP_ERROR, PROVIDER_NETWORK_ERROR, TIMEOUT, MALFORMED_RESPONSE, BUDGET_EXCEEDED, OUTPUT_TRUNCATED (output cut by max_tokens; raise REVIEWER_MAX_TOKENS), BLINDNESS_LEAK, INTERNAL_ERROR (unexpected exception).

Budget policy (near-hard cap):

  • Before every call, max_tokens = min(REVIEWER_MAX_TOKENS, remaining budget - prompt estimate). The estimate is deliberately pessimistic (~3 ASCII chars per token, every non-ASCII character = 1 token). If fewer than 256 tokens would remain, the call is not made.

  • After every call, cumulative usage.total_tokens (provider-reported; estimated if the provider omits it) is compared with MAX_REVIEW_TOKENS; once reached, no further call is made.

  • So completion tokens never exceed the clamped max_tokens; the cap can only be crossed by the call that crosses it, and only if the provider counts more prompt tokens than the pessimistic estimate.

  • Before or inside Phase 1 (including its repair retry): BUDGET_EXCEEDED error. After Phase 1 with too little left for Phase 2: Phase 2 is skipped and the result is INSUFFICIENT_EVIDENCE with confidence: 0 and meta.budget_exhausted: true (the Phase 1 position is still not returned).

  • Malformed answers get at most one repair retry, then MALFORMED_RESPONSE. If the output was cut off (finish_reason: "length") and is not valid JSON, there is no retry: OUTPUT_TRUNCATED.

should_review (deterministic gate, no LLM)

src/gate/decision-gate.ts. Recommends a review for: architecture changes, DB schema changes and migrations, public API changes, key dependency replacements, large refactors, multi-module changes, performance/security decisions, complex debugging with several plausible hypotheses, high/critical risk. Not for formatting, renames, simple UI tweaks, obvious bug fixes, docs, small local changes (unless a structural signal overrides, e.g. renaming a public API field). Advisory: the main agent decides whether to call review_decision.

record_outcome

Appends an outcome for a past review (see telemetry).

When to call it

Suggested instruction for your agent (e.g. in CLAUDE.md, AGENTS.md or Cursor rules):

Before committing to an expensive or hard-to-reverse decision (architecture, DB schema/migration, public API, key dependency, large/multi-module refactor, security/performance, debugging with several plausible causes), call should_review; if it recommends a review, call review_decision once with your proposal, then run the returned falsification_probe before implementing. Describe the problem in objective/constraints/context/evidence and put your plan only in proposed_solution. Do not call it for trivial changes.

Telemetry and outcomes

Opt-in (TELEMETRY_ENABLED=true), local JSONL only (./data/reviews.jsonl), no external analytics. A review record contains only: id, timestamp, review_mode, decision_type, risk_level, reviewer_model, token usage, latency, verdict, confidence, status/error_code. No objective, context, constraints, evidence, proposal or reviewer text is stored (tested).

Mark what happened later, so you can eventually compute cost per prevented bad decision:

npm run outcome -- <review_id> --accepted true --rework false --useful true

or call the record_outcome MCP tool with review_id, accepted_review, later_rework_required, review_was_useful.

Benchmark (v0.3: blind_first vs controls)

The benchmark tests one question: does blind_first give better review decisions than ordinary review at comparable compute? It is an evaluation harness only; none of the benchmark-only modes are exposed through the MCP tool.

npm run benchmark -- --label my-model --model vendor/model --base-url https://openrouter.ai/api/v1 \
  --runs 3 --concurrency 3 --timeout 240000 --out benchmark-results

All config is external (CLI flag overrides env): --base-url (REVIEWER_BASE_URL), --model (REVIEWER_MODEL), --reasoning-effort (REVIEWER_REASONING_EFFORT), --reasoning-param, --temperature (REVIEWER_TEMPERATURE), --max-tokens per call (REVIEWER_MAX_TOKENS), --max-review-tokens total cap per review (MAX_REVIEW_TOKENS), --timeout (REVIEW_TIMEOUT), --runs (BENCHMARK_RUNS, default 3), --concurrency (BENCHMARK_CONCURRENCY, default 2), --retries (default 2), --seed, --modes, --only, --label, --resume. Every finished (case, run, mode) record is appended to <out>/<label>/checkpoint.jsonl; after an interruption, rerun the same command with --resume to reuse finished records (refused if model/config/case set differ); successes and non-transient failures are reused, transient failures (timeout, network, 429, 5xx) are re-run. For rate-limited (free) models: --max-rpm N (BENCHMARK_MAX_RPM) caps request starts per minute, HTTP 429 gets a longer backoff, and --stop-on-rate-limit stops the run (resumable) instead of recording failures when 429 persists, e.g. on a daily cap. The number of provider requests is printed. The API key is read only from REVIEWER_API_KEY and is never written to results.

Output: <out>/<label>/<timestamp>.json (every run), .md (main report), .decision_judge.md (separate experiment), .review-sheet.csv + .sheet-key.json (human review). Every result records provider, base URL host, model, reasoning effort, token limits, temperature, timeout, timestamp, git SHA + dirty flag, case-set hash and prompt hashes. Runs of different models are independent; npm run benchmark:compare -- <report.json> <report.json> ... puts several labels side by side without declaring a winner.

Modes

mode

what the reviewer sees

calls

proposal_first

problem + proposal, single critique (public control mode)

1

proposal_first_2pass

benchmark-only compute-matched control: problem + proposal; pass 1 maps assumptions/alternatives/failure modes, pass 2 gives the verdict (same session)

2

blind_first

pass 1 without the proposal, pass 2 reveals it in the same session

2

independent_only

benchmark-only: pass 1 = the blind_first Phase 1 prompt (never the proposal); pass 2 is a fresh session that gets only the Phase-1 structured position (JSON) + the proposal and returns the verdict

2

decision_judge

separate experiment: problem + proposal, single pass, instructed to intervene only for a material problem and to value preserving a correct proposal equally

1

Modes 2–4 use the same model, the same max tokens per call, the same total cap per review and the same reasoning effort and temperature (all from one config); actual tokens are reported. The order of modes is shuffled per (case, run) with a seeded PRNG.

Why independent_only is designed this way. The requirement is that the reviewer never receives proposed_solution, but a verdict on a proposal cannot be produced without someone looking at it. Options considered: (a) an LLM judge that sees the ground truth decides — rejected, it puts an LLM in the scoring loop; (b) only human scoring of required observations — kept as a supplementary measure (see human review), but it gives no verdict; (c) the chosen design: the independent position is produced blind, and a separate fresh call converts it into a verdict without the Phase-1 transcript. Compared with blind_first, this removes the in-session Reveal/Compare trajectory (self-anchoring on its own reasoning, or being pulled back toward the proposal on reveal). Known confound: the comparer does not see the problem statement (objective/context), only the position, so a difference vs blind_first can also come from that missing context. The independent reviewer itself never sees the proposal (tested with a sentinel); the comparer never sees the blind fields (tested).

Cases

25 cases, one JSON file per case in benchmark/cases/ (structure ready for 30–50; add files, the loader validates them). 13 flawed (materially or fundamentally), 12 correct. 12 are adapted from public material (postmortems, incident writeups, engineering blogs, an SEC order), 13 are synthetic (11 migrated from v0.2 plus 2 new). The source URL is kept in metadata only.

{ "id": "...", "title": "...", "category": "...",
  "source": { "type": "real_pr|reverted_pr|postmortem|issue|incident|synthetic", "url": "...", "note": "..." },
  "input": { "objective": "...", "constraints": ["..."], "context": "...", "environment": "...", "evidence": "...",
             "proposed_solution": "...", "decision_type": "...", "risk_level": "..." },
  "ground_truth": { "proposal_status": "correct|materially_flawed|fundamentally_flawed|insufficient_information",
                    "acceptable_verdicts": ["REPLACE", "MODIFY"], "material_issue": "...",
                    "required_observations": ["..."], "notes": "..." },
  "diagnostics": { "hidden_flaw_keywords": ["regex", "..."] } }
  • The reviewer request is built by an explicit pick of case.input fields only. A test runs every case through every mode with sentinels planted in source, ground_truth and diagnostics and checks that neither the sentinel nor any ground-truth string reaches any provider request.

  • acceptable_verdicts[0] is the expected primary verdict. Correct proposals accept KEEP only; flawed proposals never accept KEEP.

  • Every case passes the blindness overlap check below the warn threshold (tested).

  • Real-sourced cases are compact, self-contained adaptations; no private code is copied. Their known outcome (what actually happened) may still be in the reviewer model's training data, which is a contamination risk.

Metrics (mechanical, no LLM judge)

  • Decision accuracy: verdict ∈ acceptable_verdicts. Shown over successful runs and strict (failed runs count as wrong).

  • Correct KEEP: KEEP rate on correct proposals. Defect detection: MODIFY or REPLACE on flawed proposals. Exact verdict: verdict = primary expected verdict.

  • False intervention: MODIFY on a correct proposal. Severe false intervention: REPLACE on a correct proposal. Insufficient-evidence rate.

  • Tokens and latency: mean and median; relative token cost vs proposal_first; tokens of failed attempts are counted in the cost section.

  • Keyword hit-rate: diagnostic only (narrow regexes), not a quality measure.

  • Rates have 95% Wilson intervals. Mode differences use a paired cluster bootstrap over cases (5000 resamples, seeded).

  • Failures never shrink denominators silently: expected / successful / failed / failure reasons and retries are reported per mode. Transient errors (timeout, network, HTTP 429/5xx) are retried up to --retries times with backoff and logged.

Pre-registered decision rules

Written and committed before the v0.3.0 runs (benchmark/thresholds.ts). Δ = metric(A) − metric(B), positive = A better, with the 95% paired bootstrap CI [lo, hi]:

label

rule

clear signal

Δ ≥ +0.10 and lo > 0

weak signal

Δ ≥ +0.05 and lo > −0.05 (and not clear)

no observed advantage

Δ ≤ 0

inconclusive

anything else

  • Q1 blind_first vs proposal_first, Q2 blind_first vs proposal_first_2pass (main test), Q3 independent_only vs blind_first: primary metric = strict decision accuracy.

  • Q4 decision_judge vs proposal_first (critic bias): primary metric = reduction in intervention (MODIFY/REPLACE) on correct proposals; the label gets the suffix "(with defect-detection loss)" if defect detection drops by more than 0.10.

Human review (no LLM judge)

Each run exports <ts>.review-sheet.csv: one row per (flawed-case result, required observation), with the reviewer's risks / alternative / probe and an empty hit column (yes/no). The mode is hidden behind an item id (key in <ts>.sheet-key.json) so the human scorer is blind to the mode. After filling it in: npm run benchmark:ingest -- <ts>.review-sheet.csv <ts>.sheet-key.json prints required-observation coverage per mode.

Honest limits of the blindness protection

  • The overlap check is lexical. A semantic paraphrase of the plan in context cannot be reliably detected by string checks.

  • Blind isolation is guaranteed only against an honest caller. A caller that writes its plan into the blind fields defeats it.

  • decision_type and risk_level are visible to Phase 1 by design.

Results

See benchmark-results/sample/ (per-label folders; v1 and v0 runs of v0.2 kept for history).

v0.4.0 experiment: evidence_gate (benchmark only)

One final architectural experiment after v0.3.0: a single judge with read-only, sandboxed tools (grep, symbol search, size-capped file read, config inspection, allowlisted existing tests, tsc --noEmit) over a small repository snapshot. It may only intervene (MODIFY/REPLACE) with a claim that the harness validates against a mechanical artifact from the same session (failing test, typecheck error, or a search hit outside the patch's own lines); otherwise the verdict is capped at WARNING. 20 repo-snapshot cases in benchmark/repo-cases/. Pre-registration, design and results: docs/v0.4-evidence-gate.md; raw results in benchmark-results/sample/v0.4/.

REVIEWER_BASE_URL=https://openrouter.ai/api/v1 npm run benchmark:evidence -- --model <id> --modes evidence_gate,decision_judge --runs 5 --out benchmark-results/v0.4
npm run benchmark:evidence-summary -- benchmark-results/v0.4/<label> [...] --ledger benchmark-results/v0.4/spend.jsonl

The MCP tool surface is unchanged: evidence_gate is not exposed through review_decision.

Development

npm run lint       # eslint (typescript-eslint flat config)
npm run typecheck  # tsc --noEmit, strict
npm run build
npm test           # vitest with a fake provider; also spawns the built server over stdio

Layout:

src/server.ts                 stdio entry point
src/create-server.ts          MCP tools wiring
src/reviewer/reviewer.ts      review orchestration, timeout, blindness check, telemetry
src/reviewer/blind-first.ts   phase 1 (blind) + phase 2 (reveal) in one session
src/reviewer/blindness.ts     deterministic leak check (5-gram containment)
src/reviewer/proposal-first-2pass.ts  benchmark-only compute-matched control
src/reviewer/benchmark-modes.ts      benchmark-only independent_only + decision_judge
src/reviewer/prompt-fingerprint.ts   prompt hashes (frozen by tests/prompt-freeze.test.ts)
src/reviewer/proposal-first.ts control mode
src/reviewer/session.ts       per-review state, token budget, JSON validation + 1 repair retry
src/reviewer/prompts.ts       prompt builders (phase 1 builder only accepts BlindInput)
src/reviewer/json.ts          JSON extraction / normalisation
src/providers/provider.ts     provider interface (no tools field by design)
src/providers/openai-compatible.ts  the only adapter in the MVP
src/gate/decision-gate.ts     deterministic gate
src/schemas/review.ts         Zod schemas and types
src/telemetry/telemetry.ts    opt-in JSONL telemetry
src/cli/outcome.ts            outcome marking CLI
benchmark/                    cli, runner, metrics, stats, thresholds, report, compare, human review
benchmark/cases/              benchmark cases (one file per case)
src/evidence/                 v0.4.0 evidence_gate: sandbox tools, gate validation, tool loop, tool-calling provider
benchmark/evidence/           v0.4.0 repo-snapshot runner, metrics, pre-registered criteria, report, summary
benchmark/repo-cases/         v0.4.0 repo-snapshot cases (case.json + repo/)

New providers (Anthropic, Gemini, native OpenRouter/xAI, local runtimes) implement ReviewerProvider in src/providers/; nothing else changes.

Limitations

  • Research MVP: the hypothesis is not established (see benchmark results). Synthetic cases were written by the tool's author; real-sourced cases may be in model training data. The keyword metric is diagnostic only.

  • The reviewer only knows what the main agent puts into context/evidence; it has no repo access or tools. Garbage in, garbage out.

  • Blind-first costs roughly 2-2.5x the tokens of a single-pass critique (two calls; Phase 2 resends Phase 1).

  • The blindness check is lexical: it detects copied or near-copied plans; a semantic paraphrase hidden in context cannot be reliably detected by string checks. Blind isolation is not guaranteed against a dishonest caller.

  • Token budget is a near-hard cap (see budget policy), not an exact one.

  • Only an OpenAI-compatible adapter is implemented.

Intentionally not implemented

Web UI, database server, Docker, auth, cloud backend, vector DB, RAG, memory, multiple reviewers, teams, voting, debate rounds, orchestration frameworks, reviewer tools in the MCP tool (the read-only evidence tools exist only in the v0.4.0 benchmark mode).

License

MIT

Available Tools

3 tools
record_outcomeRecord what happened after a reviewA

Append an outcome record for a past review (local telemetry only, requires TELEMETRY_ENABLED=true). Use review_id from review_decision meta.

ParametersJSON Schema
NameRequiredDescriptionDefault
review_idYes
accepted_reviewNo
review_was_usefulNo
later_rework_requiredNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
noteYes
recordedYes

TDQS

A3.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds real behavioral context beyond the annotations: local-only telemetry scope and a hard precondition (TELEMETRY_ENABLED=true). The 'append' wording is consistent with readOnlyHint=false and idempotentHint=false. It stops short of stating what happens on a duplicate or failed write.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, zero filler, with the core action front-loaded and the operational precondition parenthesized so it does not bury the main clause.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The output schema exists, so return values need no explanation, and the env-var precondition is disclosed. However, for a 4-parameter mutation with 0% schema coverage, the description leaves the three optional booleans entirely undefined, which is a meaningful gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 4 parameters, and the description explains only review_id's origin. The three booleans (accepted_review, review_was_useful, later_rework_required) get no meaning, defaulting, or interaction guidance, leaving the agent to guess their semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Append an outcome record for a past review.' The append/mutation nature distinguishes it from the read-ish siblings review_decision and should_review, though the sibling relationship is only implied rather than named.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete sourcing hint ('Use review_id from review_decision meta'), which tells the agent where to obtain the required argument, but says nothing about when to call this versus the sibling tools or when not to record an outcome.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

review_decisionBlind independent review of a decisionA
Read-only

Get ONE independent external review before an expensive decision (architecture, DB schema/migration, public API, key dependency, large refactor, security/performance, multi-hypothesis debugging). Default blind_first: the reviewer first solves the problem WITHOUT seeing proposed_solution, then the proposal is revealed and compared. Returns a compact verdict (KEEP | MODIFY | REPLACE | INSUFFICIENT_EVIDENCE), risks, an optional better alternative and the cheapest falsification probe to run. Costs one or two LLM calls; skip it for trivial changes (see should_review). BLINDNESS CONTRACT: describe the problem in objective/constraints/context/environment/evidence and put your plan ONLY in proposed_solution. Do not mention, hint at or paraphrase your planned solution in the other fields. decision_type and risk_level are visible to the blind phase. If proposed_solution is largely copied into the other fields, blind_first refuses with BLINDNESS_LEAK.

ParametersJSON Schema
NameRequiredDescriptionDefault
contextYesRelevant facts about the codebase/system (summaries and key snippets). Describe the problem only. Do NOT put your planned solution, preferred approach or its wording here; it belongs in proposed_solution only (the blind phase must not see it).
evidenceNoObserved facts: logs, error messages, benchmark numbers, test output. Describe the problem only. Do NOT put your planned solution, preferred approach or its wording here; it belongs in proposed_solution only (the blind phase must not see it).
objectiveYesThe goal / problem to solve, not the solution. Describe the problem only. Do NOT put your planned solution, preferred approach or its wording here; it belongs in proposed_solution only (the blind phase must not see it).
risk_levelYesVisible to the blind phase by design.
constraintsYesHard requirements: compatibility, SLAs, deadlines, forbidden approaches. Describe the problem only. Do NOT put your planned solution, preferred approach or its wording here; it belongs in proposed_solution only (the blind phase must not see it).
environmentNoRuntime/deploy environment: versions, topology, scale, traffic. Describe the problem only. Do NOT put your planned solution, preferred approach or its wording here; it belongs in proposed_solution only (the blind phase must not see it).
review_modeNoblind_first (default): reviewer forms an independent position before seeing the proposal. proposal_first: control mode, plain critique.
decision_typeYesVisible to the blind phase by design.
proposed_solutionYesYour proposed solution. The ONLY field that may describe it. Hidden from the reviewer until phase 2 in blind_first mode.

Output Schema

ParametersJSON Schema
NameRequiredDescription
metaYes
verdictYes
confidenceYes
material_risksYes
recommendationYes
better_alternativeNo
falsification_probeYes
critical_assumptionsYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, but the description adds substantial non-obvious behavior: cost ('one or two LLM calls'), the two-phase blind protocol, the refusal path (BLINDNESS_LEAK), and that decision_type/risk_level are deliberately visible to the blind phase. This is well beyond what the annotations convey. It stops short of describing latency or rate limits, so 4 rather than 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded followed by a clearly headed 'BLINDNESS CONTRACT' block, and every sentence carries operational weight rather than filler. The opening sentence is dense with parenthetical enumeration, but the structure keeps it scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't document return values, yet it still previews the verdict shape (KEEP | MODIFY | REPLACE | INSUFFICIENT_EVIDENCE). Combined with the blindness contract and cost note, an agent has everything needed to call this correctly for a 9-parameter tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description earns more by explaining the contract governing how fields interact: plan content belongs ONLY in proposed_solution, and leaking it triggers a BLINDNESS_LEAK refusal. That cross-field rule is a semantic layer the per-field schema descriptions state only piecemeal.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb and resource ('Get ONE independent external review before an expensive decision') and enumerates the qualifying decision categories, so the agent knows exactly what this tool produces. It also names the sibling should_review for the trivial-change case, letting an agent separate the two tools without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is explicit on both sides: call it 'before an expensive decision' enumerated by type, and 'skip it for trivial changes (see should_review)' with a pointer to the gating sibling. It further clarifies review_mode's default and control behavior, leaving nothing to inference about when to invoke.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

should_reviewDeterministic review gateA
Read-onlyIdempotent

Deterministic (no LLM, free) check whether a decision is expensive enough to justify review_decision. Advisory only.

ParametersJSON Schema
NameRequiredDescriptionDefault
summaryNoOne-line description of the change (scanned for keywords).
risk_levelYes
irreversibleNo
trivial_kindNoSet if the change is one of the known-cheap kinds.
decision_typeYes
files_changedNo
modules_touchedNo
changes_db_schemaNo
changes_public_apiNo
includes_migrationNo
security_sensitiveNo
competing_hypothesesNoDebugging: number of plausible root-cause hypotheses still open.
performance_criticalNo
replaces_key_dependencyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
reasonsYes
should_reviewYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/idempotentHint/openWorldHint=false, so the safety profile is covered. The description adds non-obvious traits beyond them: no LLM call, no cost/latency, and a non-binding (advisory) result. It still doesn't say what the response shape is, though an output schema exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the determinism/cost trait and the purpose; nothing is padded or repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need no explanation, but for a 14-parameter decision-gate tool the description is thin on how inputs drive the advisory verdict and on ordering relative to review_decision/record_outcome. Adequate to identify the tool, not to fully define its contract.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 14 parameters and only 21% schema description coverage, the description must compensate and does not: it never mentions any input, not even that summary is keyword-scanned or how risk_level/trivial_kind shift the verdict. 'Expensive enough' hints at cost-oriented fields (files_changed, modules_touched, irreversible) but gives no mapping.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: a deterministic gate that checks whether a decision is 'expensive enough' to warrant review. It also names the sibling it gates (review_decision), so an agent can separate it from review_decision and record_outcome without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly positions itself as a pre-step to review_decision ('justify review_decision') and labels itself 'Advisory only', which tells the agent the verdict is a recommendation rather than a hard gate. It does not state an explicit when-not condition or a cost threshold for skipping it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.4.0
    • First observedrecord_outcome
    • First observedreview_decision
    • First observedshould_review

TDQS

A4/5.0

Scored across 3 tools

Disambiguation5/5

The three tools serve clearly distinct roles: review_decision performs the review, should_review is a deterministic gate, and record_outcome is telemetry. No overlap in purpose or output.

Naming Consistency4/5

All names use snake_case, but the pattern is not uniform: review_decision and record_outcome are verb_noun, while should_review is a modal-verb question form. Minor deviation but still readable.

Tool Count5/5

Three tools is well-scoped for a focused review workflow; each tool has a clear, non-redundant purpose. No bloat or missing essentials.

Completeness4/5

The core lifecycle (should_review → review_decision → record_outcome) is covered. A minor gap exists: no tool to list or retrieve past reviews/outcomes, but telemetry is append-only and the main workflow has no dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Panel Review is the guardian agent for AI's highest-stakes coding decisions. Before an agent's riskiest designs, diffs, or commits ship, four frontier models, from OpenAI, Anthropic, Google, and xAI, argue them through and return severity-tagged findings, with review gates the agent cannot silently skip.
    47 npm
    2
    MIT