# Sync the lineup graph to the cloud natively \(\#196\) · dara/icarus

[View on GitCafe](https://git.cafe/dara/icarus/commit/37e59ca30d28c20b5805fbc146929c3a3b73c438)

Repository: [dara/icarus](https://git.cafe/dara/icarus)

Visibility: public

Requested revision: 37e59ca30d28c20b5805fbc146929c3a3b73c438

Requested commit: 37e59ca30d28c20b5805fbc146929c3a3b73c438

Commit: 37e59ca30d28c20b5805fbc146929c3a3b73c438

Tree: 249d9566c136e4e6e49e63f4a956eb18989a03c8

Author: Dara Adedeji

Committer: GitHub

## Message

```
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>
```

## Parents

- [b3f15ddd8a42c8094fa91fbe598862dea1766085](https://git.cafe/dara/icarus/commit/b3f15ddd8a42c8094fa91fbe598862dea1766085?format=markdown)

[Source at this commit](https://git.cafe/dara/icarus/tree/37e59ca30d28c20b5805fbc146929c3a3b73c438?format=markdown)
