CRDT sync: adopt peer snapshot on first open, don't merge
Bug: a device joining a project for the first time was creating a
local Automerge doc via Automerge.from(INITIAL) — fresh actor with
its own root operations (create the meta/files/conversations/turns/
bots maps). Merging that empty starter into a peer's populated doc
gave Automerge two concurrent assigns to each root field, and LWW
could pick the empty side, dropping the peer's data on the floor.
Worse, the merged-but-empty state could then be persisted and
broadcast back, propagating the loss.
Fix:
- crdt.ts: new adoptDoc(bytes) — Automerge.load + clone, so the
device shares the peer's root history but writes new changes
under its own actor.
- store.ts: ProjectStore tracks isFresh (true when there was no
IndexedDB entry at open). The first incoming snapshot on a
fresh store is adopted wholesale via adoptDoc rather than
merged. After adoption isFresh flips false and subsequent
snapshots merge normally (which is safe because the histories
now share a common root).
Existing populated-then-corrupted local docs aren't auto-healed
by this — they have to be wiped + re-seeded from a known-good
source (e.g. the push-project script).