Summary
Clicking Apply in the Edit Clip modal after changing both the source range and the crop fires two concurrent saves built from the same stale document. Whichever lands last wins, so one of the two edits is silently lost.
Reproduction
- Open the Edit Clip modal for any clip.
- Change both the source range and the crop.
- Click Apply.
Expected: both edits persist.
Actual: one of them is silently discarded — no error, no toast. Which one survives depends on IPC timing, so it is intermittent and looks like "the app randomly forgets my crop".
Cause
src/components/ai-edition/NewEditorShell.tsx:1301-1302:
updateClipSourceRange builds next1 from document
updateClipCrop builds next2 from the same document — it never sees the source-range change
Both are written independently. The second write is not derived from the first, so it clobbers it.
This is exactly the race useSequentialTimelineOps exists to prevent — its own file header documents the shape ("two concurrent calls both read the pre-edit doc and the second clobbers the first"). These two calls just don't go through it.
Suggested fix
Compose the two mutations into one document and save once:
updateClipCrop(setClipSourceRange(document, …), …)
…or route the Apply through applyTimelineOp so it inherits the existing serialisation.
Related
Distinct from #282 (a failed save being invisible) — this one is a lost update on a successful save, no failure involved. Worth noting that once #308 lands, the failure case of this path also stacks two identical "Save failed" toasts for a single click.
Found while reviewing #308.
Summary
Clicking Apply in the Edit Clip modal after changing both the source range and the crop fires two concurrent saves built from the same stale document. Whichever lands last wins, so one of the two edits is silently lost.
Reproduction
Expected: both edits persist.
Actual: one of them is silently discarded — no error, no toast. Which one survives depends on IPC timing, so it is intermittent and looks like "the app randomly forgets my crop".
Cause
src/components/ai-edition/NewEditorShell.tsx:1301-1302:updateClipSourceRangebuildsnext1fromdocumentupdateClipCropbuildsnext2from the samedocument— it never sees the source-range changeBoth are written independently. The second write is not derived from the first, so it clobbers it.
This is exactly the race
useSequentialTimelineOpsexists to prevent — its own file header documents the shape ("two concurrent calls both read the pre-edit doc and the second clobbers the first"). These two calls just don't go through it.Suggested fix
Compose the two mutations into one document and save once:
…or route the Apply through
applyTimelineOpso it inherits the existing serialisation.Related
Distinct from #282 (a failed save being invisible) — this one is a lost update on a successful save, no failure involved. Worth noting that once #308 lands, the failure case of this path also stacks two identical "Save failed" toasts for a single click.
Found while reviewing #308.