Document source-root configuration controls

This commit is contained in:
2026-11-02 00:45:36 +00:00
parent e5475ea7f8
commit 68ec552b52
8 changed files with 73 additions and 34 deletions

View File

@@ -356,7 +356,7 @@ independent of where the source file lives on disk. Consider this file layout:
With imports enabled, `config.yml` can contain:
```yaml
configkit:
config:
imports:
- path: fragments/prompt.yml
destination: [prompts, welcome]
@@ -383,10 +383,10 @@ existing inputs keep their behavior. Main inputs and underlay inputs use the
same destination model. Destination segments are literal mapping keys: dots,
slashes, and numeric-looking text are not split or interpreted as sequence
indexes. Explicit index segments and paths that introduce the reserved
`configkit` or mapping-valued `extends` structure are invalid input options and
mapping-valued `extends` structure are invalid input options and
produce a structured `LoadError` before source acquisition.
Source-root `configkit.imports` directives are processed before placement. An
Source-root `config.imports` directives are processed before placement. An
import without an explicit destination inherits the effective destination of
its importing document, including through nested imports. An import may instead
declare a relative destination as a sequence of literal mapping keys; its keys
@@ -449,7 +449,7 @@ child uses the parent's settings that remain after that combination.
An identified list uses a mapping reference instead of a sibling name:
```yaml
configkit:
config:
sequences:
- destination: [presets]
key: id
@@ -468,7 +468,7 @@ Any input mapping may declare `extends: name` to inherit from the mapping named
`name` among its immediate siblings. Names are exact keys, including the empty
string; lookup does not search ancestor or unrelated scopes. A mapping inside a
sequence can also participate in record inheritance when that sequence has an
effective identity declaration from `configkit.sequences` or an application
effective identity declaration from `config.sequences` or an application
registration. In that case, `extends` is a mapping containing exactly the
sequence's identity fields, with scalar values; lookup is limited to that final
sequence. The document root and a sequence-element mapping without an effective
@@ -683,7 +683,8 @@ each fragment conforms to a complete application schema. Final assembled
decoding and application validation still run once.
A header that is absent below a structural control follows the same omission
policy. Actual header/control overlap is rejected, and ordinary structural
policy. Actual source-local header/control overlap is rejected; an application
path beginning with `config` alone does not imply overlap. Ordinary structural
validation and main-version ownership protection continue independently.
Accepted non-main declarations are consumed from their owned payload before
@@ -1292,7 +1293,7 @@ tell which records refer to the same entry. Declare the identity and desired
operation at the source root, for example:
```yaml
configkit:
config:
sequences:
- destination: [records]
key: id
@@ -1330,7 +1331,7 @@ lists replace whole lower lists.
mapping-only `Path`; the matching form accepts literal-key and any-mapping-key
segments created by `SequenceLiteralKey` and `SequenceAnyMappingKey`. A literal
`"*"` key is distinct from the wildcard constructor. Parsed numeric path tokens
remain literal mapping keys; explicit index segments and reserved `configkit` or
remain literal mapping keys; explicit index segments and reserved
mapping-valued `extends` segments are invalid. The root selector is harmless under
the mapping-root schema. Selector segments consume one mapping level and match a
complete destination; an any-mapping-key segment matches exactly one key, not an
@@ -1384,7 +1385,7 @@ completed configuration.
### YAML declarations and policy lifetime
Ordinary sources, layered inputs, and selected overlays may declare collection
behavior under `configkit.sequences` at the document root. Each entry has a
behavior under `config.sequences` at the document root. Each entry has a
required literal `destination` and may specify `key` as one string or a nonempty
list of strings, plus a `mode` of `replace`, `append`, or `upsert`. These
controls are removed before application decoding. A declaration destination is
@@ -1450,7 +1451,7 @@ policies follow their owning records: retaining a record retains its rules, whil
whole-record upsert replacement discards the displaced record's rules. Matching
identity or reusing an index does not transfer them. These policies do not change
CLI behavior: CLI sequence assignments replace whole sequences after decoding.
A root `configkit.imports` declaration still requires `Options.Imports`;
A root `config.imports` declaration still requires `Options.Imports`;
`NoSource` has no declaration scope.
An empty mapping contributes no child definitions. Two empty mappings compose, and
@@ -1503,8 +1504,8 @@ origin data; history retains the displaced definitions and identifies the parent
replacement. Surviving overlay paths have overlay origins, while recursively shared
mapping containers retain their contributing locations.
An overlay may contain only root `configkit.sequences` declarations; overlay
imports and nested directives are rejected. Sequence declarations are removed
An overlay may contain only root `config.sequences` declarations; overlay
imports are rejected and nested `config` fields are ordinary data. Sequence declarations are removed
before application decoding. At declared or registered sequence destinations,
`replace`, `append`, and `upsert` use the same identity and policy rules as source
composition. Other overlay sequences remain atomic replacements. An overlay may
@@ -1544,12 +1545,12 @@ values recursively overlay existing mappings. Map keys are sorted canonically an
sequence order is preserved.
All records are detached and validated before any is applied. They cannot create
`configkit` or `extends` controls. Root and explicit
`extends` controls. Root and explicit
sequence-index paths are invalid; parsed numeric tokens remain mapping keys. A path
may create missing mapping ancestors but cannot traverse an existing scalar, null,
or sequence. Duplicate and ancestor/descendant-overlapping records are rejected.
Neither paths nor values may create `configkit`, and no record may overlap the
configured schema-version path. Unknown fields and effective type mismatches remain
Paths and values may contain ordinary `config` and `configkit` data; no record may
overlap the configured schema-version path. Unknown fields and effective type mismatches remain
ordinary strict-decoder errors.
At most 1,024 records are accepted, and each record creates one synthetic CLI
@@ -1582,7 +1583,7 @@ files, not three declarations that must be used together:
Named file:
```yaml
configkit:
config:
imports:
- path: fragments/service.yml
```
@@ -1590,7 +1591,7 @@ configkit:
Directory search, restricted here to YAML filenames:
```yaml
configkit:
config:
imports:
- dir: fragments
pattern: "*.yml"
@@ -1599,7 +1600,7 @@ configkit:
Path pattern:
```yaml
configkit:
config:
imports:
- glob: "fragments/*.yml"
```
@@ -1621,7 +1622,7 @@ rules below explain absence, patterns, placement, and ordering.
### Declaration and selection rules
Imports are opt-in. With `Options.Imports` enabled, a document may use its root
`configkit` mapping for `imports` and `sequences` controls. Ordinary `Load` may use
`config` mapping for `imports` and `sequences` controls. Ordinary `Load` may use
`sequences` without enabling imports, but an `imports` field still requires
`Options.Imports`. ConfigKit removes recognized root controls before application
decoding. Each imports sequence item is a mapping with exactly one `path`,
@@ -1633,9 +1634,26 @@ removed `optional` field is invalid. `destination` is an optional sequence of
strings. Every destination string is one literal mapping key and follows the
input-destination validation rules above; quote numeric-looking keys so YAML
reads them as strings. Null, scalar destinations, and non-string members are
`import.invalid`, as are all other unsupported shapes and fields. A `configkit`
key below the root is invalid. With imports disabled, root imports and nested
`configkit` keys remain `import.disabled`.
`import.invalid`, as are all other unsupported shapes and fields. Nested `config`
keys are ordinary application data. With imports disabled, root imports remain
`import.disabled`.
Each acquired document has its own root, including imported files mounted below
another value or as sequence items. Controls are extracted before placement;
assembled application-root `config` data from placement, defaults, CLI, or
resolution is not reinterpreted. For example:
```yaml
config:
sequences: []
service:
config:
imports: application-data
```
The [mounted-root example](../examples/consumers/config_controls_test.go) shows
an imported payload becoming application-root `config`. Inspection YAML can
contain that data and retains its existing lack of a reloadability guarantee.
Path declarations use slash-separated relative paths. Absolute paths and network
URLs are invalid. Each path resolves from its declaring file's canonical directory.
@@ -1679,8 +1697,8 @@ the supplied pointers and slice before source acquisition.
The Go fields are named `FollowSymlinks` and `IncludeHidden`; the YAML keys
remain `follow` and `hidden`.
A source's `configkit.sequences` declarations apply independently of import
enablement; a `configkit.imports` declaration still requires `Options.Imports`.
A source's `config.sequences` declarations apply independently of import
enablement; a `config.imports` declaration still requires `Options.Imports`.
Sequence identities can be supplied either by these YAML declarations or by
application registrations. The [strict composition contract](#strict-document-composition)
defines their agreement, operation, and lifetime rules.
@@ -1800,11 +1818,13 @@ named `conf.d`, and ConfigKit performs no remote fetching.
### Earlier directive spelling
The directive key is `configkit`, not the `_configkit` spelling used in v0.1.0.
There is no legacy alias: `_configkit` is ordinary application data, subject to
normal schema validation, and does not trigger imports. When upgrading, rename
directive keys in root files and imported fragments, and rename any application
fields previously named `configkit` to avoid the newly reserved key.
The directive key is `config`. Neither `configkit` nor `_configkit` is an alias:
both are ordinary application data subject to normal schema validation and do
not trigger imports. Rename genuine old control blocks in each source; move,
rename, or separately mount source-root application fields named `config`.
Stored source-local declaration paths change from `/configkit/...` to
`/config/...`. Permissive schemas may decode old blocks as data without
executing their imports.
## YAML documents