* Refuse deleting a lineup end a live link still names A teammate's new lineup from a shared origin could land first; deleting a lineup on a canvas that had not drawn it yet took the origin with it, and the server accepted that delete. The teammate's link was left without its origin, so hydration dropped the lineup for everyone while the chip said synced. - Server: with the new opt-in applyBatch flag checkLineupEndDeletes, an origin or landing delete fails with LINEUP_END_IN_USE while a live link on its page names it. Links deleted earlier in the batch are already tombstones. Clients without the flag (shipped builds, the web build that sends only checkLineupLinkEnds) keep today's behavior. - Client: sends the flag; the outbox claims link deletes first and holds an origin or landing delete while a link delete on its page is still unsent (restart key order, batch cap, another client id, backoff). - The refusal lands in attention with a plain message. Keep mine asks again; use theirs brings back the origin with the teammate's lineup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read only a page's live links when checking an end delete Review follow-up: each origin or landing delete scanned every lineup row on its page, tombstones and payloads included, so a batch deleting many lineups could exceed the transaction read budget. A new index reads only the page's live links. It is additive; Convex backfills it on deploy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check an end delete with one indexed lookup Review follow-up: reading every live link on the page per end delete still re-read their payloads (notes can be large) once per end, which a batch of many deletes could push past the transaction read limit. Two additive indexes on a link's page, kind, deleted flag and origin or landing id let each check read at most one row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read each page's links once per batch for end deletes Review follow-up: the nested-path indexes went through payload.data, a record, which Convex's schema check rejects, and a lookup still read the whole link row once per end. Back to the live-links index: a batch reads a page's live links once, keeps only their origin and landing ids, and follows its own accepted link adds, patches and deletes. Reads per batch are at most one page's live links per page, which the page snapshot query already reads in one go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep a patched tombstone out of a batch's live links Review follow-up: the server accepts a patch to a deleted link and leaves it deleted, but the batch's copy of the page's live links took any accepted payload write as live, so a later delete of that link's origin was refused. Only an add makes a link live; a patch updates a link the copy already holds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say why an origin or landing delete was refused Wires LINEUP_END_IN_USE into #212's specific attention reasons, so the sync button and toast explain the refusal instead of calling it a conflict: another lineup in the cloud still uses the spot, Keep mine tries again, Use cloud brings that lineup back here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
17 files changedLine totals unavailable