Skip to content

New Subthought under a pending parent hides its not-yet-loaded siblings until reload #5531

Description

@raineorshine

In the app, the first step below is what freeThoughts does to a thought's child once more than 500 thoughts are in memory, and the second is any New Subthought under that thought before the pull queue has restored the child. That timing is not reliably reachable by hand, so the steps are given at the reducer level, where they reproduce every time.

Steps to Reproduce

- a
  - b
  - c
  1. Deallocate b from memory without deleting it, as freeThoughts does: deleteThought({ pathParent: [a], thoughtId: b, local: false, remote: false }). This leaves a marked pending with b and c still in its childrenMap, and b out of the thoughtIndex.
  2. New Subthought (CmdEnter, or swipe →↓→) under a with the value d: newThought({ at: [a], insertNewSubthought: true, value: 'd' }).

Current Behavior

  • The console warns Sibling <id of b> with missing thought found while creating new thought d (<id of d>).
  • a.childrenMap holds only c and d, and a.pending is false.
  • b is not rendered, and it stays hidden until the app is reloaded.

Expected Behavior

In this case

After step 2, a.childrenMap should hold b, c, and d, a should still be pending, and no warning should be logged. b should reappear once the pull queue pulls a again.

In general

Creating a thought under a pending parent should leave the parent's existing childrenMap entries and its pending flag intact, whether the missing children were deallocated by freeThoughts or left unfetched at the buffer depth by fetchDescendants, so that the pull queue can restore them.

Notes

  • b is intact in storage. The TreeCRDT write path stores only the thought payload and derives childrenMap on read, so this is a visibility defect rather than data loss.
  • The sweep in createThought that drops the missing children dates from 63873b4, when YJS persisted thoughts non-atomically and an interrupted import could leave a parent referencing an unsaved child. Under TreeCRDT a create is a single atomic insert, so that failure can no longer occur.
  • The same lines evaluate childrenMapKey against the map before the sweep, so when the sweep drops a stale =note entry the new =note child is keyed by id and findDescendant(state, parent, '=note') cannot find it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions