Honor parsed sequence indexes in version protections

This commit is contained in:
2026-10-17 04:46:53 +00:00
parent 8a89806e44
commit 884215c403
3 changed files with 47 additions and 3 deletions

View File

@@ -124,7 +124,13 @@ appends do not falsely claim to change earlier records.
## 4. Honor contextual parsed version-path indexes — P2 ## 4. Honor contextual parsed version-path indexes — P2
- [ ] Addressed and regression-tested. - [x] Addressed and regression-tested.
Completion: protected-path sequence traversal uses the existing canonical
contextual index interpreter. Public import and grouped-underlay parity tests
cover parsed and explicitly indexed rules; existing version-path tests preserve
numeric-key intent and invalid index behavior. Focused tests and the everyday
check passed.
**Owner:** `fragmentVersionClaimProjected` in **Owner:** `fragmentVersionClaimProjected` in
[directives.go](../../internal/engine/directives.go), lines 190–194. [directives.go](../../internal/engine/directives.go), lines 190–194.

View File

@@ -63,6 +63,44 @@ func TestFacadeSourceVersionEvidenceFollowsAuthoredSequenceRanges(t *testing.T)
} }
} }
func TestFacadeSourceVersionOwnershipAcceptsContextualIndexes(t *testing.T) {
t.Parallel()
parsed, err := configkit.ParsePath("/records/0/version")
if err != nil {
t.Fatal(err)
}
for _, grouped := range []bool{false, true} {
for _, path := range []configkit.Path{configkit.Keys("records").Index(0).Key("version"), parsed} {
definition := configkit.Definition[map[string]any]{
SequenceCompositions: []configkit.SequenceComposition{{Path: configkit.Keys("records"), IdentityField: "id"}},
Version: &configkit.VersionRule{Path: path, Check: func(configkit.Version, *configkit.Validation) error { return nil }},
}
first := "records: [{id: first, version: first}]\n"
child := "records: [{id: child, version: child}]\n"
var result *configkit.Result[map[string]any]
if grouped {
result, err = configkit.LoadLayered(configkit.Input{Source: configkit.Bytes("main", []byte(first))}, configkit.LayeredOptions[map[string]any]{
Definition: definition, SourceVersionMode: configkit.SourceVersionModeRequired,
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.Bytes("lower", []byte(child))}}}},
})
} else {
filesystem := configkit.NewFilesystem("versions", fstest.MapFS{"main.yml": {Data: []byte("configkit: {imports: [{path: child.yml}]}\n" + first)}, "child.yml": {Data: []byte(child)}})
result, err = configkit.Load(filesystem.File("main.yml"), configkit.Options[map[string]any]{Definition: definition, SourceVersionMode: configkit.SourceVersionModeRequired, Imports: true})
}
if err != nil || result == nil {
t.Fatalf("grouped=%t path=%#v: %v", grouped, path, err)
}
want := 2
if grouped {
want = 1
}
if len(result.Config["records"].([]any)) != want {
t.Fatal("version path changed composition")
}
}
}
}
type sourceVersionModeReader struct { type sourceVersionModeReader struct {
*strings.Reader *strings.Reader
reads int reads int

View File

@@ -188,10 +188,10 @@ func fragmentVersionClaimProjected(root *document.Node, rule *VersionRule, sourc
current = current.Mapping[index].Value current = current.Mapping[index].Value
currentPath = currentPath.Key(segment.key) currentPath = currentPath.Key(segment.key)
case document.Sequence: case document.Sequence:
if segment.kind != pathSegmentIndex { index, valid := schemaIndex(segment)
if !valid {
return current return current
} }
index := segment.index
for _, assigned := range ranges { for _, assigned := range ranges {
if assigned.sourceIndex != sourceIndex || assigned.path.String() != currentPath.String() { if assigned.sourceIndex != sourceIndex || assigned.path.String() != currentPath.String() {
continue continue