* perf(engine): skip the screenshot on frames the page reports unchanged in beginframe capture
* fix(engine): beginframe reuse never returns a frame older than a cross-frame page change
* fix(engine): the BeginFrame reuse test gets a headless shell in the flake gate
* fix(ci): run the headless shell installer through its package
* fix(ci): install the headless shell with bunx, npx finds no bin in the workspace
* feat(engine): a chunk-fed encoder and the rule for which films may use it, off by default
* test(engine): end the chunk encoder's ffprobe call with --
* fix(producer): a render stops when a capture session sees every composition script fail
* fix(producer): only capture workers decide a script failure; prep sessions stop without recording it
* fix(producer): a script failure in a capture worker still reports as the composition-script error
* style(producer): format the script-failure test
* feat(producer): report browser-computed reference backgrounds
Preserve one initial root/page reference across local, streamed and distributed
render results without guessing from film pixels.
— Jerrai
* fix(producer): preserve plan compatibility and optional inspection
Keep reference metadata outside the v2 manifest hash and read it from the
verified execution-plan artifact. Static planning uses a non-gating page
inspection instead of requiring render-worker capabilities.
— Jerrai
* fix(producer): poll readiness independently of animation frames
Use timed polling while the render clock parks animation frames. Cover readiness
that changes after the first check with a short real-browser regression.
— Jerrai
* fix(producer): budget measured reference metadata runtime growth
Producer adds 2,228 compressed bytes over current main. Tests, fixtures and source
maps are already absent from its dist-only tarball. Allow 7,766 bytes of headroom
above the verified 837,234-byte payload while retaining the unpacked limit.
— Jerrai
* fix(engine): a scene that has ended gives up its WebGL context so the next scene is not slowed
* build(engine): raise the engine package size budget for the WebGL release module
* chore(engine): shorten the WebGL release comment
* fix(engine): a scene gives up its WebGL context only when the runtime says its window ended
* chore(engine): drop a comment the type already says
* chore(engine): shorten a comment line
* fix: cache paths follow HYPERFRAMES_CACHE_DIR
Every place HyperFrames keeps downloaded or built files now takes its
folder from one helper, hyperframesCacheDir() in @hyperframes/core. The
default stays ~/.cache/hyperframes; HYPERFRAMES_CACHE_DIR moves it.
* fix: register the cache-dir subpath for bun and node, and build inspector from core source
* test: the cache-dir override test uses a folder that is absolute on every platform
* fix: the opencv cache follows HYPERFRAMES_CACHE_DIR
* fix(cli): npm installs run in their staging dir, so no cache path reaches a command line
* fix(cli): whisper.cpp is cloned inside its build dir, so no cache path is a git argument
* test(cli): the whisper clone test finds no system whisper on Windows either
* fix(engine): GPU renders use the NVENC preset names the host's ffmpeg lists
* fix(engine): one ffmpeg stdout reader for encoder detection and preset listing
* ci(engine): raise the engine unpacked size budget for the NVENC preset reader
* fix(engine): passthrough output follows the page frames and holds a short video's last frame
* test(engine): a film whose audio outlasts its video still qualifies for passthrough
* test(engine): passthrough encodes one output frame per page frame when the video is shorter
* test(engine): end the ffprobe options before the file in the passthrough frame count test
* feat(engine): composite a source video inside the streaming encoder
* feat(engine): opt-in flag and rule for compositing a lone full-timeline video
* feat(producer): opt-in video layer passthrough for a lone full-canvas video
* feat(producer): launch the probe, drop its page and capture PNG when passthrough is on
* refactor(engine): trim the passthrough doc comments to one line
* fix(producer): refuse passthrough when a wrapper or sub-composition paints a background
* chore(producer): mark the in-page layout check as one deliberate unit
* fix(engine): a video left out of capture no longer blocks static-frame dedup
* refactor(engine): shorten two long comments in the static-frame eligibility scan
* refactor(engine): one builder for the video frame filter chain
* refactor(engine): keep the filter chain builder module-private until it has a caller
* fix(producer): a failed video extraction names which clip files failed
* fix(producer): the extraction source label hides scheme payloads and drive paths
* style(producer): format the extraction source label
* fix(engine): scale static-frame verification budget with the job
* fix(engine): keep comment share flat
* fix(engine): drop added comment
* fix(engine): time seek in verify budget, state the policy
* fix(producer): render telemetry reports beyond-viewport capture as the workers used it
* fix(producer): apply the sessions' beyond-viewport value at the summary, on every capture path
* chore(engine): drop a comment so the types file's comment share does not rise
* fix(producer): auto renders with color-graded media stay SDR instead of promoting to HDR
* fix(producer): keep auto renders SDR when a graded canvas is present
* fix(cli): capture's public-address check uses the engine's list; 192.0/16 is not all reserved
Only 192.0.0.0/24 and 192.0.2.0/24 are special-use. The capture proxy blocked all of
192.0.0.0/16, so sites on WordPress VIP (192.0.64.0/18, e.g. nasa.gov at 192.0.66.108)
failed every capture with net::ERR_TUNNEL_CONNECTION_FAILED. isPublicIPv4 now reads the
engine's isBlockedNetworkHost, so capture and asset downloads share one list.
— Jerrai (cutroom-debug)
* refactor(engine): one special-use network list, in a light subpath capture imports
Importing all of @hyperframes/engine into capture's publicNetwork slowed the lookup child's
start-up past publicNetwork.process.test's 6 s mid-lookup exit. The list and
isBlockedNetworkHost move to engine/src/utils/networkPolicy.ts, exported as
@hyperframes/engine/network-policy; urlDownloader and the engine index read it there.
— Jerrai (cutroom-debug)
* test(engine): pin 192.0.0.0/24 and 192.0.2.0/24 blocked, the rest of 192.0/16 public
— Jerrai (cutroom-debug)
* fix(cli): alias the engine's network-policy subpath in the CLI bundle
esbuild's root alias rewrote @hyperframes/engine/network-policy to src/index.ts/network-policy;
the exact-subpath list now carries it. The publicNetwork docstring is cut to keep the file's
comment share under its base.
— Jerrai (cutroom-debug)
Frames ffmpeg has written now reach the render's stage progress, so a long source video no longer leaves the render silent between extract_video and capture.
* fix(engine): a video clip starting between frames shows its media on the right frame
* chore(engine): trim the helper comment to one line
* chore(engine): drop the helper comment
* test(engine): extract once per start in the off-grid clip test
* fix(engine): a looped clip keeps its loop origin when the start is off the frame grid
* test(engine): three off-grid starts cover the failure modes and keep the flake gate under its limit
* fix(cli): a missing registry item names the closest ones, and a blocked browser says how to allow it
* fix(cli): let closestNames carry its rule in its name
* fix(engine): a video on a non-square pixel grid renders at its display shape
* chore(engine): drop comments the names already carry
* fix(engine): a rotated clip with a non-square pixel grid keeps its display shape
* test(engine): the frame probes end their options with -- like every ffprobe call
* fix(engine): a render no longer waits for a sub-composition timeline no script can register
* fix(engine): the timeline wait matches a twice-mounted id and a template-literal key