Prepare ConfigKit v0.3.0

This commit is contained in:
2026-09-12 18:35:51 +00:00
parent f2c650b5eb
commit 34380f62a5
9 changed files with 82 additions and 1026 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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
View 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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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).