From 27797d2392aa16cd3fd439e91d5cc1a8b521a03b Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Thu, 22 Oct 2026 15:16:42 +0000 Subject: [PATCH] Settle sequence inheritance and lifecycle coverage --- docs/roadmap/sequences.md | 178 ++++++++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 54 deletions(-) diff --git a/docs/roadmap/sequences.md b/docs/roadmap/sequences.md index acd7161..c7ff81d 100644 --- a/docs/roadmap/sequences.md +++ b/docs/roadmap/sequences.md @@ -1,11 +1,8 @@ -# Sequence Identity And Cross-Tier Combination +# Sequence Identity And Combination -Status: draft proposal for discussion. This document captures the requested -capabilities, settled design choices, and initial recommendations. Choices under -Open Decisions remain unsettled. -It is not an implementation plan or an implemented contract. The declaration -structure is settled under OD-2; mode names and default selection are settled -under OD-5. +Status: feature roadmap with OD-1 through OD-7 settled. This document captures +the targeted behavior for review before preparing a staged implementation plan. +It is not an implementation plan or an implemented contract. ## Motivation @@ -19,7 +16,8 @@ records and whose application or installation layers add or supersede records. Across tiers, authors need three operations: whole-sequence replacement, duplicate-rejecting append, and whole-record replacement by identity. The latter retains unmatched lower-tier records and adds higher-tier records with new -identities. Matching records are replaced in full. +identities. Matching records are replaced in full. Sibling inheritance through +`extends` uses the same sequence operations and effective-identity defaults. ## Current Baseline @@ -37,6 +35,10 @@ precedence tiers, a higher sequence replaces the lower sequence wholesale. The [composition contract](../config.md#strict-document-composition) owns these rules; [layered loading](../config.md#layered-loading) owns tier precedence. +Current [sibling inheritance](../config.md#sibling-inheritance) recursively +overlays an inheriting mapping onto its resolved parent and replaces supplied +sequences wholesale. The proposed inheritance extension is settled in OD-7. + Source-backed underlays and `Definition.Defaults` have different lifecycle roles. Underlays participate in source assembly; typed defaults are applied later. Extending one does not automatically imply extending the other. @@ -119,7 +121,8 @@ conflicting declaration is invalid. Whole-sequence replacement does not itself require identity matching, but application-owned constraints still apply. Identity-policy lifetime across tiers is settled in OD-3 below. -Mode describes the current tier's combination instruction. A lower tier's +Mode describes the collection's combination instruction at source-precedence +and inheritance boundaries, as detailed in OD-7. A lower tier's `append` does not silently select append for a later overlay. Gather collection declarations before composing their relevant scope so their effect does not depend on which contribution is assembled first. Identity agreement is settled @@ -352,7 +355,7 @@ contributions, empty sequences, null, and kind mismatches. Use the mode names `replace`, `append`, and `upsert`, with one spelling per operation: -| Mode | Behavior between tiers | +| Mode | Behavior at source-precedence and inheritance boundaries | | --- | --- | | `replace` | Replace the complete lower sequence with the assembled higher sequence | | `append` | Retain lower records and append higher records; duplicate identities fail | @@ -383,7 +386,8 @@ YAML-only identity with the lower sequence, use the replacing tier's identity declaration if supplied, and preserve application-owned constraints. Selecting `append` or `upsert` explicitly still requires an effective identity. -This default governs combination between tiers. Within a tier, identified +This default governs combination between tiers and during sibling inheritance +under OD-7. Within a tier, identified sequences append distinct records and reject duplicate identities regardless of mode. Unidentified sequences retain their existing strict-composition conflict behavior; this decision does not introduce unkeyed concatenation. @@ -481,12 +485,12 @@ nor exact causal reconstruction for records supplied by defaults or callbacks. ### Assembly-Time Enforcement For YAML-Only Identities -Enforce identities declared only in YAML during structural composition and -precedence assembly. They do not carry a final-result guarantee after later -defaulting, inheritance, CLI overrides, or application resolution. Those stages -may produce a collection that no longer satisfies a YAML-only identity, including -duplicate identities. OD-7 still owns whether any additional lifecycle stages -participate in the new combination operations. +Enforce identities declared only in YAML during structural composition, +precedence assembly, and identified collection operations during sibling +inheritance. They do not carry a final-result guarantee after defaulting, CLI +overrides, or application resolution. Those operations may produce a collection +that no longer satisfies a YAML-only identity, including duplicate identities. +OD-7 defines the combination and validation boundaries. Applications requiring a final identity constraint register it through the application-owned interface. This keeps YAML declarations aligned with the @@ -525,6 +529,100 @@ Identity validation checks actual sequences; application schema and validation own the permitted shape. Null as a collection is distinct from a present null identity component inside a record, which is valid under OD-3. +### Lifecycle Coverage And Sibling Inheritance + +**OD-7: settled.** + +Apply the sequence operations during source composition and precedence assembly, +selected overlays, and sibling inheritance through `extends`. Preserve the +existing combination semantics of `Definition.Defaults`, CLI overrides, and +application resolution callbacks. + +#### Inheritance Operations And Controls + +Complete source-tier assembly before resolving inheritance, retaining the +existing dependency-ready inheritance algorithm and scope rules. At each +inheritance combination, the resolved parent's collection is the lower value +and the inheriting mapping's local collection is the higher value. Use the same +operation selection as source precedence: an explicit mode wins; otherwise use +`upsert` with effective identity and `replace` without it. There is no separate +default-append rule for inheritance. + +Use the inheriting destination collection's controls. Its selected mode governs +both source-precedence and inheritance combinations at that destination, without +separate per-phase mode settings. A mode governing the parent's source collection +does not automatically become an instruction at the inheriting destination merely +because records are inherited. Retain the relevant controls until inheritance +uses them, then keep them out of application decoding. Identity agreement, +application constraints, and replacement lifetime follow OD-3. + +For example: + +```yaml +configkit: + sequences: + - destination: [parent, items] + key: id + +child: + items: + - id: foo + value: A + - id: bar + value: B +parent: + extends: child + items: + - id: baz + value: C +``` + +Despite the mapping names, `parent` inherits from `child` in this example. +The effective identity at `[parent, items]` selects `upsert`, yielding records +`[foo, bar, baz]`. If the local contribution instead supplies a revised `foo`, +the result is `[foo*, bar]`: replace the complete record in its existing position. +Adding `mode: append` would reject that repeated identity; `mode: replace` would +retain only the local sequence. + +Mappings continue to combine recursively. Identified sequence records remain +whole-record replacements, not recursive field merges. Identity supplies the +matching key; ConfigKit does not infer identity from a field named `id` or from +record position. Append and upsert continue to require effective identity, and +this extension does not introduce unkeyed sequence concatenation. + +Apply OD-4 ordering and OD-6 presence and kind rules at inheritance boundaries. +An omitted local sequence retains the inherited sequence. An explicit empty +sequence clears it under replace, while append and upsert contribute no records. + +#### Other Lifecycle Stages + +Source-backed underlays participate in the new combination model, including +application-supplied mapping sources. Typed `Definition.Defaults` remain the +initial decoded state: supplied sequences replace default sequences. Source +`mode: append` does not append to a typed default list. + +CLI overrides retain explicit replacement for supplied sequences. Application +resolution retains its existing assignment and recorded-transformation semantics; +ConfigKit does not automatically combine a callback's assigned sequence with +its previous value. Application-registered identities still receive final +validation under OD-1, independently of how these stages produce their values. + +#### Validation Timing + +Allow item-local helper imports to complete, and consume applicable non-main +source-version headers, before checking the application payload's identity for +collection assembly. A helper may supply an identity field; a consumed header +cannot supply a component after removal. Validate each identified contribution +before combination and uniqueness in the resulting identified collection, +including inheritance operations. An upsert match across a precedence boundary +is a replacement, not a duplicate error; duplicates within a contribution remain +errors. + +Later inheritance, typed defaults, or callbacks do not repair an identity missing +at an earlier assembly boundary. Final application-registered validation examines +the final effective typed collection, while YAML-only enforcement remains limited +to the structural operations described above. + ## Architectural Direction The settled collection declaration gives imported and inline records at the same @@ -535,8 +633,9 @@ OD-3 settles identity agreement and lifetime; OD-4 settles tier-wide operation selection, strict peer composition, and ordering at precedence boundaries. This requires collection policy to survive import expansion through precedence -assembly. Correctness includes final values, projected item paths, source -destinations, provenance, history, sensitivity, and passive resource contexts. +assembly and the applicable inheritance operations. Correctness includes final +values, projected item paths, source destinations, provenance, history, +sensitivity, and passive resource contexts. Appending changes final indexes; replacing changes which contributions survive. The implementation must account for both through the existing loading lifecycle. @@ -551,44 +650,15 @@ require adopting them. The proposal would extend identity declaration ownership and revise the cross-tier replacement decision in -[ADR 0014](../adr/0014-sequence-placement-and-composition.md). If adopted, record +[ADR 0014](../adr/0014-sequence-placement-and-composition.md), as well as sequence +replacement during inheritance in +[ADR 0009](../adr/0009-sibling-inheritance.md). Record the architectural change in a superseding decision and reconcile affected architecture policy before implementation. The current ADR and contracts remain authoritative until that decision is made. This draft neither changes them nor selects migration machinery. -A staged implementation plan should follow resolution of the questions below. +A staged implementation plan should follow review of this settled roadmap. It should cover ordinary and layered loading, item-local scopes, source-version -processing, identity validation, precedence, and inspection together. Public API -design and test sequencing belong to that later plan. - -## Open Decisions - -### OD-7. Which Lifecycle Stages Participate? - -Do the new cross-tier operations extend only source-backed tiers, or also -`Definition.Defaults`, inheritance, CLI overrides, and application-created records? -At which boundaries -must identity be validated, including helper-supplied fields and consumed source -version headers? - -**Initial recommendation:** Start with source-backed strict composition and -precedence assembly. Validate records at the relevant collection assembly -boundaries and uniqueness across each combined result. Preserve existing -typed-default and application-resolution semantics unless their inclusion is -explicitly chosen. Selected-overlay combination is included in the settled scope; -its declaration mechanism is settled in OD-2. Account for item-local helpers and -source-version consumption before deciding identity is available, consistent -with the current lifecycle. - -The settled OD-1 guarantee covers final validation of application constraints. -Validating the final collection does not itself change how defaults, inheritance, -CLI overrides, or callbacks combine values. - -**Alternative:** Extend the combination operations through defaulting, -inheritance, overrides, and application resolution. This would support -progressively extending collections across more of the lifecycle, but requires -additional combination, callback, and inspection rules. Supporting typed default -lists is a separate scope choice from extending source-backed baseline -collections; the application-owned final-result guarantee remains independent -of this combination-scope decision. +processing, identity validation, precedence, inheritance, and inspection together. +Public API design and test sequencing belong to that later plan.