Build reusable schema descriptors
This commit is contained in:
@@ -69,6 +69,41 @@ boolean/null words are rejected unless quoted.
|
||||
Mapping keys use the same scalar classification and must classify as strings. Quote
|
||||
keys that otherwise classify as a boolean, number, or null.
|
||||
|
||||
## Application schema binding
|
||||
|
||||
ConfigKit builds a private reflection descriptor graph for each schema operation;
|
||||
it keeps no global mutable descriptor cache. A configuration root must be a struct
|
||||
or string-keyed map. Nested supported shapes are:
|
||||
|
||||
- booleans, strings, signed and unsigned integers, and finite floating-point types;
|
||||
- `time.Duration` and paired `encoding.TextMarshaler`/`TextUnmarshaler` scalars;
|
||||
- pointers, structs, string-keyed maps, slices, fixed arrays, and `any`; and
|
||||
- recursive type definitions whose eventual application values are acyclic.
|
||||
|
||||
Named scalar types retain their Go identity. `uintptr`, complex numbers, functions,
|
||||
channels, unsafe pointers, non-string map keys, and nonempty interfaces are not
|
||||
configuration types. A custom text scalar must provide both directions on its value
|
||||
or pointer method set. Building a descriptor only inspects method sets; it never
|
||||
executes application methods or parser-specific hooks.
|
||||
|
||||
Exported struct fields use an explicit `yaml` name or the lowercase Go field name.
|
||||
Unexported fields and `yaml:"-"` are ignored. `omitempty` is accepted but does not
|
||||
change binding or future projection. An explicit `yaml:",inline"` flattens a struct
|
||||
or pointer-to-struct, or designates at most one string-keyed inline map for otherwise
|
||||
unmatched names. Ambiguous names, named inline fields, multiple inline maps, and
|
||||
unsupported or malformed tag options are schema errors. Embedded fields are ordinary
|
||||
named fields unless explicitly inlined. `configkit:"secret"` is the only current
|
||||
ConfigKit field tag and records sensitivity for later lifecycle work.
|
||||
|
||||
Schema path traversal uses these same bindings. Explicit key segments address
|
||||
mappings, explicit index segments address sequences, and parsed numeric pointer
|
||||
tokens become indices only in a sequence. Sequence indices cannot have leading
|
||||
zeroes; fixed-array indices are bounds checked. Dynamic `any` and inline-map entries
|
||||
remain schema-valid for application-defined descendants.
|
||||
|
||||
Descriptors do not yet decode documents, clone defaults, or project application
|
||||
values. Those operations will reuse this graph as their implementation lands.
|
||||
|
||||
## Safe failures
|
||||
|
||||
Source failures use stable `source.*` codes and YAML failures use stable `yaml.*`
|
||||
|
||||
@@ -9,7 +9,9 @@ module and package documentation boundary. Its implemented public primitives are
|
||||
- opaque file, byte, reader, and no-input source construction and bounded reads;
|
||||
- a parser-independent ordered tree in `internal/document`; and
|
||||
- the sole upstream YAML integration in `internal/yaml`, which translates validated
|
||||
input into that owned tree and exposes no parser types outside its boundary.
|
||||
input into that owned tree and exposes no parser types outside its boundary; and
|
||||
- a per-operation private reflection descriptor graph in `schema.go` for supported
|
||||
types, YAML field binding, inlining, secret tags, recursion, and semantic paths.
|
||||
|
||||
The [configuration input contract](../config.md) owns implemented source and YAML
|
||||
behavior. ConfigKit intentionally exposes no typed loading or decoding API yet.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Status and ownership
|
||||
|
||||
Status: implementation in progress; Stages 1–3 are complete and later stages are
|
||||
Status: implementation in progress; Stages 1–4 are complete and later stages are
|
||||
pending.
|
||||
|
||||
Audience: a `gpt-5.6-terra` coding agent implementing one stage per prompt, in
|
||||
@@ -463,7 +463,27 @@ Acceptance:
|
||||
|
||||
## Stage 4 — Build the reusable schema binding graph
|
||||
|
||||
Status: pending.
|
||||
Status: complete.
|
||||
|
||||
Completion evidence (2026-09-06):
|
||||
|
||||
- Implemented a per-operation private descriptor graph in [`schema.go`](../../schema.go)
|
||||
for structural roots, ordinary/named scalars, duration and paired text scalars,
|
||||
pointers, structs, string-keyed maps, slices, arrays, dynamic values, and recursive
|
||||
definitions. No global cache or application method execution is involved.
|
||||
- Implemented declared-field YAML binding, ignored/private fields, explicit struct/
|
||||
pointer/map inlining, ambiguity and tag validation, one inline map, secret-tag
|
||||
retention, and schema-aware key/index path traversal. Runtime default collisions,
|
||||
cyclic values, cloning, projection, and decoding remain assigned to later stages.
|
||||
- Added the [current schema contract](../config.md#application-schema-binding) and
|
||||
updated the component inventory and target-roadmap ownership links.
|
||||
- Focused tests cover nested and inline conflicts, ignored/private fields, maps of
|
||||
structs, named scalars, pointer text method sets, missing codec pairs, root and
|
||||
unsupported-kind rejection, recursive descriptors, side-effect freedom, dynamic
|
||||
and inline-map paths, parsed numeric indices, and array bounds. Verified
|
||||
`go test ./...`, `go vet ./...`, `go build ./...`, `go mod tidy -diff`, and
|
||||
`git diff --check` with Go 1.26.7 on linux/amd64. No deviations were required;
|
||||
typed value operations remain later work.
|
||||
|
||||
Read: feature [typed decoding](initial.md#typed-decoding) and
|
||||
[type/projection contract](initial.md#supported-types-and-projection).
|
||||
|
||||
@@ -584,17 +584,16 @@ through validation. Null never means reset to the application default.
|
||||
|
||||
## Typed decoding
|
||||
|
||||
The schema-binding portion of this target is implemented. The [current application
|
||||
schema contract](../config.md#application-schema-binding) owns root/type support,
|
||||
field and tag binding, inlining, text-codec pairing, and schema-aware path rules.
|
||||
The remaining document-to-value behavior below is target decoding work.
|
||||
|
||||
Strictness must be enforced by ConfigKit across the effective tree:
|
||||
|
||||
- Unknown struct fields, type mismatches, overflow, and invalid null are errors.
|
||||
- Support ordinary exported fields and `yaml` names/ignore tags. Untagged field
|
||||
names use the lowercase Go field name. Embedded structs follow the same rule
|
||||
unless explicitly tagged `yaml:",inline"`; inline struct fields contribute to
|
||||
the containing mapping. Permit at most one inline string-keyed map per object,
|
||||
which receives otherwise unmatched keys. Duplicate declared field names and
|
||||
default inline-map entries colliding with declared fields are schema errors.
|
||||
Without an inline map, unmatched keys remain errors. Ignore `omitempty` for
|
||||
decoding, defaults, and effective inspection.
|
||||
- Without an inline map, unmatched keys remain errors. Default inline-map entries
|
||||
colliding with declared fields are checked when runtime defaults are introduced.
|
||||
- String-keyed maps accept application-defined keys. Struct values inside them
|
||||
remain strict. `map[string]any` permits arbitrary keys and recursively accepts
|
||||
the supported scalar/container model; application validation owns its schema.
|
||||
@@ -1298,12 +1297,10 @@ model, not the application's separately constructed runtime module objects.
|
||||
|
||||
### Supported types and projection
|
||||
|
||||
The supported root `T` is a struct or string-keyed map. Within it, support ordinary
|
||||
exported scalar fields, pointers, structs, string-keyed maps, slices, fixed-length
|
||||
arrays, dynamic `any`, and the explicitly supported scalar conversions. Reject
|
||||
unsupported kinds, non-string map keys, cyclic object graphs, and ambiguous field
|
||||
bindings as schema/projection errors. Arrays require matching sequence length.
|
||||
`yaml:"-"` fields and unexported fields are outside the configuration projection;
|
||||
Supported schema types and field bindings are implemented and owned by the
|
||||
[current application schema contract](../config.md#application-schema-binding).
|
||||
Projection must additionally reject cyclic object graphs. Arrays require matching
|
||||
sequence length. Ignored and unexported fields stay outside configuration projection;
|
||||
hooks must not use them to carry runtime resources or configuration needing
|
||||
inspection. Non-configuration runtime state belongs in separate application types.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user