From f404168900786d953898036465e0e7d5cdc8efd9 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sun, 25 Oct 2026 13:32:01 +0000 Subject: [PATCH] Extract inheritance input preparation --- docs/roadmap/implementation.md | 10 ++- internal/engine/inheritance_prepare.go | 88 ++++++++++++++++++++ internal/engine/inheritance_schedule.go | 106 +++++++++--------------- 3 files changed, 133 insertions(+), 71 deletions(-) create mode 100644 internal/engine/inheritance_prepare.go diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index efa15d3..e4a1ecd 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,6 +1,6 @@ # Consolidation Implementation Plan -Status: in progress. Stages 1 through 13 are complete; follow Stage 14 through Stage 20 in order. +Status: in progress. Stages 1 through 14 are complete; follow Stage 15 through Stage 20 in order. The target executor is a Luna coding agent working through one end-to-end sprint. ## Authority, Baseline, And Execution @@ -566,7 +566,7 @@ retains containment, identity/cycle checks, graph updates, and recursive placement coordination; item placement receives the admitted record directly. Validation group A and `./scripts/check.sh` passed. -## Stage 14 — Extract Inheritance Child And Parent Preparation +## Stage 14 — Extract Inheritance Child And Parent Preparation (Complete) **Owners:** engine `inheritance_schedule.go`, new `inheritance_prepare.go`, and existing scope/policy helpers. @@ -589,6 +589,12 @@ existing scope/policy helpers. **Finish:** the executor no longer embeds child/control and parent/policy preparation. Actual recursive combination/evidence moves in Stage 15. +Progress notes (2026-10-25): extracted child/control and parent/policy preparation +into focused records in `inheritance_prepare.go`. The executor retains its +preliminary budget checks, scheduler, and combination path, while history and +transfer inputs stay available to combination. Engine inheritance coverage and +facade inheritance tests passed. + ## Stage 15 — Return One Inheritance Combination Result **Owners:** new engine `inheritance_combine.go`, `inheritance_collection.go`, diff --git a/internal/engine/inheritance_prepare.go b/internal/engine/inheritance_prepare.go new file mode 100644 index 0000000..3e92774 --- /dev/null +++ b/internal/engine/inheritance_prepare.go @@ -0,0 +1,88 @@ +package engine + +import "gitea.maximumdirect.net/eric/ConfigKit/internal/document" + +type preparedInheritanceChild struct { + payload *document.Node + origins map[string]Origin + policyTemplates collectionPolicyScope + control *inheritanceControl + controlOrigin Origin +} + +type preparedInheritanceParent struct { + payload *document.Node + historyParent *document.Node + policyTemplates []collectionPolicyScope + effectivePolicyTemplates []collectionPolicyScope + parentPolicyScope collectionPolicyScope + childPolicyScope collectionPolicyScope + replaced collectionReplacementPaths + transfer inheritanceTransfer +} + +func prepareInheritanceChild(template inheritanceExecutionTemplate, inputOrigins map[string]Origin) preparedInheritanceChild { + policyTemplates := cloneCollectionPolicyScopeForInheritance(template.collectionPolicies) + payload := withoutInheritanceDirective(template.entry.Value) + prepared := preparedInheritanceChild{ + payload: payload, origins: directInheritanceOrigins(payload, template.path, inputOrigins), + policyTemplates: policyTemplates, + } + if template.hasDirective { + control := inheritanceControl{ + path: template.directivePath, locations: []Location{publicLocation(template.directive.Value.Location)}, + cancelled: template.cancelled, + } + controlOrigins := directInheritanceOrigins(template.directive.Value, control.path, inputOrigins) + prepared.control = &control + prepared.controlOrigin = controlOrigins[control.path.String()] + } + return prepared +} + +func prepareInheritanceParent( + template inheritanceExecutionTemplate, + parentPath Path, + parent *document.Node, + resolvedParentPolicyTemplates []collectionPolicyScope, + resolvedParentPolicies collectionPolicyScope, + child preparedInheritanceChild, + applicationPolicy sequencePolicy, +) preparedInheritanceParent { + parentPolicies := cloneInheritancePolicyTemplates(resolvedParentPolicyTemplates) + effectivePolicies := append( + cloneInheritancePolicyTemplates(parentPolicies), + cloneCollectionPolicyScopeForInheritance(child.policyTemplates), + ) + parentPolicyScope := projectInheritanceCollectionPolicies(resolvedParentPolicies, template.path) + childPolicyScope := projectCurrentInheritanceCollectionPolicies(child.policyTemplates, template.path) + var replaced collectionReplacementPaths + if len(parentPolicyScope.collections) != 0 || len(childPolicyScope.collections) != 0 { + claims := compositionClaims{byPath: make(map[string][]compositionClaim)} + claims.append(child.payload, template.path, 0) + declarations := make(map[string][]sequenceDeclarationSpec, len(childPolicyScope.collections)) + for key, entry := range childPolicyScope.collections { + declarations[key] = inheritanceDeclarationsAt(entry.destination, entry.policy) + } + parentPolicyScope, replaced = lowerCollectionPoliciesForClaims(parentPolicyScope, claims.ordered, declarations, applicationPolicy) + } + parentPayload := inheritanceParentPayload(parent, template.identityFields) + historyParent := parent + if len(template.identityFields) != 0 { + // The retained parent snapshot is the inherited payload, before child + // fields are applied, with parent identity excluded. + historyParent = document.Clone(parentPayload) + } + transfer := inheritanceTransfer{ + from: parentPath, to: template.path, reference: template.directivePath, + locations: []Location{publicLocation(template.directive.Value.Location)}, + policyTemplates: parentPolicies, templatePolicies: cloneCollectionPolicyScopeForInheritance(child.policyTemplates), + projectedPolicies: projectInheritancePolicyTemplates([]collectionPolicyScope{resolvedParentPolicies}, template.path), + } + return preparedInheritanceParent{ + payload: parentPayload, historyParent: historyParent, + policyTemplates: parentPolicies, effectivePolicyTemplates: effectivePolicies, + parentPolicyScope: parentPolicyScope, childPolicyScope: childPolicyScope, + replaced: replaced, transfer: transfer, + } +} diff --git a/internal/engine/inheritance_schedule.go b/internal/engine/inheritance_schedule.go index a8f3c18..5ed8f24 100644 --- a/internal/engine/inheritance_schedule.go +++ b/internal/engine/inheritance_schedule.go @@ -188,27 +188,19 @@ func applyInheritanceScopeTemplates(adapter inheritanceScopeAdapter, order []int for _, index := range order { node := templates[index] - templatePolicies := cloneCollectionPolicyScopeForInheritance(node.collectionPolicies) - payload := withoutInheritanceDirective(node.entry.Value) operationStart, replacementStart := len(result.operations), len(result.replacements) - childOrigins := directInheritanceOrigins(payload, node.path, inputOrigins) - if node.hasDirective { - control := inheritanceControl{ - path: node.directivePath, - locations: []Location{publicLocation(node.directive.Value.Location)}, - cancelled: node.cancelled, - } - result.controls = append(result.controls, control) - controlOrigins := directInheritanceOrigins(node.directive.Value, control.path, inputOrigins) + child := prepareInheritanceChild(node, inputOrigins) + if child.control != nil { + result.controls = append(result.controls, *child.control) result.operations = append(result.operations, inheritanceOperation{ - control: &control, controlOrigin: controlOrigins[control.path.String()], + control: child.control, controlOrigin: child.controlOrigin, }) } if limits.nodes >= 0 { remaining := limits.nodes - retained - adapter.slotOverhead() - within := inheritanceLowerBoundWithin(payload, remaining) + within := inheritanceLowerBoundWithin(child.payload, remaining) if node.hasParent { - within = inheritanceOverlayLowerBoundWithin(resolved[node.parent], payload, remaining) + within = inheritanceOverlayLowerBoundWithin(resolved[node.parent], child.payload, remaining) } if !within { return nil, []Diagnostic{inheritanceBudgetDiagnostic(node.path, node.entry.Value.Location)} @@ -217,65 +209,41 @@ func applyInheritanceScopeTemplates(adapter inheritanceScopeAdapter, order []int if node.hasParent { parent := resolved[node.parent] - parentPolicies := cloneInheritancePolicyTemplates(resolvedPolicyTemplates[node.parent]) - effectivePolicies := append(cloneInheritancePolicyTemplates(parentPolicies), cloneCollectionPolicyScopeForInheritance(templatePolicies)) - parentPolicyScope := projectInheritanceCollectionPolicies(resolvedPolicies[node.parent], node.path) - childPolicyScope := projectCurrentInheritanceCollectionPolicies(templatePolicies, node.path) - var replaced collectionReplacementPaths - if len(parentPolicyScope.collections) != 0 || len(childPolicyScope.collections) != 0 { - claims := compositionClaims{byPath: make(map[string][]compositionClaim)} - claims.append(payload, node.path, 0) - declarations := make(map[string][]sequenceDeclarationSpec, len(childPolicyScope.collections)) - for key, entry := range childPolicyScope.collections { - declarations[key] = inheritanceDeclarationsAt(entry.destination, entry.policy) - } - parentPolicyScope, replaced = lowerCollectionPoliciesForClaims(parentPolicyScope, claims.ordered, declarations, adapter.applicationPolicy()) - } - merged := inheritanceParentPayload(parent, node.identityFields) - historyParent := parent - if len(node.identityFields) != 0 { - // The retained parent snapshot is the inherited payload, before - // child fields are applied, with parent identity excluded. - historyParent = document.Clone(merged) - } + preparedParent := prepareInheritanceParent( + node, templates[node.parent].path, parent, resolvedPolicyTemplates[node.parent], + resolvedPolicies[node.parent], child, adapter.applicationPolicy(), + ) mergedPolicyScope, policyDiagnostics := mergeInheritanceCollectionPolicies( - parentPolicyScope, childPolicyScope, node.path, parent, payload, adapter.applicationPolicy(), replaced, + preparedParent.parentPolicyScope, preparedParent.childPolicyScope, + node.path, preparedParent.payload, child.payload, + adapter.applicationPolicy(), preparedParent.replaced, ) if len(policyDiagnostics) != 0 { return nil, policyDiagnostics } - transfer := inheritanceTransfer{ - from: templates[node.parent].path, to: node.path, - reference: node.directivePath, - locations: []Location{publicLocation(node.directive.Value.Location)}, - policyTemplates: parentPolicies, templatePolicies: cloneCollectionPolicyScopeForInheritance(templatePolicies), - projectedPolicies: projectInheritancePolicyTemplates( - []collectionPolicyScope{resolvedPolicies[node.parent]}, node.path, - ), - } - mergedOrigins := transferredInheritanceOrigins(resolvedOrigins[node.parent], transfer.from, node.path, transfer) - mergeInheritanceOrigin(mergedOrigins, node.path, childOrigins[node.path.String()]) + mergedOrigins := transferredInheritanceOrigins(resolvedOrigins[node.parent], preparedParent.transfer.from, node.path, preparedParent.transfer) + mergeInheritanceOrigin(mergedOrigins, node.path, child.origins[node.path.String()]) var sequenceContributions []sequenceHistoryContribution var combinationDiagnostics []Diagnostic - mergeRecursiveMappings(merged, payload, node.path, new(mappingIndex), recursiveMergeHooks{ + mergeRecursiveMappings(preparedParent.payload, child.payload, node.path, new(mappingIndex), recursiveMergeHooks{ added: func(childPath Path, incoming *document.Node) { - copyInheritanceSubtree(mergedOrigins, childOrigins, incoming, childPath) + copyInheritanceSubtree(mergedOrigins, child.origins, incoming, childPath) }, combined: func(childPath Path, _ *document.Node) { - mergeInheritanceOrigin(mergedOrigins, childPath, childOrigins[childPath.String()]) + mergeInheritanceOrigin(mergedOrigins, childPath, child.origins[childPath.String()]) }, replaced: func(childPath Path, existing, incoming *document.Node) { previous := displacedOrigins(existing, mergedOrigins, childPath) result.replacements = append(result.replacements, inheritanceReplacement{ path: childPath.clone(), before: existing, after: incoming, - previous: previous, effective: cloneOrigin(childOrigins[childPath.String()]), + previous: previous, effective: cloneOrigin(child.origins[childPath.String()]), }) deleteOriginSubtree(mergedOrigins, childPath, existing) - copyInheritanceSubtree(mergedOrigins, childOrigins, incoming, childPath) + copyInheritanceSubtree(mergedOrigins, child.origins, incoming, childPath) }, merge: func(childPath Path, existing, incoming *document.Node) (*document.Node, bool) { resolution, _, _, active, issues := inheritanceCollectionResolution( - childPath, parentPolicyScope, childPolicyScope, adapter.applicationPolicy(), incoming, + childPath, preparedParent.parentPolicyScope, preparedParent.childPolicyScope, adapter.applicationPolicy(), incoming, ) if len(issues) != 0 { combinationDiagnostics = append(combinationDiagnostics, issues...) @@ -291,11 +259,11 @@ func applyInheritanceScopeTemplates(adapter inheritanceScopeAdapter, order []int return existing, false } policy := applyInheritanceSequencePolicy( - childPath, combined, resolution, parentPolicyScope, childPolicyScope, + childPath, combined, resolution, preparedParent.parentPolicyScope, preparedParent.childPolicyScope, ) mergedPolicyScope.set(childPath, policy) applyInheritanceSequenceOrigins( - result, mergedOrigins, childOrigins, childPath, lowerValue, incoming, combined, resolution.operation, + result, mergedOrigins, child.origins, childPath, lowerValue, incoming, combined, resolution.operation, ) history := sequenceHistoryContribution{path: childPath.clone(), mode: sequenceHistoryMapped} if resolution.operation == sequenceModeReplace { @@ -311,32 +279,32 @@ func applyInheritanceScopeTemplates(adapter inheritanceScopeAdapter, order []int return combined.value, true }, }) - preserveInheritanceIdentityFields(merged, payload, node.identityFields) + preserveInheritanceIdentityFields(preparedParent.payload, child.payload, node.identityFields) if len(combinationDiagnostics) != 0 { return nil, canonicalDiagnostics(combinationDiagnostics) } - if problems := validateRegisteredInheritanceSequences(merged, node.path, adapter.applicationPolicy()); len(problems) != 0 { + if problems := validateRegisteredInheritanceSequences(preparedParent.payload, node.path, adapter.applicationPolicy()); len(problems) != 0 { return nil, problems } - merged.Location = node.entry.Value.Location - resolved[index], resolvedOrigins[index] = merged, mergedOrigins + preparedParent.payload.Location = node.entry.Value.Location + resolved[index], resolvedOrigins[index] = preparedParent.payload, mergedOrigins resolvedPolicies[index] = inheritancePolicyScopeAtPath(mergedPolicyScope, node.path) - result.transfers = append(result.transfers, transfer) + result.transfers = append(result.transfers, preparedParent.transfer) result.operations = append(result.operations, inheritanceOperation{ - transfer: &transfer, parent: historyParent, effective: merged, + transfer: &preparedParent.transfer, parent: preparedParent.historyParent, effective: preparedParent.payload, parentOrigins: resolvedOrigins[node.parent], effectiveOrigins: mergedOrigins, - parentPolicies: cloneInheritancePolicyTemplates(parentPolicies), - templatePolicies: cloneCollectionPolicyScopeForInheritance(templatePolicies), - effectivePolicies: cloneInheritancePolicyTemplates(effectivePolicies), + parentPolicies: cloneInheritancePolicyTemplates(preparedParent.policyTemplates), + templatePolicies: cloneCollectionPolicyScopeForInheritance(child.policyTemplates), + effectivePolicies: cloneInheritancePolicyTemplates(preparedParent.effectivePolicyTemplates), sequences: cloneSequenceHistoryContributions(sequenceContributions), identityFields: cloneStrings(node.identityFields), }) - resolvedPolicyTemplates[index] = effectivePolicies + resolvedPolicyTemplates[index] = preparedParent.effectivePolicyTemplates } else { - resolved[index] = document.Clone(payload) - resolvedOrigins[index] = childOrigins - resolvedPolicyTemplates[index] = []collectionPolicyScope{templatePolicies} - resolvedPolicies[index] = cloneCollectionPolicyScopeForInheritance(templatePolicies) + resolved[index] = document.Clone(child.payload) + resolvedOrigins[index] = child.origins + resolvedPolicyTemplates[index] = []collectionPolicyScope{child.policyTemplates} + resolvedPolicies[index] = cloneCollectionPolicyScopeForInheritance(child.policyTemplates) } count, _ := inheritanceLowerBoundCount(resolved[index], limits.nodes)