Skip to content

perf(studio): add timeline clip-window index primitive - #2702

Open
miguel-heygen wants to merge 1 commit into
codex/studio-timeline-d-playhead-follow-v2from
codex/studio-timeline-d-clip-index-v2
Open

perf(studio): add timeline clip-window index primitive#2702
miguel-heygen wants to merge 1 commit into
codex/studio-timeline-d-playhead-follow-v2from
codex/studio-timeline-d-clip-index-v2

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

What

Add the immutable per-row clip-window index primitive consumed by D8.

Exact-PR scope

This PR deliberately does not call the index from Timeline, pass a render range, or change runtime rendering by itself. Runtime horizontal clip/ruler windowing and actor-pin wiring land in the immediate child PR, #2703.

How

  • Build intervals in original projection order and sort a query view by clip start.
  • Build balanced max trees for positive-duration ends and zero/negative-duration point starts.
  • Prune both branches that cannot reach the requested time window, avoiding the prior prefix-max full backward scan.
  • Query overlaps with half-open boundaries and union explicit identity pins.
  • Restore original projection order before returning results.

Construction is O(n log n) from sorting. The overlap tree walk is O((k + 1) log n) for k matches; identity-pin lookup is proportional to pinned matches, followed by sorting the unique result set back into projection order.

Test plan

  • 50,000-clip adversarial row: one early full-duration clip plus 49,999 ended clips queried near the end
  • Deterministic randomized oracle coverage
  • Boundary, point, duplicate-identity, pin, immutable-snapshot, and fractional-row coverage
  • Full Studio suite: 291 files / 3,195 tests
  • Studio typecheck, oxfmt, and oxlint
  • Exact-tip Chrome 50k virtualized gate passed required quorum through D8 adoption

Review fixes are included at head 06c0f1df5.

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 9fda27f to 9b91ca5 Compare July 27, 2026 20:51
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from f387e18 to 6883a5e Compare July 27, 2026 20:51
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 9b91ca5 to 8a8a531 Compare July 28, 2026 20:43
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 6883a5e to 99125c6 Compare July 28, 2026 20:43
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 8a8a531 to 0a94dbf Compare July 28, 2026 22:10
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 99125c6 to 994baf5 Compare July 28, 2026 22:10
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 0a94dbf to 14895e2 Compare July 28, 2026 22:36
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch 2 times, most recently from ef83c66 to 2dd814a Compare July 28, 2026 23:02
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 14895e2 to e494530 Compare July 28, 2026 23:03
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 2dd814a to b09e9f9 Compare July 28, 2026 23:22
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch 2 times, most recently from 6359e3f to bde178d Compare July 28, 2026 23:53
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from b09e9f9 to c2cae90 Compare July 28, 2026 23:53
@miguel-heygen
miguel-heygen changed the base branch from codex/studio-timeline-d-playhead-follow-v2 to main July 29, 2026 02:07
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from c2cae90 to 6f98580 Compare July 29, 2026 02:28
@github-actions

Copy link
Copy Markdown

Fallow audit report

Found 5 findings.

Duplication (4)
Severity Rule Location Description
minor fallow/code-duplication packages/studio/src/player/components/timelineClipDragGestureLifecycle.ts:56 Code clone group 1 (25 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/player/components/useTimelineClipDrag.ts:359 Code clone group 1 (25 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/player/components/useTimelineTrackLayout.test.ts:26 Code clone group 2 (25 lines, 2 instances)
minor fallow/code-duplication packages/studio/src/player/components/useTimelineTrackLayout.test.ts:73 Code clone group 2 (25 lines, 2 instances)
Health (1)
Severity Rule Location Description
minor fallow/high-crap-score packages/studio/src/player/hooks/useTimelinePlayer.ts:69 'syncTimelineElements' has CRAP score 31.6 (threshold: 30.0, cyclomatic 10)

Generated by fallow.

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 6f98580 to c588a64 Compare July 29, 2026 14:46
@miguel-heygen
miguel-heygen changed the base branch from main to codex/studio-timeline-d-playhead-follow-v2 July 29, 2026 15:03
@miguel-heygen
miguel-heygen marked this pull request as ready for review July 29, 2026 15:06
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from c588a64 to 143d281 Compare July 29, 2026 23:35
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from ee01158 to 108bd87 Compare July 29, 2026 23:35

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE — grade A — rubric CORRECT

Role in stack: D7 data-structure — immutable per-row clip-window index (sorted-by-start + prefixMaxEnd + byIdentity) that D8's virtualizer will query for visible time-range slices.

Root-cause fit: Correct shape for the access pattern. Timeline queries are build-once (per tracks snapshot) / query-many-per-frame (scroll, zoom, overscan). Sorted-by-start with a monotonically non-decreasing prefixMaxEnd gives O(log n + k) queries via upperBoundStart(end) - 1 then walk-backward while prefixMaxEnd[cursor] >= range.start — classic Cormen interval-stabbing pruning, beats a segment tree in constant factors, no rebalance cost. Per-row scoping matches how the virtualizer will fetch one row at a time.

Claims verified (file:line at HEAD 143d281b):

  • Immutable per-row + prefix-max: timelineClipIndex.ts:47-67 (Object.freeze on byStart, prefixMaxEnd, byIdentity, row entry, outer rows).
  • Overlap query returns projection order: :110-118 (final sort by ordinal, then map to element).
  • Identity pins survive filter: :105-108 (byIdentity.get(identity) ?? []).
  • Half-open [start, end) semantics consistent: interval-at-range.end excluded (overlaps L90); zero-duration point at range.end correctly excluded. Tested at timelineClipIndex.test.ts:22-34.
  • Negative-duration coerced to 0-duration point via Math.max(0, element.duration) (L34) — no infinite-result pathology; explicitly asserted.
  • Non-finite guards on both start and durationInfinity correctly excluded.
  • 8 files, +399/-22. Matches PR body.

Adversarial findings:

  • Nice-to-have — no property/fuzz test comparing query vs O(n) linear-scan reference; hand-crafted cases cover the edges but a 100-random × 100-query fuzz would raise confidence.
  • Nice-to-have — in-place mutation of TimelineElement.start after index build leaves interval.start/end frozen but the returned element fresh. Safe under React/Zustand setElements (new array each time), but worth a one-line JSDoc so future callers don't reach in and mutate.
  • Nice-to-have — range.end < range.start silently returns pins-only; consider an early assertion.
  • Nice-to-have — Big-O not documented in module JSDoc.
  • D7→D8 handoff note: rebuild-only. Correct for build-once/query-many; D8 must memoize on tracks identity. Flag for D8 review. (Confirmed D8 does memoize — see #2703 review.)

CI state: All required green.

Suggested next step: APPROVE and land. Ask D8 to (1) memoize on tracks identity (done), (2) add one fuzz test at the D7/D8 boundary comparing queryTimelineClipIndex against a linear-scan oracle over 1k random intervals, (3) add a one-line JSDoc on createTimelineClipIndex noting the "snapshot at build" mutation contract.

Review by Via

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 108bd87 to e454d16 Compare July 30, 2026 00:23
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 143d281 to 4f820c4 Compare July 30, 2026 00:23
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Addressed at 4f820c4e3.

  • Added deterministic randomized fuzz coverage against a linear oracle across windows, negative/zero durations, scoped identities, and actor pins.
  • Added construction/rebuild and query complexity documentation without overstating the backward scan’s worst case.
  • Reused the canonical timeline element identity helper in both index code and tests.

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 4f820c4 to e5ce10a Compare July 30, 2026 01:31
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from e454d16 to 32554de Compare July 30, 2026 01:31

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-delta review at e5ce10ac59 (additive to the existing review).

Important — this PR does not yet window the runtime clip/ruler render path. Repository-wide use at this head finds no production caller of createTimelineClipIndex or queryTimelineClipIndex; Timeline never computes/passes renderTimeRange; and useTimelineTicks.ts:12-14 still calls generateTicks without the new range. The only non-test references to TimelineTimeRange are optional helper/prop definitions. Therefore the exact delta still scans and renders the complete horizontal project, despite the title/body saying visible clip windows are indexed and actor pins are preserved. If adoption intentionally belongs to the next stack PR, scope this PR honestly as index/windowing primitives; otherwise wire and integration-test the behavior here.

Important — the advertised visible-window bound is not guaranteed by the prefix-max scan. timelineClipIndex.ts:111-116 walks backward while the prefix maximum crosses the range start. One early full-duration clip makes that prefix maximum large for every later position, so a query near the end scans all preceding short, already-finished clips: O(total row clips) work to return one overlapping clip. The current “long-lived earlier interval” test has only two entries and the randomized oracle checks correctness, not visited work. Add an adversarial 50k shape and either use a structure that can prune both branches (for example an interval/segment tree) or explicitly narrow the complexity/performance claim.

The overlap boundaries, duplicate identities, pin union, projection order, fractional row keys, and randomized oracle are otherwise well tested.

Verdict: REQUEST CHANGES
Reasoning: The exact PR currently has no runtime performance effect, and its core query can still degrade to the full-row scan the PR claims to remove.

— Magi

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 32554de to bd0dccf Compare July 30, 2026 02:19
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from e5ce10a to 06c0f1d Compare July 30, 2026 02:19
@miguel-heygen miguel-heygen changed the title perf(studio): index timeline clip windows perf(studio): add timeline clip-window index primitive Jul 30, 2026
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Resolved at 06c0f1df5.

Scope is now explicit in both the commit/PR title and body: D7 adds the clip-window index primitive only. It has no production caller and no runtime render effect at this exact PR; #2703 is the immediate child that wires the primitive into Timeline, ruler ranges, and actor pins.

The prefix-max backward scan is replaced with balanced max trees:

  • one tree stores maximum positive-duration ends;
  • one stores maximum point starts for zero/negative-duration clip semantics;
  • the query prunes either branch when neither tree can reach the requested window.

This prevents one early full-duration clip from forcing a scan through every already-ended clip. The complexity claim is narrowed to the implemented bound: O((k + 1) log n) for the overlap walk, plus pin lookup and result-order sorting.

A new adversarial test builds 50,000 clips—one early full-duration clip and 49,999 short ended clips—then queries a late window. The complete clip-index suite, randomized oracle, full Studio suite (291 files / 3,195 tests), and exact-tip 50k Chromium gate pass.

@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 06c0f1d to 83f737a Compare July 30, 2026 03:15
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch 2 times, most recently from 0690067 to 9f3ef4a Compare July 30, 2026 03:47
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 83f737a to 3a8a488 Compare July 30, 2026 03:47
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from 9f3ef4a to a451e13 Compare July 30, 2026 04:05
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 3a8a488 to e2d7d99 Compare July 30, 2026 04:05
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from a451e13 to ca7d212 Compare July 30, 2026 15:25
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from e2d7d99 to 029971e Compare July 30, 2026 15:25
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-clip-index-v2 branch from 029971e to 65a82ab Compare July 30, 2026 17:02
@miguel-heygen
miguel-heygen force-pushed the codex/studio-timeline-d-playhead-follow-v2 branch from ca7d212 to 5fc5ef6 Compare July 30, 2026 17:02
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.

2 participants