What happens
Reordering clips under imported audio does not carry the audio with its clip: the tracks are rebuilt, and the result is stored (autosave), so it survives a restart once the undo history is gone.
Measured on v2.0.0 (macOS), 3 clips — b6cdac 0–102.1 s, 63fedd 102.1–163.97 s, d51b65 163.97–266.07 s — with a 30 s music bed across the b6cdac/63fedd junction and a looping 4 s track across 63fedd/d51b65 (both kind: music):
|
before |
after moving 63fedd to the end |
| bed fragments |
b6cdac 89.869–102.1 s, 63fedd 102.1–119.869 s |
b6cdac 89.869–102.1 s, d51b65 102.1–204.2 s (offset 12231), 63fedd 204.2–221.969 s (offset 114331) |
| loop fragments |
63fedd 119.869–163.97 s, d51b65 163.97–199.977 s |
221.969–324.069 s and 324.069–385.939 s — entirely past the 266.07 s programme end |
The 30 s bed now spans 132 s and covers a clip it never touched; the loop is pushed out of the film. Moving the clip back makes it worse (bed 89.869–266.07 s, loop 266.07–430.04 s). Only Ctrl+Z restores it.
Cause
mapAllRegionCollections (src/lib/ai-edition/document/timeline.ts:396) runs every structural clip edit's audio through separateAudioLanes(reanchorAudioTracks(fn(audioTracks), …)):
fn — here rederiveAnchoredRegion via withClipsChanged → rederiveRegionMs — moves each anchored fragment with its own clip. After a reorder that is correct, but the fragments of one track are no longer adjacent on the ruler (bed: 89.869–102.1 s and 204.2–221.969 s).
reanchorAudioTracks → collapseTracksToPills (audioTracks.ts:132) folds each trackId back into ONE span from the first fragment's startMs to the last fragment's endMs (89.869–221.969 s). That assumes the fragments are contiguous; after a reorder the span swallows whatever clip now sits between them.
anchorAudioTrackFragments re-ventilates that inflated span over the new layout, so the clip in the middle gets a full-length fragment, and offsetMs keeps advancing by elapsed span (12231 → 114331), pointing past the end of a 30 s file.
separateAudioLanes (audioTracks.ts:463) then sees the inflated bed overlapping the loop (same music row) and shifts the whole loop pill to the bed's end — past the programme end. It shifts startMs/endMs only, so the shifted fragments no longer agree with their own clipId/sourceStartSec anchors.
Every number in the table follows from these four steps. The reanchor pass was added in 521d0bd (in v1.11.0) to repair offsetMs after re-ventilation, which copies it verbatim. A move/reorder never re-ventilates — rederiveAnchoredRegion keeps each fragment's own offsetMs, which already encodes its file position — so for these ops the pass is unnecessary and destructive.
Possible fix
- Only re-run
reanchorAudioTracks on fragments that were actually re-ventilated (orphans / never-anchored), not on anchored fragments whose clip survived; or
- collapse per run of fragments that still meet on the ruler with continuous
offsetMs (the existing takesJoin test), instead of per trackId; and
- have
separateAudioLanes move anchors with the ms, or resolve overlap without detaching fragments from their clips.
No test covers moveClip with audio tracks (timeline.test.ts, insertion.test.ts), which is how this got through.
Expected
Each audio fragment travels with the clip it was placed over (checklist "Imported audio and voice-over": "Reorder the clips underneath a track and confirm the track follows the clip it was placed over"), keeps its length and offsetMs, and no clip gains audio it never had.
Steps
- Make a project with 3 clips (e.g. drag the same recording to the timeline three times).
- Import an audio file (M) across the clip 1/clip 2 junction; import a second one across clip 2/clip 3 and turn Loop on.
- Drag clip 2 to the end.
- The first track now covers all of the old clip 3; the second is pushed past the end of the timeline. Inspect
audioTracks in the project file to see the spans.
Environment
v2.0.0 (DMG, notarized), macOS 27.0, Mac mini M1. main has no app changes since v2.0.0. Found in the v2.0.0 computer-use E2E pass (checklist line 376).
What happens
Reordering clips under imported audio does not carry the audio with its clip: the tracks are rebuilt, and the result is stored (autosave), so it survives a restart once the undo history is gone.
Measured on v2.0.0 (macOS), 3 clips —
b6cdac0–102.1 s,63fedd102.1–163.97 s,d51b65163.97–266.07 s — with a 30 s music bed across theb6cdac/63feddjunction and a looping 4 s track across63fedd/d51b65(bothkind: music):63feddto the endb6cdac89.869–102.1 s,63fedd102.1–119.869 sb6cdac89.869–102.1 s,d51b65102.1–204.2 s (offset 12231),63fedd204.2–221.969 s (offset 114331)63fedd119.869–163.97 s,d51b65163.97–199.977 sThe 30 s bed now spans 132 s and covers a clip it never touched; the loop is pushed out of the film. Moving the clip back makes it worse (bed 89.869–266.07 s, loop 266.07–430.04 s). Only Ctrl+Z restores it.
Cause
mapAllRegionCollections(src/lib/ai-edition/document/timeline.ts:396) runs every structural clip edit's audio throughseparateAudioLanes(reanchorAudioTracks(fn(audioTracks), …)):fn— hererederiveAnchoredRegionviawithClipsChanged→rederiveRegionMs— moves each anchored fragment with its own clip. After a reorder that is correct, but the fragments of one track are no longer adjacent on the ruler (bed: 89.869–102.1 s and 204.2–221.969 s).reanchorAudioTracks→collapseTracksToPills(audioTracks.ts:132) folds eachtrackIdback into ONE span from the first fragment'sstartMsto the last fragment'sendMs(89.869–221.969 s). That assumes the fragments are contiguous; after a reorder the span swallows whatever clip now sits between them.anchorAudioTrackFragmentsre-ventilates that inflated span over the new layout, so the clip in the middle gets a full-length fragment, andoffsetMskeeps advancing by elapsed span (12231 → 114331), pointing past the end of a 30 s file.separateAudioLanes(audioTracks.ts:463) then sees the inflated bed overlapping the loop (samemusicrow) and shifts the whole loop pill to the bed's end — past the programme end. It shiftsstartMs/endMsonly, so the shifted fragments no longer agree with their ownclipId/sourceStartSecanchors.Every number in the table follows from these four steps. The reanchor pass was added in 521d0bd (in v1.11.0) to repair
offsetMsafter re-ventilation, which copies it verbatim. A move/reorder never re-ventilates —rederiveAnchoredRegionkeeps each fragment's ownoffsetMs, which already encodes its file position — so for these ops the pass is unnecessary and destructive.Possible fix
reanchorAudioTrackson fragments that were actually re-ventilated (orphans / never-anchored), not on anchored fragments whose clip survived; oroffsetMs(the existingtakesJointest), instead of pertrackId; andseparateAudioLanesmove anchors with the ms, or resolve overlap without detaching fragments from their clips.No test covers
moveClipwith audio tracks (timeline.test.ts,insertion.test.ts), which is how this got through.Expected
Each audio fragment travels with the clip it was placed over (checklist "Imported audio and voice-over": "Reorder the clips underneath a track and confirm the track follows the clip it was placed over"), keeps its length and
offsetMs, and no clip gains audio it never had.Steps
audioTracksin the project file to see the spans.Environment
v2.0.0 (DMG, notarized), macOS 27.0, Mac mini M1.
mainhas no app changes since v2.0.0. Found in the v2.0.0 computer-use E2E pass (checklist line 376).