6431 Commits
Author SHA1 Message Date
Miguel Ángel 6df754fbf3 chore: release v0.8.216 (#6735) v0.8.216 2026-10-29 10:01:01 +00:00
Miguel Ángel 5d437d5eec docs(cli): say which audio probes run once per file (#6731) 2026-10-29 08:10:51 +00:00
Miguel Ángel 48ba0e5b2b fix(player): iPhone Safari keeps every take's sound after the first seconds of an edit (#6724)
* fix(player): iPhone Safari keeps every take's sound after the first seconds of an edit

* fix(player): shorten the priming comment

* fix(player): prime iPhone takes only, and only unmuted ones

* fix(player): shorten a comment

* fix(player): skip only clips authored muted, and drop a doc comment
2026-10-29 07:56:37 +00:00
Miguel Ángel 74f3234736 ci: one regression shard writes the test image cache (#6729)
* ci: one regression shard writes the test image cache, so it stops evicting the rest

* test(ci): each image cache has one writer, shard 0 of a main push
2026-10-29 07:53:07 +00:00
Miguel Ángel f0665f7181 fix(aws-lambda): the release ffmpeg build falls back to verified mirrors when a source host is down (#6728) 2026-10-29 07:47:04 +00:00
Miguel Ángel fa97f9d4b7 docs(cli): list check's audio findings with severity, trigger and fix (#6730) 2026-10-29 07:41:09 +00:00
Miguel Ángel 077de9bfe9 test(cli): the publish abort test checks the summary hint is absent (#6726) 2026-10-29 07:37:49 +00:00
Miguel Ángel ba8ffffa0b fix(cli): a failed render names its failure code for the Desktop app (#6725) 2026-10-29 07:36:32 +00:00
Miguel Ángel cf15892425 fix(cli): a snapshot still shows what playback shows, whatever other times are asked for (#6720)
* fix(cli): a snapshot still shows what playback shows, whatever other times are asked for

* test(cli): pin that still-capture pages ask for the skipped-frame replay
2026-10-29 06:44:51 +00:00
Miguel Ángel 98a28cc4b2 fix(cli): the lint summary points at hyperframes lint, not a flag only render takes (#6723) 2026-10-29 06:44:01 +00:00
Miguel Ángel be05c86932 perf(engine): encode high quality with the x264 fast preset (#6640)
* perf(engine): encode high quality with the x264 fast preset

* test(engine): name the NVENC p5 case by its preset, not the high tier
2026-10-29 06:40:21 +00:00
Miguel Ángel 17f5d67643 chore: release v0.8.215 (#6719) 2026-10-29 06:19:54 +00:00
WaterrrForever cd003e2010 perf(engine): size render workers from measured heap and the container's CPU quota (#6641)
* perf(cli): give render the heap its auto workers need in small containers

Auto worker sizing caps capture workers at (V8 heap - 1024 MB) / 640 MB.
Node sizes its default heap from visible memory, so in a 10 GB container
the heap is 2,096 MB and every render captures with one worker, whatever
the core count.

When no --workers is given, no --docker, no heap was set by hand, and the
heap is the only bound holding workers back, render relaunches itself once
with --max-old-space-size set to what CPU and memory allow (capped at half
of memory). The engine exposes heapMbForAutoWorkers() for this.

* fix(cli): count a heap-relaunched render once, keep detached renders alive

The relaunched child skips the CLI's command telemetry, update and skills
notices, and app-history notice; the parent already records and prints
them. A detached render no longer gets SIGHUP forwarded, which its
default handler would treat as a kill. A heap set as --max_old_space_size
counts as set by hand.

* fix(engine): keep the desktop app's per-worker heap figure at 640 MB

Inside Electron the heap is a hard 4,192 MB cage, and that is where the
field OOM behind the old figure happened (#5149: 5 auto workers on a heavy
export). The new 320 MB figure is measured only in a system Node, so the
desktop app keeps 640 MB (4 workers there) until a heavy desktop export is
measured. Containers and plain Node keep 320.

* fix(engine): know the packaged desktop app by its client tag, which runs the CLI on plain Node
2026-10-29 06:17:07 +00:00
Miguel Ángel 49e0312300 fix(ci): skills sync steps past the five hidden ClawHub versions blocking it (#6713)
* fix(ci): skills sync steps past any taken ClawHub version, not one hard-coded skill

* fix(ci): skills sync steps past the five hidden ClawHub versions blocking it
2026-10-29 05:20:41 +00:00
Miguel Ángel bb4deb8014 fix(cli): an unknown command like search says which command does that (#6710)
* fix(cli): an unknown command like search says which command does that

* fix(cli): the synonym hint is checked before the spelling match, so list, find and browse reach it
2026-10-29 05:03:43 +00:00
Miguel Ángel 2f8c4b1daf fix(cli): check says how many browser-playable copies are done while it makes them (#6712) 2026-10-29 05:02:58 +00:00
Miguel Ángel fa9acbd739 fix(core): a multi-worker or chunked render lands every frame where an in-order render does (#6707)
* fix(core): a multi-worker or chunked render lands every frame where an in-order render does

* fix(core): replay only where tweens overlap, and from any off-grid scene time

* fix(core): a film with paused scenes replays only where its tweens overlap
2026-10-29 04:58:34 +00:00
Miguel Ángel 11312b359b fix(cli): check says a lint error is why the browser pass was skipped (#6709)
* fix(cli): check says a lint error is why the browser pass was skipped

* style(cli): drop a doc comment the comment ratchet would count
2026-10-29 04:08:41 +00:00
Miguel Ángel f0c5db6ea2 fix(cli): capture names the network's proxy when it refuses the connection to the site (#6706)
* fix(cli): capture names the network's proxy when it refuses the connection to the site

* fix(cli): capture words a refused tunnel and an unreachable proxy apart, and echoes no raw address

* fix(cli): shorten the proxy failure comment
2026-10-29 04:08:34 +00:00
Miguel Ángel 6c17659337 fix(cli): check waits as long as the --timeout it was given before it counts as idle (#6705)
* fix(cli): check waits as long as the --timeout it was given before it counts as idle

* style(cli): shorten a comment line

* style(cli): drop the idleWindow doc comment the comment ratchet counts
2026-10-29 04:08:29 +00:00
James RussoandClaude Opus 5.5 1c3fcf4d23 feat(telemetry): identify signed-in CLI users by their HyperFrames user id, and say signed-in usage is not anonymous (#6703)
* docs(telemetry): say signed-in usage is linked to your account, not anonymous

After hyperframes auth login the CLI sends a PostHog $identify with the
account email (username as fallback) and merges the install's earlier
anonymous usage; media-use does the same when it finds a signed-in
account. Several places still called all telemetry anonymous. Say it is
anonymous until you sign in, in the CLI reference (media-use, --skill,
feedback, events, telemetry), the hyperframes-cli skill references, and
the CLI help and notice strings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* feat(telemetry): identify signed-in CLI users by their HyperFrames user id

The CLI and media-use identified a signed-in install by account email, so
CLI usage landed on a different PostHog person than the same account in
the HyperFrames web app, which keys on toUUID(username). Identify by that
same sha256-derived id instead, and $set email plus heygen_username on the
person. heygen_username is the cross-project join column for the Snowflake
event table (HeyGen's own project stores it as `name`). An account with no
username still falls back to the lowercased email.

Notices and docs now say usage is keyed on an id derived from the username
with email and username stored on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(telemetry): trim comments to the package's comment share

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* feat(telemetry): put heygen_username on every event while signed in

The Snowflake event sync may not carry PostHog person properties, so a
$set alone does not let an event row join across projects. The CLI now
adds the signed-in HeyGen username (raw, from ~/.heygen/credentials) to
every event and drops it once the credentials hold no user; media-use
does the same on each event it sends.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ci: count auth/paths.ts as an edit-accuracy bench input

Telemetry now imports it to read the signed-in username.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(telemetry): stat and read the credentials through one file handle

CodeQL flagged the stat-then-read as a file system race.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(telemetry): key signed-in events on the account id; fix stale anonymity copy

Genesis review on #6703:
- telemetry --help listed 'Any personally identifiable information' as
  never collected, and media-use's meta.md still said anonymous.
- Installs already identified by email under an older CLI never merge
  into the new person, so send signed-in events with distinct_id = the
  account id (back to the install id after logout), in the CLI and in
  media-use, and stop claiming earlier usage always merges.
- A user block with no credential no longer counts as signed in.
- A UUID-shaped username passes through unhashed, as on the web.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-29 02:57:48 +00:00
Jerrai 4fa5f54519 fix(cli): audio windows read beats off the whole clip; beats snap to the signal and mark silence (#6692)
* fix(cli): audio windows read beats off the whole clip; beats snap to the signal and mark silence

A film query that covers part of a clip measures beats, downbeats, bpm and sections on the
whole clip and cuts them to the window, as #6653 did for kind, so a window under 6 s still
has its beats.

One step after every provider ladder moves a provider's beats and downbeats onto the
signal's own beats within a quarter beat (the snapping api did alone) and marks each with
`silent`, true where the beat falls in silence. Beats are marked, never dropped; the JSON
change is the additive `silent` array.

— Jerrai

* fix(cli): beats with no pulse are unheard; a window with none answers no steady beat

Review fixes for #6692:
- The per-beat flag is now `unheard` (renamed from `silent`, not released): true in silence
  or where no onset falls within 50 ms of the beat, so hiss, room tone or a held pad no
  longer pass as beats.
- A window keeps the whole clip's beats only when at least two of its beats are heard;
  otherwise it answers "no steady beat" as before, with no downbeats and no bpm.
- A window's bpm is the tempo of its heard beats, never the whole clip's.
- Comment lines trimmed so analyze.ts, run.ts and api.ts are at or under main's share.

— Jerrai

* fix(cli): a beat is unheard only where two beats in a row have no onset

Scored on the ground-truth set, flagging every onset-less beat also flagged heard beats the
onset detector misses: the drop downbeat after a snare roll, single beats under narration,
quiet beats late in a fade. A pulse gap now needs two onset-less beats in a row; hiss, room
tone and held pads span many beats and stay flagged, and silence is flagged as before.

— Jerrai

* fix(cli): a window with no pulse on the clip's grid reads its own beats, as before

Genesis found a window over a section far under the clip's loudest part answered "no steady
beat" where main, reading the window alone, found every beat: the clip's grid and onsets
skip a section that quiet (from about 40 dB under, for clicks). When a window keeps fewer than
two heard beats from the clip's grid, its beats, downbeats and bpm now come from the window
alone, as they did on main. Hiss still answers "no steady beat" on its own.

The hiss test's noise() repeats every 10,466 samples (0.22 s at 48 kHz), which the window's
own reading took for a pulse; that test now uses a xorshift generator.

— Jerrai

* fix(audio): unheard comes from level-free rhythm evidence, so quiet music stays heard

A beat was flagged by an absolute -50 dBFS silence and by onsets under 10% of the clip's
loudest hit, so music 30 dB or more under the loudest part read as unheard. Now:
- core localBeatEvidence reads the cached standardised onset curve over the 6 s around each
  beat: the tracker's steady-beat confidence there (clarity at the local beat lag times the
  beat's own lift) and the beat's lift over the span's mean.
- A beat is unheard in digital silence (-80 dBFS), or when its span keeps no steady beat
  (confidence under 0.15) and its own onset does not stand out (lift under 1.5). Lift alone
  keeps beats under narration heard, where syllables lower the span's clarity.
- A window keeps the clip's beats when at least two, and at least half, of them are heard.

— Jerrai
2026-10-29 01:52:48 +00:00
Miguel Ángel a5496b1887 fix(producer): a BeginFrame capture that draws no first frame in 20 s switches to screenshot (#6701)
* fix(producer): a BeginFrame capture that draws no first frame in 20 s switches to screenshot

* test(cli): cover the Chrome launch failure messages a failed render prints
2026-10-29 00:49:04 +00:00
Miguel Ángel cfa653af4d fix(cli): media-treatment --capability preset finds the presets area (#6700) 2026-10-28 23:27:27 +00:00
Miguel Ángel ac449555f4 fix(cli): a Windows render no longer prints taskkill's already-exited message (#6698)
* fix(cli): a Windows render no longer prints taskkill's already-exited message

* fix(cli): fold the taskkill note into the hidden() doc comment
2026-10-28 23:04:48 +00:00
Miguel Ángel 0620067eb0 fix(cli): snapshot reads https video in the Desktop app instead of failing at ffprobe (#6699)
* fix(cli): snapshot reads https video in the Desktop app instead of failing at ffprobe

* fix(cli): give a remote clip download the same 30 s a stalled host had before
2026-10-28 22:52:28 +00:00
Miguel Ángel 81959201f0 fix(cli): a stopped model download resumes on the next run instead of starting over (#6694)
* fix(cli): a stopped model download resumes on the next run instead of starting over

* fix(cli): resume whisper models too and take over stale download locks safely

* style(cli): shorten a download lock doc line
2026-10-28 21:14:09 +00:00
Miguel Ángel 2c3d1e07fe fix(cli): media-treatment finds an area by the name its overview prints (#6675)
* fix(cli): media-treatment finds an area by the name its overview prints

* fix(cli): drop the comment lines the ratchet counts
2026-10-28 20:29:47 +00:00
Miguel Ángel 9830059bdb fix(engine): downloads refuse hosts that resolve to private addresses (#6571)
* fix(engine): downloads refuse hosts that resolve to private addresses

* fix(core): shorten the env proxy doc comment

* test(producer): remote-media tests stub the download fetch seam
2026-10-28 20:08:50 +00:00
Miguel Ángel 3ca1005a81 feat(producer): report which element set the film length (#6689)
* feat(producer): report which element set the film length

* fix(core): keep the duration guard and comment share in step with the length helper
2026-10-28 19:05:59 +00:00
Miguel Ángel 9f65efb19c fix(cli): check no longer reports outline text as not painted (#6693) 2026-10-28 19:03:27 +00:00
Miguel Ángel ec10aae54f fix(cli): beats --json answers with no track when the composition has no music (#6690)
* fix(cli): beats --json answers with no track when the composition has no music

* fix(cli): drop a comment the name already says
2026-10-28 18:57:53 +00:00
Miguel Ángel aa4a71fe41 fix(core): graded media honors object-position vertically instead of mirroring it (#6691)
* fix(core): graded media honors object-position vertically instead of mirroring it

* fix(core): name the bottom-up position instead of commenting it
2026-10-28 18:48:57 +00:00
Miguel Ángel 9aa6346a77 fix(studio): audio keeps playing after a timeline move or trim in a static composition (#6428)
A composition with no GSAP timeline was treated as zero length after an edit, which dropped preview audio, and an edit made during playback left the transport stopped. The runtime now keeps the declared length when it rebinds, and the soft reload resumes a transport that was playing.
2026-10-28 18:34:56 +00:00
Miguel Ángel 1428f87689 docs(skills): the registry skill says its commands are catalog and add (#6680) 2026-10-28 17:55:50 +00:00
Miguel Ángel cdc86117c7 fix(engine): a render from an app that bundles the engine stamps the engine's version (#6686)
* fix(engine): a render from an app that bundles the engine stamps the engine's version

* test(engine): the bundled-layout fixture names a generic app
2026-10-28 17:51:43 +00:00
Miguel Ángel 710314c15e fix(studio): an undo or redo counts as a preview change from its key, not from its reload (#6683)
* fix(studio): an undo or redo counts as a preview change from its key, not from its reload

* refactor(studio): count a history step from its key with the page flag on noteIdle
2026-10-28 17:51:22 +00:00
Miguel Ángel 067cbf7acf fix(producer): a capture that landed every frame is not failed for the work after its last frame (#6674)
* fix(producer): a capture that landed every frame is not failed for the work after its last frame

* chore(producer): drop a comment the ratchet counts
2026-10-28 17:48:50 +00:00
Miguel Ángel c2cde2fb62 perf(producer): the audio true-peak check seeks to each segment instead of decoding up to it (#6672)
* perf(producer): the audio true-peak check seeks to each segment instead of decoding up to it

Each parallel segment scan decoded the AAC from the start and trimmed after, so the last one decoded the whole file. It now seeks to a whole second one second before its segment and trims relative to it: the same peak, measured in about half the time.

* test(producer): the true-peak check's ffmpeg tests run in the integration lane
2026-10-28 17:48:36 +00:00
Miguel Ángel 203a08dd7c fix(cli): check no longer treats a transparent canvas as covering the frame (#6681)
* fix(cli): check no longer treats a transparent canvas as covering the frame

* fix(cli): a canvas with an opaque CSS background still counts as covering
2026-10-28 17:48:26 +00:00
Vance IngallsandClaude Opus 5.5 c569760a7a fix(core): grade groups resolve per composition; freeze frames keep their lane (#6685)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:51:17 -07:00
Vance Ingalls 4719543e97 chore: release v0.8.214 (#6684) v0.8.214 2026-10-28 10:17:04 -07:00
Vance IngallsandClaude Opus 5.5 348971c222 fix(studio): a tab's save keeps the newer video version another tab recorded (#6682)
* fix(studio): a tab's save keeps the newer video version another tab recorded

The thumbnail index merge let this tab's ETags win for every source, including
ones it only read at startup, so another tab's move to a new version was undone
by this tab's next save: the next session validated the old ETag, missed the
disk and decoded once more. Now another tab's ETag wins except for sources this
tab recorded a new version of since its last save.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(studio): the ETag merge says whose moves it keeps in its names, not comments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(studio): the stored-thumbnail reset forgets this tab's drops and moves too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:10:56 -07:00
Vance IngallsandClaude Opus 5.5 09b1290474 feat(core): grade groups grade a whole lane from one hf-color-group (#6678)
An inert <hf-color-group> holds a lane's data-color-grading; <video>, <img>
and <canvas> join with data-color-group. A member's own grading overrides
its group's. The runtime discovers members, regrades them when the group's
grading or a member's data-color-group changes, and ungrades them when the
group element or its grading goes, drawing only members on screen.

Render keeps group-graded video at full size like directly graded video,
and lint accepts (and still validates) grading on hf-color-group.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:10:42 -07:00
Vance IngallsandClaude Opus 5.5 c9017f45f4 feat(core): bake a colour grade into a 3D LUT (.cube) (#6679)
bakeHfColorGradingCube evaluates the runtime shader's colour path on the
CPU (primary, tonal wheels, RGB and hue curves, secondaries, output LUT,
intensity mix) through the same 8-bit texture quantisation, and writes a
red-fastest .cube for ffmpeg's lut3d. Grades with spatial stages (grain,
vignette, effects) throw, as does a grade whose LUT was not passed in.

A Chromium test draws a 4,096-colour lattice through the real runtime
and compares every tile with the bake.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:45:15 +00:00
Miguel Ángel c6af88d2e6 chore: release v0.8.213 (#6673) v0.8.213 2026-10-28 16:40:44 +00:00
Vance IngallsandClaude Opus 5.5 e38585ef5f perf(studio): timeline thumbnails come back from disk in the next session (#6676)
* perf(studio): timeline thumbnails come back from disk in the next session

Reopening a project re-read every video's index and re-decoded every strip:
the frame cache and the source facts lived in memory, keyed by the session.

Now each source's facts (aspect, duration) and every decoded frame are kept
in Cache Storage under the file's ETag, the version the server says it is. A
new session checks each source once with a conditional one-byte read: a 304
means the stored frames are still that file's, and a strip they cover paints
without opening a reader. A new ETag drops what was stored for the old one.
Sources are dropped whole, least recently used first, past 256 MiB. A store
that is missing or fails (private window, quota) warns once and the strip
decodes as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(studio): a reopened project checks its videos again, and the disk index counts a frame once

The stored version a session validated was kept per video for the whole page,
so reopening a project after one of its videos was replaced at the same path
painted the old file's frames until a full reload. It is now kept per video
and project open (its contentVersion): each open re-validates once.

The index now counts a frame stored again under its name once, and merges
with what other tabs saved before writing, keeping caches this tab dropped
out, so the 256 MiB budget holds across tabs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:39:02 +00:00
Miguel Ángel 67c7ec27d9 fix(core): bundled fonts in a linked stylesheet or a font shorthand are seen by the bundler (#6665)
* fix(core): a font declared in a linked stylesheet no longer trips the bundler's font warning

* fix(core): drop the guard's explanatory comment

* fix(core): bundled fonts named only in a CSS font shorthand load

* fix(core): a font shorthand loads faces but never fails the cloud system-font check

* test(producer): regenerate static-volume-future-set for the font shorthand face

* test(producer): regenerate two more fixtures for the font shorthand face
2026-10-28 14:08:07 +00:00
Miguel Ángel 5b5633fbbc fix(cli): website capture ends with what it has instead of running until it is killed (#6662)
* fix(cli): website capture stops inlining slow images and stylesheets and says what it skipped

* fix(cli): a capture ends at four minutes with its partial bundle unless told otherwise

* fix(cli): a capture's deadline starts once its browser is up, and the kept-image count is exact

* fix(cli): an image whose fetch the deadline cut off always counts as kept
2026-10-28 13:56:48 +00:00
Miguel Ángel cf627a8619 fix(core): a preview media load that fails is reported, not swallowed (#6666)
* fix(core): a preview media load that fails is reported, not swallowed

* fix(core): an audio fed by source children reports its failed load; the shape lives in the protocol

* fix(core): the audio load watcher finds elements from any realm

* build(core): regenerate the inline position-edits render script
2026-10-28 13:49:30 +00:00