Files
hyperframes/packages
JamesandClaude Opus 4.7 30f931b503 fix(producer): wire video frame injector into renderChunk
The chunk worker passed `createRenderVideoFrameInjector: () => null` to
`runCaptureStage`, leaving the page's `<video>` elements to decode the
source mp4 against the virtual clock. Chrome's native video pipeline
seeks ±1 frame off what the in-process renderer captures (which uses
pre-extracted frames injected as images via createVideoFrameInjector).
That ±1 frame drift produced the PSNR gap on sub-composition-video and
style-1-prod against the in-process baselines.

Two pieces:

1. `plan()` now persists the engine's `VideoElement[]` (composition.videos)
   and a serialized form of `extractionResult.extracted` (videoId,
   srcPath, framePattern, fps, totalFrames, metadata — paths omitted) to
   `<planDir>/meta/videos.json`. This is the data renderChunk needs to
   reconstruct a `FrameLookupTable` without re-running the extract stage.

2. `plan()` no longer calls `frameLookup.cleanup()` after extraction.
   That cleanup was rm-rf-ing each video's outputDir, which for the
   in-process orchestrator is a scratch tree the renderer owns — but for
   plan() that "scratch" IS `compiledDir/__hyperframes_video_frames/<videoId>/`,
   the source material that the subsequent rename moves into
   `planDir/video-frames/`. Cleaning it up before the rename left
   planDir/video-frames/ with only the `_downloads/` subdirectory and no
   actual frame files. Both `style-1-prod` and `sub-composition-video`
   reproduced this on every distributed-simulated run; both pass after
   the cleanup is dropped.

3. `renderChunk` reads `meta/videos.json`, rebuilds `ExtractedFrames[]`
   by re-listing `planDir/video-frames/<videoId>/` for each video, calls
   `createFrameLookupTable(videos, extracted)`, and wraps the result in
   `createVideoFrameInjector` — the same hook the in-process renderer
   uses. The rebuilt entries set `ownedByLookup: false` so any later
   cleanup() call from the engine doesn't rm the planDir bytes another
   worker may still be reading.

Validated in `docker:test --mode=distributed-simulated`:
  font-variant-numeric:           PASSED
  many-cuts:                      PASSED
  gsap-letters-render-compat:     PASSED
  style-1-prod:                   PASSED (was: 15 frames at 26-29 dB)
  sub-composition-video:          PASSED (was: most frames at 21-25 dB)

In-process unchanged; 54 distributed unit tests still pass in Docker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 18:56:05 +00:00
..
2026-05-13 17:01:15 -07:00
2026-05-13 17:01:15 -07:00