Repository navigation
Capture saves to the cloud library while signed in, through a pending capture queue - #294
Conversation
A replay's Capture always wrote to the local library, so a signed-in user's captured strategy landed on this device while the rest of their work was in the cloud. Signed in, Capture now makes the strategy in the cloud (the open cloud folder, else the top level) and adds each captured page there; signed out it stays local as before. Each cloud capture is one page and its items sent together; one that doesn't land is taken back and the user is told. Offline it refuses with a message rather than queueing: a cloud strategy can't be made offline, and the replay is still there to capture from. Items get fresh ids (the server keys them globally) with one root per player across pages, so page transitions still move players between captured moments. Open lands on the last captured page for cloud strategies too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
Astra's review: a capture whose answer was lost (a timeout) may still land, so it no longer says the capture failed; it says it couldn't confirm, and the next capture adds to the same strategy instead of starting another. A capture the server took without some items stays and says so, rather than deleting the strategy or page with a revision read later, which could take a teammate's edits with it. A player alive on one captured page and dead on the next now glides between them in a cloud strategy too: copies of an agent pair whether or not they show a view cone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The capture's server-error check moves to isConvexFunctionError beside the repository's other error helpers (convex_architecture_test). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Astra's second review showed the direct-to-server capture could forget its strategy or make a second one, and it kept Capture waiting on the network. Dara chose a client-only queue with no server change. Each capture is now one versioned JSON record in its own Hive box, with every id fixed when the moment is captured (strategy, pages, items, op ids, client id). Capture returns once the record is written. The queue sends captures of one strategy in order: createWithInitialPage, which the server answers with the strategy it already has; then PageAddOp under a fresh revision (a revision conflict resends under a new op id); then the items. A lost answer resends the same ops; a refusal waits for Retry, which gives what didn't land new op ids. A strategy deleted or moved to another map fails its captures, and the next capture starts a new one. The capture card, the library's cloud work card and the sign-out check count captures still on their way or refused, and Open waits until the page is in the cloud. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Astra round 3: - Item delivery that resumes after the page landed (a retry, a lost answer) now reads the strategy again first, so a strategy deleted or moved to another map meanwhile fails the capture instead of taking the positions; a page deleted meanwhile says so. A whole batch refused as not found marks the strategy lost. - A failed write of a capture's progress no longer escapes delivery: the capture is kept in memory as stopped on this device, shown with Retry on the capture card and the library card, and blocks sign-out; other strategies' captures keep going. - A capture goes into the open cloud folder only when it is this account's own; a shared folder, or one the server refuses, leaves it at the top level. - A server that needs a newer Icarus is noted, so the app's update notice shows, and the card says to update Icarus to finish saving. - The card shows a send whose answer was lost as unconfirmed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Astra round 4: - Items aren't checked against the strategy's map, so the strategy and page are read again before each batch of items, not only before the page: a map change landing between the page and its items now fails the capture instead of placing old-map positions. - A server that needs a newer Icarus now stops sending until it accepts this build again, instead of retrying every 15 s. - A capture moved to the top level because its folder was gone or not this account's says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Capture in the replay viewer always saved to the local library. A signed-in player's captured strategy ended up on this device while the rest of their library lived in the cloud, so it never showed on their other devices. Now, while signed in, Capture saves to the cloud library the way New Strategy does: in the cloud folder open in My Library when that folder is the player's own (never a shared one), or at the top level. Signed out, or on a build without cloud, nothing changes.
The first version of this PR wrote each capture straight to the server and kept Capture disabled until the server answered. Astra's second review showed that path could forget its strategy or make a second one, and it put the network in the player's way. Dara chose a client-only queue with no server change. This is that queue.
How a capture gets to the cloud
Capture waits only on this device. Each capture becomes one versioned JSON record in its own Hive box (
replay_captures_box, opened with the other outboxes). The record holds every id the server will see, fixed at capture time: the strategy, the page, each item, each op, and a client id kept for the whole replay session. Capture returns as soon as the record is written, about 100 ms in the release build from click to "N pages captured" on screen. Rapid captures are written one after another in the order they were made.ReplayCaptureQueuesends captures of one strategy in order (lib/providers/collab/replay_capture_queue_provider.dart), and only while the record's account is signed in and online:createWithInitialPagewith its fixed ids. When it's sent again, the server answers with the strategy it already has.PageAddOpagainst the revision it just read. If someone changed the strategy in between, it reads again and resends under a new op id, since the server answers a repeated op id with its first answer.Where the player sees it:
Kept from the first version: items get fresh ids, because the server keeps one row per item id across all strategies and a replay's ids repeat in every strategy captured from it. Each player keeps one root id across pages (
<root>~cp1~<uuid>, via move-to-cloud'sMovedItemIds).transition_planner.dartnow pairs an agent with its copy whether or not it shows a view cone, so a player who died between two captures moves instead of fading out and back in. That also affects copy-to-page: an agent whose copy gained or lost its view cone now glides instead of fading, which local strategies already did.openCloudStrategytakes apageId, so Open lands on the captured page.No server change. No
convex/change, no new op type, no payload change. The queue usescreateWithInitialPage,fetchShellandapplyBatchwithPageAddOp/ElementAddOp, all of which shipped clients already send.Ground truth
Windows release build against the dev deployment, test account
+icarus-l2c-final4, driven from inside the app. To stand in for "can't reach the server", a scratch switch in my build (not in this PR) held the queue's sends.<page-1 id>~cp1~<uuid>ids. The queue was empty afterwards.--hive-store-dir, same account): one strategy with all three pages, an empty local library, and players moving between pages (the second half of the GIF).Tests:
test/providers/replay_capture_queue_test.dartruns against a fake server that dedupes by op id, answers identical pages and items without writing, keeps one row per item id, and checks page revisions. It covers: offline captures sent in order once online; lost answers (to the create, a page add, or a batch of items) resent without duplicates; items accepted beside a refused one kept, with Retry sending only the refused item; a map change landing between the page and its items stopping the items; a retry rechecking the strategy (moved map, deleted) before sending; a storage failure holding one capture while another strategy's goes, then Retry; a folder the account can't use, and the toast saying so; a server needing a newer Icarus, with sending stopped; records surviving a restart; a revision conflict resent under a new op id; a refusal held, then retried under new op ids; a deleted strategy failing its captures, with the next capture starting a new strategy even after discard; a moved-map strategy left untouched; discarding the first capture so the next one makes the strategy; discard reporting that a sent capture may still land; an unreadable record kept.replay_capture_test.dartchecks a shared folder is never the destination.page_copy_id_test.dartchecks that an alive player pairs with its dead copy.Not covered, and gaps Dara accepted
test/widgets/web_beta_library_test.dart: one test's name claimed a replay capture "still starts a local strategy" while signed in. It still testscreateNewStrategy(local: true), so I renamed it to say what it checks.ci: test replay
🤖 Generated with Claude Code