Clarify resolution metadata and sensitivity names
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Pre-v1 Public API Cleanup Implementation Plan
|
# Pre-v1 Public API Cleanup Implementation Plan
|
||||||
|
|
||||||
Status: planned; Stages 1–11 are pending.
|
Status: in progress; Stage 1 is complete and Stages 2–11 are pending.
|
||||||
Baseline: `07e4ae5d9236935389e81b207d4a8f00543fafce`.
|
Baseline: `07e4ae5d9236935389e81b207d4a8f00543fafce`.
|
||||||
Audience: a Luna coding agent executing the stages sequentially.
|
Audience: a Luna coding agent executing the stages sequentially.
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ it is not a reason to leave obvious neighboring comments wrong.
|
|||||||
|
|
||||||
## Stage 1 — Clarify Resolution Entry Metadata And Sensitivity
|
## Stage 1 — Clarify Resolution Entry Metadata And Sensitivity
|
||||||
|
|
||||||
Status: pending. Implements D1 and D2.
|
Status: complete. Implements D1 and D2.
|
||||||
|
|
||||||
1. In [lifecycle.go](../../lifecycle.go), rename root `Resolution.InputInfo` to
|
1. In [lifecycle.go](../../lifecycle.go), rename root `Resolution.InputInfo` to
|
||||||
`EntryInfo` and `Definition.Secret` to `Sensitive`, without old aliases.
|
`EntryInfo` and `Definition.Secret` to `Sensitive`, without old aliases.
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func LoadNotariusPaths(path, invocationDir string, overrides ...configkit.Overri
|
|||||||
resolveNotariusReference(configuration, resolution, invocationDir)
|
resolveNotariusReference(configuration, resolution, invocationDir)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Secret: func(path configkit.Path) bool { return path.String() == "/sensitive" },
|
Sensitive: func(path configkit.Path) bool { return path.String() == "/sensitive" },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ func resolveNotariusReference(configuration *NotariusPathConfig, resolution *con
|
|||||||
if configuration.Reference == "" || filepath.IsAbs(configuration.Reference) {
|
if configuration.Reference == "" || filepath.IsAbs(configuration.Reference) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
info, found := resolution.InputInfo(path)
|
info, found := resolution.EntryInfo(path)
|
||||||
if !found {
|
if !found {
|
||||||
resolution.Warning(path, "relative reference has no entry provenance")
|
resolution.Warning(path, "relative reference has no entry provenance")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func resolveCollectionResources(configuration *CollectionBundleConfig, resolutio
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
contentPath := configkit.Keys("collections", collectionID, "prompts", promptID, "content_file")
|
contentPath := configkit.Keys("collections", collectionID, "prompts", promptID, "content_file")
|
||||||
info, found := resolution.InputInfo(contentPath)
|
info, found := resolution.EntryInfo(contentPath)
|
||||||
if !found || len(info.Origin.Locations) != 1 {
|
if !found || len(info.Origin.Locations) != 1 {
|
||||||
return fmt.Errorf("prompt resource has no single declaring source")
|
return fmt.Errorf("prompt resource has no single declaring source")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func LoadVersionedService(mode configkit.SourceVersionMode, overrides ...configk
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Secret: func(path configkit.Path) bool {
|
Sensitive: func(path configkit.Path) bool {
|
||||||
return path.String() == "/service/version"
|
return path.String() == "/service/version"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ func TestFacadeLoadBridgesLifecycleCallbacks(t *testing.T) {
|
|||||||
if !resolution.IsExplicit(configkit.Keys("input")) {
|
if !resolution.IsExplicit(configkit.Keys("input")) {
|
||||||
resolution.Error(configkit.Keys("input"), "input is required")
|
resolution.Error(configkit.Keys("input"), "input is required")
|
||||||
}
|
}
|
||||||
input, ok := resolution.InputInfo(configkit.Keys("input"))
|
input, ok := resolution.EntryInfo(configkit.Keys("input"))
|
||||||
if !ok || !input.Explicit {
|
if !ok || !input.Explicit {
|
||||||
resolution.Error(configkit.Keys("input"), "missing input provenance")
|
resolution.Error(configkit.Keys("input"), "missing input provenance")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestFacadeInactiveContextsPreserveClosedFailure(t *testing.T) {
|
|||||||
call func()
|
call func()
|
||||||
}{
|
}{
|
||||||
{"IsExplicit", func() { state.resolution.IsExplicit(path) }},
|
{"IsExplicit", func() { state.resolution.IsExplicit(path) }},
|
||||||
{"InputInfo", func() { state.resolution.InputInfo(path) }},
|
{"EntryInfo", func() { state.resolution.EntryInfo(path) }},
|
||||||
{"Source", func() { state.resolution.Source("source") }},
|
{"Source", func() { state.resolution.Source("source") }},
|
||||||
{"SourceContext", func() { state.resolution.SourceContext("source") }},
|
{"SourceContext", func() { state.resolution.SourceContext("source") }},
|
||||||
{"Record", func() { state.resolution.Record(path, configkit.ChangeNormalization, "reason") }},
|
{"Record", func() { state.resolution.Record(path, configkit.ChangeNormalization, "reason") }},
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ func TestFacadeMountedMainAndUnderlayInspectionResourcesAndDiff(t *testing.T) {
|
|||||||
Resolve: func(configuration *map[string]any, resolution *configkit.Resolution) error {
|
Resolve: func(configuration *map[string]any, resolution *configkit.Resolution) error {
|
||||||
callbacks.resolve++
|
callbacks.resolve++
|
||||||
resourcePath := configkit.Keys("catalog", "entries", "item", "resource")
|
resourcePath := configkit.Keys("catalog", "entries", "item", "resource")
|
||||||
input, ok := resolution.InputInfo(resourcePath)
|
input, ok := resolution.EntryInfo(resourcePath)
|
||||||
if !ok || len(input.Origin.Locations) != 1 {
|
if !ok || len(input.Origin.Locations) != 1 {
|
||||||
return fmt.Errorf("mounted resource input has no single source origin")
|
return fmt.Errorf("mounted resource input has no single source origin")
|
||||||
}
|
}
|
||||||
@@ -373,8 +373,8 @@ func TestFacadeMountedMainAndUnderlayInspectionResourcesAndDiff(t *testing.T) {
|
|||||||
resolution.Record(configkit.Keys("catalog", "entries", "item", "resource_text"), configkit.ChangeNormalization, "read source-relative summary", resourcePath)
|
resolution.Record(configkit.Keys("catalog", "entries", "item", "resource_text"), configkit.ChangeNormalization, "read source-relative summary", resourcePath)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Validate: func(*map[string]any, *configkit.Validation) { callbacks.validate++ },
|
Validate: func(*map[string]any, *configkit.Validation) { callbacks.validate++ },
|
||||||
Secret: func(candidate configkit.Path) bool { return candidate.String() == tokenPath.String() },
|
Sensitive: func(candidate configkit.Path) bool { return candidate.String() == tokenPath.String() },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestFacadeDiffKeepsPublicSequenceChangesWithHistoricalDescendants(t *testin
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
result, err := configkit.Load(configkit.Bytes("historical-sequence.yml", []byte("items: [{secret: disposable-canary}, "+public+"]\n")),
|
result, err := configkit.Load(configkit.Bytes("historical-sequence.yml", []byte("items: [{secret: disposable-canary}, "+public+"]\n")),
|
||||||
configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
|
configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
|
||||||
Secret: func(path configkit.Path) bool { return path.String() == "/items/0/secret" },
|
Sensitive: func(path configkit.Path) bool { return path.String() == "/items/0/secret" },
|
||||||
Resolve: func(current *configuration, resolution *configkit.Resolution) error {
|
Resolve: func(current *configuration, resolution *configkit.Resolution) error {
|
||||||
current.Items[0] = "public scalar"
|
current.Items[0] = "public scalar"
|
||||||
resolution.Record(configkit.Keys("items"), configkit.ChangeNormalization, "normalize first item")
|
resolution.Record(configkit.Keys("items"), configkit.ChangeNormalization, "normalize first item")
|
||||||
@@ -71,7 +71,7 @@ func TestFacadeDiffKeepsPublicScalarAcrossHistoricalAbsence(t *testing.T) {
|
|||||||
Dynamic map[string]any `yaml:"dynamic"`
|
Dynamic map[string]any `yaml:"dynamic"`
|
||||||
}
|
}
|
||||||
options := configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
|
options := configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
|
||||||
Secret: func(path configkit.Path) bool { return path.String() == "/dynamic/entry/token" },
|
Sensitive: func(path configkit.Path) bool { return path.String() == "/dynamic/entry/token" },
|
||||||
Resolve: func(current *configuration, resolution *configkit.Resolution) error {
|
Resolve: func(current *configuration, resolution *configkit.Resolution) error {
|
||||||
delete(current.Dynamic, "entry")
|
delete(current.Dynamic, "entry")
|
||||||
resolution.Record(configkit.Keys("dynamic", "entry"), configkit.ChangeNormalization, "remove entry")
|
resolution.Record(configkit.Keys("dynamic", "entry"), configkit.ChangeNormalization, "remove entry")
|
||||||
@@ -281,7 +281,7 @@ func TestFacadeDiffUsesFrozenFilesAndRunsCallbacksOnlyDuringLoad(t *testing.T) {
|
|||||||
|
|
||||||
func TestFacadeDiffCombinesSecretSensitivityAndLoadErrors(t *testing.T) {
|
func TestFacadeDiffCombinesSecretSensitivityAndLoadErrors(t *testing.T) {
|
||||||
t.Setenv("FACADE_DIFF_SECRET", "credential-one")
|
t.Setenv("FACADE_DIFF_SECRET", "credential-one")
|
||||||
secretOptions := configkit.Options[facadeDiffSecretConfig]{Definition: configkit.Definition[facadeDiffSecretConfig]{Secret: func(path configkit.Path) bool {
|
secretOptions := configkit.Options[facadeDiffSecretConfig]{Definition: configkit.Definition[facadeDiffSecretConfig]{Sensitive: func(path configkit.Path) bool {
|
||||||
return path.String() == "/secret"
|
return path.String() == "/secret"
|
||||||
}}}
|
}}}
|
||||||
left, err := configkit.Load[facadeDiffSecretConfig](configkit.Bytes("left.yml", []byte("secret: FACADE_DIFF_SECRET\n")), secretOptions)
|
left, err := configkit.Load[facadeDiffSecretConfig](configkit.Bytes("left.yml", []byte("secret: FACADE_DIFF_SECRET\n")), secretOptions)
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func TestReplacedSecretRemainsAbsentFromSafeOutput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
result, err := configkit.LoadLayered(configkit.Input{Source: configkit.Bytes("main", []byte("secret: new-secret-canary\n"))}, configkit.LayeredOptions[config]{
|
result, err := configkit.LoadLayered(configkit.Input{Source: configkit.Bytes("main", []byte("secret: new-secret-canary\n"))}, configkit.LayeredOptions[config]{
|
||||||
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.Mapping("lower", map[string]any{"secret": "old-secret-canary"})}}}},
|
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.Mapping("lower", map[string]any{"secret": "old-secret-canary"})}}}},
|
||||||
Definition: configkit.Definition[config]{Secret: func(path configkit.Path) bool { return path.String() == "/secret" }},
|
Definition: configkit.Definition[config]{Sensitive: func(path configkit.Path) bool { return path.String() == "/secret" }},
|
||||||
})
|
})
|
||||||
if err != nil || result.Config.Secret != "new-secret-canary" {
|
if err != nil || result.Config.Secret != "new-secret-canary" {
|
||||||
t.Fatalf("secret load = %#v, %v", result, err)
|
t.Fatalf("secret load = %#v, %v", result, err)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ entries:
|
|||||||
Definition: Definition[map[string]any]{
|
Definition: Definition[map[string]any]{
|
||||||
Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }},
|
Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }},
|
||||||
SequenceIdentities: []SequenceIdentity{SequenceIdentityAt(Keys("services", "records"), "id")},
|
SequenceIdentities: []SequenceIdentity{SequenceIdentityAt(Keys("services", "records"), "id")},
|
||||||
Secret: func(candidate Path) bool {
|
Sensitive: func(candidate Path) bool {
|
||||||
return candidate.String() == "/services/records/0/version"
|
return candidate.String() == "/services/records/0/version"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -107,7 +107,7 @@ entries:
|
|||||||
Definition: Definition[map[string]any]{
|
Definition: Definition[map[string]any]{
|
||||||
Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }},
|
Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }},
|
||||||
SequenceIdentities: []SequenceIdentity{SequenceIdentityAt(Keys("services", "records"), "version")},
|
SequenceIdentities: []SequenceIdentity{SequenceIdentityAt(Keys("services", "records"), "version")},
|
||||||
Secret: func(candidate Path) bool {
|
Sensitive: func(candidate Path) bool {
|
||||||
return candidate.String() == "/services/records/0/version"
|
return candidate.String() == "/services/records/0/version"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFacadeInheritancePreservesPresenceOriginsAndHistory(t *testing.T) {
|
|||||||
if !resolution.IsExplicit(configkit.Keys("entries", "child", "enabled")) {
|
if !resolution.IsExplicit(configkit.Keys("entries", "child", "enabled")) {
|
||||||
t.Error("inherited false is not explicit")
|
t.Error("inherited false is not explicit")
|
||||||
}
|
}
|
||||||
info, found := resolution.InputInfo(configkit.Keys("entries", "child", "enabled"))
|
info, found := resolution.EntryInfo(configkit.Keys("entries", "child", "enabled"))
|
||||||
if !found || len(info.Origin.Inheritance) != 1 {
|
if !found || len(info.Origin.Inheritance) != 1 {
|
||||||
t.Errorf("resolver inherited origin = %#v, %v", info, found)
|
t.Errorf("resolver inherited origin = %#v, %v", info, found)
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ func TestFacadeIdentifiedInheritanceResolvesChainsAndNestedCollections(t *testin
|
|||||||
result, err := configkit.Load(filesystem.File("config/identified.yml"), configkit.Options[configuration]{
|
result, err := configkit.Load(filesystem.File("config/identified.yml"), configkit.Options[configuration]{
|
||||||
Definition: configkit.Definition[configuration]{
|
Definition: configkit.Definition[configuration]{
|
||||||
SequenceIdentities: registrations,
|
SequenceIdentities: registrations,
|
||||||
Secret: func(path configkit.Path) bool {
|
Sensitive: func(path configkit.Path) bool {
|
||||||
return path.String() == "/jobs/0/credential" || path.String() == "/jobs/1/credential" || path.String() == "/jobs/2/credential"
|
return path.String() == "/jobs/0/credential" || path.String() == "/jobs/1/credential" || path.String() == "/jobs/2/credential"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -221,7 +221,7 @@ func TestFacadeIdentifiedInheritanceFailureDoesNotRevealSecretCanary(t *testing.
|
|||||||
_, err := configkit.Load(configkit.Bytes("identified-failure.yml", input), configkit.Options[configuration]{
|
_, err := configkit.Load(configkit.Bytes("identified-failure.yml", input), configkit.Options[configuration]{
|
||||||
Definition: configkit.Definition[configuration]{
|
Definition: configkit.Definition[configuration]{
|
||||||
SequenceIdentities: []configkit.SequenceIdentity{configkit.SequenceIdentityAt(configkit.Keys("jobs"), "id")},
|
SequenceIdentities: []configkit.SequenceIdentity{configkit.SequenceIdentityAt(configkit.Keys("jobs"), "id")},
|
||||||
Secret: func(path configkit.Path) bool { return path.String() == "/jobs/0/credential" },
|
Sensitive: func(path configkit.Path) bool { return path.String() == "/jobs/0/credential" },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err == nil || strings.Contains(err.Error(), canary) {
|
if err == nil || strings.Contains(err.Error(), canary) {
|
||||||
@@ -468,7 +468,7 @@ func TestFacadeInheritedSecretsStayRedactedAfterLookup(t *testing.T) {
|
|||||||
Entries map[string]entry `yaml:"entries"`
|
Entries map[string]entry `yaml:"entries"`
|
||||||
}
|
}
|
||||||
result, err := configkit.Load[configuration](configkit.Bytes("secret.yml", []byte("entries: {parent: {credential: CONFIGKIT_INHERITED_TEST_SECRET, plain: visible}, child: {extends: parent}}\n")), configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
|
result, err := configkit.Load[configuration](configkit.Bytes("secret.yml", []byte("entries: {parent: {credential: CONFIGKIT_INHERITED_TEST_SECRET, plain: visible}, child: {extends: parent}}\n")), configkit.Options[configuration]{Definition: configkit.Definition[configuration]{
|
||||||
Secret: func(path configkit.Path) bool { return path.String() == "/entries/parent/credential" },
|
Sensitive: func(path configkit.Path) bool { return path.String() == "/entries/parent/credential" },
|
||||||
}})
|
}})
|
||||||
if err != nil || result.Config.Entries["child"].Credential.Value() != canary {
|
if err != nil || result.Config.Entries["child"].Credential.Value() != canary {
|
||||||
t.Fatalf("inherited credential = %#v, %v", result, err)
|
t.Fatalf("inherited credential = %#v, %v", result, err)
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ func TestFacadeLayeredMixedSourcesAndInspection(t *testing.T) {
|
|||||||
configkit.Default(resolution, configkit.Keys("derived"), &config.Derived, config.Settings["native"], "derive from native", configkit.Keys("settings", "native"))
|
configkit.Default(resolution, configkit.Keys("derived"), &config.Derived, config.Settings["native"], "derive from native", configkit.Keys("settings", "native"))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Validate: func(config *layeredFacadeConfig, validation *configkit.Validation) { checks.validation++ },
|
Validate: func(config *layeredFacadeConfig, validation *configkit.Validation) { checks.validation++ },
|
||||||
Secret: func(path configkit.Path) bool { return path.String() == "/settings/secret" },
|
Sensitive: func(path configkit.Path) bool { return path.String() == "/settings/secret" },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func TestFacadeResolutionPresenceUsesDynamicEntryShape(t *testing.T) {
|
|||||||
Definition: configkit.Definition[configuration]{
|
Definition: configkit.Definition[configuration]{
|
||||||
Resolve: func(_ *configuration, resolution *configkit.Resolution) error {
|
Resolve: func(_ *configuration, resolution *configkit.Resolution) error {
|
||||||
check := func(path configkit.Path, want bool) error {
|
check := func(path configkit.Path, want bool) error {
|
||||||
info, found := resolution.InputInfo(path)
|
info, found := resolution.EntryInfo(path)
|
||||||
explicit := resolution.IsExplicit(path)
|
explicit := resolution.IsExplicit(path)
|
||||||
if found != want || explicit != want || (found && info.Explicit != explicit) {
|
if found != want || explicit != want || (found && info.Explicit != explicit) {
|
||||||
return errors.New("resolution presence disagrees with dynamic entry shape")
|
return errors.New("resolution presence disagrees with dynamic entry shape")
|
||||||
|
|||||||
@@ -598,7 +598,7 @@ entries:
|
|||||||
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.File(underlayPath), Imports: true}}}},
|
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{{Source: configkit.File(underlayPath), Imports: true}}}},
|
||||||
Definition: configkit.Definition[configuration]{
|
Definition: configkit.Definition[configuration]{
|
||||||
SequenceIdentities: []configkit.SequenceIdentity{configkit.SequenceIdentityAt(configkit.Keys("entries"), "id")},
|
SequenceIdentities: []configkit.SequenceIdentity{configkit.SequenceIdentityAt(configkit.Keys("entries"), "id")},
|
||||||
Secret: func(path configkit.Path) bool {
|
Sensitive: func(path configkit.Path) bool {
|
||||||
return path.String() == "/entries/0/token" || path.String() == "/entries/1/token"
|
return path.String() == "/entries/0/token" || path.String() == "/entries/1/token"
|
||||||
},
|
},
|
||||||
Resolve: func(value *configuration, resolution *configkit.Resolution) error {
|
Resolve: func(value *configuration, resolution *configkit.Resolution) error {
|
||||||
@@ -611,7 +611,7 @@ entries:
|
|||||||
if resolution.IsExplicit(configkit.Keys("entries").Index(0).Key("omitted")) {
|
if resolution.IsExplicit(configkit.Keys("entries").Index(0).Key("omitted")) {
|
||||||
t.Error("resolver treated an omitted field as explicit")
|
t.Error("resolver treated an omitted field as explicit")
|
||||||
}
|
}
|
||||||
info, found := resolution.InputInfo(configkit.Keys("entries").Index(0).Key("count"))
|
info, found := resolution.EntryInfo(configkit.Keys("entries").Index(0).Key("count"))
|
||||||
if !found || info.Origin.Layer != configkit.LayerBase {
|
if !found || info.Origin.Layer != configkit.LayerBase {
|
||||||
t.Errorf("resolver current-record input info = %#v, %v", info, found)
|
t.Errorf("resolver current-record input info = %#v, %v", info, found)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func TestFacadeSourceVersionEvidenceFollowsAssembledSequenceRanges(t *testing.T)
|
|||||||
definition := configkit.Definition[map[string]any]{
|
definition := configkit.Definition[map[string]any]{
|
||||||
SequenceIdentities: []configkit.SequenceIdentity{configkit.SequenceIdentityAt(configkit.Keys("records"), "id")},
|
SequenceIdentities: []configkit.SequenceIdentity{configkit.SequenceIdentityAt(configkit.Keys("records"), "id")},
|
||||||
Version: &configkit.VersionRule{Path: configkit.Keys("records").Index(0).Key("version"), Check: func(configkit.Version, *configkit.Validation) error { return nil }},
|
Version: &configkit.VersionRule{Path: configkit.Keys("records").Index(0).Key("version"), Check: func(configkit.Version, *configkit.Validation) error { return nil }},
|
||||||
Secret: func(path configkit.Path) bool { return sensitivePath != "" && path.String() == sensitivePath },
|
Sensitive: func(path configkit.Path) bool { return sensitivePath != "" && path.String() == sensitivePath },
|
||||||
}
|
}
|
||||||
first := "records: [{id: first, version: first-version}]\n"
|
first := "records: [{id: first, version: first-version}]\n"
|
||||||
child := "records: [{id: child, version: PRIVATE-VERSION-CANARY}]\n"
|
child := "records: [{id: child, version: PRIVATE-VERSION-CANARY}]\n"
|
||||||
@@ -544,8 +544,8 @@ func TestFacadeSourceVersionEvidenceIsSanitizedAndDetached(t *testing.T) {
|
|||||||
var callbackSource configkit.SourceInfo
|
var callbackSource configkit.SourceInfo
|
||||||
var callbackContext configkit.ResourceContext
|
var callbackContext configkit.ResourceContext
|
||||||
definition := configkit.Definition[map[string]any]{
|
definition := configkit.Definition[map[string]any]{
|
||||||
Version: &configkit.VersionRule{Path: configkit.Keys("version"), Check: func(configkit.Version, *configkit.Validation) error { return nil }},
|
Version: &configkit.VersionRule{Path: configkit.Keys("version"), Check: func(configkit.Version, *configkit.Validation) error { return nil }},
|
||||||
Secret: func(candidate configkit.Path) bool { return candidate.String() == "/service/version" },
|
Sensitive: func(candidate configkit.Path) bool { return candidate.String() == "/service/version" },
|
||||||
Resolve: func(_ *map[string]any, resolution *configkit.Resolution) error {
|
Resolve: func(_ *map[string]any, resolution *configkit.Resolution) error {
|
||||||
var found bool
|
var found bool
|
||||||
callbackSource, found = resolution.Source("source-1")
|
callbackSource, found = resolution.Source("source-1")
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func TestUnderlaySequenceDescendantOrigins(t *testing.T) {
|
|||||||
Definition: configkit.Definition[map[string]any]{Resolve: func(_ *map[string]any, resolution *configkit.Resolution) error {
|
Definition: configkit.Definition[map[string]any]{Resolve: func(_ *map[string]any, resolution *configkit.Resolution) error {
|
||||||
for _, pointer := range paths {
|
for _, pointer := range paths {
|
||||||
path, _ := configkit.ParsePath(pointer)
|
path, _ := configkit.ParsePath(pointer)
|
||||||
info, ok := resolution.InputInfo(path)
|
info, ok := resolution.EntryInfo(path)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("missing resolver input %s", pointer)
|
t.Errorf("missing resolver input %s", pointer)
|
||||||
}
|
}
|
||||||
|
|||||||
29
lifecycle.go
29
lifecycle.go
@@ -7,14 +7,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Definition contains reusable application rules for a configuration type.
|
// Definition contains reusable application rules for a configuration type.
|
||||||
// Secret is a pure predicate that can add sensitivity to semantic paths.
|
// Sensitive is a pure predicate that can add sensitivity to semantic paths.
|
||||||
// Definitions should be treated as read-only after being shared.
|
// Definitions should be treated as read-only after being shared.
|
||||||
type Definition[T any] struct {
|
type Definition[T any] struct {
|
||||||
Defaults func() T
|
Defaults func() T
|
||||||
Resolve func(*T, *Resolution) error
|
Resolve func(*T, *Resolution) error
|
||||||
Validate func(*T, *Validation)
|
Validate func(*T, *Validation)
|
||||||
Version *VersionRule
|
Version *VersionRule
|
||||||
Secret func(Path) bool
|
Sensitive func(Path) bool
|
||||||
// SequenceIdentities declares the scalar fields used to combine records and
|
// SequenceIdentities declares the scalar fields used to combine records and
|
||||||
// resolve record inheritance at selected sequence destinations. Registration
|
// resolve record inheritance at selected sequence destinations. Registration
|
||||||
// errors are reported before source acquisition. Before successful return,
|
// errors are reported before source acquisition. Before successful return,
|
||||||
@@ -234,14 +234,15 @@ func (r *Resolution) IsExplicit(path Path) bool {
|
|||||||
return r.engineContext().IsExplicit(toEnginePath(path))
|
return r.engineContext().IsExplicit(toEnginePath(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
// InputInfo returns detached entry-state metadata for path. The entry state is
|
// EntryInfo returns detached entry-state metadata for path. The entry state is
|
||||||
// the decoded configuration, including retained static defaults, immediately
|
// the decoded configuration, including retained static defaults and CLI
|
||||||
// before resolution starts. It is fixed while resolution records mutate the
|
// assignments, immediately before resolution starts. It is fixed while
|
||||||
// working configuration; unknown or shape-mismatched paths return false.
|
// resolution records mutate the working configuration; unknown or
|
||||||
|
// shape-mismatched paths return false.
|
||||||
//
|
//
|
||||||
// InputInfo is valid only during the resolution callback. See the configuration
|
// EntryInfo is valid only during the resolution callback. See the configuration
|
||||||
// contract for source and provenance semantics.
|
// contract for source and provenance semantics.
|
||||||
func (r *Resolution) InputInfo(path Path) (FieldInfo, bool) {
|
func (r *Resolution) EntryInfo(path Path) (FieldInfo, bool) {
|
||||||
value, ok := r.engineContext().InputInfo(toEnginePath(path))
|
value, ok := r.engineContext().InputInfo(toEnginePath(path))
|
||||||
return fromEngineFieldInfo(value), ok
|
return fromEngineFieldInfo(value), ok
|
||||||
}
|
}
|
||||||
@@ -368,8 +369,8 @@ func toEngineDefinition[T any](definition Definition[T]) engine.Definition[T] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if definition.Secret != nil {
|
if definition.Sensitive != nil {
|
||||||
result.Secret = func(path engine.Path) bool { return definition.Secret(fromEnginePath(path)) }
|
result.Secret = func(path engine.Path) bool { return definition.Sensitive(fromEnginePath(path)) }
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func BenchmarkStartupConstruction(b *testing.B) {
|
|||||||
options := configkit.LayeredOptions[startupCatalog]{Definition: configkit.Definition[startupCatalog]{
|
options := configkit.LayeredOptions[startupCatalog]{Definition: configkit.Definition[startupCatalog]{
|
||||||
// Include a simple path-based classifier, as catalog consumers use
|
// Include a simple path-based classifier, as catalog consumers use
|
||||||
// this callback even when the representative input has no credentials.
|
// this callback even when the representative input has no credentials.
|
||||||
Secret: func(path configkit.Path) bool {
|
Sensitive: func(path configkit.Path) bool {
|
||||||
return strings.HasSuffix(path.String(), "/extra_params/token")
|
return strings.HasSuffix(path.String(), "/extra_params/token")
|
||||||
},
|
},
|
||||||
Resolve: func(catalog *startupCatalog, resolution *configkit.Resolution) error {
|
Resolve: func(catalog *startupCatalog, resolution *configkit.Resolution) error {
|
||||||
@@ -101,7 +101,7 @@ func BenchmarkStartupConstruction(b *testing.B) {
|
|||||||
"timeout_seconds", "service_tier", "reasoning_effort", "api_key_env",
|
"timeout_seconds", "service_tier", "reasoning_effort", "api_key_env",
|
||||||
"api_key_required", "extra_params",
|
"api_key_required", "extra_params",
|
||||||
} {
|
} {
|
||||||
info, exists := resolution.InputInfo(configkit.Keys("profiles", id, field))
|
info, exists := resolution.EntryInfo(configkit.Keys("profiles", id, field))
|
||||||
fields[field] = startupPresence{explicit: exists && info.Explicit, null: exists && info.Null}
|
fields[field] = startupPresence{explicit: exists && info.Explicit, null: exists && info.Null}
|
||||||
}
|
}
|
||||||
captured[id] = fields
|
captured[id] = fields
|
||||||
|
|||||||
Reference in New Issue
Block a user