diff --git a/docs/roadmap/sequences.md b/docs/roadmap/sequences.md index f399568..e6c0a49 100644 --- a/docs/roadmap/sequences.md +++ b/docs/roadmap/sequences.md @@ -4,8 +4,8 @@ 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; operation names in examples remain illustrative -pending OD-5. +structure is settled under OD-2; mode names and default selection are settled +under OD-5. ## Motivation @@ -75,22 +75,21 @@ 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; -operation names and default behavior remain under OD-5. Identity and combination +OD-5 defines its names and effective-identity default. Identity and combination controls belong to the collection declaration; import shorthand is deferred. -The intended results can be expressed without settling operation names. Letters -below denote record identities; `B*` is a higher-tier record with the same +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]` | Whole-sequence replacement | `[C]` | -| `[A, B]` | `[B*]` | Append | Duplicate-identity error | -| `[A, B]` | `[B*]` | Whole-sequence replacement | `[B*]` | -| `[A, B]` | `[B*]` | Replacement by identity | `[A, B*]` | -| `[A, B]` | `[B*, C]` | Replacement by identity | `[A, B*, C]` | +| `[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, @@ -139,14 +138,15 @@ configkit: sequences: - destination: [prompts] key: id - mode: replace-by-key + mode: upsert prompts: - id: foo text: Revised definition ``` -An overlay can instead add a distinct record: +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: @@ -175,11 +175,11 @@ prompts: text: Baz definition ``` -Explicitly selecting the default operation is valid. Under the recommended -whole-sequence replacement default, removing the last example's `configkit` -block produces the same result. An author may keep it to make intent explicit. -The names `replace`, `append`, and `replace-by-key` illustrate the three -operations; exact names and the default remain under OD-5. +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 @@ -304,14 +304,14 @@ default only after reconciling declarations. | `append` and omitted | `append` | | `replace` and omitted | `replace` | | `append` and `replace` | Error | -| All omitted | Default selected under OD-5 | +| 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. Mode names remain -illustrative pending OD-5. +destination and conflicting declaration locations. OD-5 defines mode names and +the default-selection rule. #### Strict Duplicate Handling @@ -342,12 +342,55 @@ For example, lower `[A, B, C]` and higher `[C*, A*, D]` produce preserves its position; whole-sequence replacement gives the higher tier control of the complete sequence order. -OD-5 retains default selection and operation naming. Absent contributions, empty +OD-5 settles default selection and operation naming. Absent contributions, empty sequences, null, and kind mismatches remain under OD-6. +### Mode Names And Effective-Identity Defaults + +**OD-5: settled.** + +Use the mode names `replace`, `append`, and `upsert`, with one spelling per +operation: + +| Mode | Behavior between tiers | +| --- | --- | +| `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. 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 third cross-tier operation replaces each matching lower record with the +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: @@ -388,7 +431,7 @@ 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 retains operation naming and default selection. The current +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. @@ -493,27 +536,6 @@ Limiting YAML-only identity to assembly keeps policy lifetime narrower, but late operations could produce a final collection that no longer satisfies that identity declaration. -### OD-5. What Is The Default Operation And Its Syntax? - -Choose names for the three `mode` values and decide the default operation. -OD-2 settles the declaration structure and accepts explicit selection of the -default. OD-4 settles omission as no preference among peer declarations, with the -default applied after reconciliation. - -**Initial recommendation:** Keep whole-sequence replacement as the cross-tier -default and make append and replacement by identity explicit. Lists can represent -complete ordered instructions as well as extensible catalogs; an explicit append -communicates the intent to retain -lower-layer records. Use names that distinguish whole-sequence from whole-record -replacement; the examples use `replace`, `append`, and `replace-by-key`. - -**Alternative:** Default identified collections to append or replacement by -identity and require explicit whole-sequence replacement. These defaults make -progressive collections concise, but retain lower-layer records unless an author -deliberately replaces the collection. For naming, `upsert` is a concise alternative -to `replace-by-key`, but requires explaining its whole-record replacement behavior -to authors unfamiliar with that term. - ### OD-6. What Do Absence, Empty Sequences, And Null Mean? Can a declaration selecting nothing affect a lower collection? How do explicit