Record agreed inheritance reference and presence rules

This commit is contained in:
2026-09-29 21:18:48 +00:00
parent ac1eec2e30
commit 01e38b5840

View File

@@ -1,8 +1,7 @@
# Built-In Sibling Configuration Inheritance # Built-In Sibling Configuration Inheritance
Status: proposed feature design for agreement; not implemented. This document Status: agreed feature design; not implemented. This document defines the intended
defines the intended end state and the remaining architectural decisions. A end state. A separate implementation plan will define execution steps.
separate implementation plan will define execution steps after design agreement.
## Purpose and priorities ## Purpose and priorities
@@ -82,8 +81,26 @@ and final schema validation; a referenced parent must be a mapping.
Identity comes from the sibling key, not a copied field in the parent payload. Identity comes from the sibling key, not a copied field in the parent payload.
The child keeps its key. Application fields such as `name` or `id` follow ordinary The child keeps its key. Application fields such as `name` or `id` follow ordinary
combination semantics. Reference-value details and positions without a sibling combination semantics.
namespace are addressed in Open Decisions below.
### Reference values and positions
A string-valued `extends` names an exact sibling key, including an empty string
if that key exists. Explicit `extends: null` means no parent and cancels a
relationship supplied by a lower-precedence source or inherited nested payload.
Omitting `extends` contributes no change to an existing relationship during
combination; if none remains in the effective mapping, the mapping has no parent.
Other value shapes are reference errors.
Cancellation retains the mapping's own assembled settings but adds no settings
from the canceled parent. Like a string reference, the null control field is
consumed as structural metadata before typed decoding. This follows existing
presence and replacement rules without introducing a deletion operator.
A string-valued `extends` on the document root or directly on a sequence-element
mapping has no enclosing mapping of named siblings and produces a location-bearing
reference error. ConfigKit does not search farther up for a namespace. Mappings
nested within those structures can still use their own sibling namespaces normally.
### Source precedence first, inheritance second ### Source precedence first, inheritance second
@@ -168,8 +185,8 @@ sort before or after its child without changing the meaning. Breadth-first versu
recursive traversal is an implementation choice subject to these semantics. recursive traversal is an implementation choice subject to these semantics.
Nested mappings inside sequences are also visited, while sequence combination Nested mappings inside sequences are also visited, while sequence combination
continues to replace whole sequences. The treatment of a relationship on a continues to replace whole sequences. A relationship on a sequence element itself
sequence element itself is addressed below. follows the reference-position rules above.
## Loading and validation ## Loading and validation
@@ -212,9 +229,17 @@ may load again explicitly using the existing source-reuse contracts.
Consumers must distinguish direct child input, inherited input, typed defaults, Consumers must distinguish direct child input, inherited input, typed defaults,
and recorded application-derived values. Explicit zero/false/empty/null settings and recorded application-derived values. Explicit zero/false/empty/null settings
remain distinguishable from omission. The representation of inherited presence remain distinguishable from omission. Go zero-value inference is not an acceptable
in the existing query surface is an open decision below; Go zero-value inference substitute.
is not an acceptable substitute.
An inherited value counts as present configuration input: `IsExplicit` returns
true at its destination, while derivation metadata distinguishes inherited input
from a direct child declaration. Presence answers whether configuration supplied
the value; provenance explains where it came from and how it reached the destination.
Typed defaults and application resolver defaults based on absence therefore remain
below inherited settings, including inherited zero, false, empty, and null values.
The API documentation must explain this effective-input meaning of explicitness;
it does not mean the value was written directly at the destination path.
Each inherited value retains its originating source and source path, together Each inherited value retains its originating source and source path, together
with the relationship that placed it at the child's destination path. Chains with the relationship that placed it at the child's destination path. Chains
@@ -268,12 +293,13 @@ remains the only consumer boundary.
Representative examples and behavioral tests should establish source/inheritance Representative examples and behavioral tests should establish source/inheritance
ordering, chains and shared parents, enclosing-first nested resolution, independence ordering, chains and shared parents, enclosing-first nested resolution, independence
from parent/child name sort order, failure diagnostics, partial bases, explicit from parent/child name sort order, failure diagnostics, partial bases, explicit
values versus omission, unchanged sequence/null behavior, origin/resource values versus omission, cancellation of relationships with null, invalid reference
positions and types, inherited presence during defaulting, unchanged sequence/null behavior, origin/resource
preservation, result independence, and safe secret inspection. Both loading entry preservation, result independence, and safe secret inspection. Both loading entry
points and supported source representations should share those semantics. points and supported source representations should share those semantics.
A new ADR should record the selected inheritance architecture after the open A new ADR should record the selected inheritance architecture.
decisions are resolved. Architecture/deferred-scope documentation must recognize Architecture/deferred-scope documentation must recognize
the new structural responsibility; public API, configuration, consumer, and the new structural responsibility; public API, configuration, consumer, and
internal documentation should describe implemented behavior when it lands. internal documentation should describe implemented behavior when it lands.
Implementation sequencing belongs in a separate future plan. Reserving `extends` Implementation sequencing belongs in a separate future plan. Reserving `extends`
@@ -290,41 +316,3 @@ It can inspect whether each setting was direct, inherited, defaulted, or derived
follow inherited resource origins, and validate final domain meaning. Missing or follow inherited resource origins, and validate final domain meaning. Missing or
cyclic relationships fail before a usable result is returned. The consumer needs cyclic relationships fail before a usable result is returned. The consumer needs
no separate graph resolver, generic merge pass, or provenance reconstruction. no separate graph resolver, generic merge pass, or provenance reconstruction.
## Open Decisions
### 1. Reference values and positions without a sibling namespace
**Recommendation:** an omitted `extends` means no relationship; explicit null
cancels a relationship supplied by a lower-precedence source or inherited nested
payload. A string names an exact sibling key, including an empty string if that
key exists. Other value shapes are reference errors. This follows existing
presence and replacement rules without introducing a deletion operator.
A string-valued `extends` on the document root or directly on a sequence-element
mapping has no enclosing mapping of named siblings. Report a location-bearing
reference error rather than searching elsewhere. Mappings nested within those
structures can still use their own sibling namespaces normally. This follows
the selected lookup rule, rather than imposing a general ban on nested inheritance.
**Alternative:** require every present `extends` to be a string. This makes the
keyword's value type simpler, but leaves no direct way to cancel an existing
relationship while retaining the mapping's other fields. For root/sequence-element
references, an ancestor-scope fallback could supply a namespace, but would introduce
a positional exception to the agreed immediate-sibling rule; adopting it would
require revisiting that decision rather than treating it as an implementation detail.
### 2. Meaning of inherited presence
**Recommendation:** an inherited value counts as present input for effective
`IsExplicit`/defaulting behavior, with separate derivation metadata distinguishing
it from direct child input. This treats inheritance as input expansion, keeps
typed and resolver defaults below inherited settings, and avoids consumers
reimplementing presence logic. The API must clearly explain that effective
presence differs from direct declaration at the destination path.
**Alternative:** inherited values remain non-explicit at the child path, with
a new effective-input-presence query/defaulting rule. This preserves a narrow
direct-declaration meaning of explicitness, but requires two presence concepts
through the resolver lifecycle and risks defaults overwriting inherited settings
unless callers adopt the new query consistently.