118 lines
6.0 KiB
Markdown
118 lines
6.0 KiB
Markdown
# ADR 0017: Define Identity and Merge Rules for Each Collection
|
|
|
|
## Status
|
|
|
|
Accepted.
|
|
|
|
This supersedes the decisions about who declares identity and how
|
|
sequences combine in [ADR 0014](0014-sequence-placement-and-composition.md),
|
|
and the rule that inheritance always replaces sequences in
|
|
[ADR 0009](0009-sibling-inheritance.md). The existing source-placement and
|
|
inheritance-scope rules continue to apply. It also supersedes the whole-sequence
|
|
precedence replacement rule in the proposed
|
|
[ADR 0007](0007-source-backed-underlays.md).
|
|
|
|
## Date
|
|
|
|
2026-10-22
|
|
|
|
## Context
|
|
|
|
ConfigKit can load separate files as records in a sequence. Applications can
|
|
register an identity key so records with different identities combine and
|
|
duplicates fail. However, a higher configuration layer or an inheriting mapping
|
|
currently replaces the whole sequence. Applications that need to extend a
|
|
catalog across layers must write their own merging code and keep source and
|
|
history information consistent with its results.
|
|
|
|
Configuration authors need to choose how records are identified and whether a
|
|
layer adds records, updates records, or replaces the collection. Applications
|
|
also need a way to require unique identities in the final loaded configuration.
|
|
These rules must work consistently for imported records, inline sequences,
|
|
overlays, and inheritance.
|
|
|
|
## Decision
|
|
|
|
ConfigKit manages identity and merge rules for each collection during loading.
|
|
Configuration files can declare those rules for imported and inline sequences,
|
|
including sequences in selected overlays. Applications can require a particular
|
|
identity definition, and configuration declarations must agree with it. Source
|
|
placement determines where each contribution enters the configuration.
|
|
|
|
Keep the distinction between contributions at the same priority and overrides
|
|
from a higher layer, as established in
|
|
[ADR 0001](0001-separate-composition-and-overlay.md). Within one tier, records
|
|
with distinct identities combine; duplicate identities and conflicting
|
|
declarations fail. Determine the applicable rules before combining those
|
|
contributions, including any identity established in lower tiers.
|
|
|
|
Across layers, support three operations: replace the whole sequence, append
|
|
records while rejecting duplicate identities, or replace whole records by
|
|
identity (`upsert`). Source layering and sibling inheritance use the same
|
|
operations. For inheritance, the rules at the inheriting destination determine
|
|
which operation applies.
|
|
|
|
An explicitly selected operation remains in effect at its destination until a
|
|
higher layer changes it. When no explicit operation applies, use upsert if an
|
|
identity definition is available; otherwise replace the whole sequence. Authors
|
|
can declare identity and an operation before supplying any records. Keep source
|
|
acquisition order separate from layer priority and the dependency ordering
|
|
established by [ADR 0015](0015-shared-dependency-ready-ordering.md).
|
|
|
|
Rules declared inside a record belong to that record. Retaining the record
|
|
retains its local rules; replacing it discards them. Replacing a whole collection
|
|
can discard its previous YAML identity definition, while application requirements
|
|
still apply. Inheritance can copy collection identity, but the inheriting
|
|
destination chooses its operation. Application registrations continue to govern
|
|
the paths where they were registered. The same ownership rules apply to
|
|
declarations made before a sequence exists.
|
|
|
|
Check YAML-declared identity when combining collections during source assembly
|
|
and inheritance. Application registrations also require the final loaded
|
|
sequences to satisfy their identity rules, including after typed defaults, CLI
|
|
overrides, and application resolution. This guarantee checks sequences that
|
|
exist; the application schema and validation decide whether a collection must
|
|
exist and which value types are allowed. The guarantee applies when loading
|
|
completes, rather than to every intermediate callback state or changes made by
|
|
the caller afterward.
|
|
|
|
Preserve explicit presence, source attribution, history, sensitivity, and
|
|
resource context through these operations, following
|
|
[ADR 0003](0003-preserve-presence-and-resolution-history.md). Typed defaults,
|
|
CLI assignments, and application resolution keep their own rules for changing
|
|
values. The public API provides registration and inspection; the implementation
|
|
chooses how to store and track collection rules internally.
|
|
|
|
## Alternatives Considered
|
|
|
|
- **Keep identity application-owned and always replace sequences across layers.**
|
|
Keeps the current design small, but reusable catalogs require application
|
|
changes or custom merging code.
|
|
- **Put merge rules on each import.** Keeps the rules beside the imported source,
|
|
but inline sequences and overlays need a separate way to declare them. It also
|
|
makes rules for a collection spread across several imports harder to reconcile.
|
|
- **Merge individual fields of records with matching identities.** Allows partial
|
|
record updates, but adds rules for field conflicts and retained values.
|
|
Replacing the whole record makes the update's scope clear.
|
|
- **Check every YAML identity again at the end of loading.** Gives YAML declarations
|
|
authority over values changed by defaults, CLI assignments, and application
|
|
resolution. This extends the lifetime of those declarations; application
|
|
registration provides that final guarantee when needed.
|
|
|
|
## Consequences
|
|
|
|
Consumers can build and customize catalogs in one load while retaining useful
|
|
inspection information. Configuration authors choose how collections combine
|
|
within application requirements. Duplicate identities among contributions at
|
|
the same priority remain errors.
|
|
|
|
Existing registered collections use upsert across layers by default unless an
|
|
explicit operation selects otherwise. Correct loading now requires tracking
|
|
which declarations still apply as records are replaced, copied, or moved to new
|
|
positions. Lower-tier identity must be available before combining higher-tier
|
|
contributions.
|
|
|
|
The configuration contract will define declaration syntax, identity comparison,
|
|
omission and empty-value behavior, and diagnostics when the feature is
|
|
implemented.
|