Enforce durable enemy event validation

This commit is contained in:
2026-08-09 02:03:27 +00:00
parent b3644abc0e
commit 82ffe85f2d
12 changed files with 313 additions and 28 deletions

View File

@@ -97,7 +97,7 @@ func TestExtractUsesDocumentOrderForReferencesAndTurns(t *testing.T) {
req := extractionRequest(t)
req.Source.Units = []source.SourceUnit{{ID: 30}, {ID: 10}}
req.Chunk.Units = append([]source.SourceUnit(nil), req.Source.Units...)
req.Chunk.Ref = source.SourceRef{SourceID: req.Source.ID, StartUnitID: 30, EndUnitID: 10}
req.Chunk.Ref = source.SourceRef{SourceID: req.Source.ID, StartUnitID: 10, EndUnitID: 30}
req.References = sceneReferences(t, req.Chunk, dnd.SceneKindCombat)
result, err := newExtractor(t, client).Extract(context.Background(), req)
@@ -265,7 +265,7 @@ func TestExtractAppliesSceneEligibilityBeforePromptConstruction(t *testing.T) {
name: "start mismatch",
scenes: []dnd.SceneDescription{func() dnd.SceneDescription {
value := sceneDescription(extractionRequest(t).Chunk, dnd.SceneKindCombat)
value.SourceRef.StartUnitID = 2
value.SourceRef.StartUnitID = 3
return value
}()},
wantWarning: "scene_classification_unavailable",
@@ -275,7 +275,7 @@ func TestExtractAppliesSceneEligibilityBeforePromptConstruction(t *testing.T) {
name: "end mismatch",
scenes: []dnd.SceneDescription{func() dnd.SceneDescription {
value := sceneDescription(extractionRequest(t).Chunk, dnd.SceneKindCombat)
value.SourceRef.EndUnitID = 10
value.SourceRef.EndUnitID = 9
return value
}()},
wantWarning: "scene_classification_unavailable",
@@ -340,7 +340,7 @@ func TestSceneEligibilityMetadataAndFingerprintsTrackGatingValues(t *testing.T)
change func(*dnd.SceneDescription)
}{
{"kind", func(value *dnd.SceneDescription) { value.Kind = dnd.SceneKindNarrative }},
{"range", func(value *dnd.SceneDescription) { value.SourceRef.StartUnitID = 2 }},
{"range", func(value *dnd.SceneDescription) { value.SourceRef.StartUnitID = 3 }},
} {
t.Run(test.name, func(t *testing.T) {
changed := base
@@ -477,7 +477,7 @@ func extractionRequest(t *testing.T) contracts.TypedExtractionRequest {
ID: "session-alpha:chunk:0",
SourceID: doc.ID,
Index: 0,
Ref: source.SourceRef{SourceID: doc.ID, StartUnitID: 10, EndUnitID: 2},
Ref: source.SourceRef{SourceID: doc.ID, StartUnitID: 2, EndUnitID: 10},
Content: []byte(`{"units":[10,2]}`),
MediaType: "application/json",
Units: append([]source.SourceUnit(nil), doc.Units...),

View File

@@ -179,6 +179,7 @@ func TestGroundingRejectsMissingAndInvalidReferences(t *testing.T) {
{"malformed durable content", replaceItem(valid, NPCOccurrenceReferenceSlot, contracts.ReferenceItem{MediaType: "application/json", Content: []byte(`{}`)})},
{"wrong media type", replaceItem(valid, CombatTurnReferenceSlot, contracts.ReferenceItem{MediaType: "text/plain", Content: valid.Slots[CombatTurnReferenceSlot].Items[0].Content})},
{"oversize", replaceItem(valid, CombatTurnReferenceSlot, contracts.ReferenceItem{MediaType: "application/json", Content: make([]byte, ReferenceMaxBytes+1)})},
{"reversed combat-turn evidence", reversedCombatTurnReference(t, valid)},
}
resolver, err := newGroundingResolver(valid)
if err != nil {
@@ -193,6 +194,19 @@ func TestGroundingRejectsMissingAndInvalidReferences(t *testing.T) {
}
}
func reversedCombatTurnReference(t *testing.T, references contracts.ReferenceSet) contracts.ReferenceSet {
t.Helper()
value := dnd.CombatTurnList{CombatTurns: []dnd.CombatTurn{{
Actor: "Ashfang", TurnKind: dnd.CombatTurnKindTurn,
SourceRefs: []source.SourceRef{{SourceID: "combat-session", StartUnitID: 2, EndUnitID: 1}},
}}}
content, err := combatturncodec.New().EncodeCandidate(value)
if err != nil {
t.Fatalf("EncodeCandidate() error = %v", err)
}
return replaceItem(references, CombatTurnReferenceSlot, newReferenceItem(CombatTurnReferenceSlot, content))
}
func groundingReferences(t *testing.T, enemy string, sceneKind dnd.SceneKind) contracts.ReferenceSet {
t.Helper()
npcContent, err := npccodec.New().Encode(dnd.NPCRegistry{NPCs: []dnd.NPC{{