Verify mounted inheritance and sensitivity integration

This commit is contained in:
2026-10-14 13:50:08 +00:00
parent f50122d9f8
commit 751787ec24
3 changed files with 492 additions and 4 deletions

View File

@@ -374,7 +374,7 @@ placement and physical origin without consumer reconstruction.
## Stage 9 — Verify final inheritance, sensitivity, and lifecycle integration ## Stage 9 — Verify final inheritance, sensitivity, and lifecycle integration
Status: planned. Scope: complete private engine behavior before facade exposure. Status: complete. Scope: complete private engine behavior before facade exposure.
1. Resolve sibling inheritance only after full source precedence using the 1. Resolve sibling inheritance only after full source precedence using the
existing expansion path. Cover a child and parent supplied by separate peers, existing expansion path. Cover a child and parent supplied by separate peers,
@@ -393,8 +393,33 @@ Status: planned. Scope: complete private engine behavior before facade exposure.
placement creates a distinct risk. placement creates a distinct risk.
Validate: destination/inheritance/secret/sensitivity/explain/diff/lifecycle tests Validate: destination/inheritance/secret/sensitivity/explain/diff/lifecycle tests
and everyday check. Done: all approved engine semantics work together without and everyday check. Validated with
per-fragment loading or a post-return processing pass. `go test ./internal/engine -run 'Destination|Inheritance|Secret|Sensitivity|Explain|Diff|Load|Version|Grouped' -count=1`,
`go test -race ./internal/engine`, and `./scripts/check.sh` (go1.26.8
linux/amd64); all passed.
`TestLayeredDestinationInheritanceUsesFinalSiblingScopes` verifies parent and
child contributions from separate peers, a higher-tier parent update, nested
destination-local siblings, and cancellation. `TestLayeredDestinationInheritanceDoesNotSearchOtherCollections`
keeps sibling lookup local. `TestLayeredMountedVersionProtectionCoversInheritanceAndUpperTiers`
protects inherited values, scalar/null ancestors, consumed directives, selected
overlays, and CLI overrides at mounted paths while allowing nonclaiming mapping
ancestors and unrelated inheritance. The coverage exposed an ancestor gap in
the existing inheritance-version check; it now applies the shared fragment claim
logic to the transferred parent subtree.
`TestLayeredDestinationLifecycleAndSensitiveInspectionUseFinalShape` checks one
defaults/resolution/secret-lookup/validation lifecycle, final mounted decoding,
field tags and effective-path predicates, superseded history, and safe effective
output/explanation against canaries; it compares mounted and explicitly wrapped
loads for equal configuration and no diff. `TestLayeredDestinationDiffUsesBilateralSensitivity`
checks redaction from either side and recorded dependencies, and
`TestLayeredDestinationDecodeFailureReturnsNoResultOrLaterCallbacks` checks a
mounted decode failure returns no result. Stage 8's
`TestLayeredImportedInheritedValuesRetainResourceContexts` continues to verify
the declaring context for inherited relative resources. Done: approved engine
semantics integrate without per-fragment loading or a post-return processing
pass.
## Stage 10 — Expose the root-owned facade and public acceptance tests ## Stage 10 — Expose the root-owned facade and public acceptance tests

View File

@@ -0,0 +1,463 @@
package engine
import (
"bytes"
"fmt"
"reflect"
"strings"
"testing"
"testing/fstest"
)
func TestLayeredDestinationInheritanceUsesFinalSiblingScopes(t *testing.T) {
t.Parallel()
loaded, err := LoadLayered(
Input{Source: Bytes("main", []byte("parent:\n label: final\n settings:\n primary: {height: 4}\ncancelled:\n extends: null\n own: retained\n")), Destination: Keys("catalog", "entries")},
testSecretBinding(),
LayeredOptions[map[string]any]{Underlays: []Underlay{{Inputs: []Input{
{Source: Bytes("parent", []byte("parent:\n label: lower\n settings:\n primary: {width: '10'}\n secondary: {extends: primary}\ncancelled:\n extends: parent\n own: retained\n")), Destination: Keys("catalog", "entries")},
{Source: Bytes("child", []byte("child:\n extends: parent\n settings:\n primary: {width: '20'}\n secondary: {extends: primary}\n")), Destination: Keys("catalog", "entries")},
}}}},
)
if err != nil {
t.Fatalf("LoadLayered: %v", err)
}
result := testLoaded(loaded)
entries := layeredMap(t, layeredMap(t, result.Config, "catalog"), "entries")
parent := layeredMap(t, entries, "parent")
child := layeredMap(t, entries, "child")
parentSettings := layeredMap(t, parent, "settings")
childSettings := layeredMap(t, child, "settings")
if parent["label"] != "final" || child["label"] != "final" {
t.Fatalf("higher-tier parent was not inherited after precedence: parent=%#v child=%#v", parent, child)
}
if layeredMapValue(t, parentSettings, "secondary", "width") != "10" ||
fmt.Sprint(layeredMapValue(t, parentSettings, "secondary", "height")) != "4" ||
layeredMapValue(t, childSettings, "secondary", "width") != "20" ||
fmt.Sprint(layeredMapValue(t, childSettings, "secondary", "height")) != "4" {
t.Fatalf("nested sibling scopes were not resolved at their destinations: parent=%#v child=%#v", parentSettings, childSettings)
}
cancelled := layeredMap(t, entries, "cancelled")
if len(cancelled) != 1 || cancelled["own"] != "retained" {
t.Fatalf("higher-tier cancellation did not suppress inheritance: %#v", cancelled)
}
if _, ok := result.Metadata().Lookup(Keys("catalog", "entries", "child", inheritanceKey)); ok {
t.Fatal("effective child retained its extends directive")
}
}
func TestLayeredDestinationInheritanceDoesNotSearchOtherCollections(t *testing.T) {
t.Parallel()
_, err := LoadLayered(
Input{Source: Bytes("main", []byte("parent: {value: foreign}\n")), Destination: Keys("catalog", "other")},
testSecretBinding(),
LayeredOptions[map[string]any]{Underlays: []Underlay{{Inputs: []Input{{
Source: Bytes("child", []byte("child: {extends: parent}\n")),
Destination: Keys("catalog", "entries"),
}}}}},
)
if got := groupedErrorCode(t, err); got != CodeInheritanceMissing {
t.Fatalf("cross-collection parent lookup error = %s, want %s", got, CodeInheritanceMissing)
}
}
func TestLayeredMountedVersionProtectionCoversInheritanceAndUpperTiers(t *testing.T) {
t.Parallel()
for _, test := range []struct {
name string
body string
rulePath Path
}{
{name: "inherited protected value", body: "parent: {version: v2}\nchild: {extends: parent}\n", rulePath: Keys("catalog", "entries", "child", "version")},
{name: "inherited protected ancestor", body: "parent: {schema: null}\nchild: {extends: parent}\n", rulePath: Keys("catalog", "entries", "child", "schema", "version")},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
_, err := LoadLayered(
Input{Source: NoSource()}, testSecretBinding(),
LayeredOptions[map[string]any]{
Definition: Definition[map[string]any]{Version: &VersionRule{Path: test.rulePath, Check: func(Version, *Validation) error { return nil }}},
Underlays: []Underlay{{Inputs: []Input{{Source: Bytes("underlay", []byte(test.body)), Destination: Keys("catalog", "entries")}}}},
},
)
if got := groupedErrorCode(t, err); got != CodeInheritanceVersion {
t.Fatalf("inheritance version protection code = %s", got)
}
})
}
t.Run("inheritance consumes a protected directive", func(t *testing.T) {
_, err := LoadLayered(
Input{Source: Bytes("main", []byte("parent: {value: inherited}\nchild: {extends: parent}\n")), Destination: Keys("catalog", "entries")},
testSecretBinding(),
LayeredOptions[map[string]any]{Definition: Definition[map[string]any]{Version: &VersionRule{
Path: Keys("catalog", "entries", "child", "extends"),
Check: func(version Version, _ *Validation) error {
if !version.Present || version.Text != "parent" {
t.Errorf("mounted protected directive value = %#v", version)
}
return nil
},
}}},
)
if got := groupedErrorCode(t, err); got != CodeInheritanceVersion {
t.Fatalf("consumed directive protection code = %s", got)
}
})
t.Run("inherited mapping ancestor without a version claim", func(t *testing.T) {
loaded, err := LoadLayered(
Input{Source: NoSource()}, testSecretBinding(),
LayeredOptions[map[string]any]{
Definition: Definition[map[string]any]{Version: &VersionRule{Path: Keys("catalog", "entries", "child", "schema", "version"), Check: func(Version, *Validation) error { return nil }}},
Underlays: []Underlay{{Inputs: []Input{{
Source: Bytes("underlay", []byte("parent: {schema: {name: ordinary}}\nchild: {extends: parent}\n")), Destination: Keys("catalog", "entries"),
}}}},
},
)
if err != nil {
t.Fatalf("nonclaiming inherited mapping ancestor was rejected: %v", err)
}
if got := layeredMapValue(t, testLoaded(loaded).Config, "catalog", "entries", "child", "schema", "name"); got != "ordinary" {
t.Fatalf("inherited non-version value = %#v", got)
}
})
for _, test := range []struct {
name string
body string
}{
{name: "overlay protected value", body: "catalog: {schema: {version: v2}}\n"},
{name: "overlay protected ancestor", body: "catalog: {schema: null}\n"},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
filesystem := NewFilesystem("overlay", fstest.MapFS{"overlays/test.yml": &fstest.MapFile{Data: []byte(test.body)}})
_, err := LoadLayered(
Input{Source: Bytes("main", []byte("schema: {version: v1}\n")), Destination: Keys("catalog")},
testSecretBinding(),
LayeredOptions[map[string]any]{
Definition: Definition[map[string]any]{Version: &VersionRule{Path: Keys("catalog", "schema", "version"), Check: func(version Version, _ *Validation) error {
if !version.Present || version.Text != "v1" {
t.Errorf("mounted main version = %#v", version)
}
return nil
}}},
Overlay: "test", OverlayFilesystem: &FilesystemContext{Root: ".", BaseDir: ".", FS: &filesystem},
},
)
if got := groupedErrorCode(t, err); got != CodeOverlayVersion {
t.Fatalf("overlay version protection code = %s", got)
}
})
}
for _, test := range []struct {
name string
path Path
value any
}{
{name: "override protected value", path: Keys("catalog", "schema", "version"), value: "v2"},
{name: "override protected ancestor", path: Keys("catalog", "schema"), value: map[string]any{"version": "v2"}},
} {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
_, err := LoadLayered(
Input{Source: Bytes("main", []byte("schema: {version: v1}\n")), Destination: Keys("catalog")},
testSecretBinding(),
LayeredOptions[map[string]any]{
Definition: Definition[map[string]any]{Version: &VersionRule{Path: Keys("catalog", "schema", "version"), Check: func(Version, *Validation) error { return nil }}},
Overrides: []Override{{Path: test.path, Value: test.value, Flag: "--config"}},
},
)
if got := groupedErrorCode(t, err); got != CodeOverrideVersion {
t.Fatalf("override version protection code = %s", got)
}
})
}
t.Run("unrelated inheritance under the protected mapping ancestor", func(t *testing.T) {
loaded, err := LoadLayered(
Input{Source: Bytes("main", []byte("schema: {version: v1}\nentries:\n parent: {value: inherited}\n child: {extends: parent}\n")), Destination: Keys("catalog")},
testSecretBinding(),
LayeredOptions[map[string]any]{Definition: Definition[map[string]any]{Version: &VersionRule{Path: Keys("catalog", "schema", "version"), Check: func(Version, *Validation) error { return nil }}}},
)
if err != nil {
t.Fatalf("unrelated mounted inheritance was rejected: %v", err)
}
child := layeredMapValue(t, testLoaded(loaded).Config, "catalog", "entries", "child", "value")
if child != "inherited" {
t.Fatalf("unrelated inheritance value = %#v", child)
}
})
}
type destinationDecodeProbe string
var destinationDecodeProbeValues []string
func (value *destinationDecodeProbe) UnmarshalText(text []byte) error {
destinationDecodeProbeValues = append(destinationDecodeProbeValues, string(text))
*value = destinationDecodeProbe(text)
return nil
}
func (value destinationDecodeProbe) MarshalText() ([]byte, error) {
return []byte(value), nil
}
type destinationLifecycleConfig struct {
Defaulted string `yaml:"defaulted"`
Tenant struct {
Tagged string `yaml:"tagged" configkit:"secret"`
Dynamic string `yaml:"dynamic"`
Credential Secret `yaml:"credential"`
Derived string `yaml:"derived"`
Decoded destinationDecodeProbe `yaml:"decoded"`
} `yaml:"tenant"`
}
func TestLayeredDestinationLifecycleAndSensitiveInspectionUseFinalShape(t *testing.T) {
const (
oldTagged = "SUPERSEDED-TAGGED-CANARY"
newTagged = "CURRENT-TAGGED-CANARY"
oldDynamic = "SUPERSEDED-DYNAMIC-CANARY"
newDynamic = "CURRENT-DYNAMIC-CANARY"
credential = "RESOLVED-CREDENTIAL-CANARY"
secretName = "DESTINATION_SECRET_NAME"
decodeValue = "decoded-once"
)
type calls struct{ defaults, resolve, lookup, validate int }
newLoad := func(mounted bool, observed *calls) (*testResult[destinationLifecycleConfig], error) {
mainBody := "tagged: " + newTagged + "\ndynamic: " + newDynamic + "\ncredential: " + secretName + "\ndecoded: " + decodeValue + "\n"
underlayBody := "tagged: " + oldTagged + "\ndynamic: " + oldDynamic + "\ncredential: OLD_DESTINATION_SECRET_NAME\ndecoded: old\n"
if !mounted {
mainBody = "tenant:\n" + indentDestinationBody(mainBody)
underlayBody = "tenant:\n" + indentDestinationBody(underlayBody)
}
main := loadInput{source: Bytes("main", []byte(mainBody))}
underlay := loadInput{source: Bytes("underlay", []byte(underlayBody))}
if mounted {
main.destination = Keys("tenant")
underlay.destination = Keys("tenant")
}
definition := Definition[destinationLifecycleConfig]{
Defaults: func() destinationLifecycleConfig {
observed.defaults++
return destinationLifecycleConfig{Defaulted: "from-default"}
},
Resolve: func(configuration *destinationLifecycleConfig, resolution *Resolution) error {
observed.resolve++
configuration.Tenant.Derived = configuration.Tenant.Dynamic
resolution.Record(Keys("tenant", "derived"), ChangeNormalization, "derive from mounted input", Keys("tenant", "dynamic"))
return nil
},
Validate: func(configuration *destinationLifecycleConfig, _ *Validation) {
observed.validate++
if configuration.Tenant.Decoded != destinationDecodeProbe(decodeValue) || configuration.Tenant.Dynamic != newDynamic {
t.Errorf("validation saw a non-final mounted configuration: %#v", configuration.Tenant)
}
},
Secret: func(path Path) bool { return path.String() == "/tenant/dynamic" },
}
loaded, err := loadGroupedWithSecretLookup(loadRequest[destinationLifecycleConfig]{
main: main, underlays: []loadGroup{{inputs: []loadInput{underlay}}}, definition: definition,
}, testSecretBinding(), func(name string) (string, bool) {
observed.lookup++
if name != secretName {
return "", false
}
return credential, true
})
return testLoaded(loaded), err
}
var observed calls
destinationDecodeProbeValues = nil
mounted, err := newLoad(true, &observed)
if err != nil {
if loadError, ok := err.(*LoadError); ok {
t.Fatalf("mounted load: %#v", loadError.Diagnostics())
}
t.Fatalf("mounted load: %v", err)
}
if observed != (calls{defaults: 1, resolve: 1, lookup: 1, validate: 1}) {
t.Fatalf("mounted lifecycle counts = %#v", observed)
}
sawFinalDecode := false
for _, value := range destinationDecodeProbeValues {
if value == decodeValue {
sawFinalDecode = true
} else if value != "" {
t.Fatalf("decoder observed a non-final mounted value %q: %#v", value, destinationDecodeProbeValues)
}
}
if !sawFinalDecode {
t.Fatal("final mounted value was not decoded")
}
if mounted.Config.Defaulted != "from-default" || mounted.Config.Tenant.Tagged != newTagged || mounted.Config.Tenant.Dynamic != newDynamic ||
mounted.Config.Tenant.Derived != newDynamic || mounted.Config.Tenant.Decoded != destinationDecodeProbe(decodeValue) ||
mounted.Config.Tenant.Credential.Value() != credential {
t.Fatalf("mounted final configuration = %#v", mounted.Config)
}
for _, path := range []Path{Keys("tenant", "tagged"), Keys("tenant", "dynamic"), Keys("tenant", "derived")} {
if !mounted.Inspection.secrets[path.String()] {
t.Errorf("effective sensitivity missing at %s: %#v", path, mounted.Inspection.secrets)
}
}
for _, path := range []Path{Keys("tenant", "tagged"), Keys("tenant", "dynamic"), Keys("tenant", "derived")} {
explanation, ok := mounted.Explain(path)
if !ok || !explanation.Effective.Redacted {
t.Errorf("effective explanation at %s = %#v, %v", path, explanation, ok)
continue
}
for _, change := range explanation.History {
if strings.Contains(change.Before.YAML+change.After.YAML, oldTagged) ||
strings.Contains(change.Before.YAML+change.After.YAML, newTagged) ||
strings.Contains(change.Before.YAML+change.After.YAML, oldDynamic) ||
strings.Contains(change.Before.YAML+change.After.YAML, newDynamic) {
t.Errorf("classified history leaked a canary at %s: %#v", path, change)
}
}
var rendered bytes.Buffer
if err := mounted.WriteExplanation(&rendered, path); err != nil || strings.Contains(rendered.String(), "CANARY") {
t.Errorf("safe explanation at %s = %q, %v", path, rendered.String(), err)
}
}
var effective bytes.Buffer
if err := mounted.WriteEffective(&effective); err != nil {
t.Fatal(err)
}
for _, canary := range []string{oldTagged, newTagged, oldDynamic, newDynamic, credential} {
if strings.Contains(effective.String(), canary) {
t.Fatalf("effective rendering exposed %q: %s", canary, effective.String())
}
}
if serializedConfig := fmt.Sprintf("%#v", mounted.Config); strings.Contains(serializedConfig, credential) {
t.Fatalf("configuration formatting exposed the resolved credential: %s", serializedConfig)
}
for _, path := range []Path{Keys("tenant", "tagged"), Keys("tenant", "dynamic")} {
if history := fmt.Sprintf("%#v", mounted.Metadata().History(path)); strings.Contains(history, "CANARY") {
t.Fatalf("metadata history at %s exposed a canary: %s", path, history)
}
}
var authoredCalls calls
destinationDecodeProbeValues = nil
authored, err := newLoad(false, &authoredCalls)
if err != nil {
t.Fatalf("explicitly wrapped load: %v", err)
}
if authoredCalls != (calls{defaults: 1, resolve: 1, lookup: 1, validate: 1}) {
t.Fatalf("authored lifecycle counts = %#v", authoredCalls)
}
sawFinalDecode = false
for _, value := range destinationDecodeProbeValues {
if value == decodeValue {
sawFinalDecode = true
} else if value != "" {
t.Fatalf("authored decoder observed a non-final value %q: %#v", value, destinationDecodeProbeValues)
}
}
if !sawFinalDecode {
t.Fatal("final authored value was not decoded")
}
if !reflect.DeepEqual(mounted.Config, authored.Config) {
t.Fatalf("mounted config %#v differs from explicitly wrapped config %#v", mounted.Config, authored.Config)
}
differences, err := Diff(mounted.Inspection, authored.Inspection)
if err != nil || len(differences) != 0 {
t.Fatalf("mounted versus authored diff = %#v, %v", differences, err)
}
}
func TestLayeredDestinationDiffUsesBilateralSensitivity(t *testing.T) {
t.Parallel()
type config struct {
Tenant struct {
Dynamic string `yaml:"dynamic"`
Derived string `yaml:"derived"`
} `yaml:"tenant"`
}
load := func(value string, classify bool) *testResult[config] {
t.Helper()
loaded, err := loadGroupedWithSecretLookup(loadRequest[config]{
main: loadInput{source: Bytes("input", []byte("dynamic: "+value+"\n")), destination: Keys("tenant")},
definition: Definition[config]{
Resolve: func(configuration *config, resolution *Resolution) error {
configuration.Tenant.Derived = configuration.Tenant.Dynamic
resolution.Record(Keys("tenant", "derived"), ChangeNormalization, "derive from dynamic input", Keys("tenant", "dynamic"))
return nil
},
Secret: func(path Path) bool { return classify && path.String() == "/tenant/dynamic" },
},
}, testSecretBinding(), func(string) (string, bool) { return "", false })
if err != nil {
t.Fatalf("LoadLayered: %v", err)
}
return testLoaded(loaded)
}
left := load("LEFT-DIFF-CANARY", true)
right := load("RIGHT-DIFF-CANARY", false)
differences, err := Diff(left.Inspection, right.Inspection)
if err != nil {
t.Fatal(err)
}
if len(differences) != 2 {
t.Fatalf("differences = %#v", differences)
}
seen := make(map[string]bool)
for _, difference := range differences {
path := difference.Path.String()
seen[path] = true
if !difference.Left.Redacted || !difference.Right.Redacted || !difference.HiddenChanged ||
strings.Contains(difference.Left.YAML+difference.Right.YAML, "DIFF-CANARY") {
t.Errorf("bilaterally classified difference at %s = %#v", path, difference)
}
}
for _, path := range []string{"/tenant/dynamic", "/tenant/derived"} {
if !seen[path] || !left.Inspection.secrets[path] || right.Inspection.secrets[path] {
t.Errorf("classification/difference at %s: seen=%v left=%v right=%v", path, seen[path], left.Inspection.secrets[path], right.Inspection.secrets[path])
}
}
}
func TestLayeredDestinationDecodeFailureReturnsNoResultOrLaterCallbacks(t *testing.T) {
t.Parallel()
type config struct {
Tenant struct {
Count int `yaml:"count"`
} `yaml:"tenant"`
}
calls := struct{ defaults, resolve, lookup, validate int }{}
loaded, err := loadGroupedWithSecretLookup(loadRequest[config]{
main: loadInput{source: Bytes("invalid", []byte("count: not-an-integer\n")), destination: Keys("tenant")},
definition: Definition[config]{
Defaults: func() config { calls.defaults++; return config{} },
Resolve: func(*config, *Resolution) error { calls.resolve++; return nil },
Validate: func(*config, *Validation) { calls.validate++ },
},
}, testSecretBinding(), func(string) (string, bool) { calls.lookup++; return "", false })
if loaded != nil || err == nil {
t.Fatalf("mounted decode failure returned a usable result: %#v, %v", loaded, err)
}
if calls.defaults != 1 || calls.resolve != 0 || calls.lookup != 0 || calls.validate != 0 {
t.Fatalf("callbacks after mounted decode failure = %#v", calls)
}
if got := groupedErrorCode(t, err); got != CodeDecodeType {
t.Fatalf("mounted decode failure code = %s", got)
}
}
func indentDestinationBody(body string) string {
var result strings.Builder
for _, line := range strings.Split(strings.TrimSuffix(body, "\n"), "\n") {
result.WriteString(" ")
result.WriteString(line)
result.WriteByte('\n')
}
return result.String()
}

View File

@@ -41,7 +41,7 @@ func inheritanceVersionDiagnostic(rule *VersionRule, expanded *inheritanceScope)
} }
} else if operation.transfer != nil { } else if operation.transfer != nil {
if relative, within := relativeHistoryPath(operation.transfer.to, rule.Path); within { if relative, within := relativeHistoryPath(operation.transfer.to, rule.Path); within {
_, affected = documentAtPath(operation.parent, relative) affected = fragmentVersionClaim(operation.parent, &VersionRule{Path: relative}) != nil
} else if pathContains(rule.Path, operation.transfer.to) { } else if pathContains(rule.Path, operation.transfer.to) {
affected = true affected = true
} }