6.0 KiB
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, and the rule that inheritance always replaces sequences in ADR 0009. 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.
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. 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.
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. 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.