Keep explanation origins scoped to the query

This commit is contained in:
2026-10-27 03:05:15 +00:00
parent 906ca34b6d
commit 3f389662e9
3 changed files with 53 additions and 4 deletions

View File

@@ -162,7 +162,13 @@ history regression checking source chronology and previous-origin links.
## 3. Container Explanations Take The Last Descendant Replacement's Origin
Priority: P2. Status: open.
Priority: P2. Status: complete.
Completion: explanation uses the model's direct query-scoped contribution when
available and reserves operation fallback for paths without that contribution.
`TestOverlayExplanationUsesQueryScopedOrigin` failed before the fix and now
checks root, enclosing mapping, and leaf locations. The full ordinary suite and
`git diff --check` passed, including removed/replaced descendant coverage.
Relevant code: `historySelectionForApplication` in
[explain.go](../../internal/engine/explain.go), lines 183–207; and descendant

View File

@@ -0,0 +1,41 @@
package configkit_test
import (
"testing"
"testing/fstest"
configkit "gitea.maximumdirect.net/eric/ConfigKit"
)
func TestOverlayExplanationUsesQueryScopedOrigin(t *testing.T) {
t.Parallel()
filesystem := configkit.NewFilesystem("origin", fstest.MapFS{
"main.yml": {Data: []byte("a: base\ngroup: {x: X, y: Y}\n")},
"overlays/dev.yml": {Data: []byte("a: overlay\ngroup: {x: O}\n")},
})
result, err := configkit.Load[map[string]any](filesystem.File("main.yml"), configkit.Options[map[string]any]{Overlay: "dev"})
if err != nil {
t.Fatal(err)
}
for _, test := range []struct {
path configkit.Path
line, column int
}{
{configkit.Keys(), 1, 1},
{configkit.Keys("group"), 2, 8},
{configkit.Keys("group", "x"), 2, 12},
} {
explanation, found := result.Explain(test.path)
if !found || len(explanation.History) == 0 {
t.Fatalf("missing explanation for %s", test.path)
}
origin := explanation.History[len(explanation.History)-1].Origin
if origin.Layer != configkit.LayerOverlay || len(origin.Locations) != 1 {
t.Fatalf("overlay origin for %s = %#v", test.path, origin)
}
location := origin.Locations[0]
if location.SourceID != "source-2" || location.Line != test.line || location.Column != test.column {
t.Fatalf("overlay location for %s = %#v; want line %d column %d", test.path, location, test.line, test.column)
}
}
}

View File

@@ -181,11 +181,13 @@ func historySelectionForPayload(selections []historySelection, ref historyPayloa
}
func historySelectionForApplication(model *historyModel, selections []historySelection, phase historyPhase, applicationRef historyPayloadRef) (historySelection, bool) {
if direct, ok := historySelectionForPayload(selections, applicationRef); ok {
return direct, true
}
// Only removed/replaced paths lacking a direct contribution need an
// operation fallback. A descendant's location cannot describe its container.
var selected historySelection
found := false
if direct, ok := historySelectionForPayload(selections, applicationRef); ok {
selected, found = direct, true
}
var payloads []historySourcePayload
switch phase {
case historyPhaseSources: