Prepare ConfigKit v0.3.0
This commit is contained in:
@@ -37,8 +37,9 @@ relocation mechanism, not a second supported package, feature path, or runtime
|
||||
selection mechanism; the final cutover removed it and the old root implementation.
|
||||
|
||||
The [architecture policy](../policy/architecture.md#public-api-and-dependency-direction)
|
||||
owns the binding boundary. The [public-facade roadmap](../roadmap/public.md)
|
||||
owns the intended end state and its implementation plan owns progress.
|
||||
owns the binding boundary. The [component overview](../internal/overview.md)
|
||||
describes the implemented layout; the [development guide](../development.md)
|
||||
locates the completed roadmap and implementation history.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
|
||||
@@ -47,11 +47,12 @@ The completed string-and-integer schema-version implementation and remediation
|
||||
records are preserved in Git history at `244104f`. The
|
||||
[configuration contract](config.md#application-schema-versions) owns current behavior.
|
||||
|
||||
The completed [source-origins during resolution record](roadmap/source-origins-during-resolution.md)
|
||||
links to the current contract and evidence. Its completed implementation handoffs
|
||||
are preserved at `28e7fc5:docs/roadmap/implementation.md`; inspect them with
|
||||
`git show 28e7fc5:docs/roadmap/implementation.md` until release preparation
|
||||
retires the completed roadmap material.
|
||||
The completed source-origins and public-facade roadmaps, implementation plan, and
|
||||
resolved review findings are preserved in Git history at `5760614`, for example
|
||||
`git show 5760614:docs/roadmap/remediation.md`. The earlier source-origins staged
|
||||
plan remains at `28e7fc5:docs/roadmap/implementation.md`. Current resolution
|
||||
behavior belongs to the [configuration contract](config.md#recorded-application-resolution),
|
||||
and current facade ownership belongs to the [component overview](internal/overview.md).
|
||||
|
||||
## Baseline validation
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@ but internal production packages must not import the root. Explicit forwarding
|
||||
and local conversion are acceptable where they preserve type identity,
|
||||
ownership, callback lifetime, and inspection behavior. The accepted root-wrapper
|
||||
boundary and its migration rationale are recorded in
|
||||
[ADR 0006](../adr/0006-public-facade.md); completed implementation evidence is
|
||||
recorded in the public-facade roadmap.
|
||||
[ADR 0006](../adr/0006-public-facade.md); the implemented layout is described in
|
||||
the [component overview](../internal/overview.md).
|
||||
|
||||
Separate input access, document processing, composition, decoding, metadata, and
|
||||
rendering responsibilities without prescribing an interface or package for each.
|
||||
|
||||
@@ -46,7 +46,7 @@ is no guarantee that each release has been validated on macOS.
|
||||
The [release procedure](../release.md#release-identity-and-support) owns target
|
||||
architectures and required Linux checks. This support change applies to upcoming
|
||||
releases; historical release notes and their validation records remain unchanged.
|
||||
The next release note must explain the change in Compatibility, with version
|
||||
The v0.3.0 release note must explain the change in Compatibility, with version
|
||||
selection following this policy's existing compatibility rules.
|
||||
|
||||
## Release Model
|
||||
|
||||
70
docs/releases/v0.3.0.md
Normal file
70
docs/releases/v0.3.0.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# ConfigKit v0.3.0
|
||||
|
||||
This release adds source provenance during application resolution, separates the
|
||||
public API from its internal engine, and moves macOS to best-effort support.
|
||||
|
||||
## Summary
|
||||
|
||||
Resolvers can query fixed entry-state metadata and already-acquired sources using
|
||||
`Resolution.InputInfo` and `Resolution.Source`. This supports application-owned
|
||||
decisions such as resolving relative paths against their declaring configuration
|
||||
files. See the [resolution contract](../config.md#recorded-application-resolution)
|
||||
and the maintained [source-aware path example](../consumers/README.md#source-aware-application-path-policy).
|
||||
|
||||
The root `configkit` package now owns public types and explicit wrappers around a
|
||||
single private engine. Internal changes no longer automatically expand public
|
||||
method sets through shared type aliases. See the [component overview](../internal/overview.md)
|
||||
and [facade decision](../adr/0006-public-facade.md).
|
||||
|
||||
## Compatibility
|
||||
|
||||
Existing consumer imports, exported API signatures, configuration syntax,
|
||||
precedence, secret handling, callback lifetime, and ownership behavior are
|
||||
preserved. The two resolution query methods are additive and available only
|
||||
during an active resolution callback; returned records remain detached. The
|
||||
library supplies provenance, not a new filesystem normalization policy.
|
||||
|
||||
macOS on amd64 and arm64 is now best-effort rather than release-validated.
|
||||
Neither native macOS evidence nor Darwin cross-builds are required for publication,
|
||||
and macOS-specific failures do not by themselves block a release. Linux on amd64
|
||||
and arm64 retains release-gated builds and native Linux validation. See the
|
||||
[platform support policy](../policy/release.md#platform-support).
|
||||
|
||||
The module still requires Go 1.25 or newer. The private YAML dependency remains
|
||||
pinned to `go.yaml.in/yaml/v4 v4.0.0-rc.6`, confirmed as the newest available v4
|
||||
release during preparation. It is still a prerelease dependency, isolated behind
|
||||
the existing private parser boundary; no dependency upgrade is included.
|
||||
|
||||
Linux candidate validation uses Go 1.25.14 and Go 1.26.8, including the complete
|
||||
release checker, race tests, consumer examples, filesystem fixtures, and Linux
|
||||
cross-builds. No native macOS validation is claimed for this candidate.
|
||||
|
||||
## Upgrade
|
||||
|
||||
```sh
|
||||
go get gitea.maximumdirect.net/eric/ConfigKit@v0.3.0
|
||||
```
|
||||
|
||||
No code or configuration migration is required for the facade refactor. Adopt the
|
||||
new provenance queries only if your resolver needs them. Applications retain
|
||||
ownership of relative-path bases and fallback/error behavior.
|
||||
|
||||
macOS consumers should account for the reduced validation commitment and perform
|
||||
their own application checks when upgrading if macOS behavior is important to
|
||||
their deployment.
|
||||
|
||||
## Changes
|
||||
|
||||
- Added callback-scoped, fixed entry metadata and source lookup without additional
|
||||
filesystem access or credential resolution.
|
||||
- Added a maintained Notarius-shaped path-normalization example with provenance,
|
||||
frozen effective output, and explanation coverage.
|
||||
- Moved loading, schema, resolution, provenance/history, secret, and rendering
|
||||
machinery into one internal engine while keeping public named types root-owned.
|
||||
- Added exact-type secret and callback adapters, with regression coverage for
|
||||
missing version checkers and nil/zero closed-context failures.
|
||||
- Added type-aware public-boundary checks covering aliases, collections, generic
|
||||
constraints, and exported method sets; restored public reference documentation.
|
||||
- Removed macOS release gates and retained required Linux validation.
|
||||
- Retired completed feature, implementation, and remediation documents to Git
|
||||
history and repaired navigation to current contract and component owners.
|
||||
@@ -1,571 +0,0 @@
|
||||
# Public facade: implementation plan
|
||||
|
||||
Status: complete (2026-09-12). Baseline: `28e7fc5f690b36c60e4d03586afcff00fb1dee90`.
|
||||
The [feature roadmap](public.md) owns the implemented architecture and user intent.
|
||||
This completed plan records implementation decisions, ordered work, validation, and handoffs.
|
||||
Each numbered stage is sized for one gpt-5.6-terra coding-agent assignment;
|
||||
no stage requires subagents or changes to downstream repositories.
|
||||
|
||||
Historical navigation: this filename previously held the completed source-origins
|
||||
plan, including its Stage 5 completion record. That record is preserved at the
|
||||
baseline above: `git show 28e7fc5:docs/roadmap/implementation.md`. Existing incoming
|
||||
links from the source-origins roadmap, remediation record, and development guide
|
||||
refer to that historical plan, not the stages below. Stage 1 repairs those links.
|
||||
|
||||
## Working rules
|
||||
|
||||
Before each stage read [development](../development.md), the feature roadmap,
|
||||
the preceding completion records, and all applicable policies:
|
||||
[architecture](../policy/architecture.md), [documentation](../policy/documentation.md),
|
||||
[testing](../policy/testing.md), and [release](../policy/release.md).
|
||||
Read the owning configuration/API contracts and implementation before editing.
|
||||
Prefer the code graph for discovery, verifying its results against current code.
|
||||
Preserve unrelated work and the baseline's already-fixed resolution-history copy.
|
||||
|
||||
Complete stages in order. Every handoff must build and pass its relevant tests;
|
||||
do not leave placeholder public methods, skipped regressions, or known failures
|
||||
for a later stage. Run `./scripts/check.sh` at every code-stage handoff, in addition
|
||||
to the focused gates below. Keep default tests deterministic and offline. Follow
|
||||
the development guide for dependency acquisition and supported toolchains.
|
||||
|
||||
Record completion beneath each stage: changed owners, tests and actual toolchain,
|
||||
results, deviations, remaining risks, and the next handoff. Update current
|
||||
documentation with actual changes, not promised completion. Commit stage work
|
||||
when authorized; this plan does not authorize pushes, tags, release selection,
|
||||
or publication. Release notes and native-platform release evidence remain part
|
||||
of the separate release procedure.
|
||||
|
||||
## Fixed implementation decisions
|
||||
|
||||
### Package and migration strategy
|
||||
|
||||
Final production packages are the public root, `internal/engine`, and the existing
|
||||
`internal/document` and `internal/yaml`. Keep the engine cohesive; do not create
|
||||
separate packages for every phase. Root imports engine; engine imports its
|
||||
supporting packages; no internal production package imports root. Only the YAML
|
||||
adapter imports the upstream YAML dependency.
|
||||
|
||||
Public named types remain defined in root. Use named private fields for delegated
|
||||
state, explicit forwarding methods, and ordinary public record declarations.
|
||||
Do not alias public types to engine types, anonymously embed engine types, expose
|
||||
engine types in public signatures/fields, or add public bridge methods.
|
||||
Internal type aliases used only inside implementation are not public aliases.
|
||||
|
||||
Because private-state references span most root implementation files, use a
|
||||
bounded relocation candidate rather than attempting a live package split in
|
||||
every stage. Stage 2 copies the existing implementation into `internal/engine`;
|
||||
Stages 4–7 build the eventual root facade in temporary `internal/facade` (package
|
||||
name `configkit`). Root remains the sole consumer entry point until Stage 8.
|
||||
The temporary facade is staging space, not a second supported API/package.
|
||||
|
||||
This temporary duplication is a migration mechanism only: no feature development,
|
||||
runtime selector, fallback engine, or new consumer import is introduced. Baseline
|
||||
semantics remain fixed; necessary corrections must be reconciled explicitly.
|
||||
Stage 8 moves the facade to root and removes the old implementation and temporary
|
||||
directory in the same handoff. There is one production implementation at completion.
|
||||
This makes the large textual move mechanical while separating it from the more
|
||||
demanding ownership and callback work. Do not keep two engines as a final design.
|
||||
|
||||
### Type and state ownership
|
||||
|
||||
| Public family | Selected representation and adaptation |
|
||||
| --- | --- |
|
||||
| `Path` | Root struct with named private `engine.Path` value; constructors and methods delegate. Preserve semantic segment kinds and invalid state by direct private wrapping, never stringify/reparse. |
|
||||
| `Layer`, `ChangeKind`, `Severity`, `Code`, `SecretFileStatus` | Root-defined string types and explicitly typed constants with unchanged values. Convert to engine equivalents at the boundary; engine constants may supply values without making public aliases. |
|
||||
| `Location`, `SourceInfo`, `Diagnostic`, `Origin`, `FieldInfo`, `Change` | Root-defined records with unchanged exported fields. Explicit private conversions cover nested pointers, slices, and paths. |
|
||||
| `ValueSnapshot`, `ValueChange`, `SecretReferenceInfo`, `Explanation`, `SecretFileResult` | Root-defined output records. Convert detached engine records; preserve nil/empty distinctions and field semantics. |
|
||||
| `Source` | Root struct with private `engine.Source` value. Engine owns bytes, constructor validity, and the shared reader claim; wrapper copies must not create new claims or re-read/copy source input. |
|
||||
| `Metadata` | Root struct with private engine metadata value; explicit query methods convert detached results. Engine retains effective and historical indexes. |
|
||||
| `Resolution`, `Validation` | Root structs with private pointers to their engine contexts. Engine alone owns activity and audit state; wrappers never recreate that state. |
|
||||
| `Definition[T]`, `Options[T]`, `FilesystemContext`, `Override`, `VersionRule`, `Version` | Root records retaining all current fields and signatures. Private option/callback conversion into engine records; no extra configuration clone or reflection-based generic record mapper. |
|
||||
| `Result[T]` | Root struct retaining `Config T`, plus private pointer to a non-generic engine inspection result. Engine load returns configuration and inspection separately; facade stores configuration exactly once. |
|
||||
| `Secret` | Root-owned opaque scalar retaining value-copy semantics and existing public methods. Private exact-type adapter lets engine read/build it; no public accessor for plumbing. |
|
||||
| `LoadError`, `SecretsError`, `SecretsReport` | Root structs with private engine backing state and explicit methods. Translate engine typed failures at entry points; never return or unwrap an engine error as the public structured error. |
|
||||
|
||||
The inventory in Stage 1 verifies every baseline exported declaration, not just
|
||||
these grouped examples. Public type identity stays rooted in `configkit`.
|
||||
Preserve receivers, generics, supported zero/nil behavior, interface satisfaction,
|
||||
error assertions, and supported construction/copy behavior. Private field layout
|
||||
and incidental formatting are not new compatibility promises.
|
||||
|
||||
Use handwritten, local conversion helpers. For detached outputs, transfer already
|
||||
detached data where safe instead of deep-copying everything again. Never expose
|
||||
engine-owned mutable slices/maps through public records. Preserve order and
|
||||
missing/null distinctions. Do not round-trip records through JSON/YAML or use
|
||||
`unsafe`, registration globals, or reflection to bypass private fields.
|
||||
|
||||
### Exact-type secret bridge
|
||||
|
||||
Introduce an engine-internal `SecretBinding` containing `Type reflect.Type`,
|
||||
`Read func(reflect.Value) SecretState`, and
|
||||
`Make func(SecretState) reflect.Value`. `SecretState` carries the existing name,
|
||||
value, specified, and resolved fields. It is operation data, never inspection
|
||||
data. The root constructs the binding privately for exactly `reflect.TypeFor[Secret]()`;
|
||||
Read and Make use ordinary type assertions/construction in the owning package.
|
||||
|
||||
Supply the binding explicitly to the engine load and schema construction. Store
|
||||
it in the per-operation schema/value-operation context, available to recursive
|
||||
decoding, projection, cloning, dynamic-value checks, resolution, and validation.
|
||||
Keep the application configuration type `T` unchanged: no mirror configuration
|
||||
structs and no conversion of arbitrary application objects.
|
||||
|
||||
Replace all direct engine assertions/constructions of the old `Secret`, not just
|
||||
schema recognition. Include decoding, unresolved versus validated clone modes,
|
||||
projection, required/optional references, lookup caching, and validator mutation
|
||||
audit. Recognition remains exact-type recognition, not acceptance of any type
|
||||
implementing an interface. Bindings are fixed by the facade, not configurable by
|
||||
consumers, and perform no environment access themselves. Environment lookup
|
||||
remains in its existing lifecycle phase and its existing private test seam.
|
||||
|
||||
Engine unit tests use a test-local secret type and binding. Root integration
|
||||
tests exercise the actual public `Secret`. There is no production engine-owned
|
||||
replacement secret type that consumers must put in their configuration.
|
||||
|
||||
### Callback, option, result, and error bridges
|
||||
|
||||
Keep the engine lifecycle generic in the real `T`. Convert each supplied options
|
||||
value without selecting/validating options in the facade: engine retains original
|
||||
validation order and diagnostics, including multiple-options rejection. Preserve
|
||||
nil callbacks, version pointers, defaults behavior, and override values without
|
||||
eagerly invoking callbacks or evaluating `any` values.
|
||||
|
||||
Adapt Resolve/Validate/Version.Check by closures wrapping the exact engine context
|
||||
pointer and passing the same `*T` received from engine. Convert version records and
|
||||
semantic paths at those boundaries. The secret-classification predicate similarly
|
||||
wraps the engine path. Do not recover, retry, close, or extend callbacks in the
|
||||
facade; engine retains the existing lifetime and failure behavior.
|
||||
|
||||
Generic public Default/Normalize forward to generic engine helper functions with
|
||||
the original target pointer and value. Do not implement them as just assignment
|
||||
plus Record: existing target validation and projected expected-value audit matter.
|
||||
Nil wrapper handling must preserve current inactive-context behavior.
|
||||
|
||||
Refactor engine publication to return a short-lived generic loaded value containing
|
||||
`Config T` and an inspection pointer. Inspection owns frozen trees, metadata,
|
||||
history, sources, diagnostics, and sensitivity; it does not retain `T` or callback
|
||||
bindings. Public `Result[T]` holds `Config` and that pointer, not another complete
|
||||
generic engine result. Result inspection must not reread caller-mutated Config.
|
||||
Existing caller-constructed zero results and nil result receivers retain behavior.
|
||||
|
||||
Translate engine load/bootstrap failures into root `*LoadError`/`*SecretsError`,
|
||||
respectively. Their backing state can be delegated privately, but no public
|
||||
Unwrap exposes internal failures. Retain bootstrap partial reports and ordinary
|
||||
writer errors/short-write behavior. Do not wrap arbitrary render/write errors into
|
||||
new structured load errors or add error-disclosure guarantees.
|
||||
|
||||
## Stage 1 — Record the boundary and establish compatibility evidence
|
||||
|
||||
Status: not started.
|
||||
|
||||
1. Run baseline everyday checks. Inventory the root's exported types, constants,
|
||||
fields, functions, receiver method sets, generics, and interface implementations
|
||||
with Go tooling. Record the baseline revision and concise ownership mapping in
|
||||
the stage handoff; do not create a second normative API specification.
|
||||
2. Add the next available ADR (currently `0006-public-facade.md`) recording wrappers,
|
||||
private delegation, the dependency direction, and the alias-first alternative.
|
||||
Record the temporary migration mechanism here, not as durable architecture policy.
|
||||
3. Update architecture policy's public API/dependency section for the agreed
|
||||
boundary, preserving proportionality and existing behavior contracts. Explain
|
||||
that this is accepted design, not completed implementation. Add ADR navigation.
|
||||
4. Repair the three historical incoming references described at the top of this
|
||||
plan to identify the old plan by Git revision. Preserve the completed provenance
|
||||
evidence; do not label this facade plan as its completion record.
|
||||
5. Retain/add a compact external-package facade compatibility test covering the
|
||||
realistic boundary risks: root named-type identity, exported construction,
|
||||
generic helper use, structured error assertions, and relevant interfaces.
|
||||
Use existing tests for behavior already protected. Inspect the complete exported
|
||||
surface separately; do not add golden dumps of private struct layouts.
|
||||
|
||||
Acceptance: baseline passes, public surface is understood, ADR/policy accurately
|
||||
distinguish decision from implementation, and documentation links pass.
|
||||
Run `go test . -run 'LocalDocumentationLinks|DependencyImportBoundary|Example|Facade'`
|
||||
and the everyday check. No engine migration in this stage.
|
||||
|
||||
### Completion
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
Baseline behavior was verified at `28e7fc5f690b36c60e4d03586afcff00fb1dee90`;
|
||||
this plan was introduced by `eb7de8e40ab944f022a2b6984f4d4e0086f1484d`. The
|
||||
complete root surface was inventoried with `go doc -all .`: lifecycle and generic
|
||||
entry points; root value/input types (`Path`, `Source`, `Secret`); callback and
|
||||
option records; metadata, provenance, diagnostics, history, and rendering
|
||||
records; typed errors and bootstrap reports; and the scalar enum families. The
|
||||
inventory also covered receiver methods, generic instantiations, and relevant
|
||||
interfaces (`error`, `fmt.Formatter`, `fmt.Stringer`, `json.Marshaler`, and
|
||||
`io.WriterTo`). Existing behavioral tests remain the source of detailed API
|
||||
semantics; `facade_compatibility_test.go` supplies compact external-package
|
||||
coverage for the migration boundary.
|
||||
|
||||
Added ADR 0006 and the architecture rule for root-owned public wrappers and
|
||||
inward-only implementation dependencies. They describe the accepted target, not
|
||||
an already-completed extraction. Repaired the development guide, source-origins
|
||||
record, and remediation record to identify the old provenance plan by its Git
|
||||
revision instead of linking to this plan. No engine or lifecycle migration was
|
||||
introduced.
|
||||
|
||||
Validation on Linux with Go 1.26.7 passed: the focused documentation,
|
||||
import-boundary, executable-example, and facade checks, followed by
|
||||
`./scripts/check.sh`. There were no deviations. The next handoff copies the
|
||||
unchanged root implementation into the bounded internal relocation candidate;
|
||||
it must retain the documented public behavior while proving engine independence.
|
||||
|
||||
## Stage 2 — Establish the internal relocation candidate
|
||||
|
||||
Status: not started.
|
||||
|
||||
1. Copy root production implementation except public package orientation into
|
||||
`internal/engine`, changing package declarations/import references mechanically.
|
||||
Preserve file divisions and existing document/YAML packages. Keep internal
|
||||
functions private unless a later facade connection actually needs an export.
|
||||
2. Copy behavior/mechanism tests and needed fixtures for engine validation, excluding
|
||||
repository documentation, release-script, import-boundary, public examples,
|
||||
and consumer integration tests. Keep root unchanged and passing. Repair fixture
|
||||
paths explicitly; do not make tests depend on arbitrary working directories.
|
||||
3. Avoid blind replacements inside user-facing strings, diagnostic codes, fixture
|
||||
YAML, or documentation comments. The package rename is not permission to rename
|
||||
messages or behavior. Record the candidate's source baseline.
|
||||
4. Update the component overview with an explicitly temporary candidate entry.
|
||||
Do not claim root loads delegate yet.
|
||||
|
||||
Acceptance: `go test ./internal/engine` and the everyday check pass. Engine imports
|
||||
no root code, existing internal/parser boundaries remain intact, and root consumers
|
||||
still execute the unchanged original implementation. This stage is deliberately
|
||||
mechanical; do not mix ownership redesign into the relocation diff.
|
||||
|
||||
### Completion
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
Copied the 39 root production files from the Stage 1 baseline `f81def9` into
|
||||
`internal/engine`, excluding only root package orientation in `doc.go`, and
|
||||
changed package declarations mechanically. Copied the 29 applicable root
|
||||
behavior and fuzz tests. Repository documentation, release-script and
|
||||
import-boundary checks, root external-package/API tests, and maintained consumer
|
||||
integration tests remain at their existing owners; root has not been changed to
|
||||
delegate to the candidate. No root testdata fixtures exist to copy.
|
||||
|
||||
`docs/internal/overview.md` now labels the candidate as temporary and unused.
|
||||
`go list -deps` confirms that the engine has no dependency on the root module
|
||||
package; its existing dependencies remain the private document and YAML
|
||||
boundaries. Linux validation with Go 1.26.7 passed `go test ./internal/engine`
|
||||
and `./scripts/check.sh`. There were no behavioral deviations or package-renamed
|
||||
diagnostics. The next handoff replaces the copied engine's concrete public
|
||||
secret assumption with an explicit per-operation exact-type binding.
|
||||
|
||||
## Stage 3 — Decouple reflection from the public secret type
|
||||
|
||||
Status: not started.
|
||||
|
||||
1. Implement the exact-type SecretBinding/SecretState design above in engine.
|
||||
Thread it through schema and recursive value operations explicitly; do not use
|
||||
a global binding or make it an application option.
|
||||
2. Replace concrete-secret assumptions in `schema.go`, `decode.go`, `clone.go`,
|
||||
`projection.go`, `value_operation.go`, `secret_reference.go`, and their callers.
|
||||
Find all additional references using the graph/text search; the list is not a
|
||||
reason to leave a direct assertion behind.
|
||||
3. Move engine tests to their local secret type/binding and remove the candidate's
|
||||
production concrete Secret and public-style constructor. Preserve named-type
|
||||
rejection/acceptance semantics and dynamic-value checks from the baseline.
|
||||
4. Exercise unresolved/default secrets, pointer and container shapes, borrowed
|
||||
callback values, lookup-once behavior, and validator replacements without
|
||||
disclosing canary credentials in diagnostic expectations.
|
||||
|
||||
Acceptance: engine secret/schema/decode/clone/projection/validation tests and race
|
||||
tests pass; binding instances cannot affect each other. Run
|
||||
`go test ./internal/engine -run 'Secret|Schema|Decode|Clone|Projection|Validation|Callback'`,
|
||||
`go test -race ./internal/engine`, and the everyday check.
|
||||
|
||||
### Completion
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
`internal/engine` now receives a per-operation `SecretBinding` at schema and
|
||||
load construction. Its exact `reflect.Type`, `Read`, and `Make` functions carry
|
||||
the facade-owned `SecretState` through decoding, projection, unresolved and
|
||||
validated cloning, dynamic-value rejection, secret lookup, resolution helpers,
|
||||
and validator mutation auditing. The binding is retained by each schema only;
|
||||
there is no package-global registration or engine-owned replacement secret.
|
||||
|
||||
Removed the candidate's production `Secret` and `SecretEnv` declarations.
|
||||
`secret_binding_test.go` provides a local test secret type, fresh binding for
|
||||
every test load, and exact-type/isolation coverage using a second local secret
|
||||
type. Existing secret tests continue to cover defaults, pointers and containers,
|
||||
resolution-selected names, lookup-once behavior, borrowed callback values,
|
||||
redaction, and validator replacement without credential disclosure. The root
|
||||
implementation remains unchanged for this migration handoff.
|
||||
|
||||
Linux validation with Go 1.26.7 passed the focused secret/schema/decode/clone/
|
||||
projection/validation/callback suite, `go test -race ./internal/engine`, and
|
||||
`./scripts/check.sh`. There were no deviations. The next handoff creates the
|
||||
temporary facade's root-owned value, source, diagnostic, and secret wrappers.
|
||||
|
||||
## Stage 4 — Build candidate value, source, and diagnostic wrappers
|
||||
|
||||
Status: not started.
|
||||
|
||||
1. Create temporary `internal/facade`, package `configkit`, importing engine.
|
||||
Implement root-owned Path, Source, Secret, scalar enums/constants, and provenance
|
||||
records according to the ownership table. Preserve declaration comments for
|
||||
their eventual root placement. There must be no aliases or embedded engine types.
|
||||
2. Implement private path/record conversions and the real facade secret binding.
|
||||
Keep public Secret's small access/format/JSON methods local; its private scalar
|
||||
state remains value-based, not a shared mutable reference.
|
||||
3. Add LoadError and WriteDiagnostics delegation/conversion. Candidate engine may
|
||||
export narrow constructors/accessors as needed internally, not public bridge
|
||||
functions on the candidate facade. Preserve nil handling and typed errors.
|
||||
4. Test candidate public operations from its external test package. Verify that
|
||||
numeric-key versus index versus parsed paths are not collapsed; source copies
|
||||
share the same reader claim; bytes are snapshotted at construction; formatting
|
||||
and JSON do not expose resolved secret values.
|
||||
|
||||
Acceptance: candidate wrapper tests and engine tests pass. Run
|
||||
`go test ./internal/facade ./internal/engine` and the everyday check. Lifecycle
|
||||
entry points are added in Stage 6; do not add nonfunctional stubs here.
|
||||
|
||||
### Completion
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
Created the temporary `internal/facade` package (named `configkit`) with
|
||||
root-owned `Path`, `Source`, and `Secret` wrappers; root-owned scalar and
|
||||
provenance records; explicit private conversions; `LoadError`; and
|
||||
`WriteDiagnostics`. Public fields and signatures contain only facade types.
|
||||
`Path` carries its engine value privately without text round-tripping, `Source`
|
||||
retains the engine's source value so copied reader sources preserve their shared
|
||||
claim, and the facade builds the exact-type secret binding locally. No lifecycle
|
||||
entry point or placeholder was added.
|
||||
|
||||
Candidate tests cover semantic path distinctions, source byte snapshotting and
|
||||
copied-reader ownership, external-package type ownership and interfaces, and
|
||||
safe secret formatting/JSON. Linux validation with Go 1.26.7 passed
|
||||
`go test ./internal/facade ./internal/engine` and `./scripts/check.sh`. There
|
||||
were no deviations. The next handoff separates engine publication from its
|
||||
generic result before lifecycle delegation begins.
|
||||
|
||||
## Stage 5 — Separate engine publication and inspection
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
1. Split generic engine Result into the short-lived loaded configuration carrier
|
||||
and non-generic inspection state specified above. Move inspection methods to
|
||||
the inspection receiver without changing their snapshots or algorithms.
|
||||
2. Adapt `load_result.go`, `load_metadata.go`, `metadata.go`, `explain.go`,
|
||||
`explain_render.go`, `render.go`, `history.go`, and `snapshot.go` as necessary.
|
||||
Keep the fixed entry-state view independent of final history, including the
|
||||
baseline fix that avoids copying unused resolution history.
|
||||
3. Update engine tests for the internal receiver/return changes while preserving
|
||||
observable expectations. Do not broaden nil/result-construction contracts.
|
||||
|
||||
Acceptance: engine loading, historical provenance, detached metadata, explanation,
|
||||
and rendering behavior pass. Verify publication still performs its existing
|
||||
configuration detachment but does not store Config in inspection. Run
|
||||
`go test ./internal/engine -run 'Load|Metadata|Provenance|Historical|Explain|Effective|Render|Independence'`,
|
||||
`go test -race ./internal/engine`, and the everyday check.
|
||||
|
||||
### Completion
|
||||
|
||||
Replaced the engine's generic result with `Loaded[T]`, a short-lived carrier of
|
||||
the detached configuration and a non-generic `Inspection`. `Inspection` now
|
||||
owns the frozen metadata, sources, diagnostics, document snapshots, history,
|
||||
resolution snapshots, and sensitivity state; its query, explanation, and
|
||||
rendering methods retain their previous behavior. The published configuration
|
||||
is detached as before and is not retained by `Inspection`.
|
||||
|
||||
Engine tests use a test-only adapter while exercising the moved inspection
|
||||
receiver, and a direct `Loaded` test verifies that mutating `Config` cannot
|
||||
change its frozen effective view. Linux validation with Go 1.26.7 passed
|
||||
`go test ./internal/engine -run 'Load|Metadata|Provenance|Historical|Explain|Effective|Render|Independence'`,
|
||||
`go test -race ./internal/engine`, and `./scripts/check.sh`. There were no
|
||||
deviations or remaining risks. The next handoff wires the candidate facade's
|
||||
lifecycle and callback wrappers to the split engine result.
|
||||
|
||||
## Stage 6 — Wire candidate lifecycle and callback wrappers
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
1. Add candidate Definition, Options, VersionRule, Version, FilesystemContext,
|
||||
Override, Resolution, and Validation with unchanged public declarations.
|
||||
Implement the private option and callback bridges specified above.
|
||||
2. Add candidate Load using the engine and facade secret binding. Add Result[T]
|
||||
with Config and private inspection, initially with Metadata, Sources, and
|
||||
Diagnostics. Implement Metadata's complete public query methods.
|
||||
3. Forward all resolution methods, including InputInfo and Source, and the generic
|
||||
Default/Normalize helpers. Engine owns context activity, helper verification,
|
||||
resolution snapshots, and validation mutation detection.
|
||||
4. Add focused external candidate tests using actual public wrapper types inside
|
||||
application configuration. Exercise version callbacks, defaulting/normalization,
|
||||
retained/copy contexts, root typed error assertions, and public Secret recognition.
|
||||
Private engine secret tests alone are insufficient evidence for this bridge.
|
||||
|
||||
Acceptance: real candidate loads with callbacks succeed/fail identically to the
|
||||
baseline, no callback is called by option conversion, no extra lookup occurs,
|
||||
and detached entry provenance stays fixed. Run
|
||||
`go test ./internal/facade ./internal/engine -run 'Load|Resolution|Validation|Version|Metadata|Secret|Callback|Facade'`,
|
||||
`go test -race ./internal/facade ./internal/engine`, and the everyday check.
|
||||
|
||||
### Completion
|
||||
|
||||
Added the temporary facade's root-owned lifecycle declarations, option conversion,
|
||||
live callback wrappers, `Load`, `Result`, and `Metadata`. Callback wrappers carry
|
||||
only the active engine context during callback execution; `Result` stores the
|
||||
published configuration once and keeps its non-generic engine inspection private.
|
||||
All record and path crossings use explicit conversion, and facade loads translate
|
||||
engine load failures to the root-owned `LoadError` type.
|
||||
|
||||
External candidate tests exercise public version, default, resolution, validation,
|
||||
metadata, source, and secret behavior, including inactive retained contexts and
|
||||
typed load errors. Linux validation with Go 1.26.7 passed
|
||||
`go test ./internal/facade ./internal/engine -run 'Load|Resolution|Validation|Version|Metadata|Secret|Callback|Facade'`,
|
||||
`go test -race ./internal/facade ./internal/engine`, and `./scripts/check.sh`.
|
||||
There were no deviations or remaining risks. The next handoff completes the
|
||||
candidate inspection and secrets-bootstrap surface.
|
||||
|
||||
## Stage 7 — Complete candidate inspection and bootstrap surface
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
1. Add the public explanation/value records and Result Explain, WriteEffective,
|
||||
and WriteExplanation delegation. Inspection uses the frozen engine state,
|
||||
not the mutable public Config field. Complete every remaining public method.
|
||||
2. Add SecretsReport, SecretsError, bootstrap records/status constants, and
|
||||
LoadSecretsDir. Preserve opt-in writes, partial reports, detached entries,
|
||||
writer counts/errors, and non-unwrapping typed errors.
|
||||
3. Exercise complete load-to-inspection workflows through the candidate. Prove
|
||||
that caller mutation of Config or returned records does not rewrite inspection
|
||||
history or affect a later query/load. Include malformed paths and nil receivers
|
||||
where the baseline already defines behavior.
|
||||
4. Compare the complete candidate exported surface with the baseline inventory,
|
||||
normalizing only the temporary import path. Resolve all signature/method/field
|
||||
omissions before cutover. No placeholder methods or ignored API mismatches.
|
||||
|
||||
Acceptance: the candidate implements the entire supported API and has real
|
||||
integration evidence for inspection, credentials, bootstrap, and writer failures.
|
||||
Run `go test ./internal/facade ./internal/engine`, race tests for both packages,
|
||||
and the everyday check. Root remains unchanged until Stage 8.
|
||||
|
||||
### Completion
|
||||
|
||||
Completed the candidate's inspection and bootstrap API with root-owned
|
||||
explanation, snapshot, secret-reference, and bootstrap records. Result delegates
|
||||
to the private frozen engine inspection for explanation and rendering, so public
|
||||
configuration mutation cannot affect historical or effective output. Bootstrap
|
||||
delegates to the engine while returning facade-owned report and error types with
|
||||
detached converted entries and diagnostics.
|
||||
|
||||
The candidate's exported declarations and methods now match the root surface
|
||||
after normalizing its temporary import path. External integration tests cover
|
||||
frozen inspection, secret-safe explanation/rendering, malformed and nil result
|
||||
handling, bootstrap application/skips, detached report entries, and typed
|
||||
bootstrap failures. Linux validation with Go 1.26.7 passed
|
||||
`go test ./internal/facade ./internal/engine`, `go test -race ./internal/facade ./internal/engine`,
|
||||
and `./scripts/check.sh`. There were no deviations or remaining risks. The next
|
||||
handoff moves the verified candidate to the root and removes migration copies.
|
||||
|
||||
## Stage 8 — Cut over the root and remove migration duplication
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
1. Replace root production plumbing with the tested candidate facade, retaining
|
||||
root package documentation and the root named types. Move files; do not leave
|
||||
forwarding aliases to the temporary facade. Remove `internal/facade` entirely.
|
||||
2. Root-owned unit/integration tests remain at root; engine mechanism tests remain
|
||||
with engine. Remove duplicated baseline copies after confirming coverage owners.
|
||||
Split mixed tests by boundary: assertions requiring private engine state move
|
||||
internally, while public lifecycle/consumer assertions still exercise root.
|
||||
Do not move all tests inward simply to avoid adapting public wrappers.
|
||||
3. Run existing executable API examples and maintained consumers unchanged in
|
||||
their imports/configuration types. Fix wrapper defects rather than migrating
|
||||
consumers to a different API. Test the actual root Secret binding after the move.
|
||||
4. Keep repository check tests at root. Repair documentation/fixture references,
|
||||
fuzz target locations, and development task commands as files move. For links
|
||||
formerly pointing at mixed implementation files, point API references at root
|
||||
and implementation references at engine as appropriate.
|
||||
5. Remove old root algorithms, temporary candidate tests superseded by root tests,
|
||||
and staging documentation. Preserve useful narrow private test seams, but no
|
||||
root accessors solely exposing engine internals to tests.
|
||||
|
||||
Acceptance: one engine serves the sole root facade; no candidate directory,
|
||||
parallel implementation, or root import from internal production code remains.
|
||||
Run the everyday check, `go test ./examples/consumers`, root examples, and
|
||||
`go test -race ./...`. All gates must pass in this handoff; do not leave a broken
|
||||
root suite for the final audit.
|
||||
|
||||
### Completion
|
||||
|
||||
Moved the verified facade into the root package and removed the temporary
|
||||
`internal/facade` directory and the former root implementation copies. The root
|
||||
now owns the public wrappers and delegation only; `internal/engine` is the single
|
||||
production lifecycle implementation. Retained root tests cover public facade
|
||||
behavior, examples, documentation links, import boundaries, and checker behavior;
|
||||
mechanism tests remain with the engine. Development and remediation links now
|
||||
point to their engine-owned tests and metadata implementation.
|
||||
|
||||
Linux validation with Go 1.26.7 passed `./scripts/check.sh`,
|
||||
`go test ./examples/consumers`, root examples, and `go test -race ./...`.
|
||||
There were no deviations or remaining risks. The final handoff audits the public
|
||||
boundary and brings documentation in line with the completed architecture.
|
||||
|
||||
## Stage 9 — Audit the public boundary and finalize documentation
|
||||
|
||||
Status: complete (2026-09-12).
|
||||
|
||||
1. Compare the final complete exported API against the baseline. Audit public
|
||||
signatures, fields, aliases, embedded types, method sets, and error returns
|
||||
for internal-type exposure. Review root code for remaining substantive
|
||||
algorithms; explain small value operations deliberately retained there.
|
||||
2. Extend the existing import-boundary check for inward production dependencies.
|
||||
Add a narrowly scoped public-boundary check that detects exported aliases to,
|
||||
embedded exposure of, and exported signatures/fields involving internal types.
|
||||
Use standard Go tooling and avoid private-field/layout snapshots. Account for
|
||||
generic declarations and public records referencing other public records.
|
||||
Keep behavior tests as the evidence for adapter correctness.
|
||||
3. Verify source ownership, secret binding isolation, zero/nil handling, helper
|
||||
audit semantics, typed errors, final Config versus frozen inspection, and the
|
||||
baseline resolution-entry optimization. Add tests only for uncovered risks.
|
||||
4. Finalize architecture-policy wording, ADR navigation, component overview,
|
||||
development test/fuzz commands, and public declaration comments. Add a concise
|
||||
internal facade mechanism document if needed for the secret/callback/record
|
||||
connections; do not spread implementation recipes across consumer docs.
|
||||
5. Update consumer orientation only where useful. Keep YAML behavior in its
|
||||
existing contract. Convert the feature roadmap to an implemented summary with
|
||||
canonical links and evidence; record remaining release preparation separately.
|
||||
|
||||
Acceptance: documented target matches code and no unapproved API/behavior change
|
||||
is hidden by the refactor. Run the everyday check, full race suite, consumer
|
||||
examples, documentation/import/public-boundary checks, and tests/builds using the
|
||||
supported baseline and newer Go toolchains recorded in the release procedure.
|
||||
Record actual versions/platforms; do not treat Linux tests or cross-builds as
|
||||
native macOS release evidence. Do not publish or select a version in this stage.
|
||||
|
||||
### Completion
|
||||
|
||||
Audited the final root facade against the established public surface and added a
|
||||
root AST boundary check for exported aliases, fields, and signatures involving
|
||||
internal types. The dependency boundary check now also rejects root production
|
||||
imports that bypass the engine and internal production imports of the root.
|
||||
The completed root retains only facade wrappers, records, conversions, and
|
||||
entry-point delegation; engine mechanism tests own lifecycle behavior.
|
||||
|
||||
Updated the architecture policy, ADR status, component overview, development
|
||||
test/fuzz navigation, and public-facade roadmap to describe the implemented
|
||||
layout. The roadmap is now a completed implementation record rather than a
|
||||
future plan. Root facade integration and engine tests cover source ownership,
|
||||
exact-type secret binding, callback activity/audit, structured errors, detached
|
||||
configuration versus frozen inspection, and resolution entry snapshots.
|
||||
|
||||
Linux validation passed with Go 1.25.5 and Go 1.26.7 through
|
||||
`CONFIGKIT_BASELINE_TOOLCHAIN=go1.25.5 CONFIGKIT_NEWER_TOOLCHAIN=go1.26.7 ./scripts/release-check.sh`.
|
||||
It also passed the everyday check, full race suite, consumer examples, and local
|
||||
documentation/import/public-boundary tests. This is not native macOS evidence
|
||||
and no release version, tag, or publication was selected. There were no
|
||||
deviations or remaining risks.
|
||||
|
||||
## Open Questions
|
||||
|
||||
None requiring a user decision were identified. Wrappers, public type ownership,
|
||||
one final engine, per-operation exact-type secret adaptation, and a single cutover
|
||||
are specified above. Internal helper names and file grouping remain ordinary
|
||||
implementation choices. A newly discovered conflict with an existing contract
|
||||
must be surfaced with evidence and alternatives rather than resolved by changing
|
||||
consumer behavior or reverting to public aliases.
|
||||
@@ -1,187 +0,0 @@
|
||||
# Public facade and internal implementation
|
||||
|
||||
Status: implemented on 2026-09-12. This roadmap records the intended architecture
|
||||
and user intent; the completed [implementation plan](implementation.md) records
|
||||
the ordered work, validation, and handoffs.
|
||||
|
||||
## Intent
|
||||
|
||||
Make ConfigKit easier for humans and coding agents to navigate by separating its
|
||||
consumer-facing API from its implementation machinery. Consumers should find a
|
||||
readable, documented public surface in the root `configkit` package; maintainers
|
||||
should find the loading and inspection machinery under `internal/`.
|
||||
|
||||
Long-term maintainability is the primary objective. The public API should remain
|
||||
stable while internal implementation can evolve without becoming a consumer
|
||||
commitment. An internal implementation change must not expand or alter the public
|
||||
API merely because the facade exposes the same underlying type. Repository
|
||||
aesthetics and easier navigation are useful secondary benefits, not the reason
|
||||
to minimize public-package code.
|
||||
|
||||
This is an organizational and API-boundary refactor, not a new configuration
|
||||
feature or security boundary. Unexported root-package details are already private
|
||||
under ordinary Go access rules. The concern is accidentally exposing internal
|
||||
types or exported methods that consumers can depend on even without documentation.
|
||||
Wrappers provide structural separation; they do not make internal changes that
|
||||
alter observable public behavior automatically compatible.
|
||||
|
||||
The [architecture policy](../policy/architecture.md) and
|
||||
[implementation discipline](../../AGENTS.md#implementation-discipline) govern the
|
||||
design. Existing [configuration contracts](../config.md) and exported Go
|
||||
declaration comments remain authoritative. The user-approved wrapper boundary is
|
||||
a contributor design choice, not a strengthening of consumer behavior contracts.
|
||||
|
||||
## Implemented architecture
|
||||
|
||||
The root owns public declarations, records, wrappers, and deliberate forwarding.
|
||||
`internal/engine` owns source acquisition, imports, composition and overlays,
|
||||
reflection descriptors, decoding, resolution and validation coordination,
|
||||
provenance, history, secret handling, and rendering. The
|
||||
[component overview](../internal/overview.md) owns the detailed inventory.
|
||||
|
||||
Two established internal boundaries already exist: `internal/document` owns the
|
||||
parser-independent tree, and `internal/yaml` owns upstream YAML integration.
|
||||
[ADR 0002](../adr/0002-own-yaml-semantics.md) remains applicable.
|
||||
|
||||
Public types are not merely passive records. For example, `Result[T]` combines
|
||||
its public `Config` field with private inspection snapshots; `Resolution` holds
|
||||
callback state; `Path` holds semantic segments; and `Secret` holds private
|
||||
reference and resolved-value state. Moving implementation requires deliberate
|
||||
type ownership, not just file relocation.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Public root package
|
||||
|
||||
The root remains the single supported consumer import path. It contains:
|
||||
|
||||
- package and API reference documentation;
|
||||
- public-owned type declarations, including wrappers and ordinary public records;
|
||||
- public constants, constructors, entry points, and lightweight delegation;
|
||||
- public contract tests and executable API examples where appropriate.
|
||||
|
||||
Substantial source processing, decoding, lifecycle bookkeeping, history replay,
|
||||
and rendering mechanics live internally. Small value operations or adapters may
|
||||
remain in the root when moving them would introduce disproportionate machinery.
|
||||
Public API documentation remains beside public declarations and must be useful
|
||||
through ordinary Go documentation and editor navigation.
|
||||
|
||||
### Public-owned types and private delegation
|
||||
|
||||
The agreed approach is root-defined public types, with private delegation to
|
||||
internal implementation where needed. Public wrappers expose deliberately chosen
|
||||
methods; internal state is held privately rather than exported or embedded in a
|
||||
way that promotes internal methods into the public surface. Public signatures
|
||||
and fields do not expose internal plumbing types. Adding an internal exported
|
||||
method must not add a public method automatically.
|
||||
|
||||
Simple public records and small value operations can remain directly implemented
|
||||
in the root. There is no requirement to wrap every value or move every method.
|
||||
Aliases to internally owned implementation types are not the facade strategy:
|
||||
they would couple the public method set to the implementation this work is meant
|
||||
to separate.
|
||||
|
||||
Straightforward forwarding code is an acceptable maintenance cost. Adapters must
|
||||
avoid competing mutable state and unnecessary conversions, particularly around
|
||||
`Result[T].Config`, frozen inspection data, callback lifetime, source ownership,
|
||||
and reflection-sensitive secret handling. These connections are private
|
||||
implementation details, not public bridge APIs or a generic registration framework.
|
||||
|
||||
### Internal engine and supporting packages
|
||||
|
||||
A cohesive internal engine owns the loading lifecycle and the closely coupled
|
||||
schema, decoding, resolution, provenance, and inspection implementation. The
|
||||
selected package is `internal/engine`.
|
||||
|
||||
The existing document and YAML packages retain their responsibilities. Additional
|
||||
packages are appropriate only where a cohesive responsibility and practical
|
||||
dependency boundary justify them. There is no requirement for one package per
|
||||
phase, file category, or component in the current inventory.
|
||||
|
||||
Dependencies run from the facade into internal implementation and from the
|
||||
engine into its supporting packages. Internal packages do not import the public
|
||||
root. Public types remain root-owned; internal collaborators use internally owned
|
||||
state and narrow implementation connections. Internal code does not gain direct
|
||||
access to private fields of public types across packages.
|
||||
|
||||
The engine may initially remain fairly large. A smaller root alone does not
|
||||
justify a network of adapters, generic provider interfaces, or a framework.
|
||||
Internal exports needed between packages are implementation surfaces, not new
|
||||
supported consumer APIs. Existing parser isolation remains intact.
|
||||
|
||||
## Compatibility and scope
|
||||
|
||||
The target is a behavior-preserving refactor requiring no consumer migration:
|
||||
|
||||
- Preserve the module/import path, exported names, function and method
|
||||
signatures, generic use, public fields, and supported construction patterns.
|
||||
- Preserve loading order, source ownership, defaults, composition and overlay
|
||||
behavior, resolution and validation semantics, and structured diagnostics.
|
||||
- Preserve callback lifetime, fixed entry provenance, detached query results,
|
||||
final inspection snapshots, and independence across loads.
|
||||
- Preserve secret recognition, credential timing, explicit access, redaction,
|
||||
and opt-in bootstrap behavior.
|
||||
- Preserve the supported Go and platform baseline and the existing dependency
|
||||
isolation; this work does not require a toolchain or third-party dependency
|
||||
change.
|
||||
|
||||
Type restructuring can affect more than compilation. The implementation must
|
||||
assess method sets, interface satisfaction, zero values, copying and equality
|
||||
where supported, error assertions, reflection-based special-type recognition,
|
||||
and public documentation. Keeping public named types in the root preserves their
|
||||
defining package identity, but does not by itself prove compatibility of their
|
||||
state or behavior. Observable effects must be evaluated and reported, not silently
|
||||
declared compatible or newly guaranteed forever.
|
||||
|
||||
No new input restrictions, resource limits, lifecycle restrictions, security
|
||||
guarantees, configuration semantics, or public extension hooks are in scope.
|
||||
Do not expose plumbing publicly merely to solve an import cycle. A discovered
|
||||
need to change an established contract requires a separate explicit decision.
|
||||
Version selection and publication remain governed by the
|
||||
[release policy](../policy/release.md), not by this roadmap.
|
||||
|
||||
## Completion evidence
|
||||
|
||||
Completion means the root is a legible public surface, substantive plumbing is
|
||||
internally owned, consumers require no migration, and representative
|
||||
load-to-inspection workflows still pass through the facade. Public exported
|
||||
members are deliberate declarations, not incidental exposure of engine methods
|
||||
or state. The completed architecture has one engine, no parallel implementation,
|
||||
and no competing mutable configuration state. Small operations deliberately
|
||||
retained in the root are justified by maintainability, not a numerical size gate.
|
||||
|
||||
Public behavior and maintained consumer examples exercise the root facade;
|
||||
mechanism tests live with their implementation owners. Test-only access does not
|
||||
expand the public API. Repository checks preserve parser isolation and the public
|
||||
boundary. Validation follows the [testing policy](../policy/testing.md);
|
||||
release-candidate validation remains separate under the
|
||||
[release procedure](../release.md).
|
||||
|
||||
Tests should protect behavior and intentional dependency boundaries, not exact
|
||||
private layouts, forwarding call sequences, or an arbitrary package count.
|
||||
Implementation completion must be backed by evidence; this proposal is not
|
||||
evidence that the target architecture already exists. Completed roadmap material
|
||||
is retired during release preparation under the documentation policy.
|
||||
|
||||
## Documentation ownership in the target architecture
|
||||
|
||||
Under the [documentation policy](../policy/documentation.md), architecture policy
|
||||
owns the public-wrapper and inward-dependency rules, preserving proportionality.
|
||||
An ADR records the accepted decision and why an alias-first facade was rejected:
|
||||
it reduces forwarding but couples public methods to internal implementation.
|
||||
The additional wrapper code is justified by long-term API independence.
|
||||
|
||||
The component overview describes actual facade, engine, supporting packages, and
|
||||
state ownership. Development navigation locates the tests and mechanisms at their
|
||||
new owners. Internal documentation explains the private adapters. Exported API
|
||||
reference comments remain in the public package; consumer guidance identifies
|
||||
the supported root import without requiring knowledge of the engine.
|
||||
|
||||
Configuration semantics remain in the existing contract, not in package-layout
|
||||
documentation. Internal layouts do not become public stability guarantees.
|
||||
Release documentation describes the behavior-preserving refactor without
|
||||
replacing canonical API or architecture documentation.
|
||||
|
||||
The alias-versus-wrapper decision is resolved by the user's explicit selection
|
||||
of wrappers. An implementation convenience is not authority to change that
|
||||
decision or an established consumer contract.
|
||||
@@ -1,206 +0,0 @@
|
||||
# Public facade remediation
|
||||
|
||||
Review baseline: `5dc7e18`. Reviewed commits `f81def9` through `5dc7e18` against
|
||||
`eb7de8e`, the agreed [roadmap](public.md), and [implementation plan](implementation.md).
|
||||
Status: all four findings resolved. Review evidence below describes the
|
||||
original baseline; resolution records describe the subsequent fixes.
|
||||
|
||||
The previous provenance finding remains resolved; its record is preserved at
|
||||
`5dc7e18:docs/roadmap/remediation.md`. The field-only resolution-entry snapshot
|
||||
construction remains intact in `internal/engine/load_metadata.go`.
|
||||
|
||||
## F1 — Preserve a nil schema-version checker during option conversion
|
||||
|
||||
Priority: high. Status: resolved.
|
||||
|
||||
Resolution: option conversion preserves nil Check and installs a wrapper only for
|
||||
a real checker. The public regression failed with the original panic before the
|
||||
fix, then passed with typed rejection, no reads/callbacks, and successful source
|
||||
reuse with a valid checker. Focused facade/version tests, the everyday check,
|
||||
and the full race suite passed on Linux with Go 1.26.7.
|
||||
|
||||
### Evidence and impact
|
||||
|
||||
In [lifecycle.go](../../lifecycle.go), `toEngineDefinition` (lines 233–237 at the
|
||||
review baseline) installs a non-nil Check closure whenever Version is non-nil,
|
||||
even if the supplied VersionRule.Check is nil. This defeats the existing
|
||||
nil-checker rejection in `internal/engine/options.go`. Loading then consumes input
|
||||
and invokes a nil function through the closure, panicking.
|
||||
|
||||
Reproduced through the public API with a struct containing a `version` string,
|
||||
a counting Reader over `version: v1`, and this option:
|
||||
|
||||
```go
|
||||
configkit.Options[Config]{
|
||||
Definition: configkit.Definition[Config]{
|
||||
Version: &configkit.VersionRule{Path: configkit.Keys("version")},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
At `eb7de8e`, this returns `*configkit.LoadError` with `CodeOptionsInvalid` and
|
||||
zero reader calls. At `5dc7e18`, it reads the source and panics. Reading also claims
|
||||
the single-use source, so recovering does not restore the old behavior. This is
|
||||
a regression in established validation and failure ordering, not a proposed new
|
||||
restriction on version rules.
|
||||
|
||||
### Required correction and validation
|
||||
|
||||
Convert Version and Path while preserving a nil Check. Install the forwarding
|
||||
closure only when the supplied Check is non-nil. Leave validation and diagnostic
|
||||
order in the engine; do not add facade validation or recover-and-reclassify.
|
||||
|
||||
Add a public-root regression test asserting typed `CodeOptionsInvalid`, no result,
|
||||
no source consumption, and no application callback execution. Reusing the same
|
||||
Reader source with a valid rule should still succeed. Retain valid checker
|
||||
coverage for version/context conversion. Run root facade/version tests, the
|
||||
everyday check, and the race suite.
|
||||
|
||||
## F2 — Close the public-boundary check's type-resolution gaps
|
||||
|
||||
Priority: medium. Status: resolved.
|
||||
|
||||
Resolution: [public_boundary_test.go](../../public_boundary_test.go) loads the
|
||||
active build's compiler export data and walks resolved Go types, exported method
|
||||
sets, and generic constraints. Declaration fixtures cover exposure and valid
|
||||
private-backed wrappers. The original three probe declarations now each fail
|
||||
the actual root check; the temporary probe was removed afterwards. The everyday
|
||||
check passed with Go 1.26.7, and boundary tests passed with Go 1.25.5 and Go 1.26.7
|
||||
on Linux. No production behavior or public surface was changed.
|
||||
|
||||
### Evidence and impact
|
||||
|
||||
`TestPublicFacadeBoundary` in
|
||||
[import_boundary_test.go](../../import_boundary_test.go) checks selector syntax in
|
||||
function signatures and public struct fields, but skips non-struct type bodies
|
||||
and does not resolve local type names or inspect type-declaration constraints.
|
||||
No current public engine-type leak was identified; the gap is in the guard meant
|
||||
to prevent future accidental exposure.
|
||||
|
||||
In a disposable archive of `5dc7e18`, these production declarations all compiled
|
||||
and `go test . -run '^TestPublicFacadeBoundary$' -count=1` still passed:
|
||||
|
||||
```go
|
||||
type ReviewPaths []engine.Path
|
||||
type reviewHidden = engine.Path
|
||||
func ReviewLeak() reviewHidden { return engine.Path{} }
|
||||
type ReviewGeneric[T interface{ ~[]engine.Path }] struct{}
|
||||
```
|
||||
|
||||
The collection exposes engine values; the private alias hides an engine return
|
||||
type from the scanner; the constraint exposes an internal type despite an empty
|
||||
struct body. These are ordinary declarations that can create exactly the
|
||||
accidental public commitments the selected wrapper boundary is meant to prevent.
|
||||
|
||||
### Required correction and validation
|
||||
|
||||
Resolve types across root declarations and inspect exported/reachable type
|
||||
surfaces, including named collection/function/interface types and generic
|
||||
constraints. Prefer a small type-aware implementation using standard Go tooling
|
||||
over isolated selector-pattern exceptions. Keep private backing fields legal;
|
||||
their layout is not public API. Do not add runtime restrictions or broaden the
|
||||
check into a general security scanner.
|
||||
|
||||
Use focused positive/negative declaration fixtures covering the reproduced misses,
|
||||
existing direct-exposure cases, and legitimate wrappers, public records, and
|
||||
generic public types. Fixtures need not enter production. Run boundary tests and
|
||||
the everyday check.
|
||||
|
||||
## F3 — Restore consumer contracts on public Go declarations
|
||||
|
||||
Priority: medium. Status: resolved.
|
||||
|
||||
Resolution: restored the baseline comments on root source, path, secret, and
|
||||
diagnostic declarations. The complete `go doc -all .` output now matches the
|
||||
pre-refactor `eb7de8e` reference, with no missing declarations or contract prose.
|
||||
Documentation-link and whitespace checks passed. No behavior or new guarantee
|
||||
was introduced.
|
||||
|
||||
### Evidence and impact
|
||||
|
||||
Comparing `go doc -all .` at `eb7de8e` and `5dc7e18` found unchanged exported
|
||||
declaration signatures but lost substantive reference documentation. Much of that
|
||||
text now remains only on internal engine declarations:
|
||||
|
||||
- [source.go](../../source.go): Reader omits that ConfigKit never closes the
|
||||
reader; Bytes omits label requirements and deferred constructor errors;
|
||||
NoSource omits that it creates no source record.
|
||||
- [diagnostic.go](../../diagnostic.go): LoadError loses its non-unwrapping contract;
|
||||
Error, Diagnostics, and Sources have no reference comments; WriteDiagnostics
|
||||
omits control-character escaping and writer/short-write behavior.
|
||||
- [path.go](../../path.go): Path loses concrete semantic/escaping details;
|
||||
Index loses the negative-index behavior.
|
||||
- [secret.go](../../secret.go): SecretEnv omits that invalid names are reported
|
||||
if the reference survives loading.
|
||||
|
||||
These are consumer ownership and usage facts, not plumbing to hide with the
|
||||
engine. Their loss diverges from the documented root-facade goal and the
|
||||
documentation policy's declaration-level API ownership.
|
||||
|
||||
### Required correction and validation
|
||||
|
||||
Restore substantive existing comments on root declarations, linking to the
|
||||
configuration contract for shared semantics where appropriate. Review the complete
|
||||
reference diff, not just these examples. Do not invent stronger guarantees or
|
||||
require consumers to inspect engine documentation. Internal comments can explain
|
||||
mechanics without becoming a competing public contract.
|
||||
|
||||
Validate with `go doc -all .`, documentation-link checks, and content review;
|
||||
no golden full-documentation snapshot is required.
|
||||
|
||||
## F4 — Repair the duplicated development-guide tail and table row
|
||||
|
||||
Priority: low. Status: resolved.
|
||||
|
||||
Resolution: removed the duplicate tail and stray table terminator, escaped the
|
||||
boundary command's alternation pipes, and linked the new public-boundary test
|
||||
owner. Content/structure review confirms one task table and one fuzz section.
|
||||
The documented boundary command, documentation links, and whitespace checks pass.
|
||||
|
||||
### Evidence and impact
|
||||
|
||||
[docs/development.md](../development.md), lines 122–134 at the review baseline,
|
||||
contains a stray backtick/table terminator, a duplicate checker row outside its
|
||||
table, and a second copy of the Fuzz validation section. The boundary-check
|
||||
command in the table at line 109 also contains unescaped pipes, splitting the
|
||||
Markdown row into unintended cells. The link checker passes because these are
|
||||
rendering/content errors rather than missing targets.
|
||||
|
||||
### Required correction and validation
|
||||
|
||||
Retain one well-formed task table and one fuzz section with the current engine/YAML
|
||||
commands. Escape regex alternation pipes in the table consistently with other
|
||||
rows. Review the rendered structure and run local documentation-link and
|
||||
whitespace checks. No new documentation framework or test is needed.
|
||||
|
||||
## Architecture assessment and validation boundaries
|
||||
|
||||
The principal target is implemented: public named wrappers and records remain in
|
||||
root; substantial machinery lives in one internal engine; document and YAML
|
||||
boundaries remain separate. The temporary facade and old root engine are gone.
|
||||
Callbacks wrap engine context pointers, configuration remains the actual application
|
||||
type, and inspection is frozen non-generic state separate from public Config.
|
||||
The exact-type secret binding is per-operation, not a public registration API.
|
||||
|
||||
Review validation passed on Linux:
|
||||
|
||||
- `./scripts/check.sh` with Go 1.26.7 (formatting, whitespace, tests, vet, build);
|
||||
- `go test -race ./...` with Go 1.26.7;
|
||||
- `GOTOOLCHAIN=go1.25.5 GOPROXY=off go test ./...`;
|
||||
- public reference comparison and isolated baseline/current F1 reproduction;
|
||||
- the isolated F2 negative probe, demonstrating the guard's missed violations.
|
||||
|
||||
Passing existing tests did not waive these findings. The review did not repeat
|
||||
the complete release checker or native macOS validation and is not release
|
||||
approval. The two knowledge-graph artifact modifications present before review
|
||||
were left untouched. Only this remediation document was changed during review.
|
||||
|
||||
## Remediation completion
|
||||
|
||||
All four corrections are implemented and committed as coherent units. Final
|
||||
validation passed on Linux: the everyday check and full race suite on Go 1.26.7,
|
||||
and `go test ./...` on Go 1.25.5. The full public Go reference matches `eb7de8e`;
|
||||
the new version regression was verified failing before its fix; and the original
|
||||
boundary probes now fail the real boundary check and are not retained in production.
|
||||
No release was prepared, and pre-existing knowledge-graph changes are not included
|
||||
in remediation commits.
|
||||
@@ -1,52 +0,0 @@
|
||||
# Source origins during resolution
|
||||
|
||||
Status: implemented (2026-09-12); release preparation remains separate.
|
||||
|
||||
ConfigKit now exposes fixed entry-state provenance during an active
|
||||
`Definition.Resolve` callback:
|
||||
|
||||
```go
|
||||
func (r *Resolution) InputInfo(path Path) (FieldInfo, bool)
|
||||
func (r *Resolution) Source(id string) (SourceInfo, bool)
|
||||
```
|
||||
|
||||
These methods support application-owned normalization and other domain decisions
|
||||
before ConfigKit publishes its frozen result. They are additive: existing loading,
|
||||
composition, decoding, resolution recording, credential timing, and inspection
|
||||
semantics remain unchanged.
|
||||
|
||||
## Canonical behavior
|
||||
|
||||
Go reference comments on `Resolution` own the API/lifetime definition. The
|
||||
[configuration contract](../config.md#recorded-application-resolution) owns entry
|
||||
state, exact path matching, source records, misses, detachment, and callback-scope
|
||||
behavior. The [consumer adoption guide](../consumers/README.md#source-aware-application-path-policy)
|
||||
and maintained [`NotariusPathConfig` example](../../examples/consumers/consumers.go)
|
||||
own the application workflow, including its canonical symlink-base choice.
|
||||
|
||||
`InputInfo` describes decoded configuration at callback entry, including retained
|
||||
defaults and explicit nulls, and is unaffected by subsequent application changes.
|
||||
`Source` resolves records already acquired for that load without filesystem,
|
||||
credential, or parsing work. Both return detached records and require the existing
|
||||
active callback context.
|
||||
|
||||
ConfigKit continues to provide provenance rather than filesystem policy. Consumers
|
||||
choose how to interpret relative paths, select default and CLI bases, and handle
|
||||
missing or composite file provenance. The maintained example leaves values unchanged
|
||||
with a warning when its chosen base is unavailable; it performs no existence or
|
||||
confinement check.
|
||||
|
||||
## Evidence and handoff
|
||||
|
||||
The completed staged record is preserved at
|
||||
`28e7fc5:docs/roadmap/implementation.md`; retrieve it with
|
||||
`git show 28e7fc5:docs/roadmap/implementation.md`. It maps all twelve acceptance
|
||||
criteria to focused lifecycle, provenance, ownership, consumer, explanation, and
|
||||
secret tests. The final implementation commit is expected to run the source
|
||||
checker and the documented two-toolchain gate before any release candidate is
|
||||
selected.
|
||||
|
||||
Publication remains a maintainer decision. Release preparation must choose the
|
||||
version, write a version-matched note, retire this completed roadmap with link
|
||||
repair, obtain the required exact-candidate Linux evidence, and follow the
|
||||
[release procedure](../release.md).
|
||||
Reference in New Issue
Block a user