test(producer): add mov ProRes distributed fixture#848
Conversation
3dbf67f to
7dbdb7b
Compare
a44bcfb to
271dcda
Compare
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-reviewed as part of the fixture stack. LGTM.
vanceingalls
left a comment
There was a problem hiding this comment.
(Intended verdict: APPROVE — posting as COMMENT because the self-stamp guard blocked the approval action; treat as a green review modulo the notes below.)
Clean addition. Matches the established #845/#847 stack pattern; correctly identifies that ProRes is intra-only and pins the orthogonal -c copy + QuickTime-atom contract, not state-continuity. Validated end-to-end (in-process + distributed-simulated both byte-identical against the baseline).
Strengths
meta.json:11-15—chunkSize: 15correctly produces N=4 chunks at 60 frames, and the GSAP crossfade atsrc/index.html:91-92(0.9s → 1.1s) straddles the frame-30 seam by design, so the fixture meaningfully exercises chunk boundaries even though ProRes is keyframe-only.- Fixture leans entirely on existing wiring from #847 (harness
outputFormatbranching +runDistributedSimulatedRenderformatcast atregression-harness.ts:805) — no plumbing duplication. - Description is unusually precise about what this fixture pins vs. what it doesn't (atom preservation through concat-copy, not frame inter-dependency). That framing prevents the next contributor from re-litigating "is this redundant with mp4-h264-sdr?"
Findings
important
.gitattributes:10— theoutput.webmLFS pattern is added pre-emptively, but webm is actively rejected bycheckDistributedSupport(regression-harness-distributed.ts) andplan()throwsFORMAT_NOT_SUPPORTED_IN_DISTRIBUTED(distributed/plan.ts:270). There is no current or near-future code path that produces atests/distributed/*/output/output.webm. Adding LFS rules for a path that can't exist is scope creep — recommend deferring to whichever PR actually enables webm in distributed mode. Failure mode is minor (an inert LFS pattern), but it muddies the audit trail when the webm fixture eventually does land and someone wonders "did this PR add the LFS rule, or is it the same one?".meta.json:3description — worth one sentence noting that the PSNR comparator (psnrAtCheckpointinregression-harness.ts:415) does not include the alpha plane inaverage:by default (ffmpeg'spsnrfilter measures Y/U/V only). For this fixture byte-equality +maxFrameFailures: 0catches drift, but a future author cribbing this fixture and authoringminPsnr: 30against an alpha-only regression would get a false pass. Forward-looking note prevents that footgun.
nits
src/index.htmlis missing two comments siblings have: the "no<audio>element on purpose — AAC frame quantization extends container duration past 2.0s and trips the last PSNR checkpoint" rationale (mp4-h264-sdr/src/index.html:92-98), and the explicit annotation that frames {15, 30, 45} sit inside the crossfade window (mp4-h264-sdr/src/index.html:103-105). Cribbing those over keeps the three fixtures' surface explanations parallel..labeltext isPHASEhere butCHUNKinmp4-h264-sdr. Cosmetic, but stack coherence is cheap.minAudioCorrelation/maxAudioLagWindowsinmeta.json:7-8are dead config for silent compositions — the audio block short-circuits when extracted PCM is empty. Carried over from the sibling pattern; not actionable here, just calling it out for whoever revisits the meta schema.
Verdict: APPROVE (posted as COMMENT — see top note)
Reasoning: Fixture is correct, well-scoped, and lands cleanly on the harness wiring #847 set up. The webm LFS rule and alpha-PSNR docstring are worth addressing but neither blocks merge — both are easier to fix in a follow-up than to gate a fixture PR on.
Review by Vai
…ov-prores fixture Address @vanceingalls review on #848: the mp4-h264-sdr sibling fixture explains the crossfade-straddles-frame-30 and continuous-rotation chunk-seam design choices inline; mov-prores didn't. Add the parallel comment so the next contributor reading either fixture finds the same context. Notes specifically that ProRes is intra-only and therefore exercises the QuickTime atom / -c copy contract rather than frame-level state continuity. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
7dbdb7b to
3c29060
Compare
271dcda to
967ebe6
Compare
|
Thanks @vanceingalls — addressed comment-parity in commit On the The alpha-plane-PSNR docstring nit is a fair forward-looking note but doesn't load-bear here (this fixture uses byte equality, not PSNR) — leaving for the v1.5 PR that enables alpha-aware comparison. |
…852) ## Description Phase 4 of the distributed rendering plan: test fixtures (see DISTRIBUTED-RENDERING-PLAN.md §11 Phase 4 + §10 test strategy). This is PR 4.7 of the Phase 4 remainder — the final fixture PR. This PR adds six per-adapter chunk-boundary fixtures under `tests/distributed/{gsap,anime,three,lottie,css,waapi}-boundary/` plus a single `bun:test` driver (`chunkBoundary.test.ts`) that exercises each fixture's seek-determinism contract. Each fixture is a 60-frame composition (2s @ 30fps, 320×180) that drives the named adapter through the HyperFrames runtime's seek hook. The test renders each at `chunkSize=60` (N=1 chunk, no seams) and `chunkSize=15` (N=4 chunks, three seams at frames 15/30/45), then asserts every PNG frame is byte-identical across the two runs. **Why png-sequence**: mp4 bitstreams encode keyframe placement directly. At `chunkSize=60` libx264 emits 1 IDR; at `chunkSize=15` it emits 4 IDRs at frames 0/15/30/45. Those are legitimately different bytes even when the captured pixels are identical. png-sequence's assemble path merges chunk frame directories with no re-encode, so per-frame byte equality is exactly pixel equality — the strongest contract a distributed render can satisfy. **Fixture design** (each is ~60 lines of HTML): - `gsap-boundary` — single GSAP `tl.to(...)` driving translateX + rotation linearly across 2s. - `anime-boundary` — anime.js v4 timeline registered via `window.__hfAnime`. - `three-boundary` — minimal Three.js scene; cube rotation derived from `window.__hfThreeTime`. - `lottie-boundary` — inline Lottie JSON (rectangle layer animating position+rotation) loaded via `lottie-web` and registered via `window.__hfLottie`. - `css-boundary` — pure `@keyframes` animation; the HyperFrames CSS adapter seeks via `animation-delay`. - `waapi-boundary` — `element.animate()` with linear keyframes; runtime sets `currentTime` per frame. The fixtures intentionally omit `meta.json` so the regression-harness discovery skips them with a clear `missing meta.json` log (they're driven exclusively by `chunkBoundary.test.ts`). The test passes `rejectOnSystemFonts: false` because some adapter bundles (notably anime.js's IIFE) embed CSS-shaped strings inside their JS source — `font-family: ui-monospace, monospace` for internal devtools styling — which `validateNoSystemFonts`'s document-wide regex would otherwise false-positive on every adapter fixture that loads such a bundle. The fixtures display no text, so the relaxed font validation doesn't affect the contract under test. The 7th test case is a layout sanity check that asserts every expected `*-boundary` fixture directory exists. ## Testing - `bun test packages/producer/src/services/distributed/chunkBoundary.test.ts` — 7 tests pass on host (6 adapters × byte-identical N=1 vs N=4 + the layout check, 41.6s) - `bun test packages/producer/src/services/distributed/` — all 49 distributed unit tests pass (43.6s) - `bun run --cwd packages/producer docker:test:distributed font-variant-numeric many-cuts gsap-letters-render-compat style-1-prod sub-composition-video mp4-h264-sdr png-sequence mov-prores mp4-h265-sdr -- --sequential` — full smoke set + all four prior stacked fixtures pass (9/9) - `bunx oxlint` + `bunx oxfmt --check` clean - `bunx tsc --noEmit` (producer package) clean ## After this stack lands The Phase 4 fixture set is complete: PR 4.6 (#844) pins cross-worker idempotency; 4.2/4.3/4.4/4.5 (#845/#851/#847/#848) prove each format produces correct chunked output at the fixture's `minPsnr`; 4.3-pre (#850) added the codec knob H.265 needed; and this PR proves each first-party adapter's seek-determinism survives chunk seams. Phase 5 (CLI surface for `hyperframes plan/chunk/assemble`) and Phase 6 (AWS Lambda turnkey) are unblocked. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

Description
Phase 4 of the distributed rendering plan: test fixtures (see DISTRIBUTED-RENDERING-PLAN.md §11 Phase 4 + §10 test strategy). This is PR 4.5 of the Phase 4 remainder, stacked on PR 4.4 (#847) which added png-sequence format support to the harness.
This PR adds the mov ProRes fixture (
tests/distributed/mov-prores/). The composition is a 2-second (60-frame) scene at 30fps with a transparent background, a crossfade transition straddling the frame-30 chunk seam, and a continuously rotating SVG icon — same shape as the mp4-h264-sdr and png-sequence fixtures.renderConfig.format: "mov"+chunkSize: 15produces N=4 chunks, each encoded as ProRes 4444 with alpha (yuva444p10le) and assembled via-c copy.ProRes is intra-only — every frame is a keyframe — so concat-copy at the frame level is structurally trivial. What this fixture pins is the orthogonal contract: that ffmpeg's
concat -c copyacross four mov files preserves the QuickTime atoms (moov, mvhd, trak, mdia) intact so the assembled file plays in mov-native editors (Premiere, FCPX, DaVinci) without re-encoding. Both modes produced byte-identical 60-frame output against the in-process baseline (all 100 PSNR checkpoints came back ∞ for both modes).LFS:
.gitattributesaddspackages/producer/tests/distributed/*/output/output.movandoutput.webmpatterns. The 3.8 MB ProRes baseline is the main motivation;.webmis included pre-emptively so when webm becomes distributed-supported the path is already configured.Testing
bun run --cwd packages/producer docker:test:update mov-prores— baseline rendered insideDockerfile.test(60 ProRes 4444 frames, 640×360, alpha, ~3.8 MB)bun run --cwd packages/producer docker:test mov-prores— in-process passes (100/100 checkpoints byte-identical against baseline)bun run --cwd packages/producer docker:test:distributed mov-prores— distributed-simulated passes (100/100 checkpoints byte-identical against baseline)bun run --cwd packages/producer docker:test:distributed font-variant-numeric many-cuts gsap-letters-render-compat style-1-prod sub-composition-video mp4-h264-sdr png-sequence mov-prores -- --sequential— full smoke set + all three stacked fixtures pass (8/8)bunx oxlint+bunx oxfmt --checkclean on changed filesbunx tsc --noEmit(producer package) clean🤖 Generated with Claude Code