Add redacted effective rendering

This commit is contained in:
2026-09-06 21:21:11 +00:00
parent 26cece6d53
commit 828fdd38eb
14 changed files with 662 additions and 62 deletions

View File

@@ -2,7 +2,7 @@
## Status and ownership
Status: implementation in progress; Stages 1–14 are complete and later stages are
Status: implementation in progress; Stages 1–15 are complete and later stages are
pending.
Audience: a `gpt-5.6-terra` coding agent implementing one stage per prompt, in
@@ -1092,7 +1092,36 @@ Acceptance:
## Stage 15 — Implement secret classification and effective rendering
Status: pending.
Status: complete.
Completion evidence (2026-09-06):
- Removed the dynamic-secret gate and combined descriptor `configkit:"secret"` roots
with a pure application path predicate over final and historical paths. Tagged
containers cover descendants, replaced/deleted definitions stay classified, and
resolution dependencies propagate sensitivity to a fixed point, including
container dependencies and cycles independent of record-map iteration order.
- Added `Result.WriteEffective` over the frozen resolved document. It clones and
redacts before crossing the adapter boundary, sorts mappings by decoded key,
preserves sequence order and nil/empty distinctions, and never invokes application
YAML hooks or text codecs during rendering. Caller mutation cannot change output;
there is no unredacted path or implicit logging.
- Added a private v4 dumper adapter with explicit two-space/noncompact-sequence
indentation, unlimited line width, Unicode, unique keys, block collections,
single-quote preference, LF endings, one document, and disabled start/end markers.
Secret roots render as the quoted string `'[REDACTED]'`; writer errors and short
writes propagate while internal dumper errors collapse to a fixed safe error.
- Tests cover tagged containers, dynamic current and overwritten paths, dependency
cycles, container dependencies, custom text scalars, application marshal-hook
isolation, resolved values, sorted/stable YAML, sequence order, nil/empty forms,
snapshot mutation isolation, writer failures, and canary non-disclosure through
parsing, decoding, returned hook errors, warnings, metadata, and ordinary/Go-syntax
record formatting. The
[current redaction contract](../config.md#secret-classification-and-effective-rendering)
owns the behavior. Historical-value explanation and secret references/bootstrap
remain later work. Verified `go test ./...`, `go test -race ./...`, `go vet ./...`,
`go build ./...`, `go mod tidy -diff`, and `git diff --check` with Go 1.26.7 on
linux/amd64.
Read: feature [redaction](initial.md#provenance-diagnostics-and-redaction),
[secret predicate](initial.md#schema-checking-and-secret-classification), result API.

View File

@@ -470,7 +470,8 @@ The [current application schema contract](../config.md#application-schema-bindin
owns root/type support, field and tag binding, inlining, codec pairing, and
schema-aware path rules. The [current defaults and typed-decoding contract](../config.md#defaults-and-typed-decoding)
owns strict conversions, container application, presence, safe failures, and initial
origins. Secret handling and rendering remain target work below.
origins. Secret classification and effective rendering are owned by the
[current redaction contract](../config.md#secret-classification-and-effective-rendering).
## Recorded resolution and validation
@@ -479,7 +480,7 @@ Application-owned recorded resolution is implemented. The
explicitness queries, helpers and manual records, structural mutation auditing,
coalescing, diagnostics, origins, history, and snapshot ownership. Layered input
history is owned by the [current metadata contract](../config.md#layered-metadata);
redacted explanation and secret dependency propagation remain target work.
redacted historical-value explanation remains target work.
Canonical typed projection, detached cloning, structural comparison, and publishing
independently captured resolved results are implemented. The
@@ -499,7 +500,9 @@ Safe diagnostic/provenance value records, canonical diagnostic rendering, and
Default, base, profile, and resolution records are attached to the loading lifecycle;
their source ordering, composite/synthetic origins, replacement history, and detached
queries are owned by the [current metadata contract](../config.md#layered-metadata).
Safe historical values, redaction, and rendering remain target work below.
Secret classification and effective rendering are owned by the
[current redaction contract](../config.md#secret-classification-and-effective-rendering).
Safe historical values and explanation remain target work below.
Diagnostics have stable codes, severity, semantic path when available, a safe
message, primary location, and related locations. Sort deterministically by
@@ -520,20 +523,6 @@ Applications likewise must supply value-free validation messages and resolution
reasons. ConfigKit cannot sanitize secrets embedded in arbitrary application
prose reliably.
Use `configkit:"secret"` on fields and an application path-classification hook
for dynamic entries. A secret container makes descendants secret. Secret
classification applies conservatively across current and historical definitions
of a path: replacing a secret does not declassify its history. It propagates from
recorded secret dependencies to derived outputs. Applications remain responsible
for marking secret-producing transformations whose dependencies are not recorded.
Provide source reporting and redacted effective rendering in the initial target.
Render from a canonical projection of the final typed snapshot, including zero
values and null/empty distinctions supported by the schema. Redacted output is
for inspection and is not promised to be reloadable. ConfigKit renderers must
not expose raw secrets; returning typed configuration to the application is an
intentional trusted boundary. ConfigKit does not log configuration on its own.
Provide full safe historical-value explanation in the initial release, as defined
[below](#historical-value-explanation). Defer config diffs and fingerprints. A future fingerprint design
must specify canonicalization, secret handling, and disclosure risks before
@@ -568,9 +557,9 @@ and verification against consumer models.
`Load`, `Definition`, `Options`, `FilesystemContext`, `Override`, `VersionRule`,
`Version`, `Validation`, and `Result` are declared. The current loader supports the
callback and path-import subset described by the [current contract](../config.md);
later override, secret, and rendering behavior below remains target work and
unavailable options fail explicitly rather than being ignored.
implemented boundary described by the [current contract](../config.md). Explicit
overrides, secret references/bootstrap, and historical-value explanation remain
target work; unavailable options fail explicitly rather than being ignored.
```go
func Load[T any](source Source, options ...Options[T]) (*Result[T], error)
@@ -863,8 +852,9 @@ potentially sensitive configured values.
`VersionRule`, `Version`, and the version-checking use of `Validation` are
implemented. The [current version contract](../config.md#application-schema-versions)
owns absent/null/string handling, report locations, warnings, failure behavior, and
raw-error suppression. Secret classification and later composed-source placement
checks remain target work.
raw-error suppression. The
[current redaction contract](../config.md#secret-classification-and-effective-rendering)
owns secret classification.
```go
type VersionRule struct {
@@ -918,19 +908,15 @@ versions remain strings under ConfigKit's convention; adopting consumers with
integer versions must migrate their syntax rather than enable a second scalar
policy.
`Definition.Secret` is an optional pure path predicate for dynamic entries,
combined with secret field tags. It can add sensitivity but cannot override a
tag to declassify a value. It is applied to historical as well as final paths.
Recorded dependencies propagate sensitivity under the existing redaction rules;
applications own secret-producing transformations not described by those records.
`Definition.Secret` and `configkit:"secret"` behavior are implemented and owned by
the [current redaction contract](../config.md#secret-classification-and-effective-rendering).
### Results, errors, and inspection
`Result.Config`, `Metadata`, `Sources`, and `Diagnostics`, together with
`LoadError` and `WriteDiagnostics`, are implemented at the current loading boundary.
`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. Effective rendering and richer layered inspection
below remain target work.
comments own their behavior. Historical-value explanation below remains target work.
```go
type Result[T any] struct {
@@ -961,12 +947,9 @@ locations as described [above](#provenance-diagnostics-and-redaction). No raw
parser error or unrestricted secret-bearing history is exposed. No automatic
logging occurs. Inspection methods returning slices provide defensive copies.
`Config` is caller-owned typed data. Metadata and rendering use an independently
captured final snapshot, so later caller mutation cannot rewrite the recorded
result. `WriteEffective` emits deterministic redacted YAML for inspection, with
no unredacted switch. It returns output errors rather than a new configuration validation result.
Custom projection failures must already have been rejected before a load
succeeds. Output can be partial on writer failure.
Caller-owned configuration, the frozen result snapshot, secret classification, and
`WriteEffective` behavior are owned by the
[current redaction contract](../config.md#secret-classification-and-effective-rendering).
An application can report errors and inspect a successful load without rebuilding
diagnostic formatting. This example uses `errors` and `io` and the pipeline