Document and benchmark sibling inheritance
This commit is contained in:
@@ -54,7 +54,8 @@ The current lifecycle is:
|
||||
```text
|
||||
validate options → parse the selected source → traverse/compose enabled imports
|
||||
→ build schema → check the optional application version → apply a selected overlay
|
||||
→ apply explicit CLI overrides → create and clone defaults → strictly decode
|
||||
→ apply explicit CLI overrides → expand sibling inheritance
|
||||
→ create and clone defaults → strictly decode
|
||||
→ recorded application resolution
|
||||
→ resolve typed secret references → semantic validation → classify secrets
|
||||
→ snapshot configuration and metadata
|
||||
@@ -152,6 +153,57 @@ The maintained [layered consumer example](consumers/README.md#native-filesystem-
|
||||
shows a main host file, two underlay tiers, embedded defaults, a mapping source,
|
||||
and explicit resource access without a second configuration pass.
|
||||
|
||||
### Sibling inheritance
|
||||
|
||||
Any input mapping may declare `extends: name` to inherit from the mapping named
|
||||
`name` among its immediate siblings. Names are exact keys, including the empty
|
||||
string; lookup does not search ancestor or unrelated scopes. The document root
|
||||
and a sequence-element mapping have no sibling namespace for their own string
|
||||
relationship, though mappings nested inside them can have one. An `extends`
|
||||
value of null cancels a relationship without looking up a parent. Other value
|
||||
types, missing or non-mapping parents, and cycles fail the load with value-free
|
||||
diagnostics.
|
||||
|
||||
`extends` is reserved structural input across ordinary and layered loads, every
|
||||
source kind, imports, selected overlays, and explicit CLI overrides. It is
|
||||
consumed before typed decoding and need not appear in the application's Go
|
||||
types. Applications previously using `extends` as an ordinary YAML field must
|
||||
rename that key and its field. No collection registration or keyword setup is
|
||||
required.
|
||||
|
||||
ConfigKit completes strict composition within tiers and precedence assembly
|
||||
between tiers before resolving relationships. An omitted `extends` retains a
|
||||
lower-priority relationship; `extends: null` cancels it while preserving the
|
||||
child's own settings. Superseded references are historical evidence, not active
|
||||
relationships to validate. Each child recursively overlays its own settings
|
||||
over its resolved parent: explicit zero, false, empty string, sequence, and
|
||||
null values replace inherited values, while mappings combine recursively.
|
||||
The child retains its key. All effective relationships resolve, including
|
||||
entries the application may never select for execution. Partial parent
|
||||
definitions undergo normal typed decoding and application-owned validation,
|
||||
not a special structural exemption.
|
||||
|
||||
Enclosing relationships resolve first. Nested instructions carried by a parent
|
||||
are then resolved separately at each destination, so a child's nested sibling
|
||||
override can change an inherited nested relationship. Typed defaults and
|
||||
application-derived values do not start another inheritance pass. Inherited
|
||||
input counts as explicit presence for `Metadata.IsExplicit`,
|
||||
`Resolution.IsExplicit`, and absence-based resolver defaults, even for false,
|
||||
zero, empty, or null input. `Origin.Inheritance` distinguishes it from a direct
|
||||
declaration with concrete source, destination, and controlling directive paths.
|
||||
A direct child replacement has its own effective origin; displaced ancestry
|
||||
remains inspectable in history. An inherited relative resource keeps the
|
||||
declaring source's passive `SourceContext`, not the child's directory.
|
||||
|
||||
The optional schema-version checker still runs once against the main input at
|
||||
its original point. Inheritance cannot add, replace, or consume the protected
|
||||
version path; an unrelated relationship inside an enclosing mapping does not
|
||||
invalidate an unchanged version. Expansion uses the decoded projection
|
||||
node/depth limits without charging copied values as acquired sources;
|
||||
retained evidence uses the existing history-node limit. See the
|
||||
[consumer example](consumers/README.md#named-sibling-inheritance) for one-load
|
||||
usage.
|
||||
|
||||
### Source resource contexts
|
||||
|
||||
`Result.SourceContext(id)` and callback-scoped `Resolution.SourceContext(id)`
|
||||
@@ -222,8 +274,9 @@ successful result. Callback panics remain programming failures.
|
||||
`Result.Metadata` describes every final canonical path. Queries retain explicit
|
||||
key/index distinctions using actual effective and historical container shapes;
|
||||
only parsed numeric tokens are resolved contextually. A descendant that never
|
||||
existed is not historical merely because an atomic ancestor was replaced. Exact surviving input paths
|
||||
are explicit and have a base or overlay origin; omitted paths have a default origin.
|
||||
existed is not historical merely because an atomic ancestor was replaced. Exact
|
||||
surviving direct or inherited input paths are explicit and retain their source
|
||||
layer and derivation; omitted paths have a default origin.
|
||||
An explicit parent mapping does not make omitted children explicit. Null state and
|
||||
available source coordinates are retained. `History` records the Go-zero or application
|
||||
default baseline followed by exact base or overlay definitions, including
|
||||
@@ -238,8 +291,9 @@ domain ownership rules remain application responsibilities. The method's Go
|
||||
documentation in [metadata.go](../metadata.go) defines enumeration and path-ordering
|
||||
details.
|
||||
|
||||
History is chronological: a path can have a synthetic default record, its exact base
|
||||
definition, an overlay replacement, and a recorded resolution operation. A descendant
|
||||
History is chronological: a path can have a synthetic default record, its exact
|
||||
base definition, an overlay replacement, structural inheritance, and a recorded
|
||||
resolution operation. A descendant
|
||||
query uses the ancestor's path for a whole-container or whole-sequence replacement;
|
||||
the descendant can therefore retain history after `Lookup` stops reporting it as
|
||||
effective. Equal-valued input is still an explicit base definition, while a derived
|
||||
@@ -255,6 +309,15 @@ operation. Reasons and dependency paths retain first occurrence order with dupli
|
||||
removed. Source records remain ordered as selected source, depth-first imports, the
|
||||
selected overlay, then synthetic CLI records in supplied order.
|
||||
|
||||
Inheritance adds no acquired source or precedence layer. `Origin.Inheritance`
|
||||
lists concrete transfers from contributor paths through destinations, with
|
||||
each step's controlling `extends` path and location. `History` retains source
|
||||
applications at their original paths and projects parent contributions, child
|
||||
replacements, and consumed controls at destination paths after source assembly
|
||||
and before typed decoding. `HistoricalPaths` includes consumed directives and
|
||||
displaced inherited descendants. A control can have history even when `Lookup`
|
||||
reports it absent. Queries return detached records.
|
||||
|
||||
### Secret classification and effective rendering
|
||||
|
||||
Secret classification combines `configkit:"secret"` field tags with the optional
|
||||
@@ -270,6 +333,11 @@ Propagation reaches a fixed point, so record/map iteration order does not affect
|
||||
result and dependency cycles terminate. A dependency on a classified descendant or
|
||||
on a container that contains one is treated conservatively as sensitive. Applications
|
||||
must classify secret-producing transformations whose dependencies are not recorded.
|
||||
Inherited value and control paths link source and destination classifications
|
||||
through chains and nested copies; unrelated sibling fields are not classified
|
||||
merely because another field is secret. Displaced historical evidence remains
|
||||
redacted. Secret references are copied only as unresolved names; credential
|
||||
lookup stays after decoding and application resolution.
|
||||
|
||||
`Result.WriteEffective` writes the frozen resolved snapshot as deterministic redacted
|
||||
YAML. Mappings sort by decoded key string, sequences retain order, indentation is two
|
||||
@@ -304,7 +372,10 @@ parser values. `Present=false` with empty YAML means absence or deletion; explic
|
||||
null is present and renders as `null`. The history starts with the canonical typed
|
||||
application-default or Go-zero baseline, then includes only relevant base sources in
|
||||
traversal order, the selected overlay, supplied CLI records, and audited net
|
||||
resolution operations. Overlapping parent/child resolution records are rendered
|
||||
resolution operations. Structural inheritance events appear after source replay
|
||||
and before typed reconciliation, including consumed or canceled `extends` input
|
||||
and equal-valued child replacements. Overlapping parent/child resolution records
|
||||
are rendered
|
||||
as one net subtree event with combined reasons and dependencies, rather than
|
||||
pretending that the recorded assignments ran in lexical order. Explicit equal
|
||||
writes remain visible. When input replay
|
||||
@@ -556,8 +627,8 @@ input document.
|
||||
`[a-zA-Z0-9_-]+`; they are not paths. The empty name and exact lowercase name
|
||||
`default` select no overlay, and `overlays/default.yml` is never loaded implicitly.
|
||||
Every other name resolves only to `<configuration-root>/overlays/<name>.yml`, with
|
||||
no extension fallback, case normalization, inheritance, or overlay stack. A selected
|
||||
missing overlay is an error.
|
||||
no extension fallback, case normalization, inheritance between overlay files,
|
||||
or overlay stack. A selected missing overlay is an error.
|
||||
|
||||
The overlay is a separately policy-validated YAML mapping. It is loaded after the
|
||||
base import graph is composed and its schema version is checked, but before defaults
|
||||
|
||||
Reference in New Issue
Block a user