Document and audit effective configuration diffs

This commit is contained in:
2026-10-09 12:42:17 +00:00
parent a37e6895e1
commit 2ebdfb539b
9 changed files with 133 additions and 82 deletions

View File

@@ -423,6 +423,46 @@ an explicit `LoadSecretsDir` bootstrap, then use `WriteDiagnostics`,
ConfigKit does not infer bootstrap outcomes from a secret reference and does not
bundle these independent reports into a command framework.
## Effective configuration diffs
`configkit.Diff(left, right)` compares two completed successful loads of the same
configuration type. `left` is the earlier state and `right` is the later state.
Applications load both sides with the sources and options they choose; to inspect
an overlay's impact, load the same base once without the overlay and once with it
selected. The [consumer guide](consumers/README.md#effective-configuration-diffs)
links to maintained examples. The public API returns safe changed-region records;
applications own their presentation.
Comparison uses the frozen canonical effective inspection values, not mutable
`Result.Config`, source text, provenance, or history. It does not reopen sources,
reread environment variables, or rerun defaults or callbacks. Equal effective
values produce no records even when sources, explicit-input status, defaults, or
resolution histories differ; for example, explicit zero and defaulted zero are
equal. Mappings compare recursively, sequences compare atomically, and scalar,
kind, or null/container replacements report their affected region. Records use
the existing comparator's canonical pointer ordering. `Present=false` with empty
YAML means absence; explicit null is present and renders as `null`.
Before either side is returned, sensitivity from both loads is applied to both
snapshots, including historical and dependency-derived classification. A
classification on either side protects the corresponding values on both sides.
`Redacted` reports masking, while `HiddenChanged` reports an actual concealed
value or presence change; it is not implied merely by redaction. A structural
replacement that prevents safe correspondence masks the complete reported
region, and an absent side remains absent. Record membership establishes that a
change occurred even when safe snapshots render identically. Literal
marker-looking input is ordinary input; consumers use the record fields rather
than interpreting marker text.
Typed secret references contribute their configured environment names to the
frozen comparison, never resolved credential bytes. Credential rotation behind
an unchanged reference is therefore not detected. The records contain changed
regions only, not paired full-document context; independently calling
`WriteEffective` on each result does not create cross-protected context. Nil or
uninitialized results return an error with no records. A failed `Load` retains
its normal nil-result/`LoadError` behavior, which callers handle before invoking
`Diff`.
## Typed secret references
Credential-bearing fields use the opaque `Secret` type and store an environment

View File

@@ -18,6 +18,7 @@ sanitized shapes using only ConfigKit and the Go standard library:
| Bundled file | Simple native `fs.FS` source through ordinary `Load` | Selection and lifetime of the application-owned embedded filesystem |
| Layered catalog | Embedded defaults with an import, a mapping underlay, an independent host main file, explicit false/zero input, and source-relative resource access | Domain validation and opening/interpreting the template resource |
| Named presets | One-load sibling inheritance, explicit false, nested destination resolution, and source/derivation inspection | Choosing and executing a preset |
| Effective configuration diff | Same-type frozen effective comparisons for two selected files or base-versus-overlay impact | Source/options selection and consumer-owned safe presentation |
The YAML under [`testdata`](../../examples/consumers/testdata) is loaded by the
executable examples and ordinary tests. It contains no private application data,
@@ -51,6 +52,25 @@ both without redefining their syntax. ConfigKit overlays are configuration overl
they do not select or replace an application's LLM, model, pipeline, report, or
execution profile concepts.
## Effective configuration diffs
[`CompareFiles`](../../examples/consumers/diff.go) loads two caller-selected files
with one application definition, handles each load error, calls `configkit.Diff`,
and presents the safe records through consumer-owned `FormatDifferences`.
[`CompareRolloutOverlay`](../../examples/consumers/diff.go) loads the same base
once without an overlay and once with the selected overlay, so omitted lower
priority values remain present. Its resolver records a derived endpoint URL after
overlay application, and its nested typed secret reference is classified before
the resulting records are presented.
The maintained [behavior tests and executable examples](../../examples/consumers/diff_test.go)
use temporary files and disposable secret values. Presenters should use
`ValueSnapshot.Present` for absence, record membership for change, and
`HiddenChanged` for a withheld change indicator. Do not parse the redaction marker
or add separately rendered full contexts that lack combined sensitivity. The
[configuration contract](../config.md#effective-configuration-diffs) owns shared
equality, granularity, and disclosure semantics.
## Named sibling inheritance
[`LoadPresets`](../../examples/consumers/inheritance.go) loads the maintained
@@ -168,6 +188,7 @@ focused behavioral owner:
| `LoadSecretsDir`, bootstrap reporting, typed lookup, CLI explanation | `Example_weatherStartup` |
| Native filesystem, grouped underlays, programmatic mapping, retained resource context | `Example_nativeFilesystem`, `Example_layeredCatalog`, and their focused consumer tests |
| Named sibling inheritance, presence, and derivation | `Example_inheritedPresets` and `TestPresetCatalogOneLoadAndDerivation` |
| Effective configuration diffs and consumer-owned presentation | `ExampleDiff`, `ExampleCompareFiles`, and `ExampleCompareRolloutOverlay` |
| Results, metadata, diagnostics, effective output, and writer failures | Focused root-package load, provenance, rendering, and explanation tests |
The package-level import-boundary test covers the consumer examples too: supported

View File

@@ -54,6 +54,7 @@ Accepted decisions:
- [Secret references and explicit bootstrap](adr/0004-secret-references-and-explicit-bootstrap.md)
- [Explicit overrides and safe explanation](adr/0005-explicit-overrides-and-safe-explanation.md)
- [Keep the public facade root-owned](adr/0006-public-facade.md)
- [Compare frozen effective configuration snapshots](adr/0010-effective-configuration-diff.md)
For cross-cutting changes, follow every applicable row. Completed implementation
and remediation plans are preserved in Git history at `cb5b435`; use current
@@ -161,6 +162,7 @@ code changes. Use the component overview to find the corresponding implementatio
| Paths, metadata, provenance | [Paths](../internal/engine/path_test.go), [metadata](../internal/engine/metadata_test.go), [provenance](../internal/engine/provenance_test.go) | `go test ./internal/engine -run 'Path\|Metadata\|Provenance\|Queries'` |
| Passive resource contexts | [Engine contexts](../internal/engine/resource_context_test.go), [public contexts](../facade_resource_context_test.go) | `go test ./internal/engine -run 'Resource'` and `go test . -run 'SourceContext'` |
| Secrets, bootstrap, redaction, explanation | [References](../internal/engine/secret_reference_test.go), [bootstrap](../internal/engine/secrets_test.go), [rendering](../internal/engine/render_test.go), [explanation](../internal/engine/explain_test.go) | `go test ./internal/engine -run 'Secret\|Bootstrap\|Effective\|Explain\|Explanation\|History\|Sensitivity'` |
| Effective configuration diff | [Engine comparison and masking tests](../internal/engine/diff_test.go), [engine operation tests](../internal/engine/diff_operation_test.go), [root facade tests](../facade_diff_test.go), and [consumer examples](../examples/consumers/diff_test.go) | `go test ./internal/engine -run 'Diff|Explain|History|Secret|Independent'`, `go test . -run 'Diff|FacadeInspection|PublicFacadeBoundary|DependencyImportBoundary'`, `go test ./examples/consumers`, and `go test . -run '^ExampleDiff$'` |
| Consumer examples and migration fixtures | [Consumer tests](../examples/consumers/consumers_test.go), [native and layered examples](../examples/consumers/layered_test.go), [API examples](../example_test.go) | `go test ./examples/consumers` and `go test . -run '^Example'` |
| Assembly and layered scaling evidence | [Assembly benchmarks](../internal/engine/assembly_bench_test.go), [layered benchmarks](../internal/engine/underlay_bench_test.go) | `go test ./internal/engine -run '^$' -bench '^BenchmarkUnderlay' -benchmem`; repeat with `^BenchmarkCompose` and `^BenchmarkOverlay` |
| Inheritance scaling evidence | [Expansion benchmarks](../internal/engine/inheritance_bench_test.go) | `go test ./internal/engine -run '^$' -bench '^BenchmarkInheritance' -benchmem` |

View File

@@ -12,7 +12,8 @@ Implemented component owners:
sibling inheritance before typed decoding. It also owns schema
inspection, lifecycle coordination, callback auditing,
provenance/history, passive source-context retention, secret resolution and
bootstrap, and safe inspection rendering. It never imports the root package.
bootstrap, safe inspection rendering, and effective-value comparison over
frozen inspection snapshots. It never imports the root package.
- Root `Source`, `Filesystem`, `Input`, `Underlay`, `Path`, `Secret`, records,
lifecycle callbacks, results, and bootstrap reports are root-defined wrappers
or records. Callback wrappers pass only live engine context during a callback;
@@ -21,6 +22,9 @@ Implemented component owners:
- Root conversion files explicitly cross the path, record, callback, secret, and
error boundaries. There are no public aliases, anonymous engine embeddings, or
public engine bridge methods.
- Root `Diff` and `Difference` provide the public comparison boundary and
convert detached engine records to root-owned paths and snapshots; consumer
examples own presentation of the safe changed regions.
- `internal/document` owns the parser-independent ordered tree and cloning.
`internal/yaml` is the sole upstream YAML adapter and parser/dumper boundary.
- Engine mechanism tests own source, composition, inheritance, decoding,
@@ -37,9 +41,11 @@ these constraints and rejects exported aliases, fields, or signatures involving
internal types.
The [configuration contract](../config.md) owns implemented YAML and lifecycle
semantics. The [consumer adoption guide](../consumers/README.md) owns application
guidance. The [architecture policy](../policy/architecture.md) owns contributor
boundaries, and [ADR 0006](../adr/0006-public-facade.md) records the facade
semantics, including [effective configuration diffs](../config.md#effective-configuration-diffs).
The [consumer adoption guide](../consumers/README.md) owns application guidance.
The [architecture policy](../policy/architecture.md) owns contributor
boundaries; [ADR 0006](../adr/0006-public-facade.md) records the facade decision
and [ADR 0010](../adr/0010-effective-configuration-diff.md) records the diff
decision. See the [development guide](../development.md) for validation commands
and [release procedure](../release.md) for source-candidate gates. [ADR
0009](../adr/0009-sibling-inheritance.md) records why inheritance is a built-in

View File

@@ -201,6 +201,13 @@ See [ADR 0005](../adr/0005-explicit-overrides-and-safe-explanation.md).
Detailed syntax and operation tables belong to the
[current configuration contract](../config.md).
The frozen inspection boundary also supports comparing completed same-type
results without reopening sources or rerunning the load lifecycle. The
[configuration contract](../config.md#effective-configuration-diffs) owns
comparison and disclosure behavior, the [component overview](../internal/overview.md)
owns implementation responsibility, and [ADR 0010](../adr/0010-effective-configuration-diff.md)
records the accepted decision.
## Ownership And Confidentiality
Returned configuration and metadata must not share mutable state across loads.

View File

@@ -23,12 +23,18 @@ The following remain deliberately excluded:
- automatic schema migrations;
- hot reload, runtime resource replacement, and remote resource validation;
- a bundled CLI framework or application commands; and
- configuration diffs and fingerprints.
- configuration fingerprints.
Applications may build their own validate, effective-value, source, bootstrap, and
explanation commands around the library. Broadening these boundaries requires
concrete consumer evidence, compatibility review, and an explicit design change.
Effective configuration diffs are implemented over frozen successful-load
inspections; their current comparison and disclosure contract is in the
[configuration contract](../config.md#effective-configuration-diffs), with
consumer workflows in the [adoption guide](../consumers/README.md#effective-configuration-diffs).
Fingerprints remain deferred.
Built-in sibling inheritance using `extends` is implemented as described in the
[configuration contract](../config.md#sibling-inheritance). It is distinct from
inheritance between selected overlay files, which remains excluded above.

View File

@@ -1,9 +1,9 @@
# Effective Configuration Diff Roadmap
Status: design agreed; implementation has not started. This document owns the
targeted end state, selected contract, and work needed from the current baseline.
The [implementation plan](implementation.md) owns execution order. No diff API
is available yet.
Status: implemented. This document preserves the selected contract and scope;
the [configuration contract](../config.md#effective-configuration-diffs) owns
current behavior, and the [implementation record](implementation.md) preserves
the completed sequence and validation evidence.
## Targeted end state
@@ -158,11 +158,10 @@ renderer. A diff may describe removals that existing overlay semantics cannot
express. These boundaries define this feature's selected scope, not intrinsic
correctness restrictions on every possible future extension.
## Current baseline
## Implementation references
- [Deferred scope](deferred.md) currently excludes configuration diffs and
fingerprints. This design reconsiders only the diff boundary; fingerprints
and unrelated exclusions remain deferred.
- [Deferred scope](deferred.md) continues to exclude fingerprints and unrelated
items; effective configuration diffs are implemented.
- The [configuration contract](../config.md#typed-projection-and-ownership)
describes canonical typed projection and private structural comparison.
[The existing comparator](../../internal/engine/document_compare.go) reports
@@ -181,69 +180,20 @@ correctness restrictions on every possible future extension.
- The [public facade](../internal/overview.md) is root-owned and delegates to
the engine. The new operation and records follow that same boundary.
## Work needed from the baseline
## Implementation outcome
### Architecture and scope documentation
The accepted inspection boundary is recorded in
[ADR 0010](../adr/0010-effective-configuration-diff.md), and the deferred
inventory retains fingerprints while recognizing implemented diffs. The engine
owns canonical comparison and paired disclosure in
[internal/engine/diff.go](../../internal/engine/diff.go); the root facade exposes
detached records in [diff.go](../../diff.go). The configuration
contract, consumer guidance, component overview, architecture policy, and
development guide now link to their respective behavior and implementation
owners.
Record the inspection boundary extension in an ADR and reconcile the diff
exclusion in `deferred.md`, retaining fingerprints and unrelated exclusions.
Keep planned behavior here until implementation lands. The staged implementation
plan should reference the selected contract above rather than reopen it.
### Private comparison and disclosure
Add an engine operation over two successful frozen inspections, reusing the
existing comparator where its semantics match this contract. Sharing helpers
must preserve resolution and validation auditing behavior.
Implement cross-configuration masking, structural-replacement fallback, and
`HiddenChanged`. Compare canonical values rather than serialized redacted
output, caller-mutated configuration values, or historical replay. Construct
the needed safe snapshots without replaying every path's full explanation
history. Keep comparison and disclosure distinct so masking does not erase
change detection.
### Root API and consumer guidance
Add root-owned records, the comparison entry point, and explicit conversion
across the engine boundary. Document direction, equality, failures, ownership,
secret behavior, and the credential limitation beside declarations.
Add executable examples for loading both files and handling each load failure,
and for comparing a base result with a base-plus-overlay result. Include
resolution and sensitive nested values, demonstrating consumer-owned formatting
and changed-secret indicators with disposable data.
### Behavioral validation and completion
Use engine tests for comparison and combined sensitivity, root tests for the
facade and ownership, and consumer examples for the complete workflows. Cover
meaningful risks without duplicating all existing loading tests:
- equality despite mapping order, provenance, or explicit-input differences;
canonical equality after defaults and resolution;
- addition/removal versus explicit null, empty containers, scalar and kind
replacements, atomic sequence changes, and escaped or numeric mapping keys;
- deterministic ordering and reported subtree granularity;
- changed and unchanged sensitive values, classification on only one side,
removed secrets, classified containers, and mixed public/sensitive changes;
- cross-kind replacement of a classified descendant, incompatible key/index
shapes, and complete-region masking without changing absence semantics;
- hidden changes with identical safe YAML, public-only changes alongside
unchanged masked values with `HiddenChanged=false`, and literal marker-like
input;
- changed secret reference names and unchanged names with credential rotation,
using controlled environment state;
- invalid left/right loads, unavailable results, caller mutation after loading,
and returned-record mutation isolation.
Use disposable canaries to check non-disclosure, including failure paths. Follow
the focused, everyday, ownership-race, and documentation checks in the
[development guide](../development.md). New performance benchmarks are not
required for this feature.
Update the configuration contract, consumer guidance, and component overview
when their behavior lands. Keep symbol details in Go declaration documentation.
Review compatibility and release notes under the release policy; publication is
separate from feature completion. Completion means the selected contract is
implemented, checks pass, and maintained examples and documentation agree.
Validation and coverage are recorded in the completed
[implementation plan](implementation.md). The implementation compares frozen
effective values, protects both sides with combined sensitivity, retains the
selected credential boundary, and provides executable file and overlay
workflows. Publication and release preparation remain separate.

View File

@@ -1,8 +1,9 @@
# Implement effective configuration diffs
Status: planned. Audience: a Luna coding agent implementing one stage per prompt,
in numerical order. The [diff roadmap](diff.md) owns the agreed target contract
and scope. This document owns implementation steps and completion evidence.
Status: complete. The [diff roadmap](diff.md) owns the agreed target contract and
scope. This document preserves the implementation sequence and completion
evidence; completed planning records retire during separately authorized release
preparation.
Planning baseline: `e82b1c90543a6ab92138ee8c0c2a4cd4daea8af0`.
No diff API exists at this baseline. This plan adds comparison of successful
@@ -385,7 +386,7 @@ redaction marker; no full-context rendering supplements the diff records.
## Stage 10 — Reconcile documentation and audit the complete feature
Status: planned. Scope: current-behavior documentation, integration audit, and
Status: complete. Scope: current-behavior documentation, integration audit, and
completion evidence; no release publication.
1. Add an effective-diff section to `docs/config.md` for shared equality,
@@ -417,6 +418,20 @@ completion evidence; no release publication.
Done: the agreed feature is implemented, validated, and documented without
stronger unrelated guarantees or changes to the publication contract.
Evidence: the planned feature baseline was `e82b1c90543a6ab92138ee8c0c2a4cd4daea8af0`;
the reviewed implementation through consumer examples is commit `a37e689`,
following the accepted decision at `3697a51` and public facade at `8ae542b`.
The complete baseline-to-implementation diff was reviewed against this plan and
the selected contract. `go test ./internal/engine -run 'Diff|Explain|History|Secret|Independent'`,
`go test . -run 'Diff|FacadeInspection|PublicFacadeBoundary|DependencyImportBoundary'`,
`go test ./examples/consumers`, `go test . -run '^ExampleDiff$'`, the local
documentation-link test, and `./scripts/check.sh` passed on
`go1.26.8 linux/amd64`. `GOTOOLCHAIN=local GOPROXY=off go test -race ./...` also passed.
The feature does not compare resolved credential bytes or supply paired full-
document rendering context; credential rotation behind the same reference is
not reported. Fingerprints remain deferred, no performance claim is made, and
release publication remains separate.
## Open Questions
None required for this scope. The roadmap's four choices are approved; the