memory: record the recall topic veto's real price (V-496)

#496 asked to skip the veto when the question and the hit are in
different scripts, so an English question stops losing a Russian note.
Measured first: the fixture has no cross-language case, and en-hard-024
is an English question against an English note. Both proposed fixes are
no-ops.

What the veto actually does on the fixture, with the real embedder: it
costs en-hard-024 and buys ru-silent-029. Pass count is 22/32 either
way; false recall is 0/5 with it and 1/5 without. The two cases are one
lexical class, so no rule cheap enough for RecallAllowed separates them.

Accepts the loss and pins both sides in a test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 00:01:49 +04:00
parent 197897516e
commit 9e25f18a3e
3 changed files with 89 additions and 0 deletions
+8
View File
@@ -60,6 +60,14 @@ var firstPerson = map[string]bool{
// kill one false one. A question about his own life keeps the embedder alone
// as its judge. A question about the world has to name something the memory
// actually mentions.
//
// The veto's price was re-measured on 2026-08-03 (#496,
// docs/evals/2026-08-03-recall-topic-veto.md). It costs one true recall and
// buys one false one, and the fixture pass count is the same either way. The
// lost case is an English paraphrase, not the cross-language loss it was
// reported as, and the fixture has no cross-language case at all. Do not add a
// script test or a bilingual stem map for it — both are no-ops here. The
// separating signal is semantic and belongs in a reranker, not in this file.
func RecallAllowed(query, text string) bool {
if mentionsHim(query) {
return true
+16
View File
@@ -31,6 +31,22 @@ func TestRecallAllowed(t *testing.T) {
}
}
// The known cost of the veto and the thing that pays for it, both measured on
// the held-out fixture with the real embedder (#496,
// docs/evals/2026-08-03-recall-topic-veto.md). The two are one lexical class:
// zero shared content words, no first-person marker, scores 0.826 against 0.835
// and margins 0.023 against 0.019. Recovering the first re-admits the second,
// which puts false recall back to 1/5. Anyone loosening the veto has to move
// the first line without moving the second.
func TestRecallVetoTradeIsPinned(t *testing.T) {
if RecallAllowed("what fixed the screen problem", "the flicker went away once i swapped the display cable") {
t.Error("en-hard-024 is expected to stay vetoed — if this passes now, re-measure false recall before celebrating")
}
if RecallAllowed("во сколько отходит поезд", "погулял вдоль реки") {
t.Error("ru-silent-029 must stay vetoed — this is the false recall the veto exists to stop")
}
}
// A question made only of filler has no topic word to match on, and the score
// gate is then the only judge it can have.
func TestRecallAllowedFallsBackWhenNothingToCompare(t *testing.T) {