665 lines
30 KiB
Markdown
665 lines
30 KiB
Markdown
# Sequence Identity And Combination
|
|
|
|
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
|
|
|
|
Allow configuration authors to assemble collections from independently authored
|
|
records, declare their identity in configuration, and progressively extend a
|
|
baseline collection through higher layers. Authors should also be able to
|
|
replace the accumulated collection or replace individual records by identity.
|
|
|
|
The motivating example is a prompt collection whose defaults supply some
|
|
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. Sibling inheritance through
|
|
`extends` uses the same sequence operations and effective-identity defaults.
|
|
|
|
## Current Baseline
|
|
|
|
The [import contract](../config.md#explicit-imports) owns current selector,
|
|
destination, placement, and ordering behavior. An import with
|
|
`placement: sequence-item` places each selected source mapping as one item at
|
|
its effective destination. Item-local helper imports can contribute fields
|
|
before placement.
|
|
|
|
The application currently registers sequence identity through
|
|
`Definition.SequenceCompositions`, using one literal string field at each
|
|
registered mapping path. Within a strict tier, distinct identities append and
|
|
duplicate identities fail. Repeated unregistered sequences conflict. Across
|
|
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.
|
|
|
|
## Requested Capabilities
|
|
|
|
The proposed `configkit.sequences` block introduces a `key` declaration that
|
|
accepts either one field or a list of fields for a compound identity.
|
|
Fields are treated as a set; identity components may be strings, numbers,
|
|
booleans, or explicitly supplied null values.
|
|
Applications retain an optional interface for registering a collection
|
|
and imposing its identity definition. Configuration declarations must conform
|
|
to an application-owned constraint when one exists. The exact Go interface
|
|
remains to be designed.
|
|
|
|
```yaml
|
|
configkit:
|
|
sequences:
|
|
- destination: [prompts]
|
|
key: [namespace, id]
|
|
mode: append
|
|
imports:
|
|
- dir: prompts
|
|
pattern: '*.yaml'
|
|
destination: [prompts]
|
|
placement: sequence-item
|
|
```
|
|
|
|
Each selected file would contribute a record such as:
|
|
|
|
```yaml
|
|
namespace: support
|
|
id: welcome
|
|
text: Welcome
|
|
```
|
|
|
|
Here `mode: append` illustrates retaining a lower-layer collection and adding
|
|
distinct records from the higher layer. The `mode` field expresses the operation;
|
|
OD-5 defines its names and effective-identity default. Identity and combination
|
|
controls belong to the collection declaration; import shorthand is deferred.
|
|
|
|
Letters below denote record identities; `B*` is a higher-tier record with the same
|
|
identity as `B` and a different payload. The outcomes and ordering shown are
|
|
settled below.
|
|
|
|
| Lower-tier sequence | Higher-tier sequence | Operation | Proposed result |
|
|
| --- | --- | --- | --- |
|
|
| `[A, B]` | `[C]` | `append` | `[A, B, C]` |
|
|
| `[A, B]` | `[C]` | `replace` | `[C]` |
|
|
| `[A, B]` | `[B*]` | `append` | Duplicate-identity error |
|
|
| `[A, B]` | `[B*]` | `replace` | `[B*]` |
|
|
| `[A, B]` | `[B*]` | `upsert` | `[A, B*]` |
|
|
| `[A, B]` | `[B*, C]` | `upsert` | `[A, B*, C]` |
|
|
|
|
Whole-sequence replacement discards the lower sequence. A repeated identity in the
|
|
retained combined sequence is an error under the proposed append semantics,
|
|
including when the records have identical or disjoint payloads.
|
|
|
|
## Settled Design Choices
|
|
|
|
### Collection Declarations
|
|
|
|
**OD-2: settled.**
|
|
|
|
Use a root `configkit.sequences` block containing a sequence of collection
|
|
declarations. Each entry identifies a `destination` and may declare `key`,
|
|
`mode`, or both. Application sequences remain ordinary YAML data. This declaration
|
|
form serves imported items, inline records, and sequences supplied by ordinary
|
|
mapping imports, including inline sequences in selected overlays.
|
|
|
|
`destination` uses the existing sequence of literal mapping keys. Resolve it
|
|
relative to the declaring source's placement: a fragment mounted under
|
|
`catalog` can declare `[prompts]` locally. A sequence-item source can declare a
|
|
nested collection within its own item without referring to an eventual numeric
|
|
index. Preserve those source and item-local scope boundaries.
|
|
|
|
Identity and mode can be supplied independently. Omitted identity can use an
|
|
application registration; an explicit matching declaration is accepted, and a
|
|
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 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
|
|
in OD-3; operation agreement and omission among peers are settled in OD-4.
|
|
|
|
Permit sequence declarations in selected overlays and make their use independent
|
|
of filesystem import enablement. This deliberately changes the current blanket
|
|
prohibition on overlay `configkit` declarations. Enabling imports in overlays
|
|
is not part of this decision. Consume sequence controls before application
|
|
decoding, as with other structural directives.
|
|
|
|
For example, an overlay can replace a complete record by identity:
|
|
|
|
```yaml
|
|
configkit:
|
|
sequences:
|
|
- destination: [prompts]
|
|
key: id
|
|
mode: upsert
|
|
|
|
prompts:
|
|
- id: foo
|
|
text: Revised definition
|
|
```
|
|
|
|
The explicit `mode: upsert` line can be omitted here because `key: id` supplies
|
|
an effective identity. An overlay can instead select duplicate-rejecting append:
|
|
|
|
```yaml
|
|
configkit:
|
|
sequences:
|
|
- destination: [prompts]
|
|
key: id
|
|
mode: append
|
|
|
|
prompts:
|
|
- id: baz
|
|
text: Another prompt
|
|
```
|
|
|
|
Or it can explicitly select whole-sequence replacement:
|
|
|
|
```yaml
|
|
configkit:
|
|
sequences:
|
|
- destination: [prompts]
|
|
mode: replace
|
|
|
|
prompts:
|
|
- id: bar
|
|
text: Bar definition
|
|
- id: baz
|
|
text: Baz definition
|
|
```
|
|
|
|
Explicitly selecting the default operation is valid. Removing the last example's
|
|
`configkit` block produces the same replacement behavior only when no effective
|
|
identity is available. For an identified collection, explicit `mode: replace`
|
|
overrides the inferred `upsert`; it is not redundant. OD-5 defines the selection
|
|
rule for `replace`, `append`, and `upsert`.
|
|
|
|
Start with this common collection declaration and defer `key` and `mode`
|
|
shorthand on imports. A single declaration form covers inline overlays and mixed
|
|
inline/imported collections without duplicate authoring surfaces. Local wrappers
|
|
around sequence values would require distinguishing application data from
|
|
control mappings; the existing reserved namespace keeps that distinction clear.
|
|
|
|
### Identity Definitions And Lifetime
|
|
|
|
**OD-3: settled.**
|
|
|
|
#### Field Sets
|
|
|
|
`key` accepts a string or a nonempty list of strings naming direct, literal
|
|
fields in each record. `key: id` and `key: [id]` are equivalent. A name such as
|
|
`metadata.id` denotes that literal field, not a nested lookup.
|
|
|
|
Treat the selected fields as a set: remove repeated names and disregard authored
|
|
field order when comparing definitions. For example, `[namespace, id]`,
|
|
`[id, namespace]`, and `[namespace, id, id]` define the same identity. An empty
|
|
list is invalid because it supplies no identity fields. These equivalence rules
|
|
also apply when comparing configuration declarations with application
|
|
registrations.
|
|
|
|
Use a consistent field order internally to construct compound identity tuples.
|
|
Preserve component types and tuple boundaries; concatenating unstructured values
|
|
must not cause otherwise distinct identities to collide.
|
|
|
|
#### Scalar Components And Equality
|
|
|
|
Each identified record must be a mapping, and every designated identity field
|
|
must be present. Components may be strings, numbers, booleans, or explicit null.
|
|
Missing fields and mapping or sequence components are invalid. A present null is
|
|
a valid component and is distinct from an absent field. Two single-field null
|
|
identities are equal; in a compound identity, all components must compare equal.
|
|
|
|
Strings compare exactly, with no trimming, case folding, or normalization.
|
|
Empty strings remain valid. Scalar categories remain distinct: the string
|
|
`"1"`, the number `1`, the boolean `true`, and null are different identities.
|
|
|
|
Numbers compare by mathematical value across integer, fractional, and exponent
|
|
spellings: `1`, `1.0`, and `1e0` are equal, as are `0` and `-0`. Use exact numeric
|
|
normalization for identity comparison without rounding through a floating-point
|
|
conversion. This comparison does not rewrite authored values or alter existing
|
|
typed decoding and projection semantics. The existing parser's rejection of
|
|
non-finite numeric forms remains unchanged.
|
|
|
|
#### Agreement And Introduction
|
|
|
|
Within a strict tier, identity declarations for the same collection must agree.
|
|
Equivalent field sets coalesce; different sets produce an error. Across tiers,
|
|
append and replacement by identity use one agreed definition for the records
|
|
being combined:
|
|
|
|
| Lower collection identity | Higher declaration | Result when combining records |
|
|
| --- | --- | --- |
|
|
| `id` | Omitted | Use `id` |
|
|
| `id` | `id`, `[id]`, or `[id, id]` | Accept the equivalent definition |
|
|
| `id` | `[namespace, id]` | Error: different identity definitions |
|
|
| None | `id` | Establish `id` and validate all participating records under it |
|
|
|
|
An application registration supplies identity when configuration omits it and
|
|
remains authoritative over explicit declarations. If neither the application nor
|
|
either participating collection supplies an identity, append and replacement by
|
|
identity fail; ConfigKit does not infer an `id` convention. Introducing identity
|
|
for a previously unkeyed lower sequence requires checking both lower and incoming
|
|
records, including uniqueness within each contribution.
|
|
|
|
#### Replacement And Policy Lifetime
|
|
|
|
YAML-owned identity follows the collection that survives an operation:
|
|
|
|
| Operation | Effective YAML-owned identity |
|
|
| --- | --- |
|
|
| Append | Retain or establish the agreed identity |
|
|
| Replacement by identity | Retain or establish the agreed identity |
|
|
| Whole-sequence replacement | Use the replacing tier's declaration and discard the lower collection's YAML-only identity |
|
|
|
|
Without an application constraint, whole-sequence replacement may introduce a
|
|
different identity, such as replacing an `id`-keyed collection with a `name`-keyed
|
|
collection. A whole-sequence replacement with no new identity declaration and no
|
|
application registration produces an unkeyed sequence. No separate identity
|
|
removal directive is needed.
|
|
|
|
Application registrations continue to constrain the replacement collection.
|
|
If the application requires `id`, declaring `name` is invalid under every mode.
|
|
The policy reset above applies when a sequence is actually replaced; effects of
|
|
absent contributions, null collections, and kind mismatches are settled in OD-6.
|
|
YAML-only identity declarations receive assembly-time enforcement, as settled
|
|
in OD-1 below.
|
|
|
|
### Combination Scope, Declarations, And Ordering
|
|
|
|
**OD-4: settled.**
|
|
|
|
#### One Operation Per Collection Per Tier
|
|
|
|
Strictly compose a tier's contributions first, then apply one selected operation
|
|
per collection against the accumulated lower sequence. A collection declaration
|
|
governs all contributions to that collection in its tier, including imported and
|
|
inline records; it does not govern only records in the declaring file.
|
|
|
|
Collection scope is the resolved destination within its composition scope.
|
|
Resolve relative destinations first and preserve separate item-local scopes for
|
|
collections inside different imported items. Apply the same combination
|
|
semantics to selected overlay contributions.
|
|
|
|
For example, with an agreed identity, higher-tier fragments contributing `[C]`
|
|
and `[D]` assemble `[C, D]`. Against lower `[A, B]`, append or replacement by
|
|
identity produces `[A, B, C, D]`; whole-sequence replacement produces `[C, D]`.
|
|
The tier operation does not sequentially replace peer source contributions.
|
|
|
|
#### Operation Agreement And Omission
|
|
|
|
Coalesce agreeing explicit modes for the same collection within a tier and
|
|
reject conflicting explicit modes. Omission expresses no preference; apply the
|
|
default only after reconciling declarations.
|
|
|
|
| Modes declared within one tier | Effective operation |
|
|
| --- | --- |
|
|
| `append` and `append` | `append` |
|
|
| `append` and omitted | `append` |
|
|
| `replace` and omitted | `replace` |
|
|
| `append` and `replace` | Error |
|
|
| All omitted | `upsert` with effective identity; otherwise `replace` |
|
|
|
|
Explicitly selecting the default is a real instruction and can conflict with
|
|
another explicit selection. Different modes in different tiers are valid; a
|
|
lower tier's mode does not carry forward. Neither a root declaration nor a later
|
|
declaration wins over conflicting peers. Diagnostics identify the collection
|
|
destination and conflicting declaration locations. OD-5 defines mode names and
|
|
the default-selection rule.
|
|
|
|
#### Strict Duplicate Handling
|
|
|
|
Duplicate identities among peer contributions or within one contributed list
|
|
remain strict-composition errors under all three modes. For example, peer
|
|
fragments contributing `[B]` and `[B*]` conflict even under replacement by
|
|
identity: neither has higher precedence. Lower `[B]` and higher `[B*]` can
|
|
instead produce `[B*]` at a precedence boundary. Whole-sequence replacement
|
|
does not permit duplicate identities within an identified higher-tier sequence.
|
|
|
|
#### Ordering
|
|
|
|
Retain existing dependency-ready source scheduling within each tier and authored
|
|
item order within each list. Gathering collection declarations does not change
|
|
assembly order, and identity values do not sort records.
|
|
|
|
Across tiers:
|
|
|
|
- Append retains lower records in order, followed by higher records in assembled
|
|
order.
|
|
- Whole-sequence replacement retains only the higher sequence, in its assembled
|
|
order.
|
|
- Replacement by identity replaces matched records in their existing lower
|
|
positions and appends new identities in higher-tier order.
|
|
|
|
For example, lower `[A, B, C]` and higher `[C*, A*, D]` produce
|
|
`[A*, B, C*, D]` under replacement by identity. Updating a record's contents
|
|
preserves its position; whole-sequence replacement gives the higher tier control
|
|
of the complete sequence order.
|
|
|
|
OD-5 settles default selection and operation naming. OD-6 settles absent
|
|
contributions, empty sequences, null, and kind mismatches.
|
|
|
|
### Mode Names And Effective-Identity Defaults
|
|
|
|
**OD-5: settled.**
|
|
|
|
Use the mode names `replace`, `append`, and `upsert`, with one spelling per
|
|
operation:
|
|
|
|
| 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 |
|
|
| `upsert` | Replace matching records in full, retain unmatched lower records, and append new identities |
|
|
|
|
`upsert` preserves the existing positions of matching lower records and adds new
|
|
identities in higher-tier order, as settled in OD-4. It does not recursively merge
|
|
fields within matching records.
|
|
|
|
Select the operation at each collection's precedence boundary in this order:
|
|
|
|
1. Reconcile explicit modes within the current tier under OD-4; conflicting
|
|
explicit modes fail.
|
|
2. If an explicit mode is supplied, use it.
|
|
3. Otherwise, select `upsert` when an effective identity is available.
|
|
4. Otherwise, select `replace`.
|
|
|
|
Effective identity may come from an application registration, a current-tier
|
|
declaration, or the lower collection, subject to OD-3's agreement rules.
|
|
Selection depends on identity availability, not on whether the current tier
|
|
explicitly repeats a `key` declaration. A plain overlay sequence can therefore
|
|
default to `upsert` when identity was established elsewhere. A lower tier's mode
|
|
does not carry forward.
|
|
|
|
Determine the inferred mode using the available identity before applying the
|
|
operation. Explicit `replace` still follows OD-3: discard the lower collection's
|
|
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 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.
|
|
|
|
### Whole-Record Replacement By Identity
|
|
|
|
The `upsert` operation replaces each matching lower record with the
|
|
complete higher record, retains unmatched lower records, and adds new identities.
|
|
For example, given these lower and higher records:
|
|
|
|
```yaml
|
|
# Lower record
|
|
id: B
|
|
text: Original
|
|
temperature: 0.5
|
|
```
|
|
|
|
```yaml
|
|
# Higher record
|
|
id: B
|
|
text: Revised
|
|
```
|
|
|
|
The resulting record at this precedence boundary contains `id` and `text`.
|
|
The lower record's `temperature` is discarded. Subsequent defaulting or other
|
|
lifecycle operations retain their separately defined semantics. Recursive
|
|
field-by-field overlay of matching records is outside this selected operation.
|
|
|
|
### Selected Overlay Control
|
|
|
|
Selected overlay authors must be able to choose any of the three operations
|
|
for a sequence supplied by the overlay, including records authored inline.
|
|
The operation applies against the existing effective sequence at that path.
|
|
For an existing sequence `[foo, bar]`:
|
|
|
|
| Overlay contribution | Selected operation | Result |
|
|
| --- | --- | --- |
|
|
| `[foo*]` | Replacement by identity | `[foo*, bar]` |
|
|
| `[baz]` | Append | `[foo, bar, baz]` |
|
|
| `[bar, baz]` | Whole-sequence replacement | `[bar, baz]` |
|
|
|
|
Here `foo*` has the same identity as `foo` and supplies its complete replacement
|
|
record. Append retains its duplicate-identity error semantics. The retained
|
|
records, replacement behavior, and ordering shown are settled, including the
|
|
OD-4 rule that matched records retain their lower positions.
|
|
|
|
The settled OD-2 collection declaration supplies the overlay authoring surface.
|
|
OD-5 supplies operation names and effective-identity default selection. The current
|
|
[selected-overlay contract](../config.md#selected-overlays) remains the baseline
|
|
until the new controls are implemented.
|
|
|
|
### Optional Application-Owned Identity Constraints
|
|
|
|
Retain an interface through which applications may register a collection and
|
|
impose its unique-key definition. If the application requires `key: id`, a
|
|
configuration declaration of `key: [id, name]` is invalid: the compound key
|
|
would permit repeated `id` values with different names. The application identity
|
|
constraint applies whichever cross-tier operation configuration selects.
|
|
|
|
This is an authoritative constraint, distinct from an application default that
|
|
only fills an omission. Configuration can define identity for collections
|
|
without an application-owned constraint. Omitted configuration identity uses a
|
|
registered definition when present, and an explicit matching declaration is
|
|
accepted, as settled in OD-2. OD-1 below distinguishes the final-result guarantee
|
|
for application-owned constraints from assembly-time enforcement for YAML-only
|
|
declarations.
|
|
|
|
### Final-Result Guarantee For Application Registrations
|
|
|
|
**OD-1: settled, including the YAML-only follow-up below.**
|
|
|
|
> On successful loading, the final effective collections covered by application
|
|
> registrations satisfy their declared identity constraints.
|
|
|
|
Retain assembly-time identity checks and validate the final effective
|
|
collections before publication. This covers the loaded state after defaults,
|
|
inheritance, overrides, and application resolution have taken effect. Those
|
|
operations retain their separately defined combination semantics. The guarantee
|
|
does not require every intermediate callback state to satisfy the constraint
|
|
and does not extend to caller mutations after loading returns.
|
|
|
|
Keep the promise limited to the declared identity constraints. It does not by
|
|
itself require registered paths to exist or be non-null. Identity validation checks
|
|
actual sequences; the application schema and validation determine whether absence,
|
|
null, or another shape is acceptable, as settled in OD-6. Final validation examines
|
|
effective typed values, while assembly-time checks retain their role in
|
|
validating authored contributions. A final scalar value alone does not prove
|
|
that the field was explicitly supplied in a source.
|
|
|
|
The existing canonical final-value representation and shared publication path
|
|
provide the implementation foundation. Diagnostics should identify the affected
|
|
collection and item paths, using available origin information without exposing
|
|
identity values. The guarantee requires neither a new mutation-history system
|
|
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,
|
|
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
|
|
structural operations they control and avoids extending their policy lifetime
|
|
through later replacements, callbacks, and nested-record changes solely for
|
|
final validation.
|
|
|
|
### Absence, Empty Sequences, Null, And Kind Mismatches
|
|
|
|
**OD-6: settled.**
|
|
|
|
At a collection's precedence boundary:
|
|
|
|
| Higher-tier contribution | `replace` | `append` / `upsert` |
|
|
| --- | --- | --- |
|
|
| Absent | Leave the lower value unchanged | Leave the lower value unchanged |
|
|
| Explicit `[]` | Clear the sequence | Contribute no records |
|
|
| Explicit null or another non-sequence value | Ordinary replacement, subject to application schema | Error: the operation requires a sequence |
|
|
|
|
A declaration alone does not clear a collection. An optional import selecting
|
|
nothing contributes nothing; it does not erase records supplied elsewhere.
|
|
An absent lower collection can be initialized by an incoming sequence under
|
|
any mode, subject to identity requirements. An explicitly null or other
|
|
non-sequence lower value is distinct from absence: append and upsert against it
|
|
fail, while replacement remains available. An empty incoming sequence does not
|
|
bypass these kind checks or the operation's identity requirements.
|
|
|
|
With effective identity available, an omitted mode selects upsert. Consequently,
|
|
`prompts: []` alone does not clear an identified collection. Clearing it requires
|
|
explicit `mode: replace`. Replacing an identified collection with null likewise
|
|
requires explicit replacement and an application schema that permits null.
|
|
|
|
An application identity registration does not itself require the collection path
|
|
to exist or be non-null, and does not independently reject non-sequence values.
|
|
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
|
|
destination a shared identity policy. Declarations are gathered before composing
|
|
the relevant scope.
|
|
|
|
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 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.
|
|
|
|
## Scope And Decision Ownership
|
|
|
|
This is narrower than the deferred
|
|
[identified-record sequences proposal](future.md#1-identified-record-sequences),
|
|
which also considers recursive merging of matching records and record-specific
|
|
inheritance. Those capabilities remain separate candidates. Compound keys,
|
|
duplicate-rejecting append, and whole-record replacement by identity do not
|
|
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), 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 review of this settled roadmap.
|
|
It should cover ordinary and layered loading, item-local scopes, source-version
|
|
processing, identity validation, precedence, inheritance, and inspection together.
|
|
Public API design and test sequencing belong to that later plan.
|