Match cited Regulars unambiguously and reject filler-only names
CI / ci (push) Successful in 2m25s
CI / ci (pull_request) Successful in 2m26s

A cited Regular now resolves by exact name (ignoring case and spacing)
first, then by normalized name only when that names a single Regular;
an ambiguous citation drops the Side quest instead of rewriting it to
whichever Regular shared the key (MQ-100-1).

A Side quest name with no words left after normalizing is malformed and
goes through the repair turn, so it can no longer evade the dedupe or
the dinner-name rule (MQ-100-2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 21:48:12 -04:00
co-authored by Claude Opus 5.5
parent fad1aee319
commit 1ca246b715
2 changed files with 95 additions and 10 deletions
@@ -176,6 +176,30 @@ func TestSuggestSideQuestsInventedRegularDropped(t *testing.T) {
})
}
func TestSuggestSideQuestsSharedNormalizedRegularNotRewritten(t *testing.T) {
in := household
in.Context.Regulars = append(slices.Clone(household.Context.Regulars),
harness.RegularDinner{Name: "Easy Beef Chili", Cuisine: "Tex-Mex", Method: "stovetop", Minutes: 30})
plain := sideQuest("Chili Verde", "Your Beef Chili gone green, with pork and tomatillos.", "beef chili")
easy := sideQuest("Frito Pie", "Your Easy Beef Chili over corn chips.", "EASY beef chili")
either := sideQuest("Tamale Bake", "Your Beef Chili under a cornbread crust.", "Beef Chili!")
var f llm.Fake
f.QueueGenerate(reply(batch(plain, easy, either)))
out, _, err := suggest(t, &f, in, 1)
if err != nil {
t.Fatal(err)
}
assertSideQuests(t, out, SideQuests{
Suggestions: []SideQuest{
want("Chili Verde", "Your Beef Chili gone green, with pork and tomatillos.", "Beef Chili"),
want("Frito Pie", "Your Easy Beef Chili over corn chips.", "Easy Beef Chili"),
},
Dropped: []DroppedSideQuest{
{Name: "Tamale Bake", Why: `cites "Beef Chili!", which could be any of the Regulars ["Beef Chili" "Easy Beef Chili"]`},
},
})
}
func TestSuggestSideQuestsDuplicatesDropped(t *testing.T) {
declined := sideQuest("The Best Homemade Chicken Tikka-Masala", "Spiced like your Chicken Fajitas.", "Chicken Fajitas")
oldDeclined := sideQuest("Fish Tacos!", "Tortillas like your Chicken Fajitas.", "Chicken Fajitas")
@@ -276,6 +300,30 @@ func TestSuggestSideQuestsMalformedRepairedOnce(t *testing.T) {
}
}
func TestSuggestSideQuestsFillerNameRepaired(t *testing.T) {
filler := sideQuest("The.", "Like your Beef Chili.", "Beef Chili")
first := batch(filler, filler)
var f llm.Fake
f.QueueGenerate(reply(first), reply(batch(shawarma, ragu)))
out, reqs, err := suggest(t, &f, household, 2)
if err != nil {
t.Fatal(err)
}
assertSideQuests(t, out, SideQuests{Suggestions: []SideQuest{wantShawarma, wantRagu}})
repair := reqs[1].Messages
if len(repair) != 3 || repair[1].Content != first {
t.Fatalf("repair turn does not quote the first reply: %+v", repair)
}
for _, want := range []string{
`suggestion 1 name "The." has only filler words; name the dish`,
`suggestion 2 name "The." has only filler words; name the dish`,
} {
if !strings.Contains(repair[2].Content, want) {
t.Fatalf("repair message = %q, want it to contain %q", repair[2].Content, want)
}
}
}
func TestSuggestSideQuestsMalformedTwiceFails(t *testing.T) {
var f llm.Fake
f.QueueGenerate(reply(`{"suggestions":`), reply(`{"suggestions":[]}`))