Classify remaining D&D producer diagnostics

This commit is contained in:
2026-08-27 16:04:32 +00:00
parent 6a1fd7bdb6
commit 480680b257
22 changed files with 202 additions and 182 deletions

View File

@@ -27,7 +27,6 @@ const (
ReasonCodeSourceRefsNormalized = "source_references_normalized"
ReasonCodeTurnsReordered = "combat_turns_reordered"
ReasonCodeDuplicateCollapsed = "duplicate_combat_turn_collapsed"
ReasonCodeWarningsOmitted = "combat_turn_normalization_warnings_omitted"
)
const (
@@ -114,10 +113,11 @@ func (n *Normalizer) Normalize(ctx context.Context, req contracts.TypedNormalize
index := source.NewDocumentIndex(req.Source)
order := shared.NewSourceRefOrderFromIndex(index)
value, warnings := normalizeList(req.MergeOutput.Value, index, order, npcRegistry)
return contracts.TypedNormalizeResult[dnd.CombatTurnList]{
Value: value,
Warnings: diagnostics.LimitWarnings(warnings, "combat_turns", ReasonCodeWarningsOmitted),
}, nil
diagnosticGroups, err := diagnostics.NormalizationDiagnostics(warnings)
if err != nil {
return contracts.TypedNormalizeResult[dnd.CombatTurnList]{}, normalizerErrorf("collect diagnostics: %w", err)
}
return contracts.TypedNormalizeResult[dnd.CombatTurnList]{Value: value, Diagnostics: diagnosticGroups}, nil
}
type normalizedRecord struct {

View File

@@ -47,8 +47,8 @@ func TestNormalizeCanonicalizesFieldsAndRegistryIdentities(t *testing.T) {
t.Fatalf("normalized refs = %#v, want %#v", result.Value.CombatTurns[0].SourceRefs, wantRefs)
}
for _, reason := range []string{ReasonCodeActorCanonicalized, ReasonCodeSourceRefsNormalized} {
if !hasWarningReason(result.Warnings, reason) {
t.Fatalf("warnings = %#v, missing reason %q", result.Warnings, reason)
if !hasDiagnosticReason(result.Diagnostics, reason) {
t.Fatalf("diagnostics = %#v, missing reason %q", result.Diagnostics, reason)
}
}
if !reflect.DeepEqual(input.CombatTurns[0], original) {
@@ -82,15 +82,8 @@ func TestNormalizeLimitsWarningsWithoutChangingCombatTurnValues(t *testing.T) {
if len(result.Value.CombatTurns) != len(turns) || result.Value.CombatTurns[0].Actor != "Aria" {
t.Fatalf("normalized turns = %#v, want canonicalized values", result.Value.CombatTurns)
}
if len(result.Warnings) != diagnostics.MaxWarnings {
t.Fatalf("warning count = %d, want %d", len(result.Warnings), diagnostics.MaxWarnings)
}
if first := result.Warnings[0]; first.Scope != "combat_turns[0]" || first.ReasonCode != ReasonCodeActorCanonicalized {
t.Fatalf("first warning = %#v, want first input warning", first)
}
summary := result.Warnings[len(result.Warnings)-1]
if summary.Scope != "combat_turns" || summary.ReasonCode != ReasonCodeWarningsOmitted || summary.Message != "2 additional warning(s) omitted" {
t.Fatalf("warning summary = %#v", summary)
if len(result.Warnings) != 0 || len(result.Diagnostics) == 0 || result.Diagnostics[0].Disposition != contracts.DiagnosticDispositionObservation || result.Diagnostics[0].OccurrenceCount != len(turns) || len(result.Diagnostics[0].Samples) != contracts.MaxDiagnosticSamples {
t.Fatalf("diagnostics = %#v", result.Diagnostics)
}
}
@@ -147,12 +140,12 @@ func TestNormalizeOrdersBySourcePositionAndCollapsesExactDuplicates(t *testing.T
if result.Value.CombatTurns[0].SourceRefs[0].StartUnitID != 50 || result.Value.CombatTurns[1].SourceRefs[0].StartUnitID != 90 || result.Value.CombatTurns[2].Actor != "Unknown" {
t.Fatalf("normalized order/value = %#v, want chronology then invalid evidence", result.Value.CombatTurns)
}
if !hasWarningReason(result.Warnings, ReasonCodeTurnsReordered) || !hasWarningReason(result.Warnings, ReasonCodeDuplicateCollapsed) {
t.Fatalf("warnings = %#v, want reorder and duplicate warnings", result.Warnings)
if !hasDiagnosticReason(result.Diagnostics, ReasonCodeTurnsReordered) || !hasDiagnosticReason(result.Diagnostics, ReasonCodeDuplicateCollapsed) {
t.Fatalf("diagnostics = %#v, want reorder and duplicate observations", result.Diagnostics)
}
for _, warning := range result.Warnings {
if warning.ReasonCode == ReasonCodeDuplicateCollapsed && warning.Scope != "combat_turns[1]" {
t.Fatalf("duplicate warning = %#v, want retained input scope combat_turns[1]", warning)
for _, diagnostic := range result.Diagnostics {
if diagnostic.ReasonCode == ReasonCodeDuplicateCollapsed && diagnostic.Samples[0].Scope != "combat_turns[1]" {
t.Fatalf("duplicate diagnostic = %#v, want retained input scope combat_turns[1]", diagnostic)
}
}
}
@@ -202,8 +195,8 @@ func TestNormalizePreservesStableOrderForEqualEvidencePositions(t *testing.T) {
if got := []string{result.Value.CombatTurns[0].Actor, result.Value.CombatTurns[1].Actor}; !reflect.DeepEqual(got, []string{"Aria", "Borin"}) {
t.Fatalf("equal-position order = %#v, want stable input order", got)
}
if hasWarningReason(result.Warnings, ReasonCodeTurnsReordered) {
t.Fatalf("warnings = %#v, equal-position stable sort should not warn", result.Warnings)
if hasDiagnosticReason(result.Diagnostics, ReasonCodeTurnsReordered) {
t.Fatalf("diagnostics = %#v, equal-position stable sort should not report reordering", result.Diagnostics)
}
}
@@ -361,9 +354,9 @@ func npcReferences(t *testing.T) contracts.ReferenceSet {
}}
}
func hasWarningReason(warnings []contracts.Warning, reason string) bool {
for _, warning := range warnings {
if warning.ReasonCode == reason {
func hasDiagnosticReason(diagnostics []contracts.ProducerDiagnostic, reason string) bool {
for _, diagnostic := range diagnostics {
if diagnostic.ReasonCode == reason && diagnostic.Disposition == contracts.DiagnosticDispositionObservation {
return true
}
}