Repository navigation
feat(studio): scrub full-quality video with decoded preview frames - #7379
Conversation
miguel-heygen
left a comment
There was a problem hiding this comment.
Review at faa7546 (stacked on #7378). Changes requested before this merges. Not verified in a real browser yet.
Blocking: black-after-Play is still reachable. previewSeek.ts:244-249 (and :150) publishes at the 5 s cap whether or not the native element is ready. frames.clear() then removes the decoded canvas while the <video> may have no decoded frame, and transport.play() runs over a transparent layer. Readiness is only re-checked on loadedmetadata/loadeddata/seeked/error (previewSeek.ts:10). A native element already within 0.02 s of the target gets no seek from syncRuntimeMedia, so seeked never fires; at readyState 1 it waits the full 5 s and then clears. A unit-level probe (handoff(2), native currentTime 2, readyState 1) shows exactly that. Likely real triggers:
- Play at a clip's start on an element that only loaded metadata (the frame path sets
preload="metadata") - a native seek slower than 5 s
It's intermittent because the 120 ms warm timer (init.ts:5064-5074) usually seeks first. Suggestion: never clear the decoded canvas on a timeout. Keep each video's canvas until its native element presents a frame after play() (rVFC, or playing plus a first frame). Add canplay to EVENTS, and force a real seek when the element sits at the target with readyState < 2.
Blocking: required CI didn't run on this head (32 check runs vs 100 on #7378, with no Test/Typecheck/Build/Lint/Studio/SDK checks), presumably because the base isn't main. "Heavy films: Safari vs Chrome" failed with an isPlaying timeout. That's probably the known flake, but it needs a green rerun.
High: Play can be silently dropped. init.ts:5126 bails if nativePlayGeneration moved. Any seekWhenReady/seek/setPausedVideoFrameProvider during the ~278 ms handoff (e.g. an incidental timeline time-sync) cancels it after buffering was already posted. Should only an explicit pause or a user seek cancel a pending Play?
Medium
- Two stacked 5 s caps (decode never settles, then native fallback): up to ~10 s frozen preview (
previewSeek.ts:244-247). init.ts:3711-3716: a provider swap at rest clears the presented canvases without a native resync, so a stale or blank native frame shows.pausedVideoFrames.ts:82-83: any currentTime write differing by ≥ 1e-8 permanently marks an authored seek, which disables decoded frames for that element for the life of the page. The runtime's ownmediaElementTimeadds 1e-5. I found no current path that trips it while paused, but a looser tolerance and a non-permanent mark would be safer.studio-server/src/routes/preview.ts:155,687: the runtime is now injected at head start (ahead of authored head scripts) for every preview and the sub-composition route, replacinginsertRuntimeTagand its accelerated-canvas branch. That's a global load-order change worth its own PR or explicit sign-off.
Low
- Abort isn't wired to
cancelReads/decode, so the newest seek can wait behind one stale decode. - The
source === request.sourcebranch in sourceTiming is dead. el.src || el.currentSrccan be "", which adds a cooldown keyed "".- "Original source detail" holds at quality 1 only. Hostile codecs decode the served transcode and lowered quality decodes the lighter copy; worth saying in the description.
Checked OK:
- abort and late-decode release, and the pending/runId/live() guards
- native fallback paths
- every graphics consumer (color grading, 2D drawImage patch, WebGL, WebGPU importExternalTexture with VideoFrame lifetime, rVFC)
- preview canvases are stripped from saved markup
- paused decode honours display size, SAR, rotation and flip
- frame-grid timing matches export
- arrow and drag-release settle
Locally, the 8 touched core suites pass 401/401. The Studio decoder tests weren't run (mediabunny ^1.65.2).
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits · head faa7546b
I agree with @miguel-heygen's blocker: the native handoff can uncover a frameless video at the 5 s cap and keep playing over it. An independent unit repro confirms it: at 5.1 s, playing=true, canvases=0, native readyState=1, seeking=true. Ways to make it less likely: keep each decoded canvas until its native element presents a frame, and warm native elements (preload=auto plus a seek) while the provider owns the picture, because media.ts:666 skips them while paused. The gaps below are additive.
The live plate-8 report (readyState 4, zero canvases) is not explained by this diff. remove() restores the marker, the currentTime override, the callbacks and the canvas, and nothing here writes filter or ancestor styles. The reporter has since ruled out the filter and scene parking. One caution on that evidence: readyState 4 with currentTime advancing doesn't prove frames are being presented, because the media clock keeps running while the video renderer stalls. getVideoPlaybackQuality().totalVideoFrames and chrome://media-internals in the preserved tab would tell a decoder/renderer stall apart from a compositing problem.
Scope
Read core previewSeek/pausedVideoFrames/pausedVideoCallbacks/init/media/video-texture-compat and the Studio decoder/provider/seek bar/keyboard end to end. Server and editor changes: read the diff only. Locally, the new core (50) and studio (56) tests pass. Required CI hasn't run on this stacked head.
— HyperFrames Team Review Agent (Genesis)
9d5b242 to
4ffd675
Compare
faa7546 to
aca914d
Compare
|
Review fixes pushed at aca914d, rebased on repaired parent4ffd6759d.
Validation: focused core302 and Studio decoder/facade21 tests, runtime/Studio type checks, core+Studio+CLI builds, lint/format/hooks and Fallow pass. The original implementation fails the beyond-five-second handoff regression specifically by entering playing with unready native video. Actual current IAB156 checks show a visible background through the natural7→8 cut, repeated seek/Play cycles, full-quality frame stepping and final Play/Pause. Owned Chrome152 records three sequential preview decoding sessions while scrubbing and zero such sessions during native Play, with full-resolution readers recreated on Pause. The separate thumbnail decoding sessions are not counted as provider sessions. The sustained readyState4 missing-plate failure in the preserved older trial remains unproven as to root cause. Fresh-session success and these confirmed repairs do not establish that old document is permanently repaired. The attempted real slow-network test was inconclusive because withholding the MP4 payload also withheld its trailing metadata; the timeout behavior is proven by the runtime regression. The head-start runtime hook is intentional: authored head scripts can construct callback-driven VideoTextures before the previous insertion point, so the bridge must install first. Existing head-order and GPU-consumer tests cover that contract; this still needs reviewer sign-off as part of this PR. The generic paused seek cap/coalescing and remaining review tradeoffs should be assessed against these updated heads. The normal CI workflow has been dispatched on this child branch without changing global PR-trigger policy: https://github.com/heygen-com/hyperframes/actions/runs/42414660582 . It is pending, not established green. The older Safari heavy-alpha second-run isPlaying timeout remains an observed failure, not a proven flake; new-head Safari checks must pass before landing. No merge or release requested. |
There was a problem hiding this comment.
Verdict: REQUEST CHANGES — 2 resolved, 0 open, 3 new · faa7546b → aca914de
- Blocker: the dispatched Checks run 42414660582 is red because of the fix commit. I reproduced it locally: the same suites pass 230/230 at
e220e96fand fail 4 ataca914de.init.swapScenes.test.ts: "holds a parked destination…" fails, and "waits for the scene a seek lands in…" times out.entry.test.ts: "resolves a mixed image/video seek only after both have decoded" fails.mediaClock.test.ts: the currentTime-writer census fails (↓ inline).- Build also fails: the
@hyperframes/producerpacked size is 867,161 bytes, over its 857,000 budget.
— HyperFrames Team Review Agent (Genesis)
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-review at 90555ab, covering aca914d in full plus the delta. Still changes needed: one High. Otherwise close.
Fixed:
- Black at the cap: fixed on the Play-handoff path and the paused path. My old probe (element at the target, readyState 1) no longer clears.
- CI: now runs on the stacked head. The aca914d reds are fixed: the producer size, the 3 core tests, and the raw
currentTimewriter atinit.ts:5069.- The size budget was raised to 870000; please put the reason in the description, as the budget script asks.
- Echo seeks: they no longer drop a pending Play.
- Provider swap: it re-seeks instead of clearing.
- Authored-seek tolerance: fixed.
- Audio-only
<video>: fixed. - Pick mode: fixed.
- Decoder release before Play: fixed.
- Failed decoder load in suspend: now caught.
High, the handoff never times out. In previewSeek.ts expire, request.native → check() has no fallback. isUnplayable's stopped/never-started detection only arms on a play request (media.ts:212-213, 262-264), and the handoff runs before play(). A native element that Chrome stops loading after the header (readyState 1, network idle, no events) never counts as unplayable.
- Probe: still unresolved after 60 s, buffering, and the whole composition never plays. On main it plays and skips that clip after 5 s.
- Suggestion: after the cap, treat still-unready elements as unplayable for the handoff (start Play with the held or decoded picture still covering them), or count the cap as the play request.
Medium, a paused-seek expiry leaves a stale picture with no retry. The expiry resolves the request and removes the listeners, but never publishes or releases the hold.
- When the element later fires canplay/seeked, nothing commits. The picture stays old while the playhead shows the target.
- The hold attribute also stays, so grading's
drawableFramestays null and the held videos stay hidden until the next interaction. - Suggestion: resolve, but keep the listeners so the target publishes once it's ready.
Low
- handoff
cancel()runs before awaiting suspend. With only a hold snapshot showing (theinit.ts:5137branch), the mid-seek native video is uncovered during the await. Code reading only. - No test covers the provider-swap resume-Play branch.
- The authored-seek mark is still permanent per element; fine if intended.
- swapScenes "waits for the scene a seek lands in…" was intermittent at aca914d. It passed at 90555ab; watch it.
Medium, unchanged: studio-server/src/routes/preview.ts:155 still injects the runtime at head start and drops insertRuntimeTag's accelerated-canvas tracker (htmlDocument.ts:396-400). Please confirm that's intended.
Locally the 338 core tests in the touched suites pass. Not run: a real browser, Safari, and the Studio vitest suite.
|
Addressed @miguel-heygen’s review at 90555 in 7e43269:
Four new regressions fail against 90555’s original implementations and pass repaired. Focused checks pass 527 core/runtime/compiler tests and 153 preview-route tests, runtime/server typechecks, core/server/producer/fullCLI builds and commit hooks. Original paused/scrub resolution remains unchanged. The authored-seek mark intentionally remains per element: authored seeking chooses native ownership for that node until replacement/reload. The scene wait test passes the focused run; I am not calling the earlier timeout a proven flake. This addresses the confirmed handoff/recovery defects. It does not establish the cause of the separate sustained readyState 4 missing-plate state in the preserved older document. |
There was a problem hiding this comment.
Verdict: REQUEST CHANGES — 3 resolved, 0 open, 1 new · aca914de → 7e43269d. The exact-head Checks run 42417875143 failed only the required Test: runtime contract job (↓ inline). Every other job is green.
— HyperFrames Team Review Agent (Genesis)
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-review at a1f2a76 (base #7378 @ 4ffd675).
The isVideoElement swap is correct and matters: Studio can create body elements with the editor window's prototypes, so under instanceof the keep-set came out empty. NEW-2, NEW-3, the head-start injection and the provider swap are fixed, and each has a test that fails when the fix is reverted.
Still blocking: Play can wait forever when the element is ready at the moment Play is pressed.
run() calls notePlaybackIntent(el) before host.prepare seeks the element. If the element is at readyState >= 2 at that point, noteFirstPlayAsk stores firstPlayAskedAt = null, and stallSinceFirstAsk returns null forever (askedAt == null). If the handoff seek then leaves the element metadata-only with the network idle, isUnplayable never becomes true, so ready() never passes. The request.native branch of expire only re-runs check() every 5 s, so the request never publishes and the whole composition buffers until Pause. That is the common path: Play after a decoded-frame scrub, where the element is ready and gets seeked to the target. Probed handoffs to t=3, watched for 60 s:
- already metadata-only and idle at the target: resolves at 5 s
- ready at Play, seek leaves it metadata-only and idle: never resolves
- ready at Play, seek hangs while loading: never resolves
- metadata-only, network loading forever: never resolves
main plays the rest of the composition in all three hanging cases.
Simplest fix: on the Play path, once the cap has expired a second time, publish anyway and treat every clip that is still not ready the way clearFrames already treats stalled ones (keep its picture). Alternatively, start the stall clock after the handoff seek instead of before it.
Smaller items:
- After a paused expiry the request stays pending, so
previewSeek.timestays set.transportTickreturns early, paused media sync stops, and timed-visibility updates and the metadata handler are skipped until the source becomes ready or the user interacts. Before 7e43269 the expiry cleared the request. - Every Play waits the full 5 s again for a source already skipped as stalled, because
notePlaybackIntentresets the clock whenever the element is metadata-only. main skips it straight away after the first detection. - No test covers the cap re-arm on the Play path or the picture kept by
cancel(pending.expired); reverting either one breaks nothing. - On recovery, the held picture is removed on
loadeddata/canplay/seekedwithout checking the element's time against the transport, so a stale native frame can show briefly before media sync seeks it.
The readyState-4 no-picture stall is not addressed by this delta. Only a real-browser pass can settle it.
There was a problem hiding this comment.
Verdict: REQUEST CHANGES — 1 resolved, 0 open, 0 new · 7e43269d → a1f2a762. Exact-head Checks 42418767367 are green. I'm holding the verdict for @miguel-heygen's Play-path blocker, which I confirmed by reading the code. If an element is at readyState ≥ 2 when notePlaybackIntent runs, firstPlayAskedAt becomes null, so isUnplayable can never trip. The request.native branch of expire (previewSeek.ts:277-279) then only re-checks and never publishes. If the handoff seek then stalls at readyState 1, the whole composition waits for Pause, whereas main plays the other layers. The Play path needs a hard publish deadline.
— HyperFrames Team Review Agent (Genesis)
|
Addressed the current a1f2 Magi/Genesis findings in 3f1dd2f:
The repaired lifecycle passes 60 final focused tests; a1f2 fails 10/11 selected probes. Moving the deadline back behind suspension fails the deferred-release regression. 496 existing core regressions and 86 Studio regressions pass; types, runtime guards, builds and commit hooks pass. The real-browser smoke also exposed two VideoSample GC errors. An isolated direct-WebCodecs ownership trace reproduced them in preview CFR alpha-range prefetch; the samples were orphaned inside Mediabunny's alpha merger before reaching our sink callback. Transparent tracks now use finite sample queries, preserving original dimensions; opaque CFR keeps sequential prefetch. Restoring unsafe alpha prefetch fails the new reader regression. The fixed direct run closes 3384/3384 samples with zero leaks; forced early-abort InputDisposed diagnostics are recorded separately. This avoids a dependency fork and may do more work for nearby alpha seeks. Producer 867,755B passes the existing 870,000B budget; the measured reason for the previous budget increase is in the rewritten final description. No additional increase. The separate older sustained readyState 4 visual stall still has no proven cause; sample-leak proof does not establish causality for it. |
There was a problem hiding this comment.
Verdict: REQUEST CHANGES — 1 resolved (@miguel-heygen's unbounded Play: the request-wide 10 s deadline bounds it, including decoder release), 0 open, 1 new · a1f2a762 → 3f1dd2f4. Exact-head Checks 42423260935 are green. The new finding comes from reading the code; I haven't reproduced it in a browser.
— HyperFrames Team Review Agent (Genesis)
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-review at 3f1dd2f (base #7378 @ 4ffd675).
The hard Play deadline works. The four stalled-handoff cases that hung on a1f2a76 (ready at Play then the seek stays metadata-only, seek hangs while loading, metadata-only loading forever, seek hangs after metadata) now all resolve at 10 s. Removing the deadline fails 7 tests, and reverting the keep-set fails 6. Background recovery for expired paused work and the no-re-arm for skipped sources also hold, each with a test that fails when the fix is reverted. CI on this head is green.
Blocker: a recovered video doesn't reappear while its clip is playing. This confirms the issue Genesis raised.
The recovery check (the clear callback in init.ts plus ready in pausedVideoFrames.ts) reveals the video only once currentTime is within 0.02 s of the moving transport, and otherwise seeks again. Each seeked lands one seek-latency behind the clock, so with more than about 20 ms of seek latency it re-seeks on every seeked. Probe: video ready at Play, handoff seek loads forever, the 10 s deadline passes, then the network delivers.
- 0 ms seek latency: revealed immediately.
- 40 ms: 100 seeks between t=1.05 and t=5.01, revealed only when the clip ended.
- 100 ms: 41 seeks, revealed at clip end.
- Error-then-recover path at 25 ms: 61 seeks, never revealed before the clip ended.
The result is a frozen picture for the rest of the clip, plus a seek loop on that element, which also disrupts its audio. The existing test passes only because land() completes the seek in 0 ms.
Suggested fix: during playback, reveal on the first frame presented after the video is playing (rVFC, or playing plus a timeupdate) and let media sync correct the drift. At minimum, do one corrective seek and then reveal within MEDIA_SYNC_TOLERANCE (0.04 s) instead of re-seeking on every seeked.
Smaller items:
- With a provider available, the seek hold is now a
holdNativepresented entry, butcancel()releases only the old-style hold. A cancel with no follow-up publish (for exampleonStopMedia) leaves the frozen snapshot over a hidden video until the next seek or publish. - Not covered by any test:
ownsRecovery(the transportMovableClips filter), the new!pausedFrames.retained(el)gate in the transport tick, andwallClearTimeout(deadline)inretireRequest. Removing any of them breaks nothing. - Alpha reader: the change is correct. Each sample is closed exactly once on the finite-request path, opaque CFR prefetch is unchanged, and dimensions are unchanged. Worth saying in the description that transparent tracks lose forward prefetch, so each scrub step on an alpha video is a random-access decode. Minor:
canBeTransparent()sits inside thePromise.allthat opens the reader, so a rejection there fails the whole reader; consider.catch(() => true).
The readyState-4 no-picture stall still needs a real-browser pass.
|
Addressed the current-head playback-recovery blocker and cancellation item in The first-provider-seek/stop-media case also reproduced: 2 request-owned native holds survived cancellation. Cancellation now releases those holds, while superseding seek/Pause/provider replacement retain them until publication; late decode and callback completion cannot remove the new picture. 84 focused tests pass plus runtime types/guards, lint/audit, core/producer/full CLI builds and normal hooks. An isolated real-video 12-second native-request hold preserves 2100×1800 through the 10-second deadline and recovers with one ordinary sync seek after release, without console errors. The lower-priority mutation-coverage suggestions will join the user-authorized post-merge regression follow-up. The transparency-detection rejection currently falls back to native; no reproduced defect warrants altering that path here. Transparent tracks still use finite requests, with the random-access tradeoff stated in the description; alpha decoder code and quality are unchanged in this update. The old 3065 readyState 4 stall cause remains unproven. Full CI and exact-head re-review are requested separately. |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
There was a problem hiding this comment.
Verdict: COMMENT (needs rebase) — 1 resolved, 0 open, 0 new · 3f1dd2f4 → 8a1c6ba7. I have no remaining code findings, and the dispatched Checks run 42425930335 is green. #7378 has merged and this PR now targets main, but it conflicts (mergeable_state: dirty), so the required checks can't run against main on this head. I'll approve the rebased head once its required CI is green and the patch matches this one. The intermittent VideoSample GC warning stays an open item for the author.
— HyperFrames Team Review Agent (Genesis)
Edit accuracy: accurate 267 (base branch 267), smooth 99 of thoseThe gate passes. Quarantined, measured but not gated (8)
Unstable (2)
|
8a1c6ba to
c1b235b
Compare
|
Rebased #7379 onto main after #7378 merged ( The remaining actual IAB Chrome 156 warning was attributed: two finalized-unclosed samples came from preview All four new probes fail against the pre-fix decoder; 27 Studio decoder/provider tests and 345 rebased core clock/media/presentation regressions pass. Types, guards, lint/audit, builds and normal hooks pass. Actual IAB Chrome 156 on the fixed final bytes repeats the UI sequence with 2,631/2,631 samples explicitly closed, zero live/finalized-unclosed, and console errors A permanently stalled alpha query can retain its reader beyond the independent 10-second Play deadline; healthy playback still starts with the held picture. That limitation is documented, rather than adding an unsafe forced-close timeout. This scoped ownership repair does not prove the cause of the older preserved readyState-4 visual stall. Exact-head CI and re-review remain required. No merge/release was performed by this repair pass. |
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-review at c1b235b (rebased onto main b87a01f). No blockers; this looks good to merge.
Rebase: the range-diff of 4ffd6759..8a1c6ba7 against b87a01f6..17b19cab2 shows all 7 commits identical, with no conflict-resolution edits. The only new commit is c1b235b.
Playback reveal (17b19ca): the earlier blocker is fixed. In the probe, Play's handoff stalls past the 10 s deadline and then the network delivers; transport sync is real, not stubbed.
| seek latency | seeks | revealed after delivery | 3f1dd2f |
|---|---|---|---|
| 0 ms | 0 | +25 ms | 1 seek, +25 ms |
| 25 ms | 0 | +25 ms | 126 seeks, never |
| 40 ms | 0 | +50 ms | 80 seeks, never |
| 100 ms | 0 | +100 ms | 32 seeks, never |
The error-then-recover path also reveals with 0 seeks. The stalled-handoff cases still all resolve at the 10 s deadline. Snapshots are released on stop-media and kept when a provider replaces the request. Composition-registered frame callbacks still go through staging: the observer uses the unpatched rVFC, and routing it through the patched path fails the new test.
Alpha drain (c1b235b): every sample is closed exactly once on the normal, abort, iterator-error and dispose paths.
Medium, worth doing if you push again: the drain has no bound. If a transparent-video read stalls during paused scrubbing and the user then presses Play, suspend() never resolves on this head; on 8a1c6ba it resolved immediately. Play then waits out the full 10 s deadline instead of starting at once, and the stalled reader keeps its input and connection open until the stall clears. Racing reader.tail against ~1–2 s, then cancelling and disposing, keeps the leak fix for normal drains and restores immediate Play on a stall.
Medium, fine as a follow-up: a recovered video is revealed at the position where it stalled, and nothing seeks it to the playhead. Media sync only speeds it up to 1.25×, and hard-seeks only past 3 s of drift. Delivered 1.5 s after the deadline, it is still −0.91 s behind at clip end; delivered 2.5 s after, −2.15 s, which is visible as a plate out of step with its matte. One corrective seek at reveal when |drift| > 0.5 s fixes it, and a single seek can't loop.
Low:
- These still have no tests: the
video.pausedcheck on the progress path, the stale-entry guard inreveal,!drivingon hold release,cancel(true)on a mode switch and onpause(), the deadline clear on retire,ownsRecovery, and theretained(el)transport gate. - Without a frame provider, a mode switch now keeps the old snapshot, and new clip videos aren't held until publish.
Required checks on this head are green. The missing-plate stall at readyState 4 is still unproven either way.
There was a problem hiding this comment.
Verdict: APPROVE — 0 open, 0 new · 8a1c6ba7 → c1b235be. I compared the old and rebased ranges: all 7 rebased commits are identical to their previous versions, with no conflict edits. The new alpha drain (c1b235be) keeps getSample semantics through a one-timestamp samplesAtTimestamps query and waits for in-flight alpha reads to finish before disposing the input. Every required workflow on this head is green, including CI, Windows render/tests and regression. The approval covers this SHA only. Known tradeoff, documented in the PR: if an alpha read stalls forever, its reader stays open, because cancelReads runs only after the drain finishes; the 10 s Play deadline still bounds playback.
— HyperFrames Team Review Agent (Genesis)
What
Studio scrubs and steps through original-source video frames at full display dimensions, publishing video layers and composition timing together. Play has a bounded handoff that preserves the last available full-quality picture for unready videos while the rest plays.
Why
With several video assets, independent native seeks can leave pictures behind the timeline or uncover a video before its native frame is ready. Paused editing needs coherent pictures and source quality, including when a decoder is slow, fails, or finishes after the user has moved on.
Related work
Second of two PRs replacing #7346. #7378 has merged. This child is rebased onto main at
b87a01f6; range-diff confirms the seven existing child commits are unchanged. The additional reader fix drains transparent video queries before teardown.How
getSampleearly return can cut off asynchronous alpha merges. Reader suspension/disposal waits for the active alpha query before cancelling network reads or disposing Input, and closes the selected sample on iterator failure. Opaque CFR tracks keep nearby sequential prefetch. Both paths preserve original dimensions.Test plan
Validation of the rebased child and final alpha-drain candidate:
3f1dd2f4ewith 4 seeks instead of 1.getSample(28.20001)fromperson.webm; both thumbnail consumers closed all samples. On the fixed assets, the same UI sequence settles at 2,631 created / 2,631 closed / 0 live / 0 finalized-unclosed, with console errors[]: 1,574 opaque and 1,057 alpha preview samples. Thumbnail disk cache meant no new thumbnail queries in the fixed round. This validates the actual Studio consumer rather than only a standalone decoder test.b29475a6, entryindex-24pkruBk, decoderpausedVideoDecoder-CECjAhp6and codeccodec-BmoJ1p4z; the diagnostic proxy uses the same entry/decoder and only appends ownership hooks to the codec.Transparent cleanup must drain safely. If an upstream alpha query never settles, its reader can remain alive beyond the 10-second Play deadline; healthy playback still starts, the retained picture remains visible and stale completion cannot restart Play. Network bodies have existing silence/retry limits, but connection acquisition and worker completion are not universally bounded. There is no unsafe forced-close timeout.
Full-source fidelity can cost random-access latency. Transparent nearby seeks now use finite requests for safe resource release and may do more work; no current-head end-to-end performance benchmark is claimed. Historical measurements against the pre-split 035f787c0 trial found more complete scrub updates and faster warm arrow steps, with slower first visits, settling and Play handoff; they are not a delta against #7378 or this final head.
The separate sustained readyState 4 missing-plate state in the preserved older document still has no proven root cause. Confirming and repairing this sample leak does not establish causality for that visual stall. No merge, deployment, Canary bundle change or release is part of this PR.
Before
Historical capture before review fixes, parent
9d5b242c(#7378), approximately one second into a two-second drag from 0.5 to 6.7 seconds: video and HTML retain a complete picture at 0.700 seconds while the requested timeline has advanced beyond 3 seconds.After
Same historical timed drag in child
faa7546b, before the review fixes: video and HTML have advanced together to 3.433 seconds. Both layers remain visible and aligned. This is a visual correctness demonstration, not a capture-based performance benchmark.