Preserve inherited source origins and transfer evidence

This commit is contained in:
2026-09-29 22:40:49 +00:00
parent 4207110e2f
commit 462e57df3e
7 changed files with 426 additions and 21 deletions

View File

@@ -8,34 +8,44 @@ import (
)
type inheritanceWalker struct {
root **document.Node
limits projectionLimits
transfers []inheritanceTransfer
controls []inheritanceControl
root **document.Node
origins map[string]Origin
limits projectionLimits
transfers []inheritanceTransfer
controls []inheritanceControl
replacements []inheritanceReplacement
}
// expandInheritanceTree resolves each destination scope after its enclosing
// relationships. It remains a private, test-only operation until the loading
// lifecycle also carries its complete provenance and inspection evidence.
func expandInheritanceTree(input *document.Node, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
return expandInheritanceWithOrigins(input, nil, limits)
}
func expandInheritanceWithOrigins(input *document.Node, inputOrigins map[string]Origin, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
if input == nil || input.Kind != document.Mapping {
return nil, []Diagnostic{{Code: CodeInheritanceInvalid, Severity: SeverityError,
Message: "inheritance requires a mapping document root"}}
}
root := input
walker := inheritanceWalker{root: &root, limits: limits}
walker := inheritanceWalker{root: &root, limits: limits, origins: cloneOriginIndex(inputOrigins)}
if problem := walker.visit(&root, Path{}, true); problem != nil {
return nil, []Diagnostic{*problem}
}
if problem := inheritanceFinalBudget(root, limits); problem != nil {
return nil, []Diagnostic{*problem}
}
return &inheritanceScope{root: root, transfers: walker.transfers, controls: walker.controls}, nil
return &inheritanceScope{
root: root, origins: walker.origins, transfers: walker.transfers,
controls: walker.controls, replacements: walker.replacements,
}, nil
}
func (w *inheritanceWalker) visit(node **document.Node, path Path, unscoped bool) *Diagnostic {
switch (*node).Kind {
case document.Mapping:
original := *node
if unscoped {
reference, found := inheritanceDirective(*node)
if found {
@@ -49,16 +59,22 @@ func (w *inheritanceWalker) visit(node **document.Node, path Path, unscoped bool
w.controls = append(w.controls, inheritanceControl{
path: referencePath, locations: []Location{location}, cancelled: true,
})
deleteOriginSubtree(w.origins, referencePath, reference.Value)
*node = withoutInheritanceDirective(*node)
}
}
resolved, diagnostics := expandSiblingDefinitionsWithLimits(*node, path, w.limits)
resolved, diagnostics := expandSiblingDefinitionsWithOrigins(*node, path, w.limits, w.origins)
if len(diagnostics) != 0 {
return &diagnostics[0]
}
*node = resolved.root
deleteOriginSubtree(w.origins, path, original)
for pointer, origin := range resolved.origins {
w.origins[pointer] = origin
}
w.transfers = append(w.transfers, resolved.transfers...)
w.controls = append(w.controls, resolved.controls...)
w.replacements = append(w.replacements, resolved.replacements...)
if !inheritanceLowerBoundWithin(*w.root, w.limits.nodes) {
problem := inheritanceBudgetDiagnostic(path, (*node).Location)
return &problem