From 906ca34b6daf4ddefaa1b2b88ba8a14b68e68d01 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 27 Oct 2026 03:04:31 +0000 Subject: [PATCH] Avoid duplicate intermediate source history --- docs/roadmap/remediation.md | 8 +++++- facade_tier_history_test.go | 43 +++++++++++++++++++++++++++++++ internal/engine/history_source.go | 6 +++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 facade_tier_history_test.go diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index d9b3963..dfee139 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -123,7 +123,13 @@ checks effective and historical redaction. ## 2. Intermediate Source Definitions Are Duplicated In Metadata History -Priority: P2. Status: open. +Priority: P2. Status: complete. + +Completion: displaced definitions are omitted only when a preceding operation +already introduced that concrete path with the same represented origin. +`TestTierHistoryRecordsEachContributionOnce` failed before the fix and passes +for both differing and equal-valued tiers. The full ordinary suite and +`git diff --check` passed, including the existing shape-change regressions. Relevant code: `observeSourceEvidence` in [history_source.go](../../internal/engine/history_source.go), lines 82–117; diff --git a/facade_tier_history_test.go b/facade_tier_history_test.go new file mode 100644 index 0000000..a790bb2 --- /dev/null +++ b/facade_tier_history_test.go @@ -0,0 +1,43 @@ +package configkit_test + +import ( + "testing" + + configkit "gitea.maximumdirect.net/eric/ConfigKit" +) + +func TestTierHistoryRecordsEachContributionOnce(t *testing.T) { + t.Parallel() + for _, middle := range []string{"mid", "low"} { + t.Run(middle, func(t *testing.T) { + result, err := configkit.LoadLayered[map[string]any]( + configkit.Input{Source: configkit.Bytes("main", []byte("x: high\n"))}, + configkit.LayeredOptions[map[string]any]{Underlays: []configkit.Underlay{ + {Inputs: []configkit.Input{{Source: configkit.Bytes("low", []byte("x: low\n"))}}}, + {Inputs: []configkit.Input{{Source: configkit.Bytes("middle", []byte("x: "+middle+"\n"))}}}, + }}, + ) + if err != nil { + t.Fatal(err) + } + history := result.Metadata().History(configkit.Keys("x")) + if len(history) != 3 { + t.Fatalf("history has %d records; want the three independent contributions", len(history)) + } + for index, sourceID := range []string{"source-2", "source-3", "source-1"} { + change := history[index] + if len(change.Origin.Locations) != 1 || change.Origin.Locations[0].SourceID != sourceID { + t.Fatalf("contribution %d has origin %#v", index, change.Origin) + } + if index == 0 { + if len(change.Previous) != 0 { + t.Fatal("first source contribution has a previous origin") + } + } else if len(change.Previous) != 1 || len(change.Previous[0].Locations) != 1 || + change.Previous[0].Locations[0] != history[index-1].Origin.Locations[0] { + t.Fatalf("contribution %d has incorrect previous origin %#v", index, change.Previous) + } + } + }) + } +} diff --git a/internal/engine/history_source.go b/internal/engine/history_source.go index 401aa12..1a9478f 100644 --- a/internal/engine/history_source.go +++ b/internal/engine/history_source.go @@ -89,6 +89,9 @@ func (builder *historyBuilder) observeSourceEvidence(root *document.Node, origin owned.hasApplicationRef = true } for displacedIndex, displaced := range record.displaced { + if sourceOriginRepresentedByOperation(builder.model, displaced.path, displaced.origin) { + continue + } definitionPayload := payload definitionPayload.substep = displacedIndex + 1 definition := builder.addAttribution(displaced.origin, nil, nil) @@ -249,6 +252,9 @@ func sourceOriginRepresentedByOperation(model *historyModel, path Path, origin O if observation.phase != historyPhaseSources || observation.role != historyRoleOperation { return } + if !observation.afterPresent || !sameConcreteHistoryPath(path, observation.path) { + return + } operationOrigin, ok := model.resolveAttribution(observation.attributionRef) if !ok { return