Add safe historical configuration explanations

This commit is contained in:
2026-09-06 22:14:54 +00:00
parent d4ec8607d1
commit 6ef240b1a0
17 changed files with 1032 additions and 141 deletions

View File

@@ -916,7 +916,9 @@ the [current redaction contract](../config.md#secret-classification-and-effectiv
`Result.Config`, `Metadata`, `Sources`, `Diagnostics`, and `WriteEffective`, together
with `LoadError` and `WriteDiagnostics`, are implemented at the current loading boundary.
The [current loading contract](../config.md#single-source-loading) and Go reference
comments own their behavior. Historical-value explanation below remains target work.
comments own their behavior. Historical-value explanation is also implemented and
owned by the
[current explanation contract](../config.md#historical-value-explanation).
```go
type Result[T any] struct {
@@ -976,74 +978,14 @@ func InspectPipeline(path, profile string, output io.Writer) error {
Applications can use `Metadata().Lookup`, `Metadata().History`, and `Sources()`
for structured inspection without implementing value rendering or importing a
parser. The explanation API below supplies safe values and formatting for application-owned
`config explain` commands.
parser. The implemented explanation API supplies safe values and formatting for
application-owned `config explain` commands.
### Historical-value explanation
```go
func (r *Result[T]) Explain(path Path) (Explanation, bool)
func (r *Result[T]) WriteExplanation(output io.Writer, path Path) error
type Explanation struct {
Path Path
Effective ValueSnapshot
Origin Origin
History []ValueChange
SecretReference *SecretReferenceInfo
}
type ValueSnapshot struct {
Present bool
Redacted bool
YAML string
}
type ValueChange struct {
Before, After ValueSnapshot
Origin Origin
Reasons []string
Dependencies []Path
}
type SecretReferenceInfo struct { Name string } // successful resolved lookup
```
Snapshots contain detached deterministic YAML text, never `any`, parser objects,
or mutable internal trees. `Present=false` is absence/deletion and has empty YAML;
null is present with `null` YAML. Redacted snapshots use the quoted marker
`[REDACTED]`; `Redacted` also marks containers containing redacted descendants.
Apply the same historical sensitivity/dependency closure as effective rendering
before exposing any snapshots. A `Secret` projects only its environment name;
its resolved value is never captured, even privately as history. For classified
reference paths omit `SecretReference` as well as masking the projected name.
Retain private persistent node snapshots or operation deltas with structural
sharing; do not copy the entire tree for every changed leaf. Explain a known final
or historical path, including removed paths; unknown paths return false, and the
writer returns a safe unknown-path error without output. Malformed query paths
also fail safely. Render the canonical JSON-pointer path, effective value/source,
and chronological history: initial application-default (or Go-zero baseline),
base contributions in traversal order, profile, CLI, and recorded domain changes.
Include before/after values for changed or explicitly redefined paths, equal-value
writes, removals, reasons/dependencies, and composite origins. Inputs overridden
before decoding retain policy-validated node values even when they would not fit
the final Go type. Defaults/final/domain snapshots use canonical typed projection;
label those representations rather than claiming every historical input was typed.
Container queries reconstruct the value at each relevant operation; descendant
queries include ancestor replacement/removal and whole-sequence replacements,
without implying element-wise sequence merging. Omitted fields/default retention
must agree with the presence/default contract; do not fabricate a write for every
import that leaves a path alone. Resolution history records the audited net change
per path, not every intermediate Go assignment. Lookup of a secret reference
annotates its effective name as resolved from the process environment; bootstrap
file outcomes come from the separate report and are never guessed.
`WriteExplanation` is a deterministic human renderer over this safe structured
result. Escape control characters in paths, labels, names, and reasons, propagate
writer errors/short writes, and permit partial output only on writer failure.
No unredacted switch or bundled command is provided. For example an application
can call it for `/transcription/concurrency` to show default `4`, an explicitly
imported file's `8`, then a testing profile's `1`, with file line numbers and the
profile as effective source. This does not require automatic `conf.d` discovery.
Historical explanation is implemented. The
[current explanation contract](../config.md#historical-value-explanation) owns its
public records, replay ordering, redaction, rendering, and allocation bounds.
### Module-specific options