Repository navigation
review_replay: optional judged recall over the credited hits #291
Description
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.jsondetails.matches, which a separate change adds. - Judgment: a judge model labels each pair
same point,relatedorunrelated. The call goes throughdokimi.llm, as the architecture requires. - Output: a
hits_judged.jsonper cell, with each label andrecall_judged: the threads whose pair issame point, over all gold threads.repo comparereads it as a measure, for example--metric judge:recall_judged, next torecall. - 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
noveltyrequires? (arms rundoes 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
antonwolfy commentedon Nov 1, 2026
Proposed answers to the open questions, so a PR can start from them.
noveltyapplies:novelty/client.pypicks a judge of another family than the answerer, andnovelty/measure.pyrefuses the answering model itself (a CLI alias counts as its family's full id). Here the answer models are the cells' models inmeta.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.--labels FILEtakes a person's labels for a sample of pairs. The output reports agreement and Cohen's kappa per arm next torecall_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).matches.details.matchescutscommentandmessageto 200 characters (contract §4.5). Use it for the pairing only: take the thread's full comment from the task'sgold.json, and the finding's full text from the cell's answer at indexfinding.recall_judged ≤ recalland 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.same point,relatedandunrelated.recall_judgedcountssame pointonly, andhits_judged.jsonkeeps therelatedcount, so a reader sees how much credit sits between the two. With two labels the judge has to roundrelatedone 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 throughdokimi.judged(#314), which also replays the response cache. Therepo compare --metric judge:recall_judgedreading touchesrepos/compare.py, which is at its 800-line cap. Land it after #332, which moves the per-task values intocompare_values.py, as a second PR if the command is ready first.