Apply explicit CLI configuration overrides
This commit is contained in:
@@ -41,7 +41,8 @@ The current lifecycle is:
|
||||
```text
|
||||
validate options → parse the selected source → traverse/compose enabled imports
|
||||
→ build schema → check the optional application version → apply a selected profile
|
||||
→ create and clone defaults → strictly decode → recorded application resolution
|
||||
→ apply explicit CLI overrides → create and clone defaults → strictly decode
|
||||
→ recorded application resolution
|
||||
→ resolve typed secret references → semantic validation → classify secrets
|
||||
→ snapshot configuration and metadata
|
||||
```
|
||||
@@ -54,11 +55,11 @@ missing file remains an error; it is never interpreted as no input.
|
||||
|
||||
The current `Options` boundary accepts a defaults factory, `VersionRule`, resolver,
|
||||
semantic validator, explicit path and glob imports, one selected profile, and
|
||||
filesystem context. It also accepts static secret tags and a dynamic path predicate.
|
||||
Overrides return `options.unavailable` because their lifecycle stage is not
|
||||
implemented. `NoSource` rejects imports, active profiles, and filesystem context as
|
||||
invalid combinations. Input containing a reserved `_configkit` mapping key is
|
||||
rejected while imports are disabled.
|
||||
filesystem context. It also accepts explicit CLI override records, static secret
|
||||
tags, and a dynamic path predicate. `NoSource` rejects imports, active profiles,
|
||||
and filesystem context as invalid combinations but accepts overrides. Input
|
||||
containing a reserved `_configkit` mapping key is rejected while imports are
|
||||
disabled.
|
||||
|
||||
Every source, options, schema, version, defaults, decode, resolution, or validation
|
||||
failure returns a nil result and a `*LoadError`. Successful results contain caller-owned `Config`, detached
|
||||
@@ -106,8 +107,8 @@ by the profile location; an empty mapping is still an explicit container even th
|
||||
it contributes no children. Resolution origins are synthetic: they have layer
|
||||
`resolution`, no fabricated file location, and a `derived_default` or `normalization`
|
||||
operation. Reasons and dependency paths retain first occurrence order with duplicates
|
||||
removed. Source records remain ordered as selected source, depth-first imports, then
|
||||
the selected profile.
|
||||
removed. Source records remain ordered as selected source, depth-first imports, the
|
||||
selected profile, then synthetic CLI records in supplied order.
|
||||
|
||||
### Secret classification and effective rendering
|
||||
|
||||
@@ -354,6 +355,54 @@ version, or replace an ancestor of that version with a scalar or null. These rul
|
||||
apply even if the supplied version text would be unchanged. Profiles cannot mask a
|
||||
base parse, composition, or version failure because those checks run first.
|
||||
|
||||
## Explicit CLI overrides
|
||||
|
||||
`Options.Overrides` accepts only records the application knows were explicitly set
|
||||
by its CLI parser. Each `Override` supplies a non-root mapping `Path`, a detached
|
||||
`Value`, and a required safe display `Flag` such as `--workers`; ConfigKit has no CLI
|
||||
parser dependency. Parser defaults must not be submitted as overrides, and `Flag`
|
||||
must never contain the raw argument or configured value. General environment
|
||||
variable overlays are an explicit non-goal: environment access is reserved for
|
||||
typed secret references.
|
||||
|
||||
Overrides apply after the selected profile and before typed defaults/decoding, so
|
||||
the input precedence is application defaults < composed base < selected profile <
|
||||
CLI. Recorded resolution, typed secret lookup, and semantic validation follow the
|
||||
effective override tree. Overrides also work with `NoSource`.
|
||||
|
||||
Values are converted directly into ConfigKit's owned tree without YAML parsing or
|
||||
string coercion. Supported values are `nil`, built-in strings and booleans, all
|
||||
built-in signed and unsigned integer widths except `uintptr`, finite `float32` and
|
||||
`float64`, and recursive `[]any`/`map[string]any`. Strings stay strings; duration and
|
||||
secret fields therefore receive string values. Nil is explicit null; empty strings,
|
||||
false, and zero remain intentional; empty sequences replace whole lists; and mapping
|
||||
values recursively overlay existing mappings. Map keys are sorted canonically and
|
||||
sequence order is preserved.
|
||||
|
||||
All records are detached and validated before any is applied. 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
|
||||
ordinary strict-decoder errors.
|
||||
|
||||
At most 1,024 records are accepted, and each record creates one synthetic CLI
|
||||
source after file/profile sources in supplied order. Override sources share the
|
||||
1,024-source ceiling. A record has an 8 MiB string-byte limit; override strings and
|
||||
source bytes share the 64 MiB aggregate limit. Synthetic mapping keys and values
|
||||
count toward the existing 1,000,000-node and depth-256 ceilings. Cycles, named or
|
||||
unsupported types, nonfinite floats, and limit excesses fail with safe `override.*`
|
||||
diagnostics. Diagnostics and provenance identify the safe `Flag` source, never raw
|
||||
arguments or values.
|
||||
|
||||
Override origins are explicit CLI origins. History retains replaced base/profile
|
||||
definitions and records equal-value replacements as well as structural changes.
|
||||
Caller-owned maps and slices are snapshotted during loading and must not be mutated
|
||||
concurrently with the call. The executable `ExampleOverride` shows translating a
|
||||
parser's explicit-set state into records while preserving an explicitly supplied
|
||||
zero.
|
||||
|
||||
## Explicit imports
|
||||
|
||||
Imports are opt-in. With `Options.Imports` enabled, a document may reserve its root
|
||||
|
||||
@@ -4,7 +4,8 @@ This is the contributor entry point for ConfigKit. Read the task-specific owners
|
||||
below before making changes. The repository is in early implementation: its public
|
||||
loader currently covers strict selected sources, explicit path/glob import graphs,
|
||||
composition, selected profile overlays, typed defaults, schema-version checks,
|
||||
recorded application resolution, semantic validation, layered metadata, secret
|
||||
explicit CLI overrides, recorded application resolution, semantic validation,
|
||||
layered metadata, secret
|
||||
classification, typed environment references, explicit secrets bootstrap, and
|
||||
redacted effective rendering. The
|
||||
[component overview](internal/overview.md) owns the implementation inventory.
|
||||
|
||||
@@ -15,6 +15,8 @@ module and package documentation boundary. Its implemented public primitives are
|
||||
conflicting location, and never aliases an input tree;
|
||||
- fixed selected-profile loading in `profile.go` and recursive, non-aliasing overlay
|
||||
with displacement records in `overlay.go`;
|
||||
- detached parser-neutral CLI override validation and synthetic-source application
|
||||
in `override.go`, sharing overlay, source, byte, node, and depth policies;
|
||||
- a per-operation private reflection descriptor graph in `schema.go` for supported
|
||||
types, YAML field binding, inlining, secret tags, recursion, and semantic paths;
|
||||
- descriptor-driven canonical projection, detached typed cloning, cycle/limit
|
||||
@@ -41,7 +43,7 @@ module and package documentation boundary. Its implemented public primitives are
|
||||
only upstream dumper integration isolated in `internal/yaml/write.go`.
|
||||
|
||||
The [configuration contract](../config.md) owns implemented input, schema, decoding,
|
||||
loading lifecycle, path imports, composition, profile overlay, version checking,
|
||||
loading lifecycle, path imports, composition, profile/CLI overlay, version checking,
|
||||
recorded resolution, typed secret references/bootstrap, semantic validation,
|
||||
metadata, secret classification, redacted effective rendering, and ownership
|
||||
behavior.
|
||||
|
||||
@@ -1301,7 +1301,41 @@ Acceptance:
|
||||
|
||||
## Stage 18 — Apply explicit CLI overrides above profiles
|
||||
|
||||
Status: pending.
|
||||
Status: complete.
|
||||
|
||||
Completion evidence (2026-09-06):
|
||||
|
||||
- Activated the existing `Override`/`Options.Overrides` surface through the new
|
||||
parser-neutral implementation in [`override.go`](../../override.go). It accepts
|
||||
only built-in scalar values, `nil`, `[]any`, and `map[string]any`; preserves
|
||||
scalar kinds and sequence order; sorts maps; rejects named/unsupported values,
|
||||
nonfinite floats and cycles; and snapshots all accepted caller-owned state without
|
||||
YAML reparsing or textual inference.
|
||||
- Added full-record preflight for safe flag labels, non-root mapping paths,
|
||||
sequence traversal, existing non-mapping ancestors, duplicates/overlaps,
|
||||
`_configkit`, schema-version overlap, and fixed record/source/string-byte/node/
|
||||
depth budgets. Valid records create deterministic synthetic CLI sources after
|
||||
file and profile sources; failures identify those safe flag sources without raw
|
||||
arguments or values.
|
||||
- Generalized the existing non-aliasing overlay machinery across profile and CLI
|
||||
layers. Overrides apply after the selected profile and before defaults/typed
|
||||
decode, create missing mapping ancestors, recursively overlay mappings, replace
|
||||
sequences atomically, and retain detached before/after snapshots plus displaced
|
||||
origins even for equal-value operations. Layered history reconstruction now
|
||||
preserves default/base/profile/CLI order and parent operations for descendants.
|
||||
- Added lifecycle and boundary coverage in
|
||||
[`override_test.go`](../../override_test.go) for defaults < imports/base < profile
|
||||
< CLI < recorded normalization, followed by secret lookup and validation; explicit
|
||||
zero/false/empty/list/null; defaults-only loads; map merge, whole-list replacement,
|
||||
parsed numeric map keys, caller mutation, equal replacements, safe decode source
|
||||
attribution, invalid paths/ancestors/overlaps/directives/version writes, value
|
||||
types/cycles/nonfinite values, and bounds. Added executable `ExampleOverride` and
|
||||
moved the normative behavior into the
|
||||
[current override contract](../config.md#explicit-cli-overrides), explicitly
|
||||
retaining general environment overlays as a non-goal. Verified `go test ./...`,
|
||||
`go test -race ./...`, `go vet ./...`, `go build ./...`, `go mod tidy -diff`, and
|
||||
`git diff --check` with Go 1.26.7 on linux/amd64. No deviations were required;
|
||||
safe historical-value explanation remains later work.
|
||||
|
||||
Read: [override contract](initial.md#cli-overrides), overlay/presence/version rules,
|
||||
source ordering, and the secret-reference boundary.
|
||||
|
||||
Reference in New Issue
Block a user