Skip to content

A recovered edit landing under a held item can be overwritten by the drop #220

Description

@SunkenInTime

Found while reviewing #208 (Astra and Greptile, independently). Low frequency, but it silently overwrites work, so it's tracked here.

Sequence

  1. A text edit ("old" → "new") is still in the durable outbox when the app restarts.
  2. The page loads showing "old". Restored queue entries have no in-memory overlay.
  3. The user presses and holds that item (drag, draft, lineup pin).
  4. The restored op lands. recordAckBatch advances the item's hydrated base to "new" at revision 2. The canvas still shows "old", because the merge leaves held items alone.
  5. The user drops. syncLocalPage sends the canvas content ("old") with expectedRevision 2. The server accepts it, and the recovered edit is silently replaced.

Also on icarus-cloud before #208: an edit to that item between the restored ack and the page reload hits the same path. #208's hold makes the window longer.

Why it isn't fixed yet
The fix needs live sync to tell "content the canvas drew" apart from "content the server acked". Three attempts during #208 (fc70b2b, bb80bf0, 176fda0, 52cd737, all backed out in 3095f6f) each left another path that rewound a revision or caused a false conflict:

  • snapshot-before-ack ordering, where dropSatisfiedOverlays runs before recordAckBatch
  • restored in-flight ops that can gain an overlay through pending_reconciliation
  • editing again before one's own ack lands

A correct fix probably needs explicit op provenance: mark ops restored from the outbox so their acks don't count as "seen".

Tests to keep passing: 'an acked edit the canvas never showed is still taken', 'grabbing an item again as its own edit lands is no conflict', and 'typing into an item before its own move lands is no conflict' in test/strategy_page_session_provider_test.dart.

🤖 Generated with Claude Code

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions