From 6353598bd6cce2eb78a5fe990e144cc0cadf1280 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Wed, 14 Oct 2026 12:40:06 +0000 Subject: [PATCH] Settle input destination roadmap decisions --- docs/roadmap/input-destinations.md | 88 ++++++++++++++---------------- 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/docs/roadmap/input-destinations.md b/docs/roadmap/input-destinations.md index 8509cab..4ac3216 100644 --- a/docs/roadmap/input-destinations.md +++ b/docs/roadmap/input-destinations.md @@ -1,10 +1,10 @@ # Configuration Input Destinations Roadmap -Status: proposed; implementation has not started. Execute the stages below in -order. This roadmap responds to the +Status: design decisions approved; implementation has not started. Execute the +stages below in order. This roadmap responds to the [PromptKit feature request](promptkit-feature-request.md) and owns the target -state, delivery sequence, and unresolved design choices. Public spelling and -recommendations below are proposals until selected explicitly. +state, delivery sequence, and selected design decisions. Public API spelling and +diagnostic details remain to be finalized during implementation planning. ## Targeted end state @@ -22,14 +22,21 @@ and prompt names remain ordinary application keys, not ConfigKit namespaces. The intended public surface is per-input placement, illustratively an optional `Destination Path` field on `Input`, not a new loading entry point or an operation -on completed results. The minimum supported workflow has a root main input and -mounted underlay inputs; broader main placement is an open decision below. +on completed results. Content-bearing main inputs and underlay inputs support +the same destination model; root placement remains the default. The following behavior defines the target: - Omitted and empty destinations retain root placement. Destinations consist of literal mapping keys, including dots, slashes, and numeric-looking text. Explicit sequence-index destinations are outside the requested capability. +- A valid destination on `NoSource` contributes nothing. An actual empty + application mapping materializes its destination mappings as explicit input, + matching authored wrappers, without clearing lower-tier children. +- Existing structural reservations apply to introduced mappings. Destinations + that materialize `configkit` or a mapping-valued `extends` contrary to existing + contracts fail through the structured loading-error boundary. Keys merely + containing those words remain ordinary keys. - Host files, native filesystem files, bytes, readers, and mapping sources all support placement under their existing acquisition and ownership contracts. Filesystem context, import opt-in, and precedence remain independent choices. @@ -62,6 +69,10 @@ The following behavior defines the target: - Presence, provenance, history, explanations, effective rendering, and diffs agree with the final values. Sensitivity is evaluated at effective application paths under the existing historical and dependency rules. +- Existing source records expose placement, including inherited import + placement. Introduced ancestor mappings retain the contributing source's + identity; any root-location fallback identifies that document, not an authored + wrapper. Actual content retains its real source coordinates. - Original source locations and passive resource contexts survive placement, overrides, and inheritance. `Result.SourceContext` and `Resolution.SourceContext` still identify the declaring filesystem and base @@ -113,9 +124,9 @@ unrequested features above. ### 1. Settle placement semantics and the public contract -Resolve the Open Decisions before their dependent implementation. Select the -public API spelling, main/NoSource behavior, placement inspection, reserved-key -handling, and empty-contribution semantics. Define destination validation and +Finalize public API spelling using the Selected Design Decisions below for +main/NoSource behavior, placement inspection, reserved-key handling, and +empty-contribution semantics. Define destination validation and structured errors, including when validation occurs relative to source access. Use existing error categories where suitable rather than inventing a parallel error system. @@ -192,7 +203,7 @@ or executable example, reusing existing coverage where it proves the same behavior. Include focused cases for: - omitted/empty destinations, literal escaped and numeric keys, invalid index - destinations, and the selected reserved-key and main-input rules; + destinations, and the approved reserved-key and main-input rules; - different destinations, shared destinations, ancestor overlaps, equal and unequal conflicts, explicit null, omitted fields, and empty contributions; - recursive precedence, atomic sequences, and domain-version-only overrides; @@ -222,45 +233,33 @@ Completion: all requested public scenarios pass, owning documentation describes implemented behavior, and consumers can perform one complete layered load with no compensating source-rewriting or post-load structural pass. -## Open Decisions +## Selected Design Decisions ### Main-input destinations and absent inputs -Recommendation: allow the same destination model for content-bearing main inputs -as for underlays, with `VersionRule.Path` remaining an effective application path -and any specification declaration still owned by main. The checker retains its -existing handling of an absent declaration. Treat `NoSource` as no contribution, -even with a valid destination. This keeps `Input` coherent and avoids a role-only -restriction. PromptKit can continue using root main; broader main placement must -not add per-fragment checks or move the existing version-check phase. - -Alternative: initially support non-root destinations only for underlay inputs -and reject non-root main placement explicitly. This meets PromptKit's minimum -need and reduces the first implementation's version-check interactions, but adds -a role-dependent rule to a shared public type. Adopt that restriction only as an -explicit scope choice, not because threading main placement is inconvenient. -For `NoSource`, rejecting a nonempty destination could catch mistaken assembly -declarations, but makes an otherwise absent contribution less composable. +Allow the same destination model for content-bearing main inputs and underlays, +with `VersionRule.Path` remaining an effective application path and any +specification declaration still owned by main. The checker retains its existing +handling of an absent declaration and its existing lifecycle phase. `NoSource` +contributes nothing even with a valid destination. This keeps `Input` coherent +across its roles and preserves absence without per-fragment version checks. ### Public placement inspection and synthetic ancestors -Recommendation: expose effective placement on existing source records, +Expose effective placement on existing source records, illustratively `SourceInfo.Destination`, including inherited import placement. Use original source identity for introduced ancestor mappings, documenting that any root-location fallback identifies the contributing document rather than a physically authored wrapper. Actual content keeps its real coordinates. This allows consumers to connect placement to sources without a new query system. -Alternative: add placement to origin records or a dedicated lookup, and represent -synthetic ancestors without a physical location. This can distinguish authored -and synthetic structure more precisely, but expands per-value metadata or adds -an API when placement is fundamentally per source application. Evaluate the -choice against inherited fields, composite origins, and the same source selected -in separate tiers; do not infer placement from a final path by guesswork. +Preserve that relationship through inherited fields, composite origins, and the +same source selected in separate tiers. Placement is per source application; +consumers can inspect it without inferring it from final paths. ### Reserved structural keys in destinations -Recommendation: use existing `Path` conventions for literal keys and parsed +Use existing `Path` conventions for literal keys and parsed numeric tokens, reject explicit index segments, and preserve current structural reservations in the effective tree. Reject destination segments that would materialize `configkit` or a mapping-valued `extends` contrary to those contracts, @@ -268,25 +267,18 @@ with a clear structured destination error. Ordinary keys containing those words, dots, or slashes remain ordinary keys. Source-root directives are processed before placement and are not rejected simply because their application body is mounted. -Alternative: permit structural spellings as literal synthetic wrapper keys by -distinguishing them from authored directives. This enables additional application -paths but introduces an exception where mounted and explicitly wrapped inputs -can have different meanings. It would require an explicit change to reserved-key -semantics and is not needed for the requested collection paths. Running existing -directive validation on the placed tree is another viable error mechanism, but -must still identify that the invalid structure came from destination selection. +Mounted and authored wrappers therefore retain the same structural meaning. +Destination diagnostics identify placement as the cause of invalid introduced +structure, using existing error categories where suitable. ### Empty mappings and presence of introduced containers -Recommendation: mounting an actual empty application mapping materializes its +Mounting an actual empty application mapping materializes its destination mappings as explicit contributions, matching authored wrappers. This preserves the request's equivalence principle and lets consumers inspect an intentionally empty collection. Such mappings still do not clear lower-tier children. An absent source contributes nothing. -Alternative: treat an empty mounted body as no contribution. This avoids empty -containers for fragments containing only imports or no application fields, but -loses equivalence with explicitly wrapped empty mappings and erases an observable -presence distinction. If selected, document that exception and its effect on -history and empty imported bodies rather than deriving it accidentally from -whether the implementation allocated wrapper nodes. +This applies equally to empty imported application bodies and inputs containing +only import directives. Presence follows the contribution's meaning rather than +whether an implementation happened to allocate wrapper nodes.