fix(canvas): close the data-loss and agent issues found in the canvas stack reviews
Merged
GN
2d ago+2306+2306+0123456789012345678901234567890123456789-259-259-012345678901234567890123456789
Summary
- Independent reviews of the canvas stack (#1396 to #1414) found 20 issues still present after it landed. This PR fixes all 20. Four could lose edits:
- A duplicated browser tab could silently retire the original tab's canvas.
- A brand-new canvas could drop its first edits on delete or quit.
- "Take disk" could be undone by a save that arrived at the wrong time.
- A single editor stall could trigger a crash remount.
- Each fix has a test that failed before the fix and passes after it.
Related Issue
No issue. The findings come from the per-part reviews of the canvas stack.
Type of Change
- fix: bug fix
What Changed
- Saving.
- Autosave has a maximum wait (
canvasAutosaveMaxMs, default 10 s), so an agent editing nonstop still saves. - A failed web autosave shows "Save failed" and retries.
- A reloaded editor frame reopens the saved document instead of overwriting it.
- Never-saved documents are snapshotted on close.
- Autosave has a maximum wait (
- Ownership and lifecycle.
- Each browser tab gets its own client id, and a live mount is saved before a takeover.
- A cut-off mount shows a visible state instead of failing silently.
- The mount cap is a host setting, and eviction falls back to another saveable canvas.
- Delete or archive remounts the canvas if the host refuses.
- Outside changes and crashes.
- The disk hash is accepted only after the editor loads the disk version.
- Reloads skip when the editor moved on.
- One stall counts as one timeout, and a late reply resets the count.
- Agents.
- Queued calls past their deadline are dropped instead of run late.
- Export picks its format from the file extension, re-checks the path right before writing, and is no longer marked read-only.
- The agent cursor work runs after the reply, and agent names are freed on exit.
- Hardening.
- The bundle fetch script only accepts this fork's release URLs, which addresses the CodeQL alert from #1396.
- The palette and the explorer open the same document without a remount.
Scope
- 61 files, over the 50-file guideline. The fixes overlap in the canvas store, persistence and gateway, so splitting them would mean a stacked PR again. That is still under CodeRabbit's 100-file limit.
- Two narrow races remain:
- A call within network-transit time of its timeout can still run. Closing that fully needs a cancel message.
- A reload decision can still race by one message hop. Closing that needs a figcraft fork change.
How It Was Tested
-
cargo test --lib -- canvas host_mcp web::ws::: 592 pass -
bun run typecheckandbun run ci - vitest on all touched suites
- Real run in headless Edge, 12/12 checks:
- An agent editing every 700 ms saved at 10.65 s.
- A frame reload restored all 23 nodes without overwriting the file.
Checklist
- PR title follows the conventional commit format
- Explained why no issue exists
- Added or updated tests
- Read AGENTS.md
- A human reviewed the complete diff before submission
🤖 Generated with Claude Code
Summary by cubic
Fixes the 20 issues found in the per-part reviews of the canvas stack; four of them could lose edits.
Bug Fixes
- Autosave gains a maximum wait (
canvasAutosaveMaxMs, default 10 s), a failed web autosave shows "Save failed" and retries once, and a reloaded editor frame reopens the saved document instead of overwriting it. - Never-saved documents are snapshotted on close, a duplicated tab claims a fresh client id, eviction passes over a victim that cannot be saved, and delete or archive remounts the canvas when the host refuses, keeping a newer canvas in place.
- The disk version is adopted only after the editor loaded it, and only editor answers prove a stalled canvas is alive.
- Agent calls still queued past the host's timeout are dropped unrun, export picks its format from the path's extension and anchors the write to the verified folder, and presence work moves off the reply path.
- The bundle fetch accepts only this fork's release URLs, and two spellings of one document path keep the same mount.
Each fix has a test that failed before the change and passes after it.
Summary by CodeRabbit
- New Features
- Canvas exports can target project-relative paths; formats are inferred from recognized file extensions when not specified.
- Canvases autosave during ongoing edits after a configurable maximum wait.
- Canvas calls have deadlines, and queued calls that expire are not run.
- Bug Fixes
- Improved handling of external file changes and failed reloads to prevent conflicting edits from being saved over newer disk content.
- Canvas saving, remounting, and closing are more reliable when editors time out, reload, or cannot save.
- Export destinations are checked to help prevent writing outside the project.
- Project deletion and archiving preserve open canvases if the operation fails.
- Closing a new, untouched canvas no longer creates an unnecessary file.