Add single-source loading and version checks
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# Configuration Input Contract
|
||||
|
||||
This document owns ConfigKit's currently implemented source-input, YAML syntax,
|
||||
schema, typed-decoding, and ownership contracts. The parser, owned document
|
||||
representation, and decoder are private; a public typed loading lifecycle has not
|
||||
been implemented yet.
|
||||
schema, typed-decoding, loading, metadata, and ownership contracts. The parser,
|
||||
owned document representation, and decoder remain private behind the public loading
|
||||
lifecycle.
|
||||
|
||||
## Sources
|
||||
|
||||
@@ -14,7 +14,7 @@ ConfigKit exposes opaque sources through these constructors:
|
||||
| `File(path)` | Requires a nonempty path, opens a regular file for each consumption, and closes only the library-owned file handle. Missing, inaccessible, unreadable, directory, and other nonregular inputs fail. |
|
||||
| `Bytes(label, data)` | Requires a nonempty, non-secret diagnostic label and snapshots bytes during construction. Later caller mutation cannot affect the source. |
|
||||
| `Reader(label, input)` | Requires a nonempty, non-secret label and non-nil reader. The reader is claimed once across all copies of the source, consumed but never closed, and cannot be reused. |
|
||||
| `NoSource()` | Represents an explicit absence of input and creates no source record. Its defaults-only lifecycle behavior will become available with `Load`. |
|
||||
| `NoSource()` | Selects defaults-only loading, creates no source record, and marks no paths explicit. |
|
||||
|
||||
The zero `Source` is invalid. Constructor argument failures are retained inside the
|
||||
opaque source and become safe structured diagnostics when the source is consumed.
|
||||
@@ -25,6 +25,65 @@ and override input when those layers are implemented. Reads request at most the
|
||||
remaining allowance plus one byte so excess data is rejected rather than silently
|
||||
truncated. Data returned together with a read error is not accepted as partial input.
|
||||
|
||||
## Single-source loading
|
||||
|
||||
`Load[T](source, options...)` accepts zero or one `Options[T]`. The configuration
|
||||
root `T` must satisfy the application schema contract below. With no options it
|
||||
starts from the Go zero value. A `Definition[T].Defaults` factory, when supplied,
|
||||
runs exactly once after successful source parsing, schema construction, and optional
|
||||
schema-version checking. Its result is detached before input is applied.
|
||||
|
||||
The current lifecycle is:
|
||||
|
||||
```text
|
||||
validate options → parse one source → reject reserved directives → build schema
|
||||
→ check the optional application version → create and clone defaults
|
||||
→ strictly decode → snapshot configuration and metadata
|
||||
```
|
||||
|
||||
`File`, `Bytes`, and `Reader` each contribute one base source with the deterministic
|
||||
ID `source-1`. `NoSource` skips parsing, supplies an absent version to a configured
|
||||
version checker, contributes no source, and continues through defaults, decode, and
|
||||
projection. A missing file remains an error; it is never interpreted as no input.
|
||||
|
||||
The current `Options` boundary accepts a defaults factory and `VersionRule`.
|
||||
Imports, active profiles, filesystem context, overrides, resolver and semantic
|
||||
validator callbacks, and dynamic secret classification return an explicit
|
||||
`options.unavailable` error because their lifecycle stages are not implemented.
|
||||
The reserved profile spelling `default` selects no active profile. `NoSource`
|
||||
additionally rejects imports, active profiles, and filesystem context as invalid
|
||||
combinations. Input containing a reserved `_configkit` mapping key is rejected while
|
||||
imports are disabled.
|
||||
|
||||
Every source, options, schema, version, defaults, or decode failure returns a nil
|
||||
result and a `*LoadError`. Successful results contain caller-owned `Config`, detached
|
||||
`Metadata`, a defensive source list, and defensive nonfatal diagnostics. Mutating
|
||||
the returned configuration or inspection records cannot alter ConfigKit's private
|
||||
snapshot. ConfigKit performs no logging and exposes no effective writer yet.
|
||||
|
||||
### Application schema versions
|
||||
|
||||
`VersionRule` declares a semantic path and non-nil checker. Before defaults or typed
|
||||
decoding, the checker receives a `Version` preserving absent, explicit null, or
|
||||
string input. Any other input kind is a `version.type` error and the checker does not
|
||||
run. ConfigKit does not interpret the text or impose a universal version path.
|
||||
|
||||
The checker reports safe, value-free errors and warnings through `Validation.Error`
|
||||
and `Validation.Warning`; ConfigKit resolves primary and related paths to exact or
|
||||
nearest input locations. Reported errors fail loading. If the checker returns an
|
||||
error without reporting one, ConfigKit emits a generic `version.hook_failed`
|
||||
diagnostic and suppresses the returned error text. Warnings alone are retained on a
|
||||
successful result. Callback panics remain programming failures.
|
||||
|
||||
### Base/default metadata
|
||||
|
||||
`Result.Metadata` describes every final canonical path. Exact surviving input paths
|
||||
are explicit and have a base origin; omitted paths have a default origin. An explicit
|
||||
parent mapping does not make omitted children explicit. Null state and available
|
||||
source coordinates are retained. `History` records the Go-zero or application
|
||||
default baseline followed by exact base definitions, including equal-valued input.
|
||||
All metadata queries and returned nested records are detached.
|
||||
|
||||
## YAML documents
|
||||
|
||||
The private YAML boundary currently accepts exactly one nonempty document whose root
|
||||
@@ -144,8 +203,8 @@ Paired text scalars decode only from strings through `encoding.TextUnmarshaler`,
|
||||
codec failures are classified without exposing application error text or input
|
||||
values.
|
||||
|
||||
This decoder is currently an internal lifecycle foundation, not a public conversion
|
||||
API. Applications cannot call typed loading until `Load` is introduced.
|
||||
This decoder is available only through `Load`; ConfigKit exposes no generic document
|
||||
conversion API.
|
||||
|
||||
## Typed projection and ownership
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Development
|
||||
|
||||
This is the contributor entry point for ConfigKit. Read the task-specific owners
|
||||
below before making changes. The repository is in early implementation: it has a
|
||||
Go module and documented root package, but no loading behavior or executable
|
||||
examples yet. The [component overview](internal/overview.md) owns the implementation
|
||||
inventory.
|
||||
below before making changes. The repository is in early implementation: its public
|
||||
loader currently covers one strict source, typed defaults, schema-version checks,
|
||||
and base/default metadata. The [component overview](internal/overview.md) owns the
|
||||
implementation inventory.
|
||||
|
||||
## Initial orientation
|
||||
|
||||
|
||||
@@ -183,6 +183,15 @@ multi-error collection, and repeated-result isolation. Resolution, validation,
|
||||
consumer defaults, and all campaign/session algorithms remain assigned to later
|
||||
work.
|
||||
|
||||
Lifecycle integration record (2026-09-06): the public single-source `Load` boundary
|
||||
combines the already extracted labeled-source and strict-decoding mechanisms with
|
||||
Narratio's fresh-default and presence expectations. ConfigKit adds an application-
|
||||
defined pre-decode string-version check, defaults-only `NoSource` operation,
|
||||
detached base/default metadata and source reports, and explicit gates for every
|
||||
later lifecycle option. No application loader or production line was copied.
|
||||
Narratio's resolution, validation, import/profile, campaign/session, and command
|
||||
behavior remains assigned to its later owning work.
|
||||
|
||||
### Secrets bootstrap
|
||||
|
||||
Assigned to the explicit bootstrap operation:
|
||||
|
||||
@@ -16,12 +16,13 @@ module and package documentation boundary. Its implemented public primitives are
|
||||
enforcement, and structural comparison in `projection.go`; and
|
||||
- strict schema-driven decoding over detached defaults in `decode.go`, including
|
||||
mapping/sequence semantics, null and numeric policy, safe codec errors, presence,
|
||||
baseline/final trees, and initial default/input origins.
|
||||
baseline/final trees, and initial default/input origins; and
|
||||
- public single-source lifecycle coordination in `load.go`, version-report contexts
|
||||
in `validation.go`, and detached base/default metadata assembly in
|
||||
`load_metadata.go`.
|
||||
|
||||
The [configuration contract](../config.md) owns implemented input, schema, decoding,
|
||||
and ownership behavior. ConfigKit intentionally exposes no typed loading API yet;
|
||||
the decoder and its initial metadata artifacts remain private until that lifecycle
|
||||
exists.
|
||||
single-source lifecycle, version checking, metadata, and ownership behavior.
|
||||
|
||||
The source-extraction inventory records the application foundations assigned to
|
||||
future implementation work:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Status and ownership
|
||||
|
||||
Status: implementation in progress; Stages 1–6 are complete and later stages are
|
||||
Status: implementation in progress; Stages 1–7 are complete and later stages are
|
||||
pending.
|
||||
|
||||
Audience: a `gpt-5.6-terra` coding agent implementing one stage per prompt, in
|
||||
@@ -619,7 +619,37 @@ Acceptance:
|
||||
|
||||
## Stage 7 — Assemble single-source loading and schema-version checks
|
||||
|
||||
Status: pending.
|
||||
Status: complete.
|
||||
|
||||
Completion evidence (2026-09-06):
|
||||
|
||||
- Added the public generic `Load`, `Definition`, `Options`, `Result`, version,
|
||||
filesystem-context, override, validation-context, and reserved resolution
|
||||
declarations in [`load.go`](../../load.go) and [`validation.go`](../../validation.go).
|
||||
The active lifecycle validates options, parses one source, rejects reserved
|
||||
directives, builds the schema, checks an optional application version, creates
|
||||
fresh defaults, decodes, and captures detached state.
|
||||
- Added absent/null/string version handling, non-string rejection, exact/nearest
|
||||
primary and related input locations, safe application reports, warning retention,
|
||||
and generic value-free hook failure only when no explicit error was reported.
|
||||
Defaults run only after version success and exactly once for a continuing load.
|
||||
- Populated effective base/default `Metadata` and chronological definition history
|
||||
in [`load_metadata.go`](../../load_metadata.go), with explicit and null state,
|
||||
synthetic default origins, base coordinates, equal-valued input records, and
|
||||
defensive result/source/diagnostic copies. `NoSource` performs the same schema,
|
||||
version, defaults, decode, and projection lifecycle without a source record.
|
||||
- Added explicit `options.unavailable` gates for resolution, semantic validation,
|
||||
imports, active profiles, filesystem context, overrides, and secret classification;
|
||||
invalid `NoSource` combinations and `_configkit` directives fail rather than being
|
||||
ignored. The [current single-source contract](../config.md#single-source-loading),
|
||||
package documentation, README, and component inventory own the implemented API.
|
||||
- Integration tests cover File/Bytes/Reader/NoSource, schema-version states and
|
||||
warnings, related locations, raw hook-error suppression, fresh-default ordering,
|
||||
metadata agreement and detachment, source lists, unavailable options, reserved
|
||||
directives, and nil results for source/schema/version/decode failures. 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; resolution and semantic validation remain later work.
|
||||
|
||||
Read: feature [lifecycle](initial.md#resolution-lifecycle),
|
||||
[version hook](initial.md#schema-checking-and-secret-classification),
|
||||
|
||||
@@ -425,15 +425,16 @@ configuration is never returned as a successful, usable result.
|
||||
|
||||
Source ownership, the supported YAML subset, scalar grammar, fixed input limits,
|
||||
and safe failure behavior are implemented. The [current configuration input
|
||||
contract](../config.md) now owns those definitions. Typed scalar conversion into
|
||||
application values remains target work in [typed decoding](#typed-decoding).
|
||||
contract](../config.md) now owns those definitions, including typed scalar
|
||||
conversion through the current single-source lifecycle.
|
||||
|
||||
## Paths and presence
|
||||
|
||||
Semantic path construction, JSON Pointer parsing, and current metadata query
|
||||
declarations are implemented. Their current contracts are owned by the Go
|
||||
documentation in [`path.go`](../../path.go) and [`metadata.go`](../../metadata.go).
|
||||
The remaining presence behavior below is target lifecycle work.
|
||||
Semantic path construction, JSON Pointer parsing, and base/default metadata
|
||||
population are implemented. Their current contracts are owned by the Go
|
||||
documentation in [`path.go`](../../path.go), [`metadata.go`](../../metadata.go),
|
||||
and the [single-source configuration contract](../config.md#base-default-metadata).
|
||||
Layered replacement and resolution behavior below remains target lifecycle work.
|
||||
|
||||
Distinguish absent, explicitly null, explicitly zero/false/empty, and other
|
||||
explicit values. An explicit value equal to its default remains explicit.
|
||||
@@ -672,8 +673,11 @@ Automatic migration is deferred.
|
||||
|
||||
## Consumer API design
|
||||
|
||||
The following is a proposed public API, not implemented Go reference documentation
|
||||
or a frozen naming commitment. Declarations omit method bodies and private fields.
|
||||
The core declarations and single-source loading subset are implemented; their
|
||||
current contract is owned by [configuration input](../config.md#single-source-loading)
|
||||
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 private fields.
|
||||
Consumer snippets assume the indicated standard-library imports and a `configkit`
|
||||
import using the module path selected in the [implementation plan](implementation.md#implementation-decisions).
|
||||
They are illustrative target examples, not currently runnable examples. The
|
||||
@@ -682,6 +686,13 @@ and verification against consumer models.
|
||||
|
||||
### Entry point and reusable application definition
|
||||
|
||||
`Load`, `Definition`, `Options`, `FilesystemContext`, `Override`, `VersionRule`,
|
||||
`Version`, `Validation`, and `Result` are declared. The current loader accepts only
|
||||
the defaults and version-checking subset described by the
|
||||
[single-source contract](../config.md#single-source-loading); later-feature fields
|
||||
fail explicitly rather than being ignored. The complete lifecycle and option
|
||||
behavior below remains the target as its owning stages land.
|
||||
|
||||
```go
|
||||
func Load[T any](source Source, options ...Options[T]) (*Result[T], error)
|
||||
|
||||
@@ -795,11 +806,11 @@ func ReadWorkerDefaults() (*configkit.Result[Config], error) {
|
||||
|
||||
### Paths and metadata
|
||||
|
||||
The path API, detached metadata records, and empty/unknown query behavior are now
|
||||
implemented; [`path.go`](../../path.go), [`records.go`](../../records.go), and
|
||||
[`metadata.go`](../../metadata.go) own their current Go contracts. Populating
|
||||
effective presence, origins, and history belongs to the loading stages. Callback
|
||||
path checking and safe historical values remain target behavior below.
|
||||
The path API, detached metadata records, empty/unknown query behavior, and
|
||||
base/default population are now implemented; [`path.go`](../../path.go),
|
||||
[`records.go`](../../records.go), [`metadata.go`](../../metadata.go), and the
|
||||
[metadata configuration contract](../config.md#base-default-metadata) own current
|
||||
behavior. Layered callback history and safe historical values remain target behavior.
|
||||
|
||||
### Resolution and validation callbacks
|
||||
|
||||
@@ -970,6 +981,12 @@ potentially sensitive configured values.
|
||||
|
||||
### Schema checking and secret classification
|
||||
|
||||
`VersionRule`, `Version`, and the version-checking use of `Validation` are
|
||||
implemented. The [current version contract](../config.md#application-schema-versions)
|
||||
owns absent/null/string handling, report locations, warnings, failure behavior, and
|
||||
raw-error suppression. Secret classification and later composed-source placement
|
||||
checks remain target work.
|
||||
|
||||
```go
|
||||
type VersionRule struct {
|
||||
Path Path
|
||||
@@ -1030,6 +1047,12 @@ applications own secret-producing transformations not described by those records
|
||||
|
||||
### Results, errors, and inspection
|
||||
|
||||
`Result.Config`, `Metadata`, `Sources`, and `Diagnostics`, together with
|
||||
`LoadError` and `WriteDiagnostics`, are implemented at the single-source boundary.
|
||||
The [current loading contract](../config.md#single-source-loading) and Go reference
|
||||
comments own their behavior. Effective rendering and richer layered inspection
|
||||
below remain target work.
|
||||
|
||||
```go
|
||||
type Result[T any] struct {
|
||||
Config T
|
||||
|
||||
Reference in New Issue
Block a user