# Timeline Studio: source offsets and mobile coordinate checks

Published September 7, 2026. The production code in `upstream/src/` is copied
unchanged from Timeline Studio commit
`68980d142cce421eab86cd4ef26a4475a6affd56`:
https://github.com/MartinDelophy/ai-video-editor/tree/68980d142cce421eab86cd4ef26a4475a6affd56

This is an executable engineering example, with synthetic media created for
this publication. It is not a recording of an old bug, a physical-phone test,
or a captured browser export. The test harness was run on Node v22.14.0,
macOS arm64. `verification-report.json` preserves the actual run and scope.

## Reproduce the recorded checks

Unzip `engineering-case-kit.zip`, open a terminal in its folder, and run:

```sh
node verify.mjs
```

No package installation, network access or model download is needed. The
script verifies the included source hashes, executes the pinned production
modules, asserts the outcomes, and recreates the two project archives and
JSON records. It exits unsuccessfully if an assertion fails. It writes sibling
artifacts only in the downloaded kit's directory. `make-media.py` is optional:
run `python3 make-media.py` to recreate the supplied PCM audio and color slate
before running the verifier.

`load-source.mjs` rewrites module import URLs for an in-memory Node loader.
Production function bodies are unchanged. The editor config also imports UI
icons, so the loader reads only its numeric declarations from the pinned
`src/config/editor.js`. No cut, zoom, routing or archive function is mocked.
The fflate dependency is included as `upstream/fflate.mjs` (v0.8.3, MIT).

## Audio case: a split after a trim

The original file is eight seconds long: 220 Hz at 0–2 s, 330 Hz at 2–4 s,
440 Hz at 4–6 s, and 550 Hz at 6–8 s. It is 16 kHz, mono, 16-bit PCM, with
short fades at tone boundaries. These simple tones identify source ranges
without requiring speech recognition or subjective transcript matching.

`before-split.timeline` contains a six-second synthetic visual slate and one
audio clip. The audio occupies project 2.0–5.0 s but represents source
1.0–4.0 s. The visual lane is locked; the audio lane remains editable. Open
the archive in Timeline Studio, select the audio clip, seek to project 3.5 s,
and choose Split. `after-split.timeline` is the expected reducer output:

| Clip | Project start | Project duration | sourceStart | sourceDuration |
| --- | ---: | ---: | ---: | ---: |
| Before | 2.0 | 3.0 | 1.0 | 3.0 |
| Left | 2.0 | 1.5 | 1.0 | 1.5 |
| Right | 3.5 | 1.5 | 2.5 | 1.5 |

The invariant at 1× is `sourceTime = sourceStart + (projectTime - start)`.
At project 3.5 s, the right clip must enter source 2.5 s and therefore the
330 Hz tone. Omitting sourceStart produces source 0.0 s and a 220 Hz tone.
Listen to `right-expected.wav` and `right-without-offset.wav` to hear this
difference. They are deliberately computed PCM slices of the same original;
the second is a negative control, not a result exported by an older release.

The regression goes beyond the first split. Splitting the retained right clip
again at project 4.0 s yields sourceStart 3.0 s. With 2× playback, the initial
cut yields sourceStart 4.0 s. The harness also checks a locked lane, an invalid
edge cut, waveform fixture slices, caption ownership, fades and selection.
The caption used by the unit test is not present in the downloadable projects:
the diagnostic audio has no speech and the projects have captions disabled.

Both archives embed their media. The production archive writer and reader
round-trip them; every recovered audio entry is compared byte for byte with
the original WAV. The two split clips share one in-memory Blob during the
cut, while the portable writer serializes a media entry for each clip.
`before-split.json`, `after-split.json` and `structural-diff.json` expose the
result without requiring a browser.

## Mobile case: preserve the object and the current time

The routing test presses music while the old visual selection is still set;
the returned action track must be `music`. An explicit `audio-clip` inspector
origin must return the audio context even when the persistent tool is Effects.
Voice audio offers separation; linked source audio does not. Sticker actions
contain properties/copy/delete and no audio command.

The pinch case has a 20-second project, a 400 px viewport, an 800 px track,
and a centered playhead at 5.0 s. Increasing finger distance from 100 to 150 px
produces a 1200 px track. The anchor resolver returns 100 px of scroll:
`0.25 × 1200 - 100 = 200`, keeping 5.0 s at the viewport center. Converting the
result back through canonical zoom preserves that width within 0.00001 px.
Pinching below the configured minimum leaves the track at 800 px.

For a physical-phone check, open `after-split.timeline`, press the right audio
clip, and confirm its action sheet targets that clip. Close the inspector,
pinch around the cut, and compare the two clips' start/sourceStart fields
before and after. Attempt to move the locked visual lane. Record device,
browser, orientation, whether a second touch cancelled a pending drag, and
any unintended timing change. **These physical gestures were not performed
in this published run.** `mobile-manual-checklist.csv` is intentionally marked
`not_run`; it is a worksheet, not a passing result.

## What this establishes

Ten check groups passed: source integrity; initial split ranges; neighboring
split state; a repeated split; a split at 2×; rejected edits; mobile routing;
pinch width/anchor; minimum zoom; archive round-trip. This establishes source
behavior for the explicit inputs. It does not validate browser decoding,
preview drift correction, actual touch-event arbitration, phone memory use,
pitch quality or MP4 export. Those require a separate device/browser run.

The full production repository is MIT licensed (included in `upstream/LICENSE`).
fflate's notice is in `upstream/FFLATE-LICENSE`. The new verifier, documents and
synthetic diagnostic media in this kit are released under MIT by MartinDelophy;
the same included MIT terms apply.
