Skip to content

review_replay: optional judged recall over the credited hits #291

Open

Description

@antonwolfy

Problem

dokimi:review_replay credits a human review thread to the nearest finding on the same file within window lines, one to one. It checks location only, on purpose: the grader is deterministic and runs on the image's standard-library python3 (contract §4.5). So a finding that merely sits near a thread gets credit.

The dokimi report on a replay of IntelPython/dpnp#3099 (26 tasks, 156 runs, 210 credited thread hits) quoted credited threads as evidence of what the guidance catches. An audit read the pairs behind them:

  • pr3098-t2: the thread asks to add a TODO. The credited findings complain that the TODO is repeated five times and has no issue URL. pr3055-t9 and pr3128-t1 look similar.
  • pr3066-t1: one of its two credited findings is about something else (deprecating before removing).

Chance credit also grows with the number of findings. The guidance arm reported more findings (5.26 vs 4.37 per review), so location-only recall can favour it. A list of "threads the guidance made the agent catch" needs each pair read.

Proposal

An optional step after grading, outside the grader, for example dokimi repo hits judge <batch> --judge-model <id> --judge-provider <p>:

  • Input: for every credited pair it reads the thread's comment and the finding's text. They come from grade.json details.matches, which a separate change adds.
  • Judgment: a judge model labels each pair same point, related or unrelated. The call goes through dokimi.llm, as the architecture requires.
  • Output: a hits_judged.json per cell, with each label and recall_judged: the threads whose pair is same point, over all gold threads. repo compare reads it as a measure, for example --metric judge:recall_judged, next to recall.
  • What it can and can't fix: only credited pairs are rescored. So recall_judged ≤ recall, and a finding outside the window that does raise the thread's point stays uncredited.
  • Cost on that batch: 210 pairs, roughly $0.5 with a small judge or $3 with a mid-size one.

Open questions

  • Judge model: a different family from the model under test, as novelty requires? (arms run does not check.)
  • Calibration: a person labels a sample of pairs, and the step reports agreement before any judged figure is quoted. How large a sample?
  • Uncredited findings: should near-misses (a finding on the same file outside the window) be judged too, so judged recall can also rise?
  • Ties: three labels or two?

Done when

The command runs over a batch with a fake LLM in the tests and writes hits_judged.json per cell. repo compare reads recall_judged. The docs (contract §4.5 and §9) say what judged recall is and what it is not.

Activity

  1. antonwolfy commented on Nov 1, 2026

    @antonwolfy
    ContributorAuthor

    Proposed answers to the open questions, so a PR can start from them.

    • Judge: a family other than every answer model in the batch. This is the rule novelty applies: novelty/client.py picks a judge of another family than the answerer, and novelty/measure.py refuses the answering model itself (a CLI alias counts as its family's full id). Here the answer models are the cells' models in meta.json. A batch with two of them needs a judge of a third family, or the command refuses. Share the check rather than copy it.
    • Calibrate before quoting any figure. --labels FILE takes a person's labels for a sample of pairs. The output reports agreement and Cohen's kappa per arm next to recall_judged; without labels it says the figure is uncalibrated. What matters is whether the judge errs differently in the two arms (the guidance arm wrote more findings), so the sample is stratified by arm: 25 pairs per arm, 50 in all. At an agreement near 0.8, 50 pairs give a 95% interval of about ±0.11. That is enough to tell a usable judge from a poor one, though per-arm agreement on 25 pairs stays wide (about ±0.16).
    • Read the full texts, not matches. details.matches cuts comment and message to 200 characters (contract §4.5). Use it for the pairing only: take the thread's full comment from the task's gold.json, and the finding's full text from the cell's answer at index finding.
    • Leave near-misses out of this issue. Judging only the credited pairs keeps recall_judged ≤ recall and bounds the cost by the number of credited pairs. Judging findings outside the window would be a second measure, one that can raise recall, with its own cost and calibration. Open it as its own issue if this one proves useful.
    • Three labels. same point, related and unrelated. recall_judged counts same point only, and hits_judged.json keeps the related count, so a reader sees how much credit sits between the two. With two labels the judge has to round related one way or the other, and that rounding is where the arms can differ.

    Order. This can start now: #297 merged details.matches. The judge call goes through dokimi.judged (#314), which also replays the response cache. The repo compare --metric judge:recall_judged reading touches repos/compare.py, which is at its 800-line cap. Land it after #332, which moves the per-task values into compare_values.py, as a second PR if the command is ready first.

  2. self-assigned this
    on Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions