Report moved history at final record paths
This commit is contained in:
@@ -74,7 +74,13 @@ enumeration or new public addressing mechanism.
|
|||||||
|
|
||||||
## Finding 2 — Deep descendant history reports the old record's operation path
|
## Finding 2 — Deep descendant history reports the old record's operation path
|
||||||
|
|
||||||
**Priority:** P2. **Status:** Open.
|
**Priority:** P2. **Status:** Complete.
|
||||||
|
|
||||||
|
Completion: pre-resolution operation paths now use the matched record-prefix
|
||||||
|
translation, preserving ancestor scope and suffixes. Resolution records already
|
||||||
|
in final coordinates are left unchanged. A regression covers deep inherited
|
||||||
|
fields, record/container queries, original source coordinates, and overlapping
|
||||||
|
final-position records. Focused history checks and `./scripts/check.sh` passed.
|
||||||
|
|
||||||
**Code:** [shared history selection](../../internal/engine/history_select.go),
|
**Code:** [shared history selection](../../internal/engine/history_select.go),
|
||||||
`selectHistory`, especially the reported-path translation at lines 116–120.
|
`selectHistory`, especially the reported-path translation at lines 116–120.
|
||||||
|
|||||||
@@ -113,11 +113,10 @@ func (model *historyModel) selectHistory(path Path) []historySelection {
|
|||||||
} else if !historyObservationMatchesQuery(query, observation.path) {
|
} else if !historyObservationMatchesQuery(query, observation.path) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if query.String() != path.String() && pathContains(query, reportedPath) {
|
if observation.phase != historyPhaseResolution && query.String() != path.String() {
|
||||||
suffix := reportedPath.segments[len(query.segments):]
|
if translated, matched := model.resolutionPayload.correspondence.finalPath(reportedPath); matched {
|
||||||
reportedPath = Path{segments: append(clonePathSegments(path.segments, len(suffix)), suffix...)}
|
reportedPath = translated
|
||||||
} else if query.String() != path.String() && pathContains(reportedPath, query) && len(reportedPath.segments) >= len(query.segments)-1 {
|
}
|
||||||
reportedPath = path.clone()
|
|
||||||
}
|
}
|
||||||
selectionIndex, found := selectionByPayload[observation.payload]
|
selectionIndex, found := selectionByPayload[observation.payload]
|
||||||
if !found {
|
if !found {
|
||||||
|
|||||||
@@ -40,3 +40,57 @@ func TestMovedRemovedFieldHasConsistentHistory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMovedInheritedHistoryUsesFinalRecordPrefix(t *testing.T) {
|
||||||
|
type config struct {
|
||||||
|
Items []map[string]any `yaml:"items"`
|
||||||
|
}
|
||||||
|
field := Keys("items").Index(0).Key("nested").Key("more").Key("value")
|
||||||
|
definition := DefinitionWithSequenceIdentityRegistrations(Definition[config]{
|
||||||
|
Resolve: func(value *config, r *Resolution) error {
|
||||||
|
value.Items[0], value.Items[1] = value.Items[1], value.Items[0]
|
||||||
|
r.RecordNormalization(Keys("items"), "reorder")
|
||||||
|
r.RecordNormalization(field, "retain inherited field")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}, []SequenceIdentityRegistrationInput{{ExactPath: pathPointer(Keys("items")), IdentityFields: []string{"id"}}})
|
||||||
|
result, err := testLoad(Bytes("records", []byte("items:\n - id: parent\n nested: {more: {value: old}}\n - id: child\n extends: {id: parent}\n")), Options[config]{Definition: definition})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
physical := result.Metadata().History(Keys("items"))
|
||||||
|
sourceID := result.Inspection.Sources()[0].ID
|
||||||
|
for _, query := range []Path{field, Keys("items").Index(0)} {
|
||||||
|
inherited, recorded := false, false
|
||||||
|
for _, change := range result.Metadata().History(query) {
|
||||||
|
if change.Origin.Operation == ChangeInheritance {
|
||||||
|
inherited = true
|
||||||
|
if !pathContains(Keys("items").Index(0), change.Path) {
|
||||||
|
t.Errorf("inheritance path for %s = %s", query.String(), change.Path.String())
|
||||||
|
}
|
||||||
|
if query.String() == field.String() && change.Path.String() != "/items/0" {
|
||||||
|
t.Errorf("container operation replaced by leaf path: %s", change.Path.String())
|
||||||
|
}
|
||||||
|
for _, location := range change.Origin.Locations {
|
||||||
|
if location.SourceID != sourceID || (query.String() == field.String() && location.Line != 3) {
|
||||||
|
t.Errorf("source coordinates changed: %#v", location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if change.Origin.Layer == LayerResolution && len(change.Reasons) == 1 && change.Reasons[0] == "retain inherited field" {
|
||||||
|
recorded = true
|
||||||
|
if change.Path.String() != field.String() {
|
||||||
|
t.Errorf("final resolution path translated twice: %s", change.Path.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !inherited || !recorded {
|
||||||
|
t.Errorf("missing inherited or recorded evidence for %s", query.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, change := range physical {
|
||||||
|
if change.Origin.Operation == ChangeInheritance && !pathContains(Keys("items").Index(1), change.Path) {
|
||||||
|
t.Errorf("collection history lost physical coordinates: %s", change.Path.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user