Add selected profile overlays
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Status and ownership
|
||||
|
||||
Status: implementation in progress; Stages 1–9 are complete and later stages are
|
||||
Status: implementation in progress; Stages 1–13 are complete and later stages are
|
||||
pending.
|
||||
|
||||
Audience: a `gpt-5.6-terra` coding agent implementing one stage per prompt, in
|
||||
@@ -982,7 +982,31 @@ Acceptance:
|
||||
|
||||
## Stage 13 — Implement profile overlays
|
||||
|
||||
Status: pending.
|
||||
Status: complete.
|
||||
|
||||
Completion evidence (2026-09-06):
|
||||
|
||||
- Added a private non-aliasing recursive overlay that merges mappings by key and
|
||||
atomically replaces scalars, sequences, empty sequences, nulls, and cross-kind
|
||||
subtrees. Replacement deletes stale descendant origins and records every displaced
|
||||
definition plus the parent operation for later history.
|
||||
- Added exact selected-profile loading from `profiles/<name>.yml`, with constrained
|
||||
names, inactive empty/`default` selection, canonical root containment, base-graph
|
||||
duplicate rejection, deterministic profile source ownership, and shared aggregate
|
||||
source/byte/node limits. No path selection, extension fallback, inheritance, or
|
||||
implicit default file is supported.
|
||||
- Applied profiles only after base import composition and version checking and before
|
||||
defaults and strict decoding. Profiles reject `_configkit` at any depth, direct
|
||||
version contributions, and scalar/null replacements of version ancestors; base
|
||||
parse and version failures therefore cannot be masked.
|
||||
- Tests cover recursive siblings, scalar/full/empty-sequence/null/cross-kind
|
||||
replacement, lower defaults, profile source and origin attribution, removed-child
|
||||
and stale-index history, selection errors, fixed extension, directives, version
|
||||
placement, base aliases, parse ordering, filesystem context, symlink escape, input
|
||||
budgets, and non-aliasing. The
|
||||
[current profile contract](../config.md#selected-profiles) owns the behavior.
|
||||
Verified `go test ./...`, `go test -race ./...`, `go vet ./...`, `go build ./...`,
|
||||
and `git diff --check` with Go 1.26.7 on linux/amd64.
|
||||
|
||||
Read: feature [profiles](initial.md#profiles), composition/overlay table.
|
||||
|
||||
|
||||
@@ -450,28 +450,9 @@ origin is a resolution operation, with input origins retained separately.
|
||||
|
||||
## Composition and overlay
|
||||
|
||||
Strict base composition is implemented and now owned by the
|
||||
[current configuration contract](../config.md#strict-document-composition).
|
||||
Intentional profile overlay remains target behavior. Its exhaustive operation table
|
||||
is below; “value” includes scalar, sequence, mapping, and null, while absent means no
|
||||
node rather than null.
|
||||
|
||||
| Existing/lower node | Incoming/higher node | Overlay |
|
||||
| --- | --- | --- |
|
||||
| Absent | Absent | Absent |
|
||||
| Absent | Value | Incoming |
|
||||
| Value | Absent | Existing |
|
||||
| Mapping | Mapping | Recurse by key |
|
||||
| Any other pair of present nodes | Any other pair of present nodes | Replace entire existing node |
|
||||
|
||||
A mapping can replace a lower scalar, sequence, or null during overlay, subject to
|
||||
the final schema. Sequences are atomic and the higher sequence replaces the entire
|
||||
lower sequence in an overlay. `[]` clears a sequence. There is no append, union,
|
||||
index merge, or mapping-clear operator.
|
||||
|
||||
Replacing a parent must not leave effective metadata for removed children.
|
||||
Sequence histories describe whole-sequence replacement; index origins refer to
|
||||
the surviving sequence, not an attempted correspondence with previous indices.
|
||||
Strict base composition and selected-profile overlay are implemented and owned by
|
||||
the [current composition contract](../config.md#strict-document-composition) and
|
||||
[profile contract](../config.md#selected-profiles).
|
||||
|
||||
## Explicit imports and source identity
|
||||
|
||||
@@ -481,18 +462,9 @@ filesystem context, and budgets are implemented and now owned by the
|
||||
|
||||
## Profiles
|
||||
|
||||
Load only the composed base unless the application selects a profile. The API
|
||||
accepts a name matching `[a-zA-Z0-9_-]+`, not an arbitrary path. The empty name
|
||||
and reserved lowercase name `default` both mean no overlay; a literal
|
||||
`profiles/default.yml` is never loaded. Other names resolve exactly to
|
||||
`<configuration-root>/profiles/<name>.yml`, without extension fallback or case
|
||||
normalization. A selected missing profile is an error.
|
||||
|
||||
A profile is one policy-validated YAML mapping applied using the overlay table.
|
||||
It must not contain `_configkit`, introduce imports, or alter the application's
|
||||
declared schema-version path. Reject these directives even if their values
|
||||
would be unchanged. There is no profile inheritance or multiple-profile stack.
|
||||
Applications own flags and commands that select this behavior.
|
||||
Selected profiles are implemented and owned by the
|
||||
[current profile contract](../config.md#selected-profiles). Applications continue
|
||||
to own the flags and commands that choose a profile name.
|
||||
|
||||
## Static defaults and null
|
||||
|
||||
@@ -597,8 +569,8 @@ Automatic migration is deferred.
|
||||
|
||||
## Consumer API design
|
||||
|
||||
The core declarations, callback lifecycle, strict composition, and explicit path
|
||||
imports are implemented; their current contract is owned by
|
||||
The core declarations, callback lifecycle, strict composition, explicit imports,
|
||||
and selected profiles are implemented; their current contract is owned by
|
||||
[configuration input](../config.md) and Go reference comments. Remaining declarations
|
||||
and behavior in this section are target design, not implemented Go reference
|
||||
documentation or a frozen naming commitment. Declarations omit method bodies and
|
||||
@@ -614,7 +586,7 @@ and verification against consumer models.
|
||||
`Load`, `Definition`, `Options`, `FilesystemContext`, `Override`, `VersionRule`,
|
||||
`Version`, `Validation`, and `Result` are declared. The current loader supports the
|
||||
callback and path-import subset described by the [current contract](../config.md);
|
||||
later profile, override, secret, and rendering behavior below remains target work and
|
||||
later override, secret, and rendering behavior below remains target work and
|
||||
unavailable options fail explicitly rather than being ignored.
|
||||
|
||||
```go
|
||||
@@ -660,8 +632,8 @@ ConfigKit does not synchronize application-owned captured state.
|
||||
`Source`, its constructors, and import filesystem context are implemented;
|
||||
[`source.go`](../../source.go) and the [configuration input contract](../config.md#sources)
|
||||
own their current behavior. Applications must use non-secret source labels because
|
||||
labels appear in diagnostics and source reports. Profile-related filesystem behavior
|
||||
remains target work. Filesystem options do not change application field path meanings.
|
||||
labels appear in diagnostics and source reports. Filesystem options do not change
|
||||
application field path meanings.
|
||||
|
||||
`NoSource()` explicitly selects defaults-only loading. It contributes no document
|
||||
or file source entry and marks no fields explicit by itself; CLI overrides still
|
||||
|
||||
Reference in New Issue
Block a user