Preserve append history across tiers

This commit is contained in:
2026-10-16 15:36:46 +00:00
parent bb9bde799a
commit b1b5163cc3
4 changed files with 293 additions and 11 deletions

View File

@@ -45,10 +45,11 @@ func (c *retainedNodeCounter) add(root *document.Node) bool {
// Replay reconciles with canonical decoding before domain resolution so both
// contracts agree on the effective result without retaining a tree per field.
type historyApplication struct {
layer Layer
path Path
value *document.Node
origin Origin
layer Layer
path Path
value *document.Node
origin Origin
sequences []sequenceHistoryContribution
}
func cloneHistoryApplications(values []historyApplication) []historyApplication {
@@ -59,7 +60,7 @@ func cloneHistoryApplications(values []historyApplication) []historyApplication
for index, value := range values {
cloned[index] = historyApplication{
layer: value.layer, path: value.path.clone(), value: value.value,
origin: cloneOrigin(value.origin),
origin: cloneOrigin(value.origin), sequences: cloneSequenceHistoryContributions(value.sequences),
}
}
return cloned
@@ -75,6 +76,24 @@ func applicationOperationPath(application historyApplication, query Path, lookup
}
return Path{}, false
}
sequencePathMatched := false
for _, contribution := range application.sequences {
if !pathContains(contribution.path, query) && !pathContains(query, contribution.path) {
continue
}
sequencePathMatched = true
if pathContains(contribution.path, query) && len(query.segments) > len(contribution.path.segments) {
segment := query.segments[len(contribution.path.segments)]
itemIndex, ok := schemaIndex(segment)
if !ok || itemIndex < contribution.offset || itemIndex >= contribution.offset+contribution.length {
continue
}
}
return query.clone(), true
}
if sequencePathMatched {
return Path{}, false
}
if len(query.segments) == 0 {
return Path{}, true
}
@@ -100,6 +119,22 @@ func historyApplicationOrigin(application historyApplication, operationPath Path
if !ok {
return origin
}
for _, contribution := range application.sequences {
if !pathContains(contribution.path, operationPath) {
continue
}
relative, ok = relativeHistoryPath(Path{}, operationPath)
if !ok || len(relative.segments) < len(contribution.path.segments)+1 {
break
}
itemPosition := len(contribution.path.segments)
itemIndex, valid := schemaIndex(relative.segments[itemPosition])
if !valid || itemIndex < contribution.offset || itemIndex >= contribution.offset+contribution.length {
break
}
relative.segments[itemPosition] = pathSegment{kind: pathSegmentIndex, index: itemIndex - contribution.offset}
break
}
if node, present := documentAtPathWithLookup(application.value, relative, lookup); present {
origin.Locations = []Location{publicLocation(node.Location)}
}
@@ -119,13 +154,17 @@ func pathsIntersect(left, right Path) bool {
func applyHistoryApplication(root **document.Node, application historyApplication, index *mappingIndex) {
if application.path.String() == "" {
mergeHistoryDocument(root, application.value, index)
mergeHistoryDocumentAtPath(root, application.value, Path{}, index, application.sequences)
return
}
mergeHistoryAtPath(root, application.path, application.value, index)
}
func mergeHistoryDocument(target **document.Node, incoming *document.Node, index *mappingIndex) {
mergeHistoryDocumentAtPath(target, incoming, Path{}, index, nil)
}
func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node, path Path, index *mappingIndex, sequences []sequenceHistoryContribution) {
if incoming == nil {
return
}
@@ -141,10 +180,49 @@ func mergeHistoryDocument(target **document.Node, incoming *document.Node, index
index.append(*target, cloned)
continue
}
mergeHistoryDocument(&(*target).Mapping[position].Value, entry.Value, index)
entryPath := path.Key(entry.Key)
if contribution, found := sequenceHistoryAt(sequences, entryPath); found {
applySequenceHistory(&(*target).Mapping[position].Value, entry.Value, contribution)
continue
}
mergeHistoryDocumentAtPath(&(*target).Mapping[position].Value, entry.Value, entryPath, index, sequences)
}
}
func sequenceHistoryAt(contributions []sequenceHistoryContribution, path Path) (sequenceHistoryContribution, bool) {
for _, contribution := range contributions {
if contribution.path.String() == path.String() {
return contribution, true
}
}
return sequenceHistoryContribution{}, false
}
func applySequenceHistory(target **document.Node, incoming *document.Node, contribution sequenceHistoryContribution) {
if incoming == nil || incoming.Kind != document.Sequence {
return
}
if contribution.mode == sequenceHistoryReplace || *target == nil || (*target).Kind != document.Sequence {
*target = document.Clone(incoming)
return
}
for _, item := range incoming.Sequence {
(*target).Sequence = append((*target).Sequence, document.Clone(item))
}
}
func cloneSequenceHistoryContributions(values []sequenceHistoryContribution) []sequenceHistoryContribution {
if values == nil {
return nil
}
cloned := make([]sequenceHistoryContribution, len(values))
for index, value := range values {
cloned[index] = value
cloned[index].path = value.path.clone()
}
return cloned
}
func mergeHistoryAtPath(root **document.Node, path Path, incoming *document.Node, index *mappingIndex) {
if root == nil || *root == nil || len(path.segments) == 0 {
mergeHistoryDocument(root, incoming, index)
@@ -152,7 +230,19 @@ func mergeHistoryAtPath(root **document.Node, path Path, incoming *document.Node
}
current := *root
for segmentIndex, segment := range path.segments {
if current.Kind != document.Mapping || segment.kind == pathSegmentIndex {
if segment.kind == pathSegmentIndex {
position, ok := schemaIndex(segment)
if current.Kind != document.Sequence || !ok || position >= len(current.Sequence) {
return
}
if segmentIndex == len(path.segments)-1 {
mergeHistoryDocument(&current.Sequence[position], incoming, index)
return
}
current = current.Sequence[position]
continue
}
if current.Kind != document.Mapping {
return
}
position := index.find(current, segment.key)