Implement canonical projection and detached copies
This commit is contained in:
@@ -101,8 +101,39 @@ 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.
|
||||
Descriptors do not yet decode documents. Document decoding will reuse this graph as
|
||||
its implementation lands.
|
||||
|
||||
## Typed projection and ownership
|
||||
|
||||
ConfigKit can internally project a schema-valid application value directly into its
|
||||
owned canonical tree without YAML serialization. Projection includes every bound
|
||||
field, including ordinary zero values; `omitempty` has no effect. Struct fields keep
|
||||
binding order, dynamic map keys sort by decoded string, and sequences keep their
|
||||
order. Integers retain exact base-10 values, finite floats use their destination
|
||||
precision, durations use `time.Duration.String`, and paired text scalars use their
|
||||
canonical marshaled text.
|
||||
|
||||
Nil pointers, maps, slices, and dynamic values project as null. Non-nil empty maps
|
||||
and slices remain empty containers, preserving the distinction. Recursive acyclic
|
||||
objects are supported. Cyclic values, non-finite floats, unsupported dynamic concrete
|
||||
types, invalid codec output, and depth/node-limit violations fail with safe schema
|
||||
classifications that omit codec error text.
|
||||
|
||||
The same descriptor graph creates detached typed copies. Pointers, maps, slices,
|
||||
arrays, nested dynamic values, and shared references are recursively copied; shared
|
||||
pointer identity is not preserved as configuration meaning. Paired text scalars are
|
||||
rebuilt by unmarshaling their canonical text into fresh values, detaching private
|
||||
backing storage. Ignored and unexported struct fields are zeroed rather than copied.
|
||||
|
||||
Private structural comparison operates on canonical trees. Mapping additions,
|
||||
removals, and descendant replacements retain semantic paths. A sequence is atomic:
|
||||
any sequence change reports the sequence path and never invents correspondence
|
||||
between old and new indices. Cross-kind and null/container changes replace the
|
||||
affected path.
|
||||
|
||||
These mechanisms currently support later decoding, resolution auditing, metadata,
|
||||
and rendering. They are not a public generic conversion or cloning API.
|
||||
|
||||
## Safe failures
|
||||
|
||||
|
||||
@@ -11,10 +11,13 @@ module and package documentation boundary. Its implemented public primitives are
|
||||
- the sole upstream YAML integration in `internal/yaml`, which translates validated
|
||||
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.
|
||||
types, YAML field binding, inlining, secret tags, recursion, and semantic paths;
|
||||
and
|
||||
- descriptor-driven canonical projection, detached typed cloning, cycle/limit
|
||||
enforcement, and structural comparison in `projection.go`.
|
||||
|
||||
The [configuration input contract](../config.md) owns implemented source and YAML
|
||||
behavior. ConfigKit intentionally exposes no typed loading or decoding API yet.
|
||||
The [configuration contract](../config.md) owns implemented input, schema, and
|
||||
ownership behavior. ConfigKit intentionally exposes no typed loading or decoding API yet.
|
||||
Private metadata population will be integrated when that lifecycle exists.
|
||||
|
||||
The source-extraction inventory records the application foundations assigned to
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Status and ownership
|
||||
|
||||
Status: implementation in progress; Stages 1–4 are complete and later stages are
|
||||
Status: implementation in progress; Stages 1–5 are complete and later stages are
|
||||
pending.
|
||||
|
||||
Audience: a `gpt-5.6-terra` coding agent implementing one stage per prompt, in
|
||||
@@ -512,7 +512,30 @@ Acceptance:
|
||||
|
||||
## Stage 5 — Implement canonical projection and detached copies
|
||||
|
||||
Status: pending.
|
||||
Status: complete.
|
||||
|
||||
Completion evidence (2026-09-06):
|
||||
|
||||
- Added descriptor-driven canonical typed projection and independently owned cloning
|
||||
in [`projection.go`](../../projection.go), without YAML serialization or a second
|
||||
binding model. Zero values, nil/empty distinctions, exact integers, destination-
|
||||
precision finite floats, durations, text scalars, dynamic concrete values, and
|
||||
recursive acyclic objects retain their configuration meaning.
|
||||
- Added active-graph cycle detection, private depth/node enforcement, safe custom
|
||||
codec and dynamic-type failures, zeroing of ignored state, reconstruction of text
|
||||
scalars into fresh instances, and non-preservation of shared pointer identity.
|
||||
- Added canonical-tree structural comparison with semantic mapping additions,
|
||||
removals and replacements, cross-kind replacement, and whole-sequence atomicity.
|
||||
The [current ownership contract](../config.md#typed-projection-and-ownership) and
|
||||
component inventory now own the implemented behavior.
|
||||
- Focused tests mutate every source alias after cloning, cover nil versus empty,
|
||||
wide integers, duration/text canonicalization, recursive acyclic values and cycles,
|
||||
unsupported dynamic types, non-finite floats, value-free codec failures, inline
|
||||
physical members/conflicts, projection bounds, ignored state, and meaningful
|
||||
comparison paths without index-wise sequence matching. 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; defaults and typed decoding
|
||||
remain later work.
|
||||
|
||||
Read: feature [ownership](initial.md#recorded-resolution-and-validation) and
|
||||
[type/projection contract](initial.md#supported-types-and-projection).
|
||||
|
||||
@@ -645,6 +645,11 @@ subsequently mutate, not language-enforced immutability of exported Go values.
|
||||
Callers should treat returned configuration as read-only; later caller mutations
|
||||
are outside the captured metadata snapshot.
|
||||
|
||||
Canonical typed projection, detached cloning, and structural comparison are now
|
||||
implemented as private foundations. The [current ownership contract](../config.md#typed-projection-and-ownership)
|
||||
owns those mechanics; publishing independently captured results remains lifecycle
|
||||
work.
|
||||
|
||||
Application semantic validation runs after successful resolution and collects
|
||||
independent errors in a standard diagnostic form. Validators are read-only and
|
||||
own domain requirements; checking live S3 buckets or starting runtime clients
|
||||
@@ -1304,20 +1309,12 @@ sequence length. Ignored and unexported fields stay outside configuration projec
|
||||
hooks must not use them to carry runtime resources or configuration needing
|
||||
inspection. Non-configuration runtime state belongs in separate application types.
|
||||
|
||||
One field-binding model supplies defaults, decoding, path lookup, change auditing,
|
||||
and rendering. `omitempty` never removes projected zero values. Preserve nil
|
||||
versus empty maps/slices where represented by the type. Shared pointer identity
|
||||
inside application values is not a configuration semantic; cyclic values are
|
||||
unsupported and independent load/snapshot ownership must still hold.
|
||||
|
||||
`time.Duration` projects to a canonical duration string. A custom scalar's text
|
||||
marshal method supplies its deterministic canonical string; unmarshalling must
|
||||
accept that representation and preserve its configuration meaning. These methods
|
||||
must be side-effect-free with respect to shared state. Custom scalars are atomic
|
||||
for comparison/history; their internal fields are not traversed. Reject types
|
||||
with only a text decoder and no supported inspection representation. Conversion
|
||||
or projection failures produce safe diagnostics. This is a bounded scalar seam,
|
||||
not a custom structural decoder or arbitrary reflection extension framework.
|
||||
Canonical projection, detached copying, cycle and finite-value enforcement, custom
|
||||
scalar reconstruction, and structural comparison are implemented and owned by the
|
||||
[current ownership contract](../config.md#typed-projection-and-ownership). The same
|
||||
field-binding model must still supply defaults, decoding, change auditing, and
|
||||
rendering. Application text methods must remain side-effect-free with respect to
|
||||
shared state; this is a bounded scalar seam, not a custom structural decoder.
|
||||
|
||||
## Internal organization
|
||||
|
||||
|
||||
Reference in New Issue
Block a user