From 6eb80b06e346a861b5968d5c7bbaccd826c0af24 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Thu, 22 Oct 2026 13:28:18 +0000 Subject: [PATCH] Settle sequence identity definitions and policy lifetime --- docs/roadmap/sequences.md | 143 ++++++++++++++++++++++++++++---------- 1 file changed, 105 insertions(+), 38 deletions(-) diff --git a/docs/roadmap/sequences.md b/docs/roadmap/sequences.md index d7e0ed2..09d5c77 100644 --- a/docs/roadmap/sequences.md +++ b/docs/roadmap/sequences.md @@ -45,6 +45,8 @@ later. Extending one does not automatically imply extending the other. 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 @@ -116,13 +118,13 @@ 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 remains under OD-3. +Identity-policy lifetime across tiers is settled in OD-3 below. Mode describes the current tier's combination instruction. 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. Agreement and omission among -peer declarations remain under OD-4. +depend on which contribution is assembled first. Identity agreement is settled +in OD-3; operation agreement and omission among peers remain under OD-4. Permit sequence declarations in selected overlays and make their use independent of filesystem import enablement. This deliberately changes the current blanket @@ -185,6 +187,90 @@ 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 remain under OD-6. +These structural rules do not settle final-result enforcement for YAML-only +identity declarations, which remains the OD-1 follow-up. + ### Whole-Record Replacement By Identity The third cross-tier operation replaces each matching lower record with the @@ -264,9 +350,9 @@ 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 settle null and non-sequence -handling; those boundaries remain under OD-3 and OD-6. Final validation examines +handling; those collection boundaries remain under OD-6. Final validation examines effective typed values, while assembly-time checks retain their role in -validating authored contributions. A final string value alone does not prove +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 @@ -286,16 +372,17 @@ The initial recommendation is to compose each tier's contributions strictly, then apply one collection operation from the higher tier against the lower sequence. A collection declaration would therefore govern its tier's contribution at that destination, without sequentially erasing peer contributions. Multiple -agreeing declarations could coalesce; conflicting explicit identity or operation -declarations would produce errors. +agreeing operation declarations could coalesce; conflicting explicit operations +would produce errors. Identity declaration agreement is already settled in OD-3. For append, the ordering recommendation is to retain lower-tier item order and then the higher tier's assembled item order. For replacement by identity, retain the position of each replaced record and append new identities in higher-tier order. Existing dependency-ready scheduling would continue to determine assembly order within a tier. Whole-sequence replacement would retain only the higher -tier's assembled sequence. Ordering and the treatment of peer declarations remain -recommendations awaiting agreement, not changes to the current ordering contract. +tier's assembled sequence. Ordering and the treatment of peer operation +declarations remain recommendations awaiting agreement, not changes to the +current ordering contract. This requires collection policy to survive import expansion through precedence assembly. Correctness includes final values, projected item paths, source @@ -333,37 +420,17 @@ Application-registered collections have the settled final-result guarantee above. Should a configuration-only identity declaration carry the same guarantee, or govern structural assembly alone? -**Initial recommendation:** With declaration scope settled in OD-2, resolve -identity-policy lifetime across replacement in OD-3 before this choice. Final -enforcement needs an unambiguous effective policy for each retained collection. The -application-owned guarantee can proceed independently of this choice. +**Discussion basis:** Declaration scope and identity-policy lifetime are settled +in OD-2 and OD-3. Revisit final enforcement using the effective identity of each +retained collection. The application-owned guarantee remains independent of +this choice. **Alternatives:** Applying the same final check to YAML-declared identities -would give both declaration forms consistent enforcement, but requires resolving -which policy survives replacement or follows nested records. Limiting YAML-only -identity to assembly keeps policy lifetime narrower, but later operations could -produce a final collection that no longer satisfies that identity declaration. - -### OD-3. What Constitutes Identity, And When Can It Change? - -Settle compound-key field selection, value types, missing/null components, -empty and repeated field lists, empty identity strings, and agreement between -declarations. Decide whether whole-sequence replacement can introduce a different identity -definition after discarding the lower collection when the application has imposed -no identity constraint. An application-owned constraint remains authoritative. - -**Initial recommendation:** Interpret `key: id` as `[id]` and a compound key as -an ordered tuple of direct, literal string fields. Retain exact string comparison -and require every component to be present. Preserve tuple boundaries rather than -concatenating values. Identity declarations participating in append or replacement -by identity must agree; equal definitions within a scope coalesce and conflicting -definitions fail. Detailed list validation and identity changes at whole-sequence -replacement boundaries remain to be settled. - -**Alternative:** Permit additional scalar types or nested field paths. These -support more schemas but add type-equality and addressing rules. A load-wide fixed -identity definition simplifies agreement across tiers; allowing a replacement to -establish a new definition gives independently authored layers more flexibility. +would give both declaration forms consistent enforcement, but requires carrying +the effective policy through later lifecycle stages, including nested records. +Limiting YAML-only identity to assembly keeps policy lifetime narrower, but later +operations could produce a final collection that no longer satisfies that +identity declaration. ### OD-4. What Does A Combination Choice Control?