Audit registered collection changes by identity

This commit is contained in:
2026-11-02 14:43:48 +00:00
parent 7f004f67ad
commit d120fe907b
8 changed files with 166 additions and 20 deletions

View File

@@ -59,16 +59,19 @@ func (record *resolutionRecord) addDependency(path Path) {
}
type resolutionSnapshot struct {
change Change
before *document.Node
after *document.Node
change Change
before *document.Node
after *document.Node
entryPath Path
hasEntry bool
}
type resolutionResult struct {
final *document.Node
deltas []documentDelta
snapshots []resolutionSnapshot
diagnostics []Diagnostic
final *document.Node
deltas []documentDelta
snapshots []resolutionSnapshot
diagnostics []Diagnostic
correspondence resolutionCorrespondence
}
// IsExplicit reports whether surviving direct or inherited input defined path.
@@ -289,7 +292,7 @@ func (state *resolutionState) addDiagnostic(code Code, path Path, message string
state.diagnostics = append(state.diagnostics, diagnostic)
}
func runResolution[T any](configuration *T, resolver func(*T, *Resolution) error, schema *schemaDescriptor, decoded decodedConfiguration, input *document.Node, entry resolutionInput) resolutionResult {
func runResolution[T any](configuration *T, resolver func(*T, *Resolution) error, schema *schemaDescriptor, decoded decodedConfiguration, input *document.Node, entry resolutionInput, policy sequencePolicy) resolutionResult {
state := &resolutionState{
active: true, schema: schema, document: input,
input: entry,
@@ -313,7 +316,8 @@ func runResolution[T any](configuration *T, resolver func(*T, *Resolution) error
state.addDiagnostic(CodeResolveInvalidValue, issue.path, "application resolution produced an invalid configuration value")
return resolutionResult{diagnostics: canonicalDiagnostics(state.diagnostics)}
}
deltas := compareResolutionValues(decoded.final, after)
correspondence := buildResolutionCorrespondence(decoded.final, after, policy)
deltas := compareResolutionValues(decoded.final, after, correspondence)
ordered := orderedResolutionRecords(state.records)
coverage := newResolutionCoverageIndexFromRecords(ordered)
beforeLookup := buildDocumentLookup(decoded.final)
@@ -324,7 +328,9 @@ func runResolution[T any](configuration *T, resolver func(*T, *Resolution) error
}
}
for _, record := range ordered {
_, beforePresent := documentAtPathWithLookup(decoded.final, record.path, beforeLookup)
beforePath, hasEntry := correspondence.entryPath(record.path)
_, beforePresent := documentAtPathWithLookup(decoded.final, beforePath, beforeLookup)
beforePresent = beforePresent && hasEntry
afterNode, afterPresent := documentAtPathWithLookup(after, record.path, afterLookup)
if !beforePresent && !afterPresent {
state.addDiagnostic(CodeResolveInvalidRecord, record.path, "resolution record does not identify an existing or removed value")
@@ -340,7 +346,11 @@ func runResolution[T any](configuration *T, resolver func(*T, *Resolution) error
snapshots := make([]resolutionSnapshot, 0, len(ordered))
for _, record := range ordered {
beforeNode, _ := documentAtPathWithLookup(decoded.final, record.path, beforeLookup)
beforePath, hasEntry := correspondence.entryPath(record.path)
var beforeNode *document.Node
if hasEntry {
beforeNode, _ = documentAtPathWithLookup(decoded.final, beforePath, beforeLookup)
}
afterNode, _ := documentAtPathWithLookup(after, record.path, afterLookup)
snapshots = append(snapshots, resolutionSnapshot{
change: Change{
@@ -349,11 +359,11 @@ func runResolution[T any](configuration *T, resolver func(*T, *Resolution) error
Reasons: append([]string(nil), record.reasons...),
Dependencies: clonePaths(record.dependencies),
},
before: document.Clone(beforeNode), after: document.Clone(afterNode),
before: document.Clone(beforeNode), after: document.Clone(afterNode), entryPath: beforePath, hasEntry: hasEntry,
})
}
return resolutionResult{
final: after, deltas: deltas, snapshots: snapshots,
final: after, deltas: deltas, snapshots: snapshots, correspondence: correspondence,
diagnostics: canonicalDiagnostics(state.diagnostics),
}
}