From 01e38b5840b4bd001f1c3c4d7a2d18a26213aa08 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 29 Sep 2026 21:18:48 +0000 Subject: [PATCH] Record agreed inheritance reference and presence rules --- docs/roadmap/named-inheritance.md | 90 ++++++++++++++----------------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/docs/roadmap/named-inheritance.md b/docs/roadmap/named-inheritance.md index e1b78e2..941b3c3 100644 --- a/docs/roadmap/named-inheritance.md +++ b/docs/roadmap/named-inheritance.md @@ -1,8 +1,7 @@ # Built-In Sibling Configuration Inheritance -Status: proposed feature design for agreement; not implemented. This document -defines the intended end state and the remaining architectural decisions. A -separate implementation plan will define execution steps after design agreement. +Status: agreed feature design; not implemented. This document defines the intended +end state. A separate implementation plan will define execution steps. ## 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. The child keeps its key. Application fields such as `name` or `id` follow ordinary -combination semantics. Reference-value details and positions without a sibling -namespace are addressed in Open Decisions below. +combination semantics. + +### 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 @@ -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. Nested mappings inside sequences are also visited, while sequence combination -continues to replace whole sequences. The treatment of a relationship on a -sequence element itself is addressed below. +continues to replace whole sequences. A relationship on a sequence element itself +follows the reference-position rules above. ## 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, and recorded application-derived values. Explicit zero/false/empty/null settings -remain distinguishable from omission. The representation of inherited presence -in the existing query surface is an open decision below; Go zero-value inference -is not an acceptable substitute. +remain distinguishable from omission. Go zero-value inference 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 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 ordering, chains and shared parents, enclosing-first nested resolution, independence 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 points and supported source representations should share those semantics. -A new ADR should record the selected inheritance architecture after the open -decisions are resolved. Architecture/deferred-scope documentation must recognize +A new ADR should record the selected inheritance architecture. +Architecture/deferred-scope documentation must recognize the new structural responsibility; public API, configuration, consumer, and internal documentation should describe implemented behavior when it lands. 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 cyclic relationships fail before a usable result is returned. The consumer needs 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.