Classify source relatedness as data-quality advisories

This commit is contained in:
2026-08-27 15:49:53 +00:00
parent 1f1967c8d2
commit ccba2ce3f9
25 changed files with 232 additions and 189 deletions

View File

@@ -14,10 +14,9 @@ import (
)
const (
Key = "extract/dnd/enemy-events/source_relatedness"
WarningReasonCode = "enemy_event_not_near_source"
OmittedReasonCode = "enemy_event_relatedness_warnings_omitted"
policy = "dnd.enemy_events.validator.source_relatedness.v1"
Key = "extract/dnd/enemy-events/source_relatedness"
ReasonCode = "enemy_event_not_near_source"
policy = "dnd.enemy_events.validator.source_relatedness.v1"
)
type Options struct{}
@@ -51,13 +50,13 @@ func (v *Validator) Validate(_ context.Context, req contracts.TypedValidationReq
}
warnings = append(warnings, contracts.Warning{
Scope: fmt.Sprintf("events[%d]", eventIndex),
ReasonCode: WarningReasonCode,
ReasonCode: ReasonCode,
Message: diagnostics.Aggregate("enemy event subject not near source", []string{
fmt.Sprintf("subject %s was not found in cited source text", diagnostics.Quote(event.Name)),
}),
})
}
return contracts.ValidationResult{Approved: true, Warnings: diagnostics.LimitWarnings(warnings, "enemy_events", OmittedReasonCode)}, nil
return diagnostics.DataQualityResult(warnings)
}
func Spec() pipeline.ValidatorSpec {

View File

@@ -14,7 +14,7 @@ func TestValidatorUsesOnlyCitedTranscriptEvidence(t *testing.T) {
value := validEventList()
references := contracts.ReferenceSet{Slots: map[string]contracts.ResolvedReferenceSlot{"npc_registry": {Items: []contracts.ReferenceItem{{Content: []byte("Ashfang")}}}}}
result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("The party waits."), References: references, Value: value})
if err != nil || !result.Approved || len(result.Warnings) != 1 || result.Warnings[0].ReasonCode != WarningReasonCode {
if err != nil || !result.Approved || len(result.Warnings) != 0 || len(result.Diagnostics) != 1 || result.Diagnostics[0].ReasonCode != ReasonCode || result.Diagnostics[0].Disposition != contracts.DiagnosticDispositionAdvisory {
t.Fatalf("Validate() = %#v, %v", result, err)
}
}
@@ -23,7 +23,7 @@ func TestValidatorAcceptsUnicodeSubjectInCitedEvidence(t *testing.T) {
value := validEventList()
value.Events[0].Name = "O'Rin Thorn"
result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("orin\u2003thorn flees."), Value: value})
if err != nil || !result.Approved || len(result.Warnings) != 0 {
if err != nil || !result.Approved || len(result.Diagnostics) != 0 {
t.Fatalf("Validate() = %#v, %v", result, err)
}
}
@@ -31,7 +31,7 @@ func TestValidatorAcceptsUnicodeSubjectInCitedEvidence(t *testing.T) {
func TestValidatorDefersMalformedValuesAndBoundsWarnings(t *testing.T) {
malformed := dnd.EnemyEventList{Events: []dnd.EnemyEvent{{Name: "Ashfang"}}}
result, err := New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("none"), Value: malformed})
if err != nil || !result.Approved || len(result.Warnings) != 0 {
if err != nil || !result.Approved || len(result.Diagnostics) != 0 {
t.Fatalf("shape deferral = %#v, %v", result, err)
}
value := dnd.EnemyEventList{Events: make([]dnd.EnemyEvent, 30)}
@@ -39,8 +39,8 @@ func TestValidatorDefersMalformedValuesAndBoundsWarnings(t *testing.T) {
value.Events[index] = dnd.EnemyEvent{Name: "Missing", Kind: dnd.EnemyEventKindEngaged, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}
}
result, err = New(Options{}).Validate(context.Background(), contracts.TypedValidationRequest[dnd.EnemyEventList]{Source: document("none"), Value: value})
if err != nil || !result.Approved || len(result.Warnings) == 0 || result.Warnings[len(result.Warnings)-1].ReasonCode != OmittedReasonCode {
t.Fatalf("bounded warnings = %#v, %v", result, err)
if err != nil || !result.Approved || len(result.Warnings) != 0 || len(result.Diagnostics) != 1 || result.Diagnostics[0].OccurrenceCount != len(value.Events) || len(result.Diagnostics[0].Samples) != contracts.MaxDiagnosticSamples {
t.Fatalf("bounded advisories = %#v, %v", result, err)
}
registry := pipeline.NewValidatorRegistry()
if err := Register(registry); err != nil {