Align layering roadmap with consistent version semantics

This commit is contained in:
2026-09-29 15:45:21 +00:00
parent 5c1aa1814c
commit 798091b276

View File

@@ -24,6 +24,13 @@ Design priorities, in order:
3. A small public surface with cohesive private implementation, usable by simple 3. A small public surface with cohesive private implementation, usable by simple
applications without requiring them to learn the layered-loading machinery. applications without requiring them to learn the layered-loading machinery.
Consumers load once and receive a complete configuration and its inspection
metadata. They should not need a second generic merge/defaulting pass, a separate
presence tracker, or reconstructed provenance. Domain-specific resolution remains
consumer-owned, but fits into the recorded loading lifecycle before the result
is validated and returned. Subject to consistent semantics and correct presence
and provenance, minimizing consumer integration code is an explicit design goal.
The [named-inheritance request](feature-request-promptkit-inheritance.md) remains The [named-inheritance request](feature-request-promptkit-inheritance.md) remains
separate and optional. This roadmap neither implements it nor adds speculative separate and optional. This roadmap neither implements it nor adds speculative
infrastructure for it. infrastructure for it.
@@ -43,6 +50,10 @@ these proposals.
- Composition rejects non-mapping overlaps, including equal values. Overlay - Composition rejects non-mapping overlaps, including equal values. Overlay
recursively merges mappings and replaces other values. Empty mappings do not recursively merges mappings and replaces other values. Empty mappings do not
clear existing children; null is an explicit value, not omission. clear existing children; null is an explicit value, not omission.
- An explicitly configured schema-version path currently belongs to the selected
source. Its checker runs before overlay/CLI input, which cannot alter that path;
imported fragments cannot contribute it. The target below deliberately revises
those ownership and timing rules.
- CLI overrides already convert supported programmatic scalars, lists, and - CLI overrides already convert supported programmatic scalars, lists, and
string-keyed maps directly to the private document representation. They have string-keyed maps directly to the private document representation. They have
CLI-specific paths, labels, restrictions, and origins, not a general source API. CLI-specific paths, labels, restrictions, and origins, not a general source API.
@@ -58,6 +69,8 @@ these proposals.
Existing source/lifecycle contracts, ownership, secret handling, and diagnostics Existing source/lifecycle contracts, ownership, secret handling, and diagnostics
remain the compatibility baseline. This work is not permission to silently remain the compatibility baseline. This work is not permission to silently
reinterpret current `Load`, imports, selected overlays, or CLI overrides. reinterpret current `Load`, imports, selected overlays, or CLI overrides.
Effective-version validation is an explicit exception described below, not an
unannounced consequence of refactoring.
## Target source model ## Target source model
@@ -119,6 +132,13 @@ Support native filesystem, host-file, byte, reader, and programmatic inputs.
The order is supplied by the caller; ConfigKit does not discover priorities or The order is supplied by the caller; ConfigKit does not discover priorities or
know about PromptKit's catalog tiers. know about PromptKit's catalog tiers.
The selected operation determines how values combine, not their source format
or loading entry point. Equivalent file, embedded, byte/reader, and programmatic
input uses the same presence and combination rules. Explicitly choosing compose,
overlay, or replacement expresses different intentions; merely changing the input
medium must not switch between them. This applies to both ordinary `Load` and
the new layered-loading interface wherever they offer the same operation.
Keep strict composition and intentional overriding visibly distinct: Keep strict composition and intentional overriding visibly distinct:
| Operation | Desired semantics | | Operation | Desired semantics |
@@ -133,23 +153,68 @@ precedence is applied. An overlay must not hide malformed YAML or a composition
conflict in an earlier contribution. There is no automatic matching by an `id` conflict in an earlier contribution. There is no automatic matching by an `id`
field, implicit sequence merge, universal profile registry, or merge callback. field, implicit sequence merge, universal profile registry, or merge callback.
Assemble the effective input before applying the typed defaults/decoding, Assemble the effective input, including selected-overlay and CLI contributions,
then check its configured schema version before typed defaults/decoding,
application resolution, secret resolution, semantic validation, and final application resolution, secret resolution, semantic validation, and final
snapshot lifecycle. Those latter operations run once per load, not once per snapshotting. Each lifecycle phase runs once per load, not once per layer.
layer. Higher layers may replace values that would not fit the final typed schema; Higher layers may replace values that would not fit the final typed schema;
effective typed validation is not validation of each intermediate snapshot. effective typed validation is not validation of each intermediate snapshot.
Input syntax, source access, and composition remain validated at acquisition. Input syntax, source access, and composition remain validated at acquisition.
The standard precedence relationship is typed defaults below assembled input, The standard precedence relationship is typed defaults below assembled input,
then the existing selected-overlay convenience and explicit CLI overrides where then the existing selected-overlay convenience and explicit CLI overrides where
those are used, followed by recorded application resolution. The new API must those are used, followed by recorded application resolution. Named-overlay lookup
make the selected overlay's owning filesystem context unambiguous; Decision 4 has an unambiguous filesystem context, separate from version ownership: ordinary
addresses the primary-root role alongside schema-version ownership. loading retains its source/root convention; a multi-context load explicitly
identifies the lookup context when that convenience is used. Other sources retain
their own import and resource contexts. Callers do not manufacture a primary
document merely to combine layers, and no overlay lookup context is needed when
no named overlay is selected.
Replacing lower input does not mean resetting or deleting typed application Replacing lower input does not mean resetting or deleting typed application
defaults. Existing defaults and null rules still apply during final decoding. defaults. Existing defaults and null rules still apply during final decoding.
The distinction must be visible in examples, especially empty maps and partially The distinction must be visible in examples, especially empty maps and partially
specified definitions. No new reset/delete operator is implied by replacement. specified definitions. No new reset/delete operator is implied by replacement.
For example, replacing input `{timeout: 10, retries: 2}` with `{timeout: 20}`
removes the lower input's `retries: 2`; a typed default of `retries: 3` can still
supply the final value. The metadata then attributes `retries` to defaults, not
to the replaced input. An empty replacement entry likewise removes lower-input
children without suppressing applicable typed defaults.
### Effective-configuration schema version
An application opts into version checking by configuring a version path. A key
spelled `version` has no automatic special status. The configured path follows
the selected combination operation like any other setting:
- Strict composition rejects conflicting scalar definitions, even if equal;
it is not last-wins.
- An intentional overlay can replace an earlier version value. Imports may
contribute the version subject to ordinary composition rules; selected overlays
and CLI input are not excluded solely because a path is the version path.
- Any selected subtree replacement has the same effect on version presence as
on other values in that subtree, including removing a lower-input version.
After all input precedence operations, inspect the effective version and invoke
the configured checker once, before typed decoding. Preserve the existing
supported scalar forms, absent/null distinction, version diagnostics, and
version-path integer-to-string decoding behavior. As today, an invalid effective
version type produces a diagnostic rather than invoking the checker. Absent
effective input is reported as absent, not manufactured from typed defaults.
Version origins and replacement history follow the same rules as other fields.
Do not check each source's version independently or fail merely because a
superseded version value would fail effective-version validation. Invalid YAML,
source failures, and strict-composition conflicts still fail at their owning
phases; precedence does not conceal those errors. A later version value does not
migrate earlier data. Consumers supply compatible inputs and own schema meaning;
automatic migration and per-document format interpretation remain out of scope.
Apply this model consistently to existing and new loading interfaces. It
deliberately changes the current selected-source ownership restrictions and moves
checking after overlay/CLI application. Document the resulting compatibility
impact and callback timing in the feature's minor release rather than retaining
different version semantics for old and new entry points.
### Programmatic presence ### Programmatic presence
@@ -169,13 +234,20 @@ boundary. Results and inspection remain independently owned under existing
contracts. This does not promise language-enforced immutability of Go values or contracts. This does not promise language-enforced immutability of Go values or
an immutable filesystem. an immutable filesystem.
A final Go value alone need not encode presence: an omitted boolean and an
explicit `false` can both decode to `false`. The returned configuration together
with its presence/provenance metadata preserves that distinction through
defaulting, layering, and recorded resolution. Null, empty values, zero, and
absence must not be collapsed by source conversion or merging.
### Provenance and operational boundaries ### Provenance and operational boundaries
Origins, presence, history, explanation, and effective rendering agree with the Origins, presence, history, explanation, and effective rendering agree with the
final typed result. Overridden and replaced definitions remain explainable without final typed result. Overridden and replaced definitions remain explainable without
retaining stale effective descendants. Replacements identify their scope and retaining stale effective descendants. Replacements identify their scope and
displaced sources; equal-value explicit overrides are still meaningful history. displaced sources; equal-value explicit overrides are still meaningful history.
Retain redaction and callback-lifetime behavior. Include typed defaults and application-derived values, not only source-supplied
values, in final-value provenance. Retain redaction and callback-lifetime behavior.
Apply existing resource-accounting principles across the whole load rather than Apply existing resource-accounting principles across the whole load rather than
resetting allowances for every layer. Audit how bytes, programmatic nodes, resetting allowances for every layer. Audit how bytes, programmatic nodes,
@@ -231,6 +303,9 @@ These are responsibility areas, not implementation stages:
with ordinary loading rather than building a parallel configuration system. with ordinary loading rather than building a parallel configuration system.
- Resolve the fixed operation and ownership decisions below, then extend source - Resolve the fixed operation and ownership decisions below, then extend source
records, histories, diagnostics, and rendering consistently across input kinds. records, histories, diagnostics, and rendering consistently across input kinds.
- Replace selected-source version ownership with the effective-version lifecycle
across both loading interfaces, including import, overlay, and CLI handling.
Keep filesystem-context selection independent of version checking.
- Refactor mapping assembly and provenance updates where current algorithms - Refactor mapping assembly and provenance updates where current algorithms
would scale poorly under layered workloads. Keep optimization scope tied to would scale poorly under layered workloads. Keep optimization scope tied to
the new workflow rather than rewriting unrelated library subsystems. the new workflow rather than rewriting unrelated library subsystems.
@@ -239,18 +314,26 @@ These are responsibility areas, not implementation stages:
and null values, caller/result independence, and source-correct redaction and and null values, caller/result independence, and source-correct redaction and
explanation. Add comparative benchmarks without making performance a brittle explanation. Add comparative benchmarks without making performance a brittle
pass/fail contract. pass/fail contract.
- Compare equivalent logical inputs across supported source kinds and loading
interfaces. Assert consistent values, presence, operation outcomes, and origin
meaning while allowing genuine location/label differences. Cover defaulted and
derived values, replacement followed by typed defaults, version conflicts under
composition, and effective-version checking after intentional overrides.
- Provide maintained simple and PromptKit-shaped examples: embedded defaults, - Provide maintained simple and PromptKit-shaped examples: embedded defaults,
operator configuration from another context, and labeled programmatic input. operator configuration from another context, and labeled programmatic input.
Demonstrate exact operations and resource origins without provider calls or Demonstrate exact operations and resource origins without provider calls or
recreating PromptKit itself. recreating PromptKit itself. The consumer supplies sources, explicit operations,
types, and domain rules; it does not implement generic merging, presence
tracking, or origin reconstruction after loading.
- Update Go declaration documentation, the configuration contract, consumer - Update Go declaration documentation, the configuration contract, consumer
guidance, component overview, and relevant architecture/deferred-scope records guidance, component overview, and relevant architecture/deferred-scope records
as behavior lands. Record significant boundary changes in an ADR without as behavior lands. Record significant boundary changes in an ADR without
rewriting accepted ADR history. Keep named inheritance explicitly separate. rewriting accepted ADR history. Keep named inheritance explicitly separate.
Plan an additive minor release for the new public capability. If any design choice Plan a minor release for the new public capability and the deliberate change to
requires changing an existing documented behavior, identify it expressly and schema-version ownership and checking order. Explain the version-checking change
provide migration guidance; do not describe it as an internal optimization. and provide migration guidance; this release is not wholly additive. Identify any
other compatibility changes expressly rather than calling them optimizations.
No release, implementation change, or current-policy revision occurs in this pass. No release, implementation change, or current-policy revision occurs in this pass.
## Completion criteria ## Completion criteria
@@ -259,11 +342,16 @@ A consumer can load embedded configuration with relative imports, combine it
with independently located operator configuration and explicit programmatic with independently located operator configuration and explicit programmatic
values, and obtain one typed result with accurate presence, sources, history, values, and obtain one typed result with accurate presence, sources, history,
and resource context. No temporary configuration files or serialization round and resource context. No temporary configuration files or serialization round
trip is needed. Simple `Load` usage retains its current behavior. Benchmarks trip or subsequent generic configuration processing is needed. Simple `Load`
usage retains its behavior except for the explicitly selected version-policy
change, which is shared by both entry points. Final-value provenance covers input,
defaults, and recorded domain resolution, and metadata distinguishes explicit
zero/false from absence even when the typed values are equal. Benchmarks
demonstrate that adding inputs does not entail avoidable full-collection copying demonstrate that adding inputs does not entail avoidable full-collection copying
or quadratic key lookup. Public contracts and examples clearly distinguish or quadratic key lookup. Public contracts and examples clearly distinguish
composition, overlay, any selected replacement operation, and downstream domain composition, overlay, any selected replacement operation, and downstream domain
resolution. resolution. Equivalent-input tests and the maintained consumer example establish
semantic consistency and reduced integration code, not merely API availability.
## Open Decisions ## Open Decisions
@@ -312,21 +400,3 @@ registry mapping IDs to their filesystems. Metadata stays capability-free and
serializable, but every consumer must maintain and correctly reconcile another serializable, but every consumer must maintain and correctly reconcile another
registry. This is viable if limiting retained filesystem capabilities proves registry. This is viable if limiting retained filesystem capabilities proves
more important than the additional integration machinery. more important than the additional integration machinery.
### 4. Primary-root ownership in multi-source loads
**Recommendation:** retain an explicit primary source/root role for application
schema version and the optional named-overlay convention. Additional inputs are
fragments or explicit precedence contributions, not independent version owners.
This best preserves current version ownership and prevents precedence from hiding
a version failure. An in-memory primary mapping can carry the version without
requiring a generated file. Other source contexts still govern their own imports
and resource references.
**Alternative:** permit independently versioned roots and validate each before
assembly, with a separately selected context for named-overlay lookup. This fits
standalone catalogs more naturally, but needs new rules for combining accepted
versions, callback counts, and the effective version's provenance. It should not
silently invoke the existing version callback once per layer or let the last
version value win. Resolve this choice with a realistic mixed-catalog example
before producing the staged implementation plan.