A behaviour suite for comments and version history written from the public documentation
(`crates/engine/src/history_behaviour_tests.rs`, 15 tests, about 80 table rows plus stepwise checks),
each row with a one-line derivation; where the documentation is silent the row says so and pins
today's behaviour. 4 of the 15 tests failed on the old code. Fixes, all in shared engine code:
- **Live sessions: comment threads made in the session couldn't be used.** Their ids come from the
creator's id space (e.g. 4294967297) and every thread command (`comment.reply`, `edit`, `react`,
`resolve`, `move`, `delete`, `markRead`, `open`, `link`) refused them as "out of range".
Comment ids are now parsed as ids, not small indexes.
- **Restoring a version lost newer comments.** Comments are a conversation about the file, kept
apart from its design history: restoring now keeps the current comments; a comment on a layer
the version doesn't have stays where it was (on the nearest surviving parent, or the canvas).
- **Restoring now keeps the replaced design in the history** as an autosave first, unless a
version of it was just saved or restored (so work isn't only in the session's undo history).
The checkpoint marker is now "none yet" for a freshly opened file and compares contents when
normalising copied the document.
- **A titled autosave becomes a named version** (`version.rename` clears `auto`), so it is no longer
dropped when autosaves make room.
- **Merging a branch adds to the main file's history:** the main file as it was (an autosave,
when it changed since its last version), then `Merged “<branch>”`.
- `version.restore` refuses up front for a view-only live session (nothing goes into the history).
Pinned where the documentation is silent: pins go to the innermost frame under them (not to a
leaf layer); copies, duplicates and duplicated pages come without comments; replying reopens a
resolved thread; only authors delete their messages (no accounts); stepping past the last thread
stays on it; comments show on the canvas by default; versions stay with whoever saved them in a
live session.
`crates/engine/src/collab_live_tests.rs`: the test `Room` is `pub(crate)` so the suite can run a
live session. `crates/engine/src/cmd/props.rs`: one line reformatted by `cargo fmt` (it was
unformatted on the base, which failed the fmt check). `crates/render/src/effects_behaviour_tests.rs`:
the paint suite expected an offset drop shadow to grow the export on every side, but the later export
suite made exports grow only where the shadow falls (40 x 50 for a shadow 10 down); the base failed
the gate on it, so the expectation now says "grows" (wider or taller). No new commands.
Docs: collaboration-parity, target-app-parity (area 16 85%/60% -> 85%/62%, 2-5 h -> 2-4 h; full
parity ~306-595 h -> ~306-594 h; percentages unchanged at this rounding), ROADMAP (also corrects the
stale area 16 row, 80%/58%/4-8 h), gaps (gap 4's landed list), mcp.md.
- Versions now belong to the document (DocState.versions) instead of a process-global map keyed by
document uid, so they survive closing the file. A new, renamed or deleted version marks the file
as changed.
- .figcraft: `versions/index.json` (id, name, description, time, image hashes) plus one deflated
`versions/<id>.json` snapshot per version; images only versions use are stored once and kept with
them. Snapshots read from a file are parsed on first use (and written back as-is). Readers that
don't know versions ignore the entries; files without versions load unchanged. Damaged, missing,
oversized or duplicate entries are skipped and reported, never failing the load. At most 100
versions per file; names and descriptions are clipped.
- Commands: `file.saveVersion {name?, description?}` returns the version; `version.list`;
`version.restore {id | index}` (one undo step, keeps the file name, stays on the current page);
`version.rename {id | index, name, description?}`; `version.delete`; `version.duplicate` (opens
the snapshot in a new tab). Indexes are validated: a negative index used to restore version 0.
`req_index` param helper shared with the guide commands.
- Version history panel in the left sidebar in place of Pages and Layers (File > Show version
history; Save to version history opens it with the form): save form (title, description),
"Current version" on top and the versions newest first on a timeline with name, relative time and
description; per-version menu (also right-click): Restore, Rename (title and description inline),
Duplicate, Delete (confirmed inline). New shared `widgets::button`.
- `figcraft-cli run --out x.figcraft` keeps the version history.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>