Document document-root config controls
This commit is contained in:
245
docs/roadmap/config-directives.md
Normal file
245
docs/roadmap/config-directives.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# Document-Root Configuration Controls
|
||||
|
||||
Status: proposed implementation of the agreed `config` spelling and
|
||||
document-root-only reservation. The remaining application-root boundary choice
|
||||
is recorded under Open Decisions.
|
||||
|
||||
Planning baseline: `1e69abc88cf748537cd7febc85e0743755fa2fc1`.
|
||||
|
||||
## Purpose
|
||||
|
||||
Use `config` for ConfigKit's document-level controls while allowing applications
|
||||
to use nested `config` fields for their own settings. An application that selects
|
||||
ConfigKit adopts one control syntax; it should not need a configurable keyword
|
||||
or a second configuration-processing pass.
|
||||
|
||||
This roadmap defines the target and the work needed to reach it. It does not
|
||||
change current behavior. The [configuration contract](../config.md) and public
|
||||
Go declarations remain authoritative until implementation lands.
|
||||
|
||||
## Current Baseline
|
||||
|
||||
ConfigKit uses a root `configkit` mapping for `imports` and `sequences` controls.
|
||||
The [directive implementation](../../internal/engine/directives.go) extracts
|
||||
those controls before source placement and composition. Nested occurrences of
|
||||
`configkit` are rejected, including within application mappings and sequences.
|
||||
Imports-disabled and selected-overlay paths have their own checks enforcing
|
||||
that reservation.
|
||||
|
||||
The reservation also reaches beyond parsing:
|
||||
|
||||
- [Destination validation](../../internal/engine/destination.go) rejects
|
||||
`configkit` in every segment. Its helpers also serve import destinations,
|
||||
collection declarations, and exact application identity registrations.
|
||||
- [CLI override validation](../../internal/engine/override.go) rejects the name
|
||||
in paths and recursively inside supplied values.
|
||||
- [Source-version processing](../../internal/engine/source_version.go) protects
|
||||
actual source-root controls from overlapping version declarations.
|
||||
- Source inspection records, diagnostics, examples, and documentation use
|
||||
source-local paths such as `/configkit/imports/0`.
|
||||
|
||||
A constant rename alone would retain the broad prohibition under a much more
|
||||
common application field name. The scope change is an essential part of this
|
||||
migration.
|
||||
|
||||
## Targeted End State
|
||||
|
||||
### One source-document control block
|
||||
|
||||
`config` replaces `configkit` as the exact, case-sensitive name of a control
|
||||
mapping at a source document's root:
|
||||
|
||||
```yaml
|
||||
config:
|
||||
imports:
|
||||
- path: fragments/provider.yml
|
||||
destination: [providers, primary, config]
|
||||
sequences:
|
||||
- destination: [jobs]
|
||||
key: id
|
||||
```
|
||||
|
||||
Recognize the block in each participating input document before mounting its
|
||||
application payload. This covers host files, native filesystem files, byte and
|
||||
reader input, mapping sources, imported fragments, underlays, and selected
|
||||
overlays through the existing loading paths. A mapping source's root is its
|
||||
source-document root even though it was not parsed from YAML.
|
||||
|
||||
Retain the existing control fields and their context-specific rules. Imports
|
||||
remain opt-in; sequence declarations remain available where currently supported;
|
||||
selected overlays continue to allow collection declarations and reject imports.
|
||||
Malformed blocks and unknown control fields remain errors under the existing
|
||||
diagnostic families. This change adds no shape-based fallback that treats an
|
||||
unrecognized root `config` as application data.
|
||||
|
||||
Remove the control block from the source payload as part of existing directive
|
||||
extraction. Placement, composition, inheritance, decoding, CLI assignment, and
|
||||
application callbacks retain their established order. They do not start another
|
||||
document-control discovery pass.
|
||||
|
||||
### Nested application fields
|
||||
|
||||
Below a source's root, `config` is ordinary application data. For example:
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
primary:
|
||||
config:
|
||||
timeout: 10s
|
||||
imports: application-owned-value
|
||||
```
|
||||
|
||||
Neither nested `config` nor the field named `imports` above acquires control
|
||||
semantics. Its contents follow normal schema, combination, presence, validation,
|
||||
and inspection rules. The same applies inside sequence records, overlays, and
|
||||
inherited application payloads. Fields inside the actual root control block
|
||||
continue to follow that block's grammar.
|
||||
|
||||
The boundary belongs to the source document, rather than its eventual mounting
|
||||
location. An imported file's own root `config` is still its control block even
|
||||
when that file becomes a nested mapping or a sequence item. To supply the
|
||||
provider settings in the first example, `fragments/provider.yml` can contain
|
||||
`timeout: 10s`; the destination places that payload under
|
||||
`/providers/primary/config`. Its filesystem paths still resolve from the
|
||||
declaring file under the existing resource-context rules.
|
||||
|
||||
### Consistent application-path handling
|
||||
|
||||
Allow nested application paths containing literal `config` in input placement,
|
||||
relative import destinations, collection declarations and registrations, CLI
|
||||
paths and values, version fields, and inspection queries, subject to each
|
||||
operation's existing rules. In particular, a relative destination beginning
|
||||
with `config` can legitimately refer to a nested application mapping after the
|
||||
parent destination is applied. Do not decide its meaning from that relative
|
||||
segment alone.
|
||||
|
||||
Protect actual source-root controls during source-version extraction while
|
||||
allowing version fields below an ordinary nested `config` mapping. Preserve
|
||||
main-version ownership, non-main header consumption, and existing version modes.
|
||||
Root application values produced through placement or later lifecycle inputs
|
||||
are the distinct boundary choice recorded under Open Decisions.
|
||||
|
||||
`extends` keeps its existing inheritance meaning, scope, and CLI restrictions.
|
||||
Allowing `config` in application data does not make other invalid paths, unknown
|
||||
typed fields, malformed controls, or overlapping version writes valid.
|
||||
|
||||
### Direct migration and observable records
|
||||
|
||||
Use one control spelling. `configkit` and the earlier `_configkit` spelling are
|
||||
ordinary application keys, with no compatibility alias or special legacy-key
|
||||
rejection. They are accepted or rejected by normal application schema rules.
|
||||
Consumers must update their actual control blocks; a permissive mapping schema
|
||||
may accept an old block as data without executing its imports.
|
||||
|
||||
Directive-related paths in diagnostics and source evidence change to the new
|
||||
spelling, such as `/config/imports/0`. Preserve authored locations, source IDs,
|
||||
effective placement, history, sensitivity, and resource ownership. Application
|
||||
values under nested `config` receive the same metadata and safe inspection as
|
||||
other ordinary fields, including explicit false, zero, empty, and null values
|
||||
where supported by the schema.
|
||||
|
||||
The Go package name, vanity module path, exported symbol names,
|
||||
`configkit:"secret"` struct tag, and library diagnostic prefix remain unchanged.
|
||||
This is a configuration-control syntax and reservation-scope migration.
|
||||
|
||||
## Work Needed From the Baseline
|
||||
|
||||
### Engine integration
|
||||
|
||||
Update directive extraction and imports-disabled/overlay checks together so
|
||||
they recognize source-root `config` and stop rejecting ordinary nested keys.
|
||||
Review the old recursive rejection helpers for removal or simplification where
|
||||
they no longer serve the selected rules.
|
||||
|
||||
Audit destination and collection-selector validation, CLI path/value checks,
|
||||
and version/control overlap detection as a coordinated change. Retain shared
|
||||
helpers when their callers need the same rule; distinguish source-local control
|
||||
coordinates from effective application coordinates where their responsibilities
|
||||
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
|
||||
the detailed implementation plan.
|
||||
|
||||
Keep the existing engine and facade boundaries. No configurable control name,
|
||||
new provider abstraction, or change to composition algorithms is needed.
|
||||
|
||||
### Documentation and migration
|
||||
|
||||
Record the document-root boundary and its rationale in a new ADR, keeping
|
||||
existing accepted decisions as historical records. Update applicable local
|
||||
architecture-policy descriptions when the behavior lands. The current
|
||||
[configuration reference](../config.md), public comments, and
|
||||
[consumer guide](../consumers/README.md) should explain the new syntax,
|
||||
per-source root boundary, and ordinary nested fields together.
|
||||
|
||||
Migrate maintained examples, fixtures, and behavioral expectations for genuine
|
||||
control blocks. Preserve literal `configkit` occurrences that identify the Go
|
||||
package, struct tag, diagnostic prefix, or ordinary application data. Include
|
||||
consumer guidance for renaming old controls, moving conflicting root application
|
||||
fields, and updating stored source-local directive paths. Historical tags and
|
||||
release records retain their original syntax.
|
||||
|
||||
Treat the syntax migration as a breaking pre-v1 change under the
|
||||
[release policy](../policy/release.md#versioning-and-compatibility), intended to
|
||||
land before v1.0.0. Explain it in that release's eventual upgrade guidance;
|
||||
this roadmap does not prepare or publish a release.
|
||||
|
||||
### Behavioral verification
|
||||
|
||||
Use the existing [test owners](../development.md#task-to-test-map) and extend
|
||||
behavioral coverage at the boundaries this change affects:
|
||||
|
||||
- New root controls work across the supported source forms, enabled imports,
|
||||
layered inputs, mounted records, and selected overlays. Invalid controls and
|
||||
imports-disabled behavior retain their established classifications.
|
||||
- Nested `config` values work with imports enabled and disabled, in mappings
|
||||
and sequence records, and through composition, overlays, inheritance, CLI,
|
||||
defaults, and recorded resolution. Control-looking children remain data.
|
||||
- Mounted inputs and relative destinations distinguish source roots from
|
||||
application paths, including a relative destination beginning with `config`.
|
||||
Collection registrations and nested version fields follow that distinction.
|
||||
- Ordinary `configkit` and `_configkit` data no longer trigger controls or
|
||||
name-based rejection. Genuine old control syntax is not executed as an alias.
|
||||
- Source-local directive evidence uses `/config/...`; ordinary application
|
||||
presence, origins, explanations, sensitivity, and resource contexts agree
|
||||
with final values. Existing inheritance and protected-version checks remain
|
||||
effective.
|
||||
|
||||
Add cases for the application-root outcome once selected. Verify behavior with
|
||||
independent expectations, rather than a test that only checks a renamed
|
||||
constant. Reuse existing coverage for unchanged semantics, run the appropriate
|
||||
focused checks and everyday validation, and check documentation links. No new
|
||||
release gate, security guarantee, or performance threshold follows from this
|
||||
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.
|
||||
Reference in New Issue
Block a user