Skip to content

fix: correct path navigation, map-nested writes, and CRDT Text convergence#39

Merged
brunoga merged 1 commit intomainfrom
fix/path-crdt-correctness
Mar 25, 2026
Merged

fix: correct path navigation, map-nested writes, and CRDT Text convergence#39
brunoga merged 1 commit intomainfrom
fix/path-crdt-correctness

Conversation

@brunoga
Copy link
Copy Markdown
Owner

@brunoga brunoga commented Mar 25, 2026

internal/core/path.go:

  • Navigate: check keyed-slice unconditionally before falling back to positional index, fixing non-numeric keys (e.g. "todo", "in-progress") that were never resolved because the old guard required IsIndex=true
  • Set/Delete: replace ResolveParent+switch with recursive setAtPath/ deleteAtPath that copy-modify-put-back at every map boundary, fixing silent write loss when a value is nested inside a map
  • makeMapKey: extracted helper with Convert for named-string and uint key types, fixing a panic on uint-keyed maps

crdt/crdt.go:

  • Replace post-apply full-tree remergeTextFields walk with per-op textAncestorPath detection during the Merge filter loop; Text field paths are collected in O(depth) per op and merged via MergeTextRuns after the LWW apply — no tree walk, no index-alignment bug on keyed slices
  • Remove the early break in textAncestorPath so that TextRun sub-field ops (e.g. /Body//Deleted, depth 3) correctly walk up to the Text ancestor

Tests added for each fix in path_test.go and crdt_test.go.

…gence

internal/core/path.go:
- Navigate: check keyed-slice unconditionally before falling back to
  positional index, fixing non-numeric keys (e.g. "todo", "in-progress")
  that were never resolved because the old guard required IsIndex=true
- Set/Delete: replace ResolveParent+switch with recursive setAtPath/
  deleteAtPath that copy-modify-put-back at every map boundary, fixing
  silent write loss when a value is nested inside a map
- makeMapKey: extracted helper with Convert for named-string and uint key
  types, fixing a panic on uint-keyed maps

crdt/crdt.go:
- Replace post-apply full-tree remergeTextFields walk with per-op
  textAncestorPath detection during the Merge filter loop; Text field paths
  are collected in O(depth) per op and merged via MergeTextRuns after the
  LWW apply — no tree walk, no index-alignment bug on keyed slices
- Remove the early break in textAncestorPath so that TextRun sub-field ops
  (e.g. /Body/<id>/Deleted, depth 3) correctly walk up to the Text ancestor

Tests added for each fix in path_test.go and crdt_test.go.
@brunoga brunoga merged commit f9f5eb5 into main Mar 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant