Summary
The frontend clears dirty item IDs before the Rust layout write succeeds. If apply_layout fails, the visible canvas position remains changed but the dirty state is lost, so the layout may never be retried or persisted.
Evidence
src/canvas/CanvasView.tsx:129-140 copies dirty.current, clears the set, and then awaits applyLayout.
- The catch branch reports an error but does not restore the failed IDs.
- A later unrelated change is not guaranteed to include the failed items.
Proposed change
- Remove dirty IDs only after their acknowledged write succeeds.
- Requeue failed IDs without overwriting newer in-memory mutations.
- Serialize layout writes per directory so stale completions cannot overwrite newer state.
- Use bounded retry with visible unsaved state rather than a silent infinite loop.
- Flush or explicitly retain pending state during canvas transitions and application shutdown.
Acceptance criteria
- A simulated write failure leaves every affected item marked dirty.
- Retrying after the failure persists the latest frame, rotation, and ordering values.
- Overlapping edits cannot let an older completion overwrite a newer layout.
- Navigating or opening media with a pending save does not silently lose the change.
- The UI distinguishes saved, saving, and unsaved/error states.
- Automated tests cover one failure followed by success, repeated failure, overlapping edits, and unmount with pending changes.
Summary
The frontend clears dirty item IDs before the Rust layout write succeeds. If
apply_layoutfails, the visible canvas position remains changed but the dirty state is lost, so the layout may never be retried or persisted.Evidence
src/canvas/CanvasView.tsx:129-140copiesdirty.current, clears the set, and then awaitsapplyLayout.Proposed change
Acceptance criteria