Skip to content

feat(engine): verdicts say what they looked at and what would settle a claim - #182

Merged
lbildzinkas merged 7 commits into
masterfrom
fm/sl-verdicts-v6
Oct 12, 2026
Merged

lbildzinkas merged 7 commits into
masterfrom
fm/sl-verdicts-v6

Conversation

@lbildzinkas

@lbildzinkas lbildzinkas commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Intent

An unverifiable verdict carried only a one-sentence reason and whatever it cited, so a reviewer could not tell a claim the code cannot settle from one the agent never looked into. This change makes every checked verdict list the lines of the head copy the agent read for its claim, re-checked by the engine like a cited line, and makes every unverifiable verdict say what could settle it, from a fixed list: the code, a library's source, a CI log, a sandboxed run, something outside the repository, or the author.

The review result's version goes up and the extension accepts the new fields, so a result of an older version is refused rather than misread. The verdicts prompt moves to version 6, with evaluation cases recorded from two public pull requests whose descriptions report runs, measurements and passing checks, and its baseline is recorded in the tested-models page. Validation never launches, downloads or installs VS Code or any other tool: extension tests that need VS Code run only in CI, and live validation goes through the engine protocol, the unit tests and rendered output.

Closes #173

What Changed

  • Every verdict now carries looked: the head-copy files and line ranges the agent read for the claim, each re-checked by the engine like a citation, with entries naming a missing file or line dropped and noted in lookedRecheck (capped at 20 ranges). Every unverifiable verdict also carries settles: what could settle the claim from a fixed list (code, library, ci-log, sandboxed-run, outside-repository, author) with a one-line note — taken from the agent when it says, and derived by the engine for verdicts it drops itself. The result version goes up to 24, the extension's validators accept the new fields, and a verdict checked behind a library fetch keeps its looked while its settles becomes the library's source.
  • The verdicts prompt is bumped to v6 to ask for both fields (with new rules for what the agent read and for claims about whole sets), and two evaluation cases are added from interview-coach pull requests Companion-enforced guard for Claude Code's file tools #97 and feat(engine): enforce a companion-side guard for Claude Code's file tools #98 with hand verdicts that label what settles each unverifiable claim; the evaluation gains settles-accuracy and verdict-looked scores, allows expected claims with no evidence, and records the new baseline in docs/tested-models.md.
  • Documentation follows the new fields: the README's verdicts section, the glossary's verdict entry in CONTEXT.md, the reviewing walkthrough, and the tested-models baseline rows.

Risk Assessment

✅ Low: The change is well-bounded and internally consistent: every acceptance criterion is source-verified (looked on every checked verdict, settles on exactly the unverifiable ones with drop-path fallbacks, dropped-range recheck notes, version-24 results refused rather than misread when older), the recorded fix rounds match their prescribed narrow remedies with genuine regression tests, and the master merge coherently re-versions the carry-over result format.

Testing

Drove the change live through the engine protocol exactly as the ticket's standing boundary allows: the real verdicts pass over the recorded interview-coach#98 case (prompt v6 delivered, looked re-checked with drop notes, settles from the fixed list on every unverifiable verdict, engine-dropped verdicts refusing the agent's settles), and the real engine server against the live GitHub pull request (version-24 result, saved-at-once control, older-version saved record ignored and replaced). The offline evaluation gate passes over the new cases against the recorded v6 baseline, and all targeted engine/evaluation/extension unit tests pass. No rendered-UI evidence was produced because the change touches no rendered surface — the extension edits are protocol validators, VS Code is never launched on this machine, and the settles UI is deferred to #107.

  • Live validation: ✅ go - 6 of 7 scenarios driven live against the product
Scenario Result Live Evidence
A reviewer reads a reviewed pull request's claim verdicts: every verdict says which head-copy lines the agent read (looked), each range re-checked against the head copy ✅ pass live live-verdicts-drive.json (live-verdicts.mjs drive over the recorded interview-coach#98 case)
Every unverifiable verdict says what could settle it, from the fixed list (code, library, ci-log, sandboxed-run, outside-repository, author), and no verified or refuted verdict carries settles ✅ pass live live-verdicts-drive.json — author for the description's live-run/measurements claims, library for the ranx claims; settles absent on the refuted and dropped-to-unverifiable non-agent answers
A looked entry naming a missing file or a line the file does not have is dropped, with a recheck note naming it (adversarial: missing file, line beyond EOF, inverted range) ✅ pass live live-verdicts-drive.json — lookedRecheck names all three dropped ranges; unit tests in packages/engine/test/verdicts.test.ts also cover it
Adversarial: when the engine drops an agent's verified/refuted verdict to unverifiable, it uses its own settles, not the agent's (a verified answer with a failing citation carrying author settles beco… ✅ pass live live-verdicts-drive.json — SHA-512 claim: unverifiable with settles {kind:'code', note:'the agent judged it verified, so the code should settle it: check it'}, looked kept
The verdicts prompt is version 6, asks for looked and settles with both required in the answer schema, and the new evaluation cases pass with the baseline recording the bumped run ✅ pass live live-verdicts-drive.json (judging stamped promptVersion 6; delivered instructions and schema inspected) + npm run eval exit 0 over both new interview-coach cases + baseline.json claude-code v6 rows…
The engine's saved-review store ignores a result from an older result version instead of misreading it ✅ pass live live-serve-drive.json — tampered version-23 saved record never served; fresh version-24 review produced and re-saved
The extension's result validator accepts version-24 results carrying looked/settles and refuses an older-version or misshapen result rather than misreading it ⏸️ untested no Driving the extension live requires the VS Code extension host, which the ticket's standing boundary forbids launching, downloading or installing on this machine; extension tests needing VS Code run o…
Evidence: Live verdicts drive: engine judgeClaims over recorded interview-coach#98, scripted agent via the adapter seam (15/15 checks)

Adversarial excerpt — claim 'A str seed is hashed with SHA-512…' answered verified citing search_eval.py:999 with settles {kind:'author'}: {"kind":"unverifiable","looked":[{"path":"src/interview_coach/search_eval.py","line":50,"endLine":60}],"recheck":"the citation src/interview_coach/search_eval.py:999 names a line src/interview_coach/search_eval.py does not have","settles":{"kind":"code","note":"the agent judged it verified, so the code should settle it: check it"}}. Dropped-range note on the live-run claim: 'dropped what the agent read at src/interview_coach/search_eval.py:1-9999 names a line … does not have; src/interview_coach/missing.py:1-5 names a file the head copy does not have; src/interview_coach/cli.py:9 names a line src/interview_coach/cli.py does not have'

{
  "promptVersion": "6",
  "judging": "judged",
  "claims": [
    {
      "claim": "Drove `uv run coach eval qasper --retriever random` live aga",
      "kind": "unverifiable",
      "looked": [
        {
          "path": "src/interview_coach/search_eval.py",
          "line": 40,
          "endLine": 60
        }
      ],
      "lookedRecheck": "dropped what the agent read at src/interview_coach/search_eval.py:1-9999 names a line src/interview_coach/search_eval.py does not have; src/interview_coach/missing.py:1-5 names a file the head copy does not have; src/interview_coach/cli.py:9 names a line src/interview_coach/cli.py does not have",
      "settles": {
        "kind": "author",
        "note": "Only the author ran the two processes."
      },
      "evidence": []
    },
    {
      "claim": "hit the report-row tolerance (recall@5 0.1273 vs ~0.13, reca",
      "kind": "unverifiable",
      "looked": [
        {
          "path": "src/interview_coach/metrics.py",
          "line": 1,
          "endLine": 30
        }
      ],
      "settles": {
        "kind": "author",
        "note": "Only the author took the measurement."
      },
      "evidence": []
    },
    {
      "claim": "A str seed is hashed with SHA-512, the same on every run",
      "kind": "unverifiable",
      "looked": [
        {
          "path": "src/interview_coach/search_eval.py",
          "line": 50,
          "endLine": 60
        }
      ],
      "settles": {
        "kind": "code",
        "note": "the agent judged it verified, so the code should settle it: check it"
      },
      "recheck": "the citation src/interview_coach/search_eval.py:999 names a line src/interview_coach/search_eval.py does not have",
      "evidence": []
    },
    {
      "claim": "tests/test_metrics.py checks these functions against ranx",
      "kind": "refuted",
      "looked": [
        {
          "path": "tests/test_metrics.py",
          "line": 70,
          "endLine": 92
        }
      ],
      "evidence": [
        {
          "path": "tests/test_metrics.py",
          "line": 82,
          "quote": "\"recall\": recall_at_k,"
        }
      ]
    },
    {
      "claim": "ranx computes the same metrics independently",
      "kind": "unverifiable",
      "looked": [
        {
          "path": "tests/test_metrics.py",
          "line": 60,
          "endLine": 92
        }
      ],
      "settles": {
        "kind": "library",
        "note": "The source of ranx could settle it."
      },
      "needsLibrary": "ranx",
      "evidence": []
    },
    {
      "claim": "ranx ranks by score, so the first id gets the highest score.",
      "kind": "unverifiable",
      "looked": [],
      "settles": {
        "kind": "library",
        "note": "The source of ranx could settle it."
      },
      "needsLibrary": "ranx",
      "evidence": []
    }
  ],
  "checks": [
    {
      "name": "judging stamped promptVersion 6",
      "ok": true,
      "detail": "6"
    },
    {
      "name": "prompt asks the agent for looked",
      "ok": true,
      "detail": "instructions contain the looked rule"
    },
    {
      "name": "prompt asks the agent for settles",
      "ok": true,
      "detail": "instructions contain the settles rule"
    },
    {
      "name": "prompt schema requires looked and settles",
      "ok": true,
      "detail": "answer schema requires both fields"
    },
    {
      "name": "every verdict carries looked",
      "ok": true,
      "detail": "6 verdicts"
    },
    {
      "name": "every unverifiable verdict carries settles from the fixed list",
      "ok": true,
      "detail": "author,author,code,library,library"
    },
    {
      "name": "no verified or refuted verdict carries settles",
      "ok": true,
      "detail": "settles only on unverifiable"
    },
    {
      "name": "valid looked range kept",
      "ok": true,
      "detail": "[{\"path\":\"src/interview_coach/search_eval.py\",\"line\":40,\"endLine\":60}]"
    },
    {
      "name": "missing file and line dropped with a recheck note",
      "ok": true,
      "detail": "dropped what the agent read at src/interview_coach/search_eval.py:1-9999 names a line src/interview_coach/search_eval.py does not have; src/interview_coach/missing.py:1-5 names a file the head copy does not have; src/interview_coach/cli.py:9 names a line src/interview_coach/cli.py does not have"
    },
    {
      "name": "verified with a bad citation drops to unverifiable",
      "ok": true,
      "detail": "unverifiable: the citation src/interview_coach/search_eval.py:999 names a line src/interview_coach/search_eval.py does not have"
    },
    {
      "name": "engine-dropped verdict uses its own settles, not the agent's",
      "ok": true,
      "detail": "{\"kind\":\"code\",\"note\":\"the agent judged it verified, so the code should settle it: check it\"}"
    },
    {
      "name": "dropped verdict keeps the looked range",
      "ok": true,
      "detail": "[{\"path\":\"src/interview_coach/search_eval.py\",\"line\":50,\"endLine\":60}]"
    },
    {
      "name": "refuted verdict keeps evidence and looked, no settles",
      "ok": true,
      "detail": "{\"claim\":\"tests/test_metrics.py checks these functions against ranx\",\"kind\":\"refuted\",\"looked\":[{\"path\":\"tests/test_metrics.py\",\"line\":70,\"endLine\":92}],\"evidence\":[{\"path\":\"tests/test_metrics.py\",\"line\":82,\"quote\":\"\\\"recall\\\": recall_at_k,\"}]}"
    },
    {
      "name": "library claim settled by the library (ranx computes the sa…)",
      "ok": true,
      "detail": "{\"claim\":\"ranx computes the same metrics independently\",\"kind\":\"unverifiable\",\"looked\":[{\"path\":\"tests/test_metrics.py\",\"line\":60,\"endLine\":92}],\"settles\":{\"kind\":\"library\",\"note\":\"The source of ranx could settle it.\"},\"needsLibrary\":\"ranx\",\"evidence\":[]}"
    },
    {
      "name": "library claim settled by the library (ranx ranks by score,…)",
      "ok": true,
      "detail": "{\"claim\":\"ranx ranks by score, so the first id gets the highest score.\",\"kind\":\"unverifiable\",\"looked\":[],\"settles\":{\"kind\":\"library\",\"note\":\"The source of ranx could settle it.\"},\"needsLibrary\":\"ranx\",\"evidence\":[]}"
    }
  ],
  "passed": true
}
Evidence: Live engine-server drive against the real interview-coach#98: version 24, saved-at-once, older-version record ignored (7/7 checks)

fresh review result is version 24 (9 parts); next visit served from the saved result at once; after rewriting the saved record to version 23 with a TAMPERED marker: 'older-version saved result is ignored, not served' PASS, 'review after an older-version record is a fresh version 24 result' PASS, 'the ignored record was replaced by a fresh current-version save' PASS (disk record 24/24)

{
  "pr": "https://github.com/lbildzinkas/interview-coach/pull/98",
  "engineResultVersion": 24,
  "freshReview": {
    "version": 24,
    "parts": 9,
    "pullRequest": "feat: add hand-written search metrics and the coach eval qasper random baseline"
  },
  "savedServedAtOnce": {
    "reviewedAt": "2026-10-12T16:17:01.469Z"
  },
  "checks": [
    {
      "name": "handshake answered",
      "ok": true,
      "detail": "initialize answered"
    },
    {
      "name": "fresh review result is version 24",
      "ok": true,
      "detail": "version 24"
    },
    {
      "name": "fresh review result is complete",
      "ok": true,
      "detail": "9 parts"
    },
    {
      "name": "next visit served from the saved result at once",
      "ok": true,
      "detail": "saved label: {\"reviewedAt\":\"2026-10-12T16:17:01.469Z\"}"
    },
    {
      "name": "older-version saved result is ignored, not served",
      "ok": true,
      "detail": "tampered marker absent from the answer"
    },
    {
      "name": "review after an older-version record is a fresh version 24 result",
      "ok": true,
      "detail": "version 24"
    },
    {
      "name": "the ignored record was replaced by a fresh current-version save",
      "ok": true,
      "detail": "disk record version 24/24"
    }
  ],
  "passed": true
}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • ℹ️ packages/evaluation/README.md:206 - The "prompts so far" registry paragraph still ends "the verdicts prompt's version 5 names a reviewer's selection as where its claim is made", but this change bumps the verdicts prompt to version 6 everywhere else in the same paragraph's verdicts description, in packages/evaluation/prompts.json and in docs/tested-models.md. The paragraph describes the current prompts (it closes with "Bump a prompt's version there and here together"), so the reference is now stale; say version 6, or "since version 5" if the historical reading is meant. Doc accuracy only — no code behavior depends on it.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 6 of 7 scenarios driven live against the product
Scenario Result Live Evidence
A reviewer reads a reviewed pull request's claim verdicts: every verdict says which head-copy lines the agent read (looked), each range re-checked against the head copy ✅ pass live live-verdicts-drive.json (live-verdicts.mjs drive over the recorded interview-coach#98 case)
Every unverifiable verdict says what could settle it, from the fixed list (code, library, ci-log, sandboxed-run, outside-repository, author), and no verified or refuted verdict carries settles ✅ pass live live-verdicts-drive.json — author for the description's live-run/measurements claims, library for the ranx claims; settles absent on the refuted and dropped-to-unverifiable non-agent answers
A looked entry naming a missing file or a line the file does not have is dropped, with a recheck note naming it (adversarial: missing file, line beyond EOF, inverted range) ✅ pass live live-verdicts-drive.json — lookedRecheck names all three dropped ranges; unit tests in packages/engine/test/verdicts.test.ts also cover it
Adversarial: when the engine drops an agent's verified/refuted verdict to unverifiable, it uses its own settles, not the agent's (a verified answer with a failing citation carrying author settles beco… ✅ pass live live-verdicts-drive.json — SHA-512 claim: unverifiable with settles {kind:'code', note:'the agent judged it verified, so the code should settle it: check it'}, looked kept
The verdicts prompt is version 6, asks for looked and settles with both required in the answer schema, and the new evaluation cases pass with the baseline recording the bumped run ✅ pass live live-verdicts-drive.json (judging stamped promptVersion 6; delivered instructions and schema inspected) + npm run eval exit 0 over both new interview-coach cases + baseline.json claude-code v6 rows…
The engine's saved-review store ignores a result from an older result version instead of misreading it ✅ pass live live-serve-drive.json — tampered version-23 saved record never served; fresh version-24 review produced and re-saved
The extension's result validator accepts version-24 results carrying looked/settles and refuses an older-version or misshapen result rather than misreading it ⏸️ untested no Driving the extension live requires the VS Code extension host, which the ticket's standing boundary forbids launching, downloading or installing on this machine; extension tests needing VS Code run o…
  • npx vitest run packages/engine/test/verdicts.test.ts packages/engine/test/review.test.ts packages/engine/test/verify.test.ts packages/engine/test/carry-over.test.ts packages/engine/test/library-verdicts.test.ts packages/engine/test/library-fetch.test.ts packages/engine/test/cli.test.ts packages/engine/test/draft-comment.test.ts — 164 tests pass
  • npx vitest run packages/engine/test/server.test.ts packages/engine/test/saved-review.test.ts — 59 tests pass
  • npx vitest run packages/evaluation/test — 149 tests pass (score, case, run, claims, cli)
  • npx vitest run packages/extension/test/review-result.test.ts packages/extension/test/engine-client.test.ts packages/extension/test/findings.test.ts packages/extension/test/overview.test.ts packages/extension/test/asks.test.ts packages/extension/test/draft-comment.test.ts packages/extension/test/side-bar-page.test.ts packages/extension/test/side-bar.test.ts packages/extension/test/tree.test.ts — 296 tests pass
  • npm run build — compiles clean
  • npm run eval — exit 0; 'baseline: 0 dropped, 0 missing, 0 gained, 105 unchanged', both new interview-coach cases covered; baseline.json inspected: claude-code/claude-sonnet-5-5/high rows stamped verdicts v6 with verdict-accuracy 1, false-verified 0, settles-accuracy 1, verdict-looked on the new cases
  • Live drive 1: node .live-tmp/live-verdicts.mjs (scratch, deleted after) — real engine judgeClaims over the recorded lbildzinkas-interview-coach-98 case via the product's AgentAdapter seam with a scripted agent answering the hand-labelled claims; 15/15 checks pass (evidence: live-verdicts-drive.json)
  • Live drive 2: GITHUB_TOKEN="$(gh auth token)" node .live-tmp/live-serve.mjs (scratch, deleted after) — real engine JSON-RPC server (initialize + review) against the real feat: add hand-written search metrics and the coach eval qasper random baseline interview-coach#98 with a disposable cache dir; 7/7 checks pass, including tampering the saved review-result.json to version 23 and proving it is ignored (evidence: live-serve-drive.json)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…a claim (verdicts prompt v6) (#173)

Each checked verdict now carries `looked`: the ranges of the head copy the
agent says it read for the claim. The engine re-checks every range as it
re-checks a cited line and drops one naming a file or a line the copy does
not have, saying so in `lookedRecheck`. Each unverifiable verdict carries
`settles`, what could settle the claim, from a fixed list (code, library,
ci-log, sandboxed-run, outside-repository, author), with a one-line note; a
verdict the engine drops to unverifiable gets one too, and a claim judged
again in a fetched library's source keeps what the agent read in the head
copy. The result version goes to 22, and the extension's validator requires
both fields, so a result of an older version is refused rather than misread.

The verdicts prompt goes to version 6, asking for both fields, refuting a
claim true only in part, and keeping a library claim unverifiable even when
memory would refute it. Two cases recorded from public interview-coach pull
requests label measured numbers and live runs as settled by the author, a
claim that the checks pass as settled by a CI log, and claims about ranx as
settled by the library. The evaluation scores settles-accuracy and
verdict-looked, and the baseline records the Claude Code run of the new
prompt.
@lbildzinkas lbildzinkas changed the title feat(engine): verdicts record what was read and what would settle a claim feat(engine): verdicts say what they looked at and what would settle a claim (verdicts prompt v6) (#173) Oct 12, 2026
Master took result version 23 for the results a review carries over from
an earlier commit's saved review, so the looked and settles fields move to
version 24, recorded after it. A carried-over claim keeps its verdict's
looked and settles unchanged, and a saved review of an older result
version is still refused rather than carried.
@lbildzinkas lbildzinkas changed the title feat(engine): verdicts say what they looked at and what would settle a claim (verdicts prompt v6) (#173) feat(engine): verdicts say what they looked at and what would settle a claim Oct 12, 2026
…a claim the reviewer picks...' (extension.test.ts:1201). Root cause: the PR's protocol bump made `looked` a required array on every checked verdict (isClaimVerdict in packages/extension/src/protocol.ts), and all fixtures were updated except the inline refuted verdict in the integration test's verifiedAnswer helper, so the fake engine's ask answer failed isAskAnswer validation and never rendered. Fix: added `looked: [{ path: 'src/retry.py', line: 1, endLine: 8 }]` to that verdict (packages/extension/test/integration/extension.test.ts:1164), matching the results.ts fixture convention. Verified: the failing test passes, the full integration suite passes 81/81, and npm run check (build, typecheck, lint, 1945 unit tests) passes
@lbildzinkas
lbildzinkas merged commit f6195ca into master Oct 12, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Verdicts say what they looked at and what would settle a claim (verdicts prompt v6)

1 participant