Prepare collection policy transport through inheritance

This commit is contained in:
2026-10-23 20:04:06 +00:00
parent 80d8c1dc82
commit 9e75496664
8 changed files with 282 additions and 46 deletions

View File

@@ -1,6 +1,6 @@
# Sequence Identity And Combination Implementation Plan # Sequence Identity And Combination Implementation Plan
Status: implementation in progress; Stages 1–16 complete. Status: implementation in progress; Stages 1–17 complete.
Audience: a Luna coding agent implementing one numbered stage per prompt, in Audience: a Luna coding agent implementing one numbered stage per prompt, in
order. This plan starts at commit order. This plan starts at commit
@@ -613,6 +613,11 @@ collection-aware inheritance operations in Stage 18.
Validation: existing inheritance, ownership, history, and resource-context tests, Validation: existing inheritance, ownership, history, and resource-context tests,
focused transport tests, and the everyday check. focused transport tests, and the everyday check.
**Completion:** Implemented detached collection-policy transport through
inheritance templates and expansion operations while preserving current values
and nested instructions. Verified focused inheritance/history/resource tests,
`go test -race ./internal/engine`, and `./scripts/check.sh`.
## Stage 18 — Apply Collection Policy During Mapping-Sibling Inheritance ## Stage 18 — Apply Collection Policy During Mapping-Sibling Inheritance
Scope: activate shared collection operations using the prepared template/state Scope: activate shared collection operations using the prepared template/state

View File

@@ -11,6 +11,9 @@ type inheritanceTransfer struct {
to Path to Path
reference Path reference Path
locations []Location locations []Location
policyTemplates []collectionPolicyScope
templatePolicies collectionPolicyScope
projectedPolicies []projectedCollectionPolicy
} }
type inheritanceControl struct { type inheritanceControl struct {
@@ -22,6 +25,7 @@ type inheritanceControl struct {
type inheritanceScope struct { type inheritanceScope struct {
root *document.Node root *document.Node
origins map[string]Origin origins map[string]Origin
collectionPolicies collectionPolicyScope
transfers []inheritanceTransfer transfers []inheritanceTransfer
controls []inheritanceControl controls []inheritanceControl
replacements []inheritanceReplacement replacements []inheritanceReplacement
@@ -36,6 +40,108 @@ type inheritanceOperation struct {
effective *document.Node effective *document.Node
parentOrigins map[string]Origin parentOrigins map[string]Origin
effectiveOrigins map[string]Origin effectiveOrigins map[string]Origin
parentPolicies []collectionPolicyScope
templatePolicies collectionPolicyScope
effectivePolicies []collectionPolicyScope
}
func cloneInheritancePolicyTemplates(templates []collectionPolicyScope) []collectionPolicyScope {
if templates == nil {
return nil
}
cloned := make([]collectionPolicyScope, len(templates))
for index, template := range templates {
cloned[index] = cloneCollectionPolicyScopeForInheritance(template)
}
return cloned
}
func cloneProjectedInheritancePolicies(policies []projectedCollectionPolicy) []projectedCollectionPolicy {
if policies == nil {
return nil
}
cloned := make([]projectedCollectionPolicy, len(policies))
for index, policy := range policies {
cloned[index] = projectedCollectionPolicy{
path: policy.path.clone(), policy: cloneCollectionPolicyForInheritance(policy.policy),
}
}
return cloned
}
func cloneInheritanceTransfer(transfer inheritanceTransfer) inheritanceTransfer {
cloned := transfer
cloned.from = transfer.from.clone()
cloned.to = transfer.to.clone()
cloned.reference = transfer.reference.clone()
cloned.locations = append([]Location(nil), transfer.locations...)
cloned.policyTemplates = cloneInheritancePolicyTemplates(transfer.policyTemplates)
cloned.templatePolicies = cloneCollectionPolicyScopeForInheritance(transfer.templatePolicies)
cloned.projectedPolicies = cloneProjectedInheritancePolicies(transfer.projectedPolicies)
return cloned
}
func cloneInheritanceOperations(operations []inheritanceOperation) []inheritanceOperation {
if operations == nil {
return nil
}
cloned := make([]inheritanceOperation, len(operations))
for index, operation := range operations {
cloned[index] = operation
if operation.control != nil {
control := *operation.control
control.path = operation.control.path.clone()
control.locations = append([]Location(nil), operation.control.locations...)
cloned[index].control = &control
}
if operation.transfer != nil {
transfer := cloneInheritanceTransfer(*operation.transfer)
cloned[index].transfer = &transfer
}
cloned[index].parent = document.Clone(operation.parent)
cloned[index].effective = document.Clone(operation.effective)
cloned[index].parentOrigins = cloneOriginIndex(operation.parentOrigins)
cloned[index].effectiveOrigins = cloneOriginIndex(operation.effectiveOrigins)
cloned[index].parentPolicies = cloneInheritancePolicyTemplates(operation.parentPolicies)
cloned[index].templatePolicies = cloneCollectionPolicyScopeForInheritance(operation.templatePolicies)
cloned[index].effectivePolicies = cloneInheritancePolicyTemplates(operation.effectivePolicies)
}
return cloned
}
func inheritancePolicyScopeAtPath(scope collectionPolicyScope, prefix Path) collectionPolicyScope {
localized := collectionPolicyScope{}
for _, entry := range scope.collections {
if relative, inside := relativeHistoryPath(prefix, entry.destination); inside {
localized.set(relative, cloneCollectionPolicyForInheritance(entry.policy))
continue
}
relative, inside := relativeHistoryPath(entry.destination, prefix)
if !inside || len(relative.segments) == 0 || relative.segments[0].kind != pathSegmentIndex {
continue
}
record, found := entry.policy.recordPolicyAt(relative.segments[0].index)
if !found {
continue
}
remainder := Path{segments: append([]pathSegment(nil), relative.segments[1:]...)}
for _, nested := range inheritancePolicyScopeAtPath(record.scope, remainder).collections {
localized.set(nested.destination, cloneCollectionPolicyForInheritance(nested.policy))
}
}
return localized
}
func projectInheritancePolicyTemplates(templates []collectionPolicyScope, prefix Path) []projectedCollectionPolicy {
var projected []projectedCollectionPolicy
for _, template := range templates {
for _, policy := range projectCollectionPolicyScope(template, prefix) {
projected = append(projected, projectedCollectionPolicy{
path: policy.path.clone(), policy: cloneCollectionPolicyForInheritance(policy.policy),
})
}
}
return projected
} }
// A replacement retains the displaced source/derivation record while the // A replacement retains the displaced source/derivation record while the

View File

@@ -73,7 +73,7 @@ func applyInheritanceMetadata(metadata *Metadata, raw *document.Node, rawOrigins
}) })
applyOverlayMetadata(&sourceView, overlays) applyOverlayMetadata(&sourceView, overlays)
metadata.history = sourceView.history metadata.history = sourceView.history
metadata.inheritance = append([]inheritanceOperation(nil), expansion.operations...) metadata.inheritance = cloneInheritanceOperations(expansion.operations)
} }
func historyWithInheritance(path Path, stored []Change, operations []inheritanceOperation) []Change { func historyWithInheritance(path Path, stored []Change, operations []inheritanceOperation) []Change {

View File

@@ -63,5 +63,5 @@ func inheritanceOperations(expanded *inheritanceScope) []inheritanceOperation {
if expanded == nil { if expanded == nil {
return nil return nil
} }
return expanded.operations return cloneInheritanceOperations(expanded.operations)
} }

View File

@@ -10,6 +10,7 @@ type inheritanceTemplate struct {
name string name string
entry document.Entry entry document.Entry
path Path path Path
collectionPolicies collectionPolicyScope
parent int parent int
hasParent bool hasParent bool
hasDirective bool hasDirective bool
@@ -29,6 +30,10 @@ type inheritanceGraph struct {
// It validates declarations separately from applying their templates, leaving // It validates declarations separately from applying their templates, leaving
// nested scopes for the existing destination-local walk. // nested scopes for the existing destination-local walk.
func analyzeInheritanceGraph(scope *document.Node, scopePath Path) (*inheritanceGraph, []Diagnostic) { func analyzeInheritanceGraph(scope *document.Node, scopePath Path) (*inheritanceGraph, []Diagnostic) {
return analyzeInheritanceGraphWithPolicies(scope, scopePath, collectionPolicyScope{})
}
func analyzeInheritanceGraphWithPolicies(scope *document.Node, scopePath Path, policies collectionPolicyScope) (*inheritanceGraph, []Diagnostic) {
if scope == nil || scope.Kind != document.Mapping { if scope == nil || scope.Kind != document.Mapping {
return nil, []Diagnostic{{ return nil, []Diagnostic{{
Code: CodeInheritanceInvalid, Severity: SeverityError, Path: scopePath, Code: CodeInheritanceInvalid, Severity: SeverityError, Path: scopePath,
@@ -57,6 +62,7 @@ func analyzeInheritanceGraph(scope *document.Node, scopePath Path) (*inheritance
graph.byName[name] = index graph.byName[name] = index
graph.nodes = append(graph.nodes, inheritanceTemplate{ graph.nodes = append(graph.nodes, inheritanceTemplate{
name: name, entry: entries[name], path: scopePath.Key(name), name: name, entry: entries[name], path: scopePath.Key(name),
collectionPolicies: inheritancePolicyScopeAtPath(policies, scopePath.Key(name)),
}) })
} }
@@ -115,7 +121,11 @@ func (graph *inheritanceGraph) schedule() ([]int, []Diagnostic) {
} }
func expandSiblingDefinitionsWithOrigins(scope *document.Node, scopePath Path, limits projectionLimits, inputOrigins map[string]Origin, budget ...*retainedNodeCounter) (*inheritanceScope, []Diagnostic) { func expandSiblingDefinitionsWithOrigins(scope *document.Node, scopePath Path, limits projectionLimits, inputOrigins map[string]Origin, budget ...*retainedNodeCounter) (*inheritanceScope, []Diagnostic) {
graph, diagnostics := analyzeInheritanceGraph(scope, scopePath) return expandSiblingDefinitionsWithPolicies(scope, scopePath, limits, inputOrigins, collectionPolicyScope{}, budget...)
}
func expandSiblingDefinitionsWithPolicies(scope *document.Node, scopePath Path, limits projectionLimits, inputOrigins map[string]Origin, policies collectionPolicyScope, budget ...*retainedNodeCounter) (*inheritanceScope, []Diagnostic) {
graph, diagnostics := analyzeInheritanceGraphWithPolicies(scope, scopePath, policies)
if len(diagnostics) != 0 { if len(diagnostics) != 0 {
return nil, diagnostics return nil, diagnostics
} }
@@ -123,7 +133,12 @@ func expandSiblingDefinitionsWithOrigins(scope *document.Node, scopePath Path, l
if len(diagnostics) != 0 { if len(diagnostics) != 0 {
return nil, diagnostics return nil, diagnostics
} }
return applyInheritanceTemplates(graph, order, limits, inputOrigins, budget...) result, diagnostics := applyInheritanceTemplates(graph, order, limits, inputOrigins, budget...)
if len(diagnostics) != 0 {
return nil, diagnostics
}
result.collectionPolicies = cloneCollectionPolicyScopeForInheritance(policies)
return result, nil
} }
// applyInheritanceTemplates builds detached templates in the supplied // applyInheritanceTemplates builds detached templates in the supplied
@@ -132,6 +147,7 @@ func expandSiblingDefinitionsWithOrigins(scope *document.Node, scopePath Path, l
func applyInheritanceTemplates(graph *inheritanceGraph, order []int, limits projectionLimits, inputOrigins map[string]Origin, budget ...*retainedNodeCounter) (*inheritanceScope, []Diagnostic) { func applyInheritanceTemplates(graph *inheritanceGraph, order []int, limits projectionLimits, inputOrigins map[string]Origin, budget ...*retainedNodeCounter) (*inheritanceScope, []Diagnostic) {
resolved := make([]*document.Node, len(graph.nodes)) resolved := make([]*document.Node, len(graph.nodes))
resolvedOrigins := make([]map[string]Origin, len(graph.nodes)) resolvedOrigins := make([]map[string]Origin, len(graph.nodes))
resolvedPolicyTemplates := make([][]collectionPolicyScope, len(graph.nodes))
result := &inheritanceScope{} result := &inheritanceScope{}
counter := newRetainedNodeCounter(historyNodeLimit) counter := newRetainedNodeCounter(historyNodeLimit)
if len(budget) != 0 { if len(budget) != 0 {
@@ -155,6 +171,7 @@ func applyInheritanceTemplates(graph *inheritanceGraph, order []int, limits proj
for _, index := range order { for _, index := range order {
node := graph.nodes[index] node := graph.nodes[index]
templatePolicies := cloneCollectionPolicyScopeForInheritance(node.collectionPolicies)
payload := withoutInheritanceDirective(node.entry.Value) payload := withoutInheritanceDirective(node.entry.Value)
operationStart, replacementStart := len(result.operations), len(result.replacements) operationStart, replacementStart := len(result.operations), len(result.replacements)
childOrigins := directInheritanceOrigins(payload, node.path, inputOrigins) childOrigins := directInheritanceOrigins(payload, node.path, inputOrigins)
@@ -183,11 +200,15 @@ func applyInheritanceTemplates(graph *inheritanceGraph, order []int, limits proj
if node.hasParent { if node.hasParent {
parent := resolved[node.parent] parent := resolved[node.parent]
parentPolicies := cloneInheritancePolicyTemplates(resolvedPolicyTemplates[node.parent])
effectivePolicies := append(cloneInheritancePolicyTemplates(parentPolicies), cloneCollectionPolicyScopeForInheritance(templatePolicies))
merged := document.Clone(parent) merged := document.Clone(parent)
transfer := inheritanceTransfer{ transfer := inheritanceTransfer{
from: graph.nodes[node.parent].path, to: node.path, from: graph.nodes[node.parent].path, to: node.path,
reference: node.path.Key(inheritanceKey), reference: node.path.Key(inheritanceKey),
locations: []Location{publicLocation(node.directive.Value.Location)}, locations: []Location{publicLocation(node.directive.Value.Location)},
policyTemplates: parentPolicies, templatePolicies: cloneCollectionPolicyScopeForInheritance(templatePolicies),
projectedPolicies: projectInheritancePolicyTemplates(parentPolicies, node.path),
} }
mergedOrigins := transferredInheritanceOrigins(resolvedOrigins[node.parent], transfer.from, node.path, transfer) mergedOrigins := transferredInheritanceOrigins(resolvedOrigins[node.parent], transfer.from, node.path, transfer)
mergeInheritanceOrigin(mergedOrigins, node.path, childOrigins[node.path.String()]) mergeInheritanceOrigin(mergedOrigins, node.path, childOrigins[node.path.String()])
@@ -214,10 +235,15 @@ func applyInheritanceTemplates(graph *inheritanceGraph, order []int, limits proj
result.operations = append(result.operations, inheritanceOperation{ result.operations = append(result.operations, inheritanceOperation{
transfer: &transfer, parent: parent, effective: merged, transfer: &transfer, parent: parent, effective: merged,
parentOrigins: resolvedOrigins[node.parent], effectiveOrigins: mergedOrigins, parentOrigins: resolvedOrigins[node.parent], effectiveOrigins: mergedOrigins,
parentPolicies: cloneInheritancePolicyTemplates(parentPolicies),
templatePolicies: cloneCollectionPolicyScopeForInheritance(templatePolicies),
effectivePolicies: cloneInheritancePolicyTemplates(effectivePolicies),
}) })
resolvedPolicyTemplates[index] = effectivePolicies
} else { } else {
resolved[index] = document.Clone(payload) resolved[index] = document.Clone(payload)
resolvedOrigins[index] = childOrigins resolvedOrigins[index] = childOrigins
resolvedPolicyTemplates[index] = []collectionPolicyScope{templatePolicies}
} }
count, _ := inheritanceLowerBoundCount(resolved[index], limits.nodes) count, _ := inheritanceLowerBoundCount(resolved[index], limits.nodes)

View File

@@ -110,6 +110,91 @@ beta:
} }
} }
func TestInheritanceTransportsReusableCollectionPolicyTemplates(t *testing.T) {
root := parseItemPolicyDocument(t, "templates", `base:
prompts:
- id: shared
children:
derived:
extends: nested-base
own: destination-local
nested-base:
inherited: source-value
first:
extends: base
second:
extends: base
`)
policyLocation := Location{SourceID: "base-resource", Line: 2, Column: 3}
nestedLocation := Location{SourceID: "nested-resource", Line: 5, Column: 11}
nested := collectionPolicy{identityFields: []string{"key"}, identityPresent: true, identityLocation: &nestedLocation}
recordScope := collectionPolicyScope{}
recordScope.set(Keys("children", "prompts"), nested)
basePolicy := collectionPolicy{identityFields: []string{"id"}, identityPresent: true, identityLocation: &policyLocation}
basePolicy.setRecordPolicyAt(0, collectionRecordPolicy{owner: newCollectionRecordPolicy().owner, scope: recordScope})
policies := collectionPolicyScope{}
policies.set(Keys("base", "prompts"), basePolicy)
got, diagnostics := expandSiblingDefinitionsWithPolicies(root, Path{}, defaultProjectionLimits(), nil, policies)
if len(diagnostics) != 0 {
t.Fatalf("expand sibling templates: %#v", diagnostics)
}
withoutPolicies, diagnostics := expandSiblingDefinitionsWithOrigins(root, Path{}, defaultProjectionLimits(), nil)
if len(diagnostics) != 0 {
t.Fatalf("expand sibling templates without policy: %#v", diagnostics)
}
if !reflect.DeepEqual(got.root, withoutPolicies.root) {
t.Fatalf("policy transport changed inherited values:\n got %#v\nwant %#v", got.root, withoutPolicies.root)
}
var transfers []inheritanceOperation
for _, operation := range got.operations {
if operation.transfer != nil {
transfers = append(transfers, operation)
}
}
if len(transfers) != 2 {
t.Fatalf("transfer operations = %d, want two destination transfers", len(transfers))
}
for index, destination := range []string{"/first", "/second"} {
operation := transfers[index]
if operation.transfer == nil || len(operation.transfer.projectedPolicies) != 2 {
t.Fatalf("transfer %d projected policies = %#v, want collection and record-local policy", index, operation.transfer)
}
projected := operation.transfer.projectedPolicies
if projected[0].path.String() != destination+"/prompts" || projected[1].path.String() != destination+"/prompts/0/children/prompts" {
t.Fatalf("transfer %d policy paths = %q, %q", index, projected[0].path, projected[1].path)
}
if got := projected[0].policy.identityLocation; got == nil || *got != policyLocation {
t.Fatalf("transfer %d collection declaration location = %#v", index, got)
}
if got := projected[1].policy.identityLocation; got == nil || *got != nestedLocation {
t.Fatalf("transfer %d record-local declaration location = %#v", index, got)
}
if len(operation.templatePolicies.collections) != 0 {
t.Fatalf("transfer %d unexpectedly copied parent YAML policy onto child template", index)
}
}
first, second := transfers[0].transfer.projectedPolicies[0].policy, transfers[1].transfer.projectedPolicies[0].policy
if first.recordOrder[0] == second.recordOrder[0] {
t.Fatal("destination policy templates share a mutable record owner")
}
first.identityFields[0] = "changed"
if second.identityFields[0] != "id" {
t.Fatalf("mutating one destination changed another: %v", second.identityFields)
}
if basePolicy.identityFields[0] != "id" {
t.Fatalf("mutating a destination changed the source template: %v", basePolicy.identityFields)
}
firstValue, found := documentAtPath(got.root.Mapping[1].Value, Keys("prompts").Index(0).Key("children").Key("derived").Key("extends"))
if !found || firstValue.Text != "nested-base" {
t.Fatalf("nested template instruction = %#v, found %t; want original instruction retained", firstValue, found)
}
}
func TestInheritanceExpansionInterleavesUnequalBranches(t *testing.T) { func TestInheritanceExpansionInterleavesUnequalBranches(t *testing.T) {
root := parseCompositionDocument(t, "branches", `a-grandchild: root := parseCompositionDocument(t, "branches", `a-grandchild:
extends: z-child extends: z-child

View File

@@ -10,6 +10,7 @@ import (
type inheritanceWalker struct { type inheritanceWalker struct {
nodes int // logical non-directive nodes in the entire working tree nodes int // logical non-directive nodes in the entire working tree
origins map[string]Origin origins map[string]Origin
collectionPolicies collectionPolicyScope
limits projectionLimits limits projectionLimits
retained *retainedNodeCounter retained *retainedNodeCounter
transfers []inheritanceTransfer transfers []inheritanceTransfer
@@ -25,10 +26,18 @@ func expandInheritanceTree(input *document.Node, limits projectionLimits) (*inhe
} }
func expandInheritanceWithOrigins(input *document.Node, inputOrigins map[string]Origin, limits projectionLimits) (*inheritanceScope, []Diagnostic) { func expandInheritanceWithOrigins(input *document.Node, inputOrigins map[string]Origin, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
return expandInheritanceWithBudgets(input, inputOrigins, limits, historyNodeLimit) return expandInheritanceWithPolicies(input, inputOrigins, collectionPolicyScope{}, limits)
} }
func expandInheritanceWithBudgets(input *document.Node, inputOrigins map[string]Origin, limits projectionLimits, historyLimit int) (*inheritanceScope, []Diagnostic) { func expandInheritanceWithBudgets(input *document.Node, inputOrigins map[string]Origin, limits projectionLimits, historyLimit int) (*inheritanceScope, []Diagnostic) {
return expandInheritanceWithPolicyBudgets(input, inputOrigins, collectionPolicyScope{}, limits, historyLimit)
}
func expandInheritanceWithPolicies(input *document.Node, inputOrigins map[string]Origin, policies collectionPolicyScope, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
return expandInheritanceWithPolicyBudgets(input, inputOrigins, policies, limits, historyNodeLimit)
}
func expandInheritanceWithPolicyBudgets(input *document.Node, inputOrigins map[string]Origin, policies collectionPolicyScope, limits projectionLimits, historyLimit int) (*inheritanceScope, []Diagnostic) {
if input == nil || input.Kind != document.Mapping { if input == nil || input.Kind != document.Mapping {
return nil, []Diagnostic{{Code: CodeInheritanceInvalid, Severity: SeverityError, return nil, []Diagnostic{{Code: CodeInheritanceInvalid, Severity: SeverityError,
Message: "inheritance requires a mapping document root"}} Message: "inheritance requires a mapping document root"}}
@@ -44,6 +53,7 @@ func expandInheritanceWithBudgets(input *document.Node, inputOrigins map[string]
} }
walker := inheritanceWalker{ walker := inheritanceWalker{
nodes: nodes, limits: limits, origins: origins, nodes: nodes, limits: limits, origins: origins,
collectionPolicies: cloneCollectionPolicyScopeForInheritance(policies),
retained: newRetainedNodeCounter(historyLimit), retained: newRetainedNodeCounter(historyLimit),
} }
if problem := walker.visit(&root, Path{}, true); problem != nil { if problem := walker.visit(&root, Path{}, true); problem != nil {
@@ -53,7 +63,9 @@ func expandInheritanceWithBudgets(input *document.Node, inputOrigins map[string]
return nil, []Diagnostic{*problem} return nil, []Diagnostic{*problem}
} }
return &inheritanceScope{ return &inheritanceScope{
root: root, origins: walker.origins, transfers: walker.transfers, root: root, origins: walker.origins,
collectionPolicies: cloneCollectionPolicyScopeForInheritance(walker.collectionPolicies),
transfers: walker.transfers,
controls: walker.controls, replacements: walker.replacements, operations: walker.operations, controls: walker.controls, replacements: walker.replacements, operations: walker.operations,
}, nil }, nil
} }
@@ -90,7 +102,7 @@ func (w *inheritanceWalker) visit(node **document.Node, path Path, unscoped bool
if limits.nodes >= 0 { if limits.nodes >= 0 {
limits.nodes -= w.nodes - before limits.nodes -= w.nodes - before
} }
resolved, diagnostics := expandSiblingDefinitionsWithOrigins(*node, path, limits, w.origins, w.retained) resolved, diagnostics := expandSiblingDefinitionsWithPolicies(*node, path, limits, w.origins, w.collectionPolicies, w.retained)
if len(diagnostics) != 0 { if len(diagnostics) != 0 {
return &diagnostics[0] return &diagnostics[0]
} }

View File

@@ -258,7 +258,9 @@ func executeLoadRequest[T any](request loadRequest[T], secrets SecretBinding, lo
accumulator.add(problems...) accumulator.add(problems...)
return nil, accumulator.loadError() return nil, accumulator.loadError()
} }
expanded, problems := expandInheritanceWithOrigins(composition.root, composition.origins, defaultProjectionLimits()) expanded, problems := expandInheritanceWithPolicies(
composition.root, composition.origins, composition.collectionPolicies, defaultProjectionLimits(),
)
if len(problems) != 0 { if len(problems) != 0 {
accumulator.add(problems...) accumulator.add(problems...)
return nil, accumulator.loadError() return nil, accumulator.loadError()