Skip to content

fix(web): keep the saved-view restore notice honest about what was lost#881

Open
aelefebv wants to merge 2 commits into
mainfrom
agent/lucida-52q-saved-view-partial-restore
Open

fix(web): keep the saved-view restore notice honest about what was lost#881
aelefebv wants to merge 2 commits into
mainfrom
agent/lucida-52q-saved-view-partial-restore

Conversation

@aelefebv

Copy link
Copy Markdown
Owner

Closes lucida-52q.

The problem

One unrecognised value in a saved view aborted the entire restore partway through. Because the camera was imported last, the usual result was that annotations came back but the viewpoint silently did not — and nothing told you anything had been dropped. The restore reported success.

The fix

Each restore step now goes through a dispatch seam that classifies what happened. An incompatible or recoverable value skips that one command and the restore continues; a genuinely fatal one still aborts. Either way the notice says what was skipped, so a partial restore looks partial.

Two further silent paths were closed in review: the camera import's own presence check now runs through the same seam, and the unknown-layout fallback reports through the shared skip path instead of dropping the step quietly.

How it was verified

Independent review, with the behaviour covered by tests in applier.test.ts. Gates under Node 24 (matching CI's lts/*): full web suite, lint, tsc -b, and vite build clean, run again in integration staging on top of the other changes in this batch.

🤖 Generated with Claude Code

aelefebv added 2 commits July 25, 2026 05:41
The heavy applier wrapped the whole restore in try/finally with no catch,
and the scene-call guard rethrows, so one value the wasm deserializer
could not accept aborted the loop. Everything after it was dropped:
global contrast/gamma, T/C/Z, and the camera — which is deliberately the
LAST step. The user was still told the view had loaded, so they sat
looking at their own framing believing it was the author's.

This is not limited to hand-corrupted input. Colormap, blend mode and
render mode are closed serde enums, and the decoder only fills in absent
keys, so a link written by a newer build carries an unknown variant
straight to a command. A share link outliving a deploy is ordinary for
this viewer.

Each scene mutation now goes through one `dispatch` seam that contains a
refusal to that single command, so a stale enum costs its own setting and
the steps after it still land. Whatever was refused is collected and
named in a loading-banner warning ("Could not apply 2 settings from this
view: channel colormap (x2). Everything else was restored."), so a
partial restore can never read as a complete one. A document command the
local engine refused is no longer broadcast to peers.

A fatal error (a trapped or borrow-poisoned instance) still aborts: it
cannot apply anything, and the notice then says the restore stopped
rather than claiming the rest landed.

Tests cover a view carrying an unrecognized colormap landing the camera
and the Z/T/C indices, the unbroadcast refused layout, and the dead-engine
abort with its unfinished-restore notice.
Review of the previous commit found two ways the end-of-apply notice could
still say something untrue.

The camera step reads live presence before writing the camera back. That read
was wrapped in a catch that recorded a skipped setting for anything it threw.
But `export_presence` returns a plain string with no refusal path of its own,
so it throws only when the wasm instance is gone — the case the apply is
supposed to abort on. Instead the apply finished, and the notice told the user
everything else had been restored by an engine that could no longer restore
anything. The read now goes through the same `dispatch` seam as the writes, so
one place decides that fatal aborts, and through `guardedSceneCall`, so the
session can raise its engine banner.

A layout the recipient does not have was dropped with only a console warning.
Paired with any refused command, the user got one warning naming the refusal
and ending "Everything else was restored." while the author's layout had gone
missing too. The fallback now joins the same skipped list, so the sentence
stays true rather than being softened.

Both are covered by tests that fail against the previous commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant