Skip to content

Fix clearBound corrupting prevBound.extent on singleton SharedBound removal (#143)#144

Merged
mikesol merged 2 commits into
mainfrom
fix/issue-143-clearbound-singleton-extent
Apr 10, 2026
Merged

Fix clearBound corrupting prevBound.extent on singleton SharedBound removal (#143)#144
mikesol merged 2 commits into
mainfrom
fix/issue-143-clearbound-singleton-extent

Conversation

@mikesol
Copy link
Copy Markdown
Owner

@mikesol mikesol commented Apr 10, 2026

Root cause

clearBound in Deku.Internal.Region has two branches — the first branch is taken when the preceding SharedBound is larger (or we're at the first region), and it extends prevBound to cover the nextBound:

void $ ST.write extentToEff prevBound.extent   -- BUG
runSTFn4 fixManagedTo selfIx (extentToIx + 1) (updateShared prevBound) children

extentToEff is a live STRef — it's ST.read managed.ixRef on the removed region. When the removed region is the sole member of its SharedBound (extentToIx == selfIx), fixManagedTo is a no-op (backwards range), and then pushIx(-1) fires on the removed region, making that ref resolve to -1. From that point on, prevBound.extent returns -1, so isLastBound never fires for survivors, and a subsequent bump fails to propagate end to the parent span.

The exact failure sequence from the issue (add C0, add C1, undo C1, undo C0, add C0 again, observe broken placement) maps to: r1.bump, r2.bump, r2.remove (singleton clear → corrupts prevBound.extent), r1.remove (isLastBound broken, updateParent never fires), r3.bump (end not notified).

Fix

Only write extentToEff into prevBound.extent when extentToIx > selfIx — i.e. when there are actually surviving regions beyond the removed one that now belong to prevBound:

runSTFn4 fixManagedTo selfIx (extentToIx + 1) (updateShared prevBound) children
when (extentToIx > selfIx) do
  void $ ST.write extentToEff prevBound.extent

Tests

Added a regression test in index.test.js covering the exact sequence: add, add, remove-last, remove-first, add — verifying that end propagates correctly to the parent span after the full cycle.

Note: the pre-existing sendTo test failure is unrelated to this PR.

🤖 Generated with Claude Code

mikesol and others added 2 commits April 10, 2026 09:44
…emoval (issue #143)

When the removed region was the sole member of its SharedBound
(extentToIx == selfIx), clearBound unconditionally wrote the live
extentToEff STRef into prevBound.extent.  After pushIx(-1) fired on
the removed region that reference resolved to -1, corrupting the
preceding SharedBound's extent.  Subsequent bumps on newly-allocated
regions then failed to propagate end to the parent span, breaking
useDynAtEndWith after an add/remove/add cycle.

Fix: only write extentToEff into prevBound.extent when extentToIx > selfIx
(i.e. there are surviving regions beyond the removed one that now belong to
prevBound).  Adds a regression test in index.test.js covering the exact
failure sequence from the issue report.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The earlier approach (guarding the extent write in clearBound) was too
broad and broke sendTo: bump(Nothing) keeps the region in the children
array and later moves (via sendTo) rely on the live ixRef being tracked
by prevBound.extent so that fixManaged can propagate the updated index.

Root cause revisited: clearBound correctly writes the cleared region's
live ixRef to prevBound.extent even when the region is the sole member of
its SharedBound (extentToIx == selfIx).  The corruption happens
specifically in remove, where pushIx(-1) is called after clearBound,
making the live ref resolve to -1.

Fix: in remove, after bump(Nothing) (which invokes clearBound) and before
pushIx(-1), check whether prevBound.extent now resolves to our soon-to-be-
invalidated index; if so, replace it with the preceding region's ix (a live
ref that won't be invalidated).  This preserves the correct tracking
behaviour for all subsequent operations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mikesol mikesol merged commit 65d6e9d into main Apr 10, 2026
1 check passed
@mikesol mikesol deleted the fix/issue-143-clearbound-singleton-extent branch April 10, 2026 10:54
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