---
name: review-jev-contract
model_tier: light
description: |
  Use when: an ADDITIONAL, machine-scored check of a PR description's own CLAIMS —
  and, when a flow is linked, that flow's frozen acceptance criteria — against the diff
  is wanted, never replacing any other reviewer. Dispatched by review-orchestrator in
  Wave B, via the CLI (`keryx review jev-contract`), when `review.jev.contract: true` in
  .metaproject/tasks.config.json and a Jev/OpenRouter credential is resolvable. It is not
  an LLM sub-agent: there is nothing to dispatch through a platform-native agent
  mechanism, and no prose is generated by a model — Jev only answers one `noul`
  probability per claim; keryx writes every word of every finding. Replaces the
  orchestrator's by-eye Stage 1 "description vs diff" judgement with a scored input when
  the opt-in is on; the by-eye check stays the fallback when it is off.
  NOT for: judging a flow's frozen criteria from scratch (that machinery is flow 328's
  `check-ac.ts`, reused, not duplicated), and not a substitute for a human reviewer
  reading the claim list it produces.
triggers:
  - "jev contract"
  - "check the PR description against the diff"
  - "review --jev-contract"
metadata:
  author: "MrCipherSmith"
  version: "1.0.0"
  category: "review"
  compatible_harnesses: "cursor,codex,zed,opencode,claude"
  engine: "jev"
  origin: "authored"
license: "MIT"
---

# Review — Jev Contract (PR-description claims and frozen acceptance criteria vs. the diff)

An ADDITIONAL orchestrator reviewer, flow 335. It is a **keryx program**, not
an LLM sub-agent: `review-orchestrator` never dispatches a platform-native
agent for it, it runs `keryx review jev-contract` and reads the `--json`
output. Every finding is composed by keryx; Jev ("System One" on OpenRouter)
supplies only one `noul` probability per claim — never prose. Its inputs are the reviewer payload only; session memory and earlier conversation are not inputs.

---

## What it does

Two independent tracks, both advisory, both merged into one report:

1. **Claims** — extracted deterministically from the PR description: every
   bullet/numbered-list item is a claim, plus every prose sentence carrying a
   verb cue (adds, fixes, removes, "does not change", tests). For each claim,
   keryx computes deterministic facts FIRST — named files/symbols/flags
   present in the diff, whether a test file was touched (for a "tests added"
   claim), whether an EXPORTED symbol changed anywhere in the diff (for a "no
   API change" claim) — then asks Jev **one `noul`**: "does the diff support
   this claim?". A claim the facts directly CONTRADICT (e.g. "no API change"
   with an exported symbol touched) is `major`, with `class_scope`,
   regardless of what Jev answered — a contradiction is a fact, not an
   opinion Jev could outvote. A claim scoring below threshold with no
   contradiction is `minor` ("unsupported"). A claim at/above threshold with
   no contradiction produces no finding.
2. **Criteria** — the linked flow's frozen acceptance criteria, when
   `--flow <id>` names one. This track reuses flow 328's
   `src/flow/check-ac.ts` **wholesale**, through `runCheckAc`
   (`src/commands/flow-check-ac.ts`) — the exact same diff acquisition, Jev
   batching, cache and degrade path `keryx flow check-ac` already uses. No
   criterion-checking logic is reimplemented for this reviewer.

---

## Input Contract

Not dispatched with a prompt — invoked as a CLI command:

```text
keryx review jev-contract (--diff <ref> | --pr <n>) [--flow <id>]
                          [--max-calls <n>] [--threshold <0..1>]
                          [--repo <owner/repo>] [--model <jev-1.13|jev-latest>]
                          [--fixtures <dir>] [--json]
```

`--pr <n>` is required to check claims at all — a `--diff <ref>` target has
no PR description to extract claims from, so that track reports zero claims
(honestly, not a refusal). `--flow <id>` is independent of either target: it
names the flow whose frozen acceptance criteria are checked against the same
diff, when one is linked.

---

## Opt-in and privacy

Refuses before any read or network call unless `.metaproject/tasks.config.json`
declares:

```json
{ "review": { "jev": { "contract": true } } }
```

Every claim and every matched diff hunk sent to Jev is redacted first through
`src/security/service.ts` — the same floor `review conform`/`review
jev-risk`/`review jev-rules` already apply. No cache or output file stores a
credential.

---

## Output Contract

Emits a `REVIEW_RESULT`-shaped object matching
`.metaproject/skills/gdskills/review/review-orchestrator/reviewer-finding.schema.json`
— `status`, `reviewer: "review-jev-contract"`, `summary`, `findings`, `stats`
— under `--json`, plus two extras: `claims` (every extracted claim, its
intent, and its `noul` score) and `budget` (`--max-calls`, claims scored,
claims skipped). When `--flow` names a flow, an `acCheck` object is merged in
(`flowId`, `verdicts`, `jevAsked`, `summary`) — the same shape `keryx flow
check-ac` reports. Its findings merge into the consolidated array exactly
like any other reviewer's: same Quality Gate, same dedup, same Wave C
verification. A `major` finding always carries a schema-valid `class_scope`
object (`sites`, `enumeration_method`) — never a bare label.

---

## Orchestrator integration

- `keryx review reviewers --json` lists it under `bundled` with
  `"engine": "jev"`.
- `review-orchestrator` dispatches it in **Wave B**, alongside the domain/
  convention reviewers and the other CLI-engine reviewers, when the opt-in is
  on and a Jev/OpenRouter credential resolves. When either is false it is
  **skipped with the stated reason**, recorded in `Skipped reviewers` — never
  silently absent.
- It is **ADDITIONAL**: it never replaces any other pass. **When the opt-in is
  on, it REPLACES the orchestrator's by-eye Stage 1 "description vs diff"
  judgement with this scored input** — see `review-orchestrator/SKILL.md`'s
  "This gate owns the description-vs-diff comparison" and `SKILL.detail.md`'s
  "CLI-engine reviewers" section. When the opt-in is off, the by-eye check
  stays the fallback, unchanged.

---

### Shared laws (every reviewer)

1. **A claim of runtime harm with no reproducible path is `info`.** If you cannot name the input, call, or condition that reaches the code, you have an observation, not a finding. Report it as `info` and say what would settle it.
2. **Never flag the theoretical.** The path you describe must exist in the code under review. Do not report a safe API because it could be misused, or a pattern because it is often wrong elsewhere.
3. **One finding per class, not one per occurrence.** When the same shape appears at several sites, report it once and list every site. Ten findings that are one finding hide the other nine problems.
4. **A deliberate choice is not a defect until its reason is read and shown wrong.** An explicit override, suppression or exemption, a comment or commit giving a reason, or a sibling doing the same is a decision: read its reason (the comment, `git log -L` or `git blame`, the sibling) before you report it, and report it only when you can show that reason wrong. A project rule is checked against accepted platform practice and its known exceptions before a deviation from it is reported.

This reviewer satisfies all three by construction: `evidence` always names the
matched artefacts/facts and the claim text (never an unreproducible claim); a
finding is synthesized only against a claim actually extracted from the real
description, never a hypothetical one; and each claim is its own site, so
there is no repeated class across claims for this reviewer to collapse — a
`major` finding's `class_scope` still enumerates every matched region, not
just the first.

---

## Red Flags

| Rationalization | Why it is wrong |
|---|---|
| "Jev said 0.9, so the claim is definitely supported" | A `noul` score is a probability the claim is evidenced, not a verdict — a contradiction detected by the deterministic facts overrides it regardless of the score |
| "The description doesn't mention an API, so 'no change' can't be contradicted" | The contradiction check only runs when the claim's own text IS API-shaped (mentions api/public/export/interface/signature/contract) — a claim like "no change to behavior for CLI callers" is checked for tokens, but the exported-symbol contradiction path is intentionally narrower |
| "No claims extracted means the description said nothing" | `--diff` targets have no PR description at all — zero claims there is a target-shape fact, not a description-quality one; check `budget.claimsSkipped` too before reading silence as clean |
| "The AC track failed, so the whole review failed" | `acCheck` degrades to facts-only (never throws) exactly like `keryx flow check-ac` does — a Jev failure on the criteria track is advisory, same as on the claims track |

---

## Verification

Before trusting a run's findings:

1. Read `budget.claimsSkipped` in the `--json` output — a capped run checked
   fewer than "every extracted claim" and the report should say so.
2. Spot-check a `major` finding's `class_scope.sites` against the actual
   diff: does the named region really touch an exported symbol the claim
   said would not change?
3. Confirm every finding carries `reviewer: "review-jev-contract"` and a
   `dedupe_key` — both are required for the orchestrator's Quality Gate and
   Wave C verification to route it correctly.

---

## Scope Boundaries

| Concern | This skill | Use instead |
|---------|------------|-------------|
| Whether the PR description's own claims match the diff | YES | — |
| Whether a flow's frozen acceptance criteria are likely met | YES (via `--flow`, reusing `check-ac.ts`) | `keryx flow check-ac` directly, for a standalone advisory run |
| An actual security/concurrency finding | NO | `review-security-code` / `review-highload` |
| Judging whether a reported finding is real | NO | `review-verifier` |
| A ranked risk map of the diff's hunks | NO | `review-jev-risk` |
