Allow ordinary config data at the application root

This commit is contained in:
2026-11-02 00:19:10 +00:00
parent 74bff9704e
commit b27fe0598a

View File

@@ -1,8 +1,8 @@
# Document-Root Configuration Controls # Document-Root Configuration Controls
Status: proposed implementation of the agreed `config` spelling and Status: proposed implementation of the agreed `config` spelling and
document-root-only reservation. The remaining application-root boundary choice source-document-root-only reservation. Ordinary application-root `config` data
is recorded under Open Decisions. created through placement or later lifecycle operations is permitted.
Planning baseline: `1e69abc88cf748537cd7febc85e0743755fa2fc1`. Planning baseline: `1e69abc88cf748537cd7febc85e0743755fa2fc1`.
@@ -103,21 +103,41 @@ provider settings in the first example, `fragments/provider.yml` can contain
`/providers/primary/config`. Its filesystem paths still resolve from the `/providers/primary/config`. Its filesystem paths still resolve from the
declaring file under the existing resource-context rules. declaring file under the existing resource-context rules.
### Application-root data from non-document operations
Input placement, mounted imports, typed defaults, CLI overrides, and application
resolution may produce ordinary `config` data at the final application root,
following their existing schema and lifecycle rules. Controls are recognized
only during source-document extraction. For example, a fragment containing
`timeout: 10s` can be placed at destination `[config]`, producing application
data at `/config/timeout`. A CLI assignment to `Keys("config", "timeout")`
addresses that data rather than introducing a control. Collection declarations
and registrations can likewise address actual data at that application path.
This keeps the reservation document-local. Placement and later lifecycle
operations do not reinterpret their output as another source document, and no
additional schema or callback restriction reserves the final root namespace.
Inspection can show a root `config` value without reinterpreting it; effective
YAML retains its existing lack of a reloadability guarantee. Explain
source-local control coordinates and assembled application coordinates
separately, even when their path text is the same; this requires no new
inspection-record format.
### Consistent application-path handling ### Consistent application-path handling
Allow nested application paths containing literal `config` in input placement, Allow application paths containing literal `config`, at root or nested, in
relative import destinations, collection declarations and registrations, CLI input placement, relative import destinations, collection declarations and
paths and values, version fields, and inspection queries, subject to each registrations, CLI paths and values, version fields, and inspection queries.
operation's existing rules. In particular, a relative destination beginning Each operation retains its existing rules. In particular, a relative destination
with `config` can legitimately refer to a nested application mapping after the beginning with `config` can legitimately refer to a nested application mapping
parent destination is applied. Do not decide its meaning from that relative after the parent destination is applied. Do not decide its meaning from that
segment alone. relative segment alone.
Protect actual source-root controls during source-version extraction while Protect actual source-root controls during source-version extraction while
allowing version fields below an ordinary nested `config` mapping. Preserve allowing version fields below an ordinary nested `config` mapping. Preserve
main-version ownership, non-main header consumption, and existing version modes. main-version ownership, non-main header consumption, and existing version modes.
Root application values produced through placement or later lifecycle inputs An assembled application path beginning with `config` does not by itself
are the distinct boundary choice recorded under Open Decisions. establish overlap with an actual source-document control block.
`extends` keeps its existing inheritance meaning, scope, and CLI restrictions. `extends` keeps its existing inheritance meaning, scope, and CLI restrictions.
Allowing `config` in application data does not make other invalid paths, unknown Allowing `config` in application data does not make other invalid paths, unknown
@@ -134,9 +154,9 @@ may accept an old block as data without executing its imports.
Directive-related paths in diagnostics and source evidence change to the new Directive-related paths in diagnostics and source evidence change to the new
spelling, such as `/config/imports/0`. Preserve authored locations, source IDs, spelling, such as `/config/imports/0`. Preserve authored locations, source IDs,
effective placement, history, sensitivity, and resource ownership. Application effective placement, history, sensitivity, and resource ownership. Application
values under nested `config` receive the same metadata and safe inspection as values under root or nested `config` receive the same metadata and safe
other ordinary fields, including explicit false, zero, empty, and null values inspection as other ordinary fields, including explicit false, zero, empty,
where supported by the schema. and null values where supported by the schema.
The Go package name, vanity module path, exported symbol names, The Go package name, vanity module path, exported symbol names,
`configkit:"secret"` struct tag, and library diagnostic prefix remain unchanged. `configkit:"secret"` struct tag, and library diagnostic prefix remain unchanged.
@@ -156,8 +176,8 @@ and version/control overlap detection as a coordinated change. Retain shared
helpers when their callers need the same rule; distinguish source-local control helpers when their callers need the same rule; distinguish source-local control
coordinates from effective application coordinates where their responsibilities coordinates from effective application coordinates where their responsibilities
differ. Do not carry a global name ban into a helper merely by replacing its differ. Do not carry a global name ban into a helper merely by replacing its
string constant. Resolve the application-root decision below before preparing string constant. Allow ordinary application-root `config` data through
the detailed implementation plan. placement and later lifecycle operations without re-extracting controls.
Keep the existing engine and facade boundaries. No configurable control name, Keep the existing engine and facade boundaries. No configurable control name,
new provider abstraction, or change to composition algorithms is needed. new provider abstraction, or change to composition algorithms is needed.
@@ -169,7 +189,8 @@ existing accepted decisions as historical records. Update applicable local
architecture-policy descriptions when the behavior lands. The current architecture-policy descriptions when the behavior lands. The current
[configuration reference](../config.md), public comments, and [configuration reference](../config.md), public comments, and
[consumer guide](../consumers/README.md) should explain the new syntax, [consumer guide](../consumers/README.md) should explain the new syntax,
per-source root boundary, and ordinary nested fields together. per-source root boundary, ordinary nested fields, and application-root data
created through non-document operations together.
Migrate maintained examples, fixtures, and behavioral expectations for genuine Migrate maintained examples, fixtures, and behavioral expectations for genuine
control blocks. Preserve literal `configkit` occurrences that identify the Go control blocks. Preserve literal `configkit` occurrences that identify the Go
@@ -197,6 +218,10 @@ behavioral coverage at the boundaries this change affects:
- Mounted inputs and relative destinations distinguish source roots from - Mounted inputs and relative destinations distinguish source roots from
application paths, including a relative destination beginning with `config`. application paths, including a relative destination beginning with `config`.
Collection registrations and nested version fields follow that distinction. Collection registrations and nested version fields follow that distinction.
- Placement, defaults, CLI overrides, and recorded resolution can produce
ordinary application-root `config` data. Collection declarations,
registrations, and inspection can address it. Source controls are still
extracted before mounting and are not rediscovered in the resulting data.
- Ordinary `configkit` and `_configkit` data no longer trigger controls or - Ordinary `configkit` and `_configkit` data no longer trigger controls or
name-based rejection. Genuine old control syntax is not executed as an alias. name-based rejection. Genuine old control syntax is not executed as an alias.
- Source-local directive evidence uses `/config/...`; ordinary application - Source-local directive evidence uses `/config/...`; ordinary application
@@ -204,42 +229,8 @@ behavioral coverage at the boundaries this change affects:
with final values. Existing inheritance and protected-version checks remain with final values. Existing inheritance and protected-version checks remain
effective. effective.
Add cases for the application-root outcome once selected. Verify behavior with Verify behavior with independent expectations, rather than a test that only
independent expectations, rather than a test that only checks a renamed checks a renamed constant. Reuse existing coverage for unchanged semantics, run
constant. Reuse existing coverage for unchanged semantics, run the appropriate the appropriate focused checks and everyday validation, and check documentation
focused checks and everyday validation, and check documentation links. No new links. No new release gate, security guarantee, or performance threshold follows
release gate, security guarantee, or performance threshold follows from this from this roadmap.
roadmap.
## Open Decisions
### May non-document operations produce application-root `config` data?
The agreed source-document rule settles authored input: root `config` is a
control block and nested `config` is data. It does not by itself settle whether
an effective root `/config` may be produced by `Input.Destination`, mounted
imports, typed defaults, CLI overrides, or application resolution.
**Recommendation:** Allow these operations to produce ordinary root application
data, following their existing schema and lifecycle rules. Recognize controls
only at source-document extraction. For example, a fragment containing
`timeout: 10s` could be placed at destination `[config]`; a CLI assignment to
`Keys("config", "timeout")` would address data rather than introduce a control.
Collection declarations and registrations could likewise address actual data
at that application path. This keeps the reservation strictly document-local,
avoids adding a separate schema or callback restriction, and treats later
operations as data processing rather than new document parsing. Inspection can
show a root `config` value without reinterpreting it; effective YAML already has
no reloadability guarantee. The cost is that the same textual path can describe
source controls or assembled application data, so documentation must distinguish
those coordinates clearly.
**Alternative:** Also reserve `/config` as an application-root namespace,
while allowing nested paths such as `/providers/primary/config`. This keeps
source-root and final-root vocabulary aligned. It requires defining consistent
behavior for destinations, collection selectors, CLI values, defaults,
resolution output, and relevant version paths; changing only the source parser
or CLI checks would leave gaps. It also restricts application data beyond the
agreed source-document reservation. Choose this alternative only as an explicit
additional policy decision, with its validation boundary defined before
implementation.