Prepare inspection history before publication

This commit is contained in:
2026-10-25 21:53:24 +00:00
parent 247a12bc50
commit 4fcff5f453
3 changed files with 53 additions and 35 deletions

View File

@@ -375,6 +375,13 @@ Focused check: `go test ./internal/engine -run 'Sequence|Collection|Inheritance|
Finding: preparation portion of **D01**. Finding: preparation portion of **D01**.
Owners: `internal/engine/load.go`, `load_result.go`, `history.go`, `inheritance_load.go`. Owners: `internal/engine/load.go`, `load_result.go`, `history.go`, `inheritance_load.go`.
Status: **complete** (preparation portion of D01). Prior completed commits:
`2736ddf`, `deb9aa5`, `ae3408a`, `ea27f17`, `08a8c25`, `877c497`,
`8aba43d`, `7bf2f0c`, `0f9ecf1`, `119b42c`, `247a12b`. Validation passed:
`env GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off go test ./internal/engine -run 'Load|History|Explain|Inheritance|Validation|Secret' -count=1`,
`env GOTOOLCHAIN=local GOPROXY=off GOTELEMETRY=off go test ./internal/engine -count=1`,
and `git diff --check`.
1. Extract preparation of the existing `Inspection` document/history fields from 1. Extract preparation of the existing `Inspection` document/history fields from
publication. Produce the actual owned pre-CLI copy, inherited-operation copies, publication. Produce the actual owned pre-CLI copy, inherited-operation copies,
application lists, raw resolution snapshots, and coalesced replay that the result application lists, raw resolution snapshots, and coalesced replay that the result
@@ -396,6 +403,12 @@ Acceptance: existing lifecycle and explanation behavior stays intact. Document
the concrete retained document fields for Stage 13. This preparatory stage alone the concrete retained document fields for Stage 13. This preparatory stage alone
does not close D01; avoid temporary public state or compatibility APIs if the does not close D01; avoid temporary public state or compatibility APIs if the
handoff is not yet buildable. handoff is not yet buildable.
Prepared retained roots for Stage 13 admission are `Inspection.baseline`,
`input`, `final`, `preCLI`, source/CLI application values, raw and coalesced
resolution before/after values, and inspection inheritance parent/effective
values. `Metadata.inheritance` holds its separate cloned parent/effective values;
the immutable document lookup is built only during publication.
Focused check when buildable: `go test ./internal/engine -run 'Load|History|Explain|Inheritance|Validation|Secret'`. Focused check when buildable: `go test ./internal/engine -run 'Load|History|Explain|Inheritance|Validation|Secret'`.
## Stage 13 — Enforce The Quota On The Prepared Published Graph ## Stage 13 — Enforce The Quota On The Prepared Published Graph

View File

@@ -341,8 +341,11 @@ func executeLoadRequest[T any](request loadRequest[T], secrets SecretBinding, lo
if hasErrors(diagnostics) { if hasErrors(diagnostics) {
return nil, accumulator.loadError() return nil, accumulator.loadError()
} }
historyApplications := append(cloneHistoryApplications(resolved.applications), cloneHistoryApplications(resolved.postDecodeApps)...) inspection := prepareInspectionHistory(resolved)
if !historyNodesWithinLimit(decoded.baseline, decoded.final, resolved.final, historyApplications, resolved.snapshots, input.inheritance) { historyApplications := make([]historyApplication, 0, len(inspection.applications)+len(inspection.postDecodeApps))
historyApplications = append(historyApplications, inspection.applications...)
historyApplications = append(historyApplications, inspection.postDecodeApps...)
if !historyNodesWithinLimit(inspection.baseline, inspection.input, inspection.final, historyApplications, inspection.resolution, input.inheritance) {
accumulator.add(Diagnostic{Code: CodeExplainLimitExceeded, Severity: SeverityError, Message: "retained configuration history exceeds the node limit"}) accumulator.add(Diagnostic{Code: CodeExplainLimitExceeded, Severity: SeverityError, Message: "retained configuration history exceeds the node limit"})
return nil, accumulator.loadError() return nil, accumulator.loadError()
} }
@@ -373,5 +376,5 @@ func executeLoadRequest[T any](request loadRequest[T], secrets SecretBinding, lo
resolved.configuration = configuration resolved.configuration = configuration
// Publication detaches equal-valued validator replacements, preserving the // Publication detaches equal-valued validator replacements, preserving the
// validated secret state, then finalizes metadata and sensitivity. // validated secret state, then finalizes metadata and sensitivity.
return publishLoadResult(schema, request.definition, resolved, secretResolution.references, &accumulator) return publishLoadResult(schema, request.definition, resolved, inspection, historyApplications, secretResolution.references, &accumulator)
} }

View File

@@ -72,7 +72,7 @@ func resolveLoadInput[T any](schema *schemaDescriptor, definition Definition[T],
} }
applications := []historyApplication(nil) applications := []historyApplication(nil)
if input.composition != nil { if input.composition != nil {
applications = cloneHistoryApplications(input.composition.applications) applications = input.composition.applications
} else if input.document != nil { } else if input.document != nil {
applications = []historyApplication{{layer: LayerBase, value: input.document}} applications = []historyApplication{{layer: LayerBase, value: input.document}}
} }
@@ -83,8 +83,8 @@ func resolveLoadInput[T any](schema *schemaDescriptor, definition Definition[T],
return resolvedLoad[T]{ return resolvedLoad[T]{
configuration: configuration, decoded: decoded, metadata: metadata, final: final, configuration: configuration, decoded: decoded, metadata: metadata, final: final,
snapshots: resolutionSnapshots, applications: applications, snapshots: resolutionSnapshots, applications: applications,
postDecodeApps: cloneHistoryApplications(prepared.postDecodeApps), structuralInput: document.Clone(prepared.structuralInput), postDecodeApps: prepared.postDecodeApps, structuralInput: document.Clone(prepared.structuralInput),
preCLI: document.Clone(prepared.preCLI), preCLIOrigins: cloneOrigins(prepared.preCLIOrigins), referenceInput: referenceInput, preCLI: prepared.preCLI, preCLIOrigins: prepared.preCLIOrigins, referenceInput: referenceInput,
versionEvidence: versionEvidence, inheritance: input.inheritance, contexts: input.contexts, versionEvidence: versionEvidence, inheritance: input.inheritance, contexts: input.contexts,
}, diagnostics }, diagnostics
} }
@@ -99,7 +99,24 @@ func detachLoadConfiguration[T any](schema *schemaDescriptor, configuration T, m
return detached.Interface().(T), nil return detached.Interface().(T), nil
} }
func publishLoadResult[T any](schema *schemaDescriptor, definition Definition[T], resolved resolvedLoad[T], references map[string]string, accumulator *diagnosticAccumulator) (*Loaded[T], error) { func prepareInspectionHistory[T any](resolved resolvedLoad[T]) *Inspection {
return &Inspection{
metadata: resolved.metadata,
baseline: resolved.decoded.baseline,
input: resolved.decoded.final,
inputOrigins: cloneOriginIndex(resolved.decoded.origins),
preCLI: resolved.preCLI,
preCLIOrigins: cloneOrigins(resolved.preCLIOrigins),
postDecodeApps: cloneHistoryApplications(resolved.postDecodeApps),
final: resolved.final,
resolution: resolved.snapshots,
resolutionReplay: coalescedResolutionSnapshots(resolved.snapshots),
applications: cloneHistoryApplications(resolved.applications),
inheritance: inheritanceOperations(resolved.inheritance),
}
}
func publishLoadResult[T any](schema *schemaDescriptor, definition Definition[T], resolved resolvedLoad[T], inspection *Inspection, applications []historyApplication, references map[string]string, accumulator *diagnosticAccumulator) (*Loaded[T], error) {
// A read-only validator can replace a value with an equal borrowed object. // A read-only validator can replace a value with an equal borrowed object.
// Detach without re-reading the environment or discarding validated state. // Detach without re-reading the environment or discarding validated state.
configuration, issue := detachLoadConfiguration(schema, resolved.configuration, cloneValidatedSecrets) configuration, issue := detachLoadConfiguration(schema, resolved.configuration, cloneValidatedSecrets)
@@ -107,38 +124,23 @@ func publishLoadResult[T any](schema *schemaDescriptor, definition Definition[T]
accumulator.add(*issue) accumulator.add(*issue)
return nil, accumulator.loadError() return nil, accumulator.loadError()
} }
decoded, metadata := resolved.decoded, resolved.metadata decoded := resolved.decoded
final, applications, resolutionSnapshots := resolved.final, resolved.applications, resolved.snapshots metadata, final, preCLI := inspection.metadata, inspection.final, inspection.preCLI
preCLI := document.Clone(resolved.preCLI) finalizeMetadataPaths(&metadata, final, applications, decoded.baseline, resolved.structuralInput, resolved.preCLI, decoded.final)
allApplications := append(cloneHistoryApplications(applications), cloneHistoryApplications(resolved.postDecodeApps)...) secrets := classifySecretPathsWithEvidence(schema, definition.Secret, metadata, final, applications, resolved.versionEvidence, decoded.baseline, resolved.structuralInput, resolved.preCLI, decoded.final)
finalizeMetadataPaths(&metadata, final, allApplications, decoded.baseline, resolved.structuralInput, resolved.preCLI, decoded.final)
secrets := classifySecretPathsWithEvidence(schema, definition.Secret, metadata, final, allApplications, resolved.versionEvidence, decoded.baseline, resolved.structuralInput, resolved.preCLI, decoded.final)
sources := sourceInfosWithVersionEvidence(accumulator.sources, resolved.versionEvidence, secrets, false) sources := sourceInfosWithVersionEvidence(accumulator.sources, resolved.versionEvidence, secrets, false)
retainedTrees := make([]*document.Node, 0, len(allApplications)+4) retainedTrees := make([]*document.Node, 0, len(applications)+4)
retainedTrees = append(retainedTrees, decoded.final, final, preCLI) retainedTrees = append(retainedTrees, decoded.final, final, preCLI)
for _, application := range allApplications { for _, application := range applications {
retainedTrees = append(retainedTrees, application.value) retainedTrees = append(retainedTrees, application.value)
} }
inspection := &Inspection{ inspection.metadata = metadata
metadata: metadata, inspection.sources = sources
sources: sources, inspection.contexts = cloneResourceContexts(resolved.contexts)
contexts: cloneResourceContexts(resolved.contexts), inspection.diagnostics = canonicalDiagnostics(accumulator.diagnostics)
diagnostics: canonicalDiagnostics(accumulator.diagnostics), inspection.immutableLookup = buildDocumentLookup(retainedTrees...)
baseline: decoded.baseline, inspection.secrets = secrets
input: decoded.final, inspection.secretReferences = cloneSecretReferences(references)
inputOrigins: cloneOriginIndex(decoded.origins),
preCLI: preCLI,
preCLIOrigins: cloneOrigins(resolved.preCLIOrigins),
postDecodeApps: cloneHistoryApplications(resolved.postDecodeApps),
final: final,
immutableLookup: buildDocumentLookup(retainedTrees...),
resolution: resolutionSnapshots,
resolutionReplay: coalescedResolutionSnapshots(resolutionSnapshots),
applications: applications,
inheritance: inheritanceOperations(resolved.inheritance),
secrets: secrets,
secretReferences: cloneSecretReferences(references),
}
return &Loaded[T]{Config: configuration, Inspection: inspection}, nil return &Loaded[T]{Config: configuration, Inspection: inspection}, nil
} }