* fix(cli): a Chrome download that stops sending is abandoned, retried, and releases the lock
* fix(cli): a stalled Chrome download is aborted at the socket so the process can exit
* fix(cli): the Chrome download runs in its own process and is killed when it stalls
* test(cli): ignore the expected socket reset in the download stall test on Windows
* fix(engine): audio encodes on FFmpeg 4.4, which has no setts duration option
The segmented AAC encode ended its stream with setts=duration, added in FFmpeg 5.0, so every render
with audio failed on Ubuntu 22.04's FFmpeg 4.4. An FFmpeg without the option now encodes the audio in
one pass, which ends the stream at the duration itself.
* fix(engine): keep the setts capability check private to the AAC encoder
A CLI killed by a signal never runs its exit listener, so its file in
~/.hyperframes/running stays. Only the background installer swept those
files, and it never runs for an install layout it skips ("Unknown install
layout", e.g. a CLI bundled inside another tool), so the folder only grew:
185 markers since 2026-10-05 on one machine, none of them a live CLI.
registerRunningCli now removes, besides its own file, every file whose pid
is gone, whose name is not a pid, or whose heartbeat is older than
RUNNING_STALE_MS (a reused pid) — the installer's own criterion. The sweep
never throws, so a failure cannot skip the heartbeat or the exit listener.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The test reads the running folder's listing instead of checking each path, so no
existence check precedes a write to the same path (CodeQL, file system race).
* perf(engine): a remote source's tail decodes run many at a time, not one per half-core
* perf(producer): the plan logs where its extract stage spends its time
* perf(engine): a window that ends inside its source takes its frame count without a decode
* test(engine): pin the tail-decode slot test to the windows that decode
* style(engine): format the tail-probe window check
* test(engine): deferred counts match extraction on a B-frame NTSC source; VFR is not deferred
* style(engine): format the tail-probe tests
* perf(aws-lambda): a site stores each video's ffprobe JSON, and the plan reads it instead of probing
* test(aws-lambda): a site stores and hands out each video's probe
* style(aws-lambda): format the probe-facts upload and tests
* perf(engine): a site's stored probe also holds the final-frame timestamp the plan would probe for
* test(engine): drop an unused import
* test(aws-lambda): the stored probe holds the ffprobe JSON and the final frame
* test(aws-lambda): the stored probe holds the ffprobe JSON and the final frame
* perf(producer): a distributed chunk encodes while it captures
* perf(producer): the audio task logs where its mix spends its time
One line per audio stage: prepare, groups, mix, master, encode and the peak check, in ms.
* perf(engine): locked warmup ticks stop sleeping once the page is ready
* perf(engine): a window reaching a stored video's end takes its frame count from stored facts
* fix(producer): the cloud audio task logs its phases
* perf(engine): stored facts keep the last packet length so late-start windows skip the tail decode
* fix(engine): a probe spawns without waiting when no stored facts are declared
* chore(aws-lambda): shorten comments over the line limit
* fix(engine): warmup keeps pacing after a failed tick; stored final frames check ffprobe major
* test(engine): the recorded ffprobe major matches the build's own banner
* test(engine): the version-banner probes go through a named binary like the other spawns
* fix(cli): snapshot rejects a negative --at and builds its sheet from its own frames
* fix(cli): snapshot --at refuses an empty value and trailing junk too
* test(cli): the contact sheet test's fake image reads orientation like the sheet now does
Seeking to a keyframe's own time landed on the one before it, so every candidate failed the
repeat-landing check and the frame was found by decoding from the start of the file. An
accurate seek to the keyframe's time decodes from that earlier keyframe and drops what precedes it.
* fix(studio-server): a history restore refuses to remove every tracked file
* test(studio-server): compare the held clip by bytes so the adopting check stays fast
* fix(studio-server): history's own hidden records do not count as files a restore keeps
* refactor(studio-server): name the restore guard's check instead of commenting it
* chore(studio-server): re-baseline the packed size budget for the restore guard
* test(cli): the lifecycle suite warms the CLI module graph in a hook, not in its first test
* test(studio): the thumbnail suite warms puppeteer in a hook and cancels leftover requests
The chrome-headless-shell that HyperFrames downloads on Windows ships
without dxil.dll and dxcompiler.dll. Dawn's D3D12 backend loads them in
requestDevice, so the adapter is found but device creation fails and
three.js WebGPURenderer silently falls back to WebGL2.
Pass --disable-dawn-features=use_dxc next to --enable-unsafe-webgpu on
win32.
Refs #5775
Co-authored-by: serdarucel <196021214+serdarucel@users.noreply.github.com>
* fix(core): color grading builds a new WebGL context when a lost one is never restored
A context the browser evicts and does not restore left every graded clip hidden for good.
After a grace period the runtime now creates a new shared context and moves each clip onto it.
* refactor(core): split the lost-context rebuild into smaller steps and test its retry
* fix(producer): a render's page-error count includes what the capture workers saw
* fix(producer): a failed parallel capture still carries its workers' page errors
* fix(producer): every capture failure carries the page's errors, in every stage
* chore(engine): drop a comment the capture classifier's name already says
* fix(engine): renames ride out Windows file locks instead of failing the render
* fix(producer): the true-peak and segment-manifest renames ride out Windows file locks too
parseAudioElements now resolves end the way core's compileTimingAttrs does. A clip
with no length at all warns, naming the clip, instead of silently playing to the end of
its source.
* fix(cli): keep beats audio paths inside the project
An encoded ../ in the music src decoded to a path outside the project, so
hyperframes beats read that file and wrote beats/<rel>.json outside the
project too. Fail the command when either path escapes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): drop added comment in beats containment check
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): resolve symlinks in beats path containment
The lexical check let a symlinked folder inside the project send the read
or the beat-file write outside it, and refused an in-project file named
..music.wav. Use the realpath-aware resolveWithinProject and isSafePath
from core for both paths.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(core): color grading rebuilds the graded clips when a lost WebGL context is restored
A clip shown while the context was lost never got an entry.
* fix(core): drop the added comment line from the color grading restore fix
* fix(engine): h265 renders no longer fail on hosts with more than 16 cores
libx265 rejects more than 16 frame threads, so the thread count taken from the
CPU quota is now capped at 16 for x265. x264 is unchanged.
* fix(engine): pass the encoder to the thread cap in the CFR re-encode
* fix(core): release decoded preview audio once no clip uses its source
The preview transport cached every decoded audio buffer by source until the player was destroyed. Each scheduling pass now drops cached buffers whose source no clip in the document still uses (scene edits, swapped sources), and keeps the rest so playback never re-decodes a clip still in the film.
* fix(core): drop the comment the comment-share ratchet rejects
* perf(producer): the audio task probes each video for sound instead of extracting its frames
* test(producer): the audio task finds a video's sound from a probe, with no frames extracted
* perf(engine): the audio mix reads clips held at a URL many at a time
* style(engine): format the remote clip slot choice
* fix(producer): the audio task places a video's sound in the window extraction would give it
* style(producer): format the slot cases of the script-src audio test
* fix(studio-server): project history no longer copies video, audio or huge files
* fix(studio-server): clean drops old video and audio copies in every history
* fix(studio-server): TypeScript source is not media, so its history is kept
* fix(studio-server): split the media reclaim and point the slow-copy route test at a non-media file
* fix(studio-server): no unused expression when collecting a history's hashes