feat(evals): score what a summary should not say (#586) #633
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/586-precision-metric"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The harness could not fail a summary for containing something false — only for missing something true. This adds the metric that can.
The gap
All four scorers measure recall or correctness over what the summary does contain. #584 was a verified beat that put the party in a town they had only discussed from the road; it survived every model tried, scored 1.000 across the board, and was found by a person reading the prose. The fix for it moved no number.
What this adds
forbidden[]on a fixture — claims a summary of this session must not make. Samementionsshape as an event, and matched by exactly the same rule (every group needs one alternative present), so there is one matching rule inscoring.pyand not two. Groups are alternatives so a claim measures meaning rather than wording, and a second group expresses "only false in combination" — "Ironroot Hold" alone is fine, the session discusses it.score_precision→ the share of claims the summary did not make, with the fired claim ids as evidence.--fail-under-precisiongate.None, not1.0, when a fixture pins nothing. "Not measured" is not a clean pass, and unpinned fixtures are excluded from the mean rather than averaged in — otherwise the aggregate would rise as fixtures without the check were added.The fixture that proves it works
fixtures/synthetic/fabricated_location.jsonmakes #584 reproducible. The party discuss Ironroot Hold from the road and deliberately take the other fork; one beat says they went in, citing lines that really do mention it and really were spoken by the named player — so every grounding check passes.That row is the whole argument: perfect on everything the harness could previously see, and wrong.
Two things worth knowing about authoring it — both cost me a cycle and are now documented:
evidencefalls outside its ownt_start/t_endis dropped before validation rather than failing it, so the false beat had to be internally consistent (which is closer to #584's real shape anyway).Scope I did not take
The issue also notes the synthetic-session (audio) scene would need re-authoring to carry this case there. I left that alone deliberately — per
CLAUDE.mdand the harness README, the committed.opusclips are the artifact (piper has no seed), so re-authoring is a separate, deliberate job rather than something to slip into this PR. The text-fixture half needs no audio and is complete here.Tests
Seven unit tests for the scorer (paraphrase groups, every-group-required, case-insensitivity, not-measured
None, an unusable claim that cannot fire) plus the fixture's pinnedexpected.precision, newly asserted in the existing parametrised test. That assertion also fails a fixture carryingforbidden[]with no pinned precision — a forbidden claim nothing asserts a number about is a check that cannot fail.Backend suite:
3457 passed, 13 skipped.ruff check/ruff format --checkclean under CI's pinned 0.4.4.Closes #586
🤖 Generated with Claude Code