Sync the lineup graph to the cloud natively (#196)

* Sync the lineup graph to the cloud natively

Store each lineup origin, landing and link as its own row in the lineups
table (payloadKind lineupOrigin / lineupLanding / lineupLink), keyed
`<kind>:<entity id>`, so fan-in and link names survive and teammates
editing different links never touch the same row. Legacy lineupGroup rows
stay readable and writable, and move to graph rows on a page's next sync.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Scope lineup keys to their strategy; convert legacy groups fail-safe

Lineup rows were looked up by key across the whole deployment, so a
strategy copied before the graph synced (same item ids) could not convert
its landings and links and lost its lineups. Keys are now unique per
strategy (by_strategyId_and_publicId).

A legacy group's delete now waits until the graph rows the canvas shows
from it are live on the server, so a failed add never leaves a lineup with
neither form. A group counts as drawn while its lineups are, so the client
showing it deletes it once converted.

A second client converting the same group adopts the server's sortIndex
for rows already written, and the server treats an add of identical
content as a noop whatever its sortIndex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Copy every lineup kind when duplicating a strategy

strategies:duplicate gave every lineup row a random key and rewrote its
data as if it were a legacy group, so graph rows came out without the
<kind>:<id> key and links still named the original's origins and
landings. It now copies graph rows under one shared id map per kind,
rewriting keys, lineUpIDs and each link's originId/landingId, and keeps
the legacy group rewrite. The content-row lookup #197 added is scoped to
the strategy like the rest.

Also ports #195's lineup clear/undo tests to graph-row fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test that a lineup link keeps a deleted image's placeholder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Port #199's lineup undo tests to graph-row fixtures

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Copy lineups through one id map so a half-converted page stays one lineup

A page mid-conversion holds a legacy group and the graph rows converted
from it, sharing ids (origin = group id, landing and link = item id).
The copy remapped graph rows through per-kind maps but gave the group a
random id and kept its item ids, so the two forms stopped matching and
the copy drew the lineup twice, then converted the group again. One map
across all kinds keeps the source's shape: the group id, each item id and
every graph id move together.

Also tightens two ported undo tests to check every row of the lineup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
37e59ca30dDara Adedeji committed on 9/26/2026, 9:58:33 PM· committed by GitHubparentb3f15dd
28 files changedLine totals unavailable