Round Cloud Save

Lets a signed-in user save a whole round (the tournament/debaters/judges wrapper around a set of flows) to their account and load it back on any device.

  • Nav: FlowHistoryDialog's "Rounds" tab — a cloud-upload icon per round, "Save all rounds" and "Save flows not in a round" buttons
  • Package: debate-round (dialog + validation + fetch client), apps/debate-ai.com (/api/rounds, saved_rounds D1 table)

What it does

A Round only references its flows indirectly via flowIds, so saving a round also saves each flow it references first. "Save all rounds" and "Save flows not in a round" cover the bulk cases. The "Saved to account" tab's Flows section badges a flow "via round" when it was only saved as part of a round's cascade.

The round save is optimistic-concurrency checked, mirroring Flow Cloud Save: it sends along the updatedAt of the cloud copy this browser last knew about (from the last list/load/save response it saw). If that doesn't match what's actually saved — someone else saved a newer version since — the save is rejected with a conflict instead of silently overwriting it, and the round's cloud icon turns into a warning that a second click (an explicit "save anyway") resolves. This covers the round row itself; each referenced flow's own save still goes through flow cloud save's own conflict check independently.

Pinned ("Featured") debates follow the account too: pinning a round is stored as a pinnedDebates tool record ({ id: "<round id>", pinnedAt }, via /api/tool-records/pinnedDebates), so the Featured section on the start screen matches across devices. Pins from before this change are plain numbers in localStorage; they are still read, and are upgraded (and start syncing) the next time any pin is toggled on that browser.

Data flow

state/bulkRoundSave.ts (pure) — collectFlowsForRounds / collectUnreferencedFlows
state/savedRounds.ts (pure) — isValidRound / deriveRoundLabel / hasRoundSaveConflict
round/saved-rounds-client.ts (fetch) — list/fetch/save/delete
apps/debate-ai.com/app/api/rounds/[route.ts, [clientId]/route.ts]

Dirty tracking on bulk saves

"Save all rounds" and "Save flows not in a round" skip re-PUTting a flow or round whose content hasn't changed since it was last saved successfully this session: state/bulkRoundSave.ts's hashFlowContent/hashRoundContent fingerprint each item's JSON payload, and filterDirtyFlows/ filterDirtyRounds compare that against a hash recorded after each successful save (FlowHistoryDialog's lastSavedFlowHashes/ lastSavedRoundHashes state, which — unlike the per-item save-status maps — survives the dialog closing and reopening). A clean item is reported as "saved" in the bulk summary without a network round-trip. The per-item cloud icon is unaffected and always saves, since that's an explicit user action. This closes the "no dirty tracking" half of the gap this section used to record (#810); the hash cache only lives for the current page session, so a fresh page load re-saves everything once, same as before.

Pinned debates follow your account

The star on a round in the history dialog (and the Featured section of the debate start screen it feeds) used to live only in one browser. Pins are now { id, roundId, pinnedAt } records under the pinnedDebates key, one entry in the Tool Data Sync catalog (@debate/data-sync), so a signed-in user's featured rounds appear on every device. A round's local id is also its saved_rounds.client_id, so a pin resolves to the same round once that round has been saved to the account. A pin whose round is not saved or present on the current device is skipped rather than shown. The older pinned-debates id list is read once as a fallback and replaced on the first pin change. When pins from two devices add up to more than 12, the start screen shows the newest 12.

Flow editor settings follow your account

The flow editor's preferences - font, default zoom, theme, side colors, vim in the RFD editor, paste/append behavior, scroll zoom, tooltips, CardMirror options and your custom keyboard shortcuts - are stored as two records (display and keymap) under the ebbSyncedSettings key, one entry in the Tool Data Sync catalog. Signing in on another device applies them there, live. Settings that describe a machine rather than you stay local: the flows folder, panel layout (sidebar, RFD drawer) and the live-collab toggles, name and contacts. When a device syncs, the account's copy of a record replaces the local one as a whole, so the last device to sync wins for all synced settings at once.