Skip to content

Make ACTZipper::reset return to an off-trie root instead of a real ancestor - #72

Open
imlvts wants to merge 1 commit into
Adam-Vandervorst:masterfrom
imlvts:bugfix/act-reset-off-trie-root
Open

Make ACTZipper::reset return to an off-trie root instead of a real ancestor#72
imlvts wants to merge 1 commit into
Adam-Vandervorst:masterfrom
imlvts:bugfix/act-reset-off-trie-root

Conversation

@imlvts

@imlvts imlvts commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

A zipper can be rooted at a path that does not exist: fork_read_zipper at an off-trie focus makes one, and so does read_zipper_at_path with such a path. reset put the path back but then cleared invalid unconditionally, so the zipper came back believing it was rooted on the trie, at whatever real node the stack still pointed at, its deepest existing ancestor. It then answered val() with that ancestor's value:

trie: root=38, [1,0,2]=22, [1,1]=72
fork at [1,1,3] (does not exist): val None  ->  after reset(): Some(72)
fork at [9]     (does not exist): val None  ->  after reset(): Some(38)

The zipper knew where its root was (origin_depth, origin_node_depth) but not how far off the trie it was, so reset had nothing to restore. origin_invalid records that, with_root_here captures it when a fork takes the focus as its root, and reset restores it.

Regression test: act_zipper_reset_returns_to_an_off_trie_root, forks at three off-trie paths and a zipper created at one. Fails before this change.

…cestor

A zipper can be *rooted* at a path that does not exist: `fork_read_zipper`
at an off-trie focus makes one, and so does `read_zipper_at_path` with
such a path.  `reset` put the path back but then cleared `invalid`
unconditionally, so the zipper came back believing it was rooted on the
trie, at whatever real node the stack still pointed at, its deepest
existing ancestor.  It then answered `val()` with that ancestor's value:

    trie: root=38, [1,0,2]=22, [1,1]=72
    fork at [1,1,3] (does not exist): val None  ->  after reset(): Some(72)
    fork at [9]     (does not exist): val None  ->  after reset(): Some(38)

The zipper knew where its root was (`origin_depth`, `origin_node_depth`)
but not how far off the trie it was, so `reset` had nothing to restore.
`origin_invalid` records that, `with_root_here` captures it when a fork
takes the focus as its root, and `reset` restores it.

Regression test: `act_zipper_reset_returns_to_an_off_trie_root`, forks at
three off-trie paths and a zipper created at one.  Fails before this
change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BZmoASqM5FUuzvJeJaYQjR
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