* feat(registry): eight more WebGPU shader backgrounds and a bundle with thirty new shaders
* refactor(registry): simpler bundle copy step in the shader build
* test(registry): every shader block ships the same bundle and names a shader in it
* refactor(registry): one block loader for the shader block tests
* test(registry): name the stale block and match only shader names
* chore(registry): shorter licence line at the top of the shader bundle
* test(registry): every shader block installs the bundle's licences
* feat(cli): models list shows which speech models transcribe can use
* refactor(cli): models list and transcribe share one Parakeet runner check
* fix(cli): models list skips whisper links transcribe would download again
* style(cli): keep models list comments under the package share
* fix(studio): serve files whose name has a bare percent sign
A browser sends src="100%.png" with the % unescaped, and decoding the whole path threw, so Studio answered 500. Only well-formed escapes are decoded now.
* fix(cli): serve files with a bare percent sign from the check and snapshot server
One shared decode rule for request paths; the image thumbnail route's malformed-escape 400 could no longer fire, so a name that is not a file is a 404.
* fix(cli): name a failed file with a bare percent sign in check
* fix(studio): keep apostrophes and dollar signs in file names when editing attributes
An attribute value is matched by its own quote kind, so src="it's.png" is replaced instead of duplicated, and every insert uses a function replacer, so $1 or $& in a file name is written as is.
* fix(studio): rename references spelled with %20 or & and read quoted media sources
WIP: not yet tested; see the resume note.
* fix(studio): match whole attribute names when editing media sources
* fix(studio): compare decoded names and escape replacement references
* fix(studio): preserve file references across URL and string consumers
* docs: clarify raw file references and URL fields
* fix(studio): keep raw path identity distinct from URL spelling
* fix: share attribute grammar and decode media URLs at their readers
* fix: preserve local filename identity in HTML URL output
* fix: decode entity references before rename candidate matching
* style: format rename entity handling and regressions
* fix: preserve platform file identity when renaming references
* fix(core): decode CSS escapes before rebasing asset URLs
* fix(core): consume CSS continuations before URL encoding
* fix(core): preserve CSS URL identity and escape rebased suffixes
* fix(core): express CSS suffix controls with Unicode categories
* fix(core): preserve URL backslash identity while rebasing
* test(core): respect platform backslash filename semantics
* fix(studio): a dropped file with %, # or ? in its name loads, and an encoded reference renames
* fix(studio): read an authored src as a URL so dropped files with spaces, %, # or ? load
* fix(studio): media routes find files named with ? or #, and the probe reads a src as a URL
* fix(studio-server): one rule for request file paths, so loudness finds files named with %, # or ?
* chore: drop the design note from the change
* fix(core): rename replaced clips whose src is URL-encoded
* fix(cli): enforce capture screenshot limits and report interruptions
* refactor(cli): split screenshot step out of capture animation phase
Keeps the screenshot limit outside the mirrored core-extraction argument
list and moves scroll screenshots into their own step, so both stay under
the audit's duplication and complexity gates with no behaviour change.
* fix(core): write only authored hue curves, so a single hue curve passes lint
* fix(cli): apply over a grading saved with empty hue curves instead of refusing it
* fix(core): read grading properties from computed style, and stop advising an inline start
* fix(cli): preserve validation during legacy hue repair
* fix(cli): preview grading writes without apply
* fix(cli): group sampled check findings by element and rule
* fix(cli): preserve unique elements when grouping audit samples
* fix(cli): retain geometry sample times until report grouping
* fix(cli): keep grouped finding evidence time paired with geometry
* fix(auth): accept host-managed HeyGen access tokens
* fix(auth): share environment header validation
* docs(auth): condense credential resolver guidance
* fix(auth): report the account a browser login just signed in
Identify the account from the tokens the login issued, not a fresh credential
lookup that an environment token or API key outranks.
* docs(auth): list the host access token in the CLI credential order
* fix(cli): name a rejected host access token on publish
Publish told a host-token user to log in again, which cannot help while the token outranks the login.
* docs(auth): show the media credential order main resolves
* fix(cli): warn at logout that a host access token still signs commands
One map of environment credential variables, owned by the resolver, now feeds the logout warning and both publish messages.
* docs(auth): scope the media credential order to the audio scripts
* refactor(cli): read every env credential name from the resolver's map
* fix(studio): recover previews after missed file changes
* fix(studio): keep queued reloads with the current owner
* fix(studio): keep a queued outside change when switching compositions
* test(cli): the project watcher closes its parent folder's watch too
* fix(studio): keep the project-folder change out of the shared signature check
* fix(studio): keep the edited file's draft and reload every session after a reconnect
A reconnect or replaced folder is a project-wide change: every open SDK session reloads, a failed save is recorded against the file the draft belongs to, and two queued changes to different files merge into one project-wide reload.
* fix(studio): refresh the preview cache when the .hyperframes folder is replaced
* fix(cli): report the files of a folder moved into the project
Replacing a populated folder such as .hyperframes/ now reaches the signature cache through its files, so the shared signature rule stays as on main and Studio's own writes inside .hyperframes/ still invalidate nothing.
* refactor(studio): name the merged file-change payload and share the test sender
* fix(studio): keep a failed draft's recovery copy until that file itself saves
* style(studio): rewrap the merged payload line after the rename
* fix(studio): deliver a held change's full reload when its block clears
A clean save, Use external and Keep Studio now all deliver the merged scope a blocked change owes, and a moved-in .hyperframes folder reports its manifests so a dropped one still clears the cached signature.
* fix(studio): keep restored drafts out of the reload scope and clear the lint gates
* fix(studio): retain held reload scope before React renders
* fix(media-use): use a host app's music tools; fail loudly without the heygen CLI
* fix(media-use): name the heygen CLI only for music and sound effects; keep the sfx test offline
* fix(skills): workflow skills use a host app's own music tools first
* fix(skills): say how a host app's music track reaches the narrated workflows
* fix(skills): set a host app's music track after the last audio pass
* fix(skills): a host-music film with no narration still fetches its sound effects
* fix(media-use): keep the stub-failure test off the machine's heygen CLI; sync the skills manifest
* fix(skills): audio passes keep a host app's music and sounds; silent needs no sound cues
* fix(skills): host audio keeps string frames, stops on a broken audio_meta.json, survives a re-run
* fix(skills): report a host audio entry with no path and a broken sidecar instead of crashing
* fix(skills): keep host audio written while an audio pass runs
* test(cli): serve the telemetry capture while the resolve child waits on it
* test(cli): fail the telemetry capture when the resolve child exits non-zero
* style(skills): wrap the host audio comment to the 120-column limit
* docs(skills): ask agents inside a host app to report its friction as feedback
* feat(cli): send missing features and host-app friction as ratingless feedback
* fix(cli): keep the rating error, refuse blank reports and document ratingless feedback
* docs(skills): leave names and figures out of missing-feature reports
* docs(skills): keep anything private out of missing-feature reports
* fix(engine): automatic worker count never exceeds what the V8 heap can feed
A 24 GB Mac exporting a 43 s portrait project with 22 videos picked 5 capture workers against a 4192 MB heap that feeds about 4, and the render died at 62% with JavaScript heap out of memory. The heap budget was only advisory; it now caps the automatic count (bound 'heap'). An explicit --workers N is untouched.
* test(engine): heap-cap test restores its mocks even when an assertion fails
Review findings on #5149: the os and v8 mocks were restored only if every assertion passed, and two comments still called the heap cap advisory.
* docs(engine): heap budget comments say it caps auto sizing
* fix(cli): load telemetry without the core and engine barrels, and stop the history test racing
* fix(cli): load history from its leaf subpath, and fail the history test fast on a crashed CLI
* fix(check): see color, filter and media time under seek, and name data-no-timeline for stills
* fix(check): warn instead of fail when only audio advances, and judge text overlap on glyphs
* fix(check): judge glyph overlap by the glyph box, and see capitalized words and one-side borders
* fix(check): capitalize past leading punctuation, test outline color, trim a comment
* fix(check): capitalize words the way Chromium does, so digit-led words stay lowercase
* feat(render): report encoding and assembling progress
* fix(render): close every progress stage and fail a render whose progress callback throws
* fix(render): send a repeated progress update once
* fix(render): keep Docker terminal output as before and drop the unneeded callback ending
* fix(render): keep one failure stage code per stage whatever its frame counts
* fix(core): the preview loads the next scene's images before it starts
* fix(cli): check stops reporting image loads that its own scrubbing cancelled
* fix: preload only a coming clip's own images, and judge aborted images by whether they still decode
* fix(cli): count an aborted image as broken only when its decode fails
* style(cli): keep the aborted-image comment within the line limit
* test(cli): pin that a reload still pending at the cap is not reported
* test(cli): run the cap test on whatever timers are pending, and always restore real timers
* feat(registry): gooey split block with metaball goo and directional motion blur
A dot splits into a three-dot menu through a gooey neck, spins into a
swirl mark under shutter motion blur, breaks back into three dots and
merges into one. Timing, spacing and eases are measured off a reference
recording frame by frame.
The motion-blur snippet gains an opt-in `sharp: 0` option that hides the
crisp frame-time copy while an element moves, leaving only the shutter
average. The default is unchanged; the inlined copies carry the same text.
* feat(registry): grow gooey-split blades out of swelling dots
* refactor(registry): tidy gooey-split pose code and document sharp 0 limits
* fix(registry): hide a sharp 0 motion-blur target by marker, not its visibility
Hiding through the element's own visibility fought timelines that
write it (autoAlpha): a target faded in after attach stayed hidden at
rest. A marker attribute and one !important rule now do the hiding, the
default path writes nothing, and a declarative sharp other than 0 or 1
is refused like any other bad option.
* fix(registry): smear a motion-blur target that was hidden when blur attached
Copies froze the target's computed visibility at attach, so a target
entering with autoAlpha never smeared (and vanished entirely under
sharp 0). The group now carries the target's current visibility each
frame, read with the sharp 0 marker lifted, and a copy keeps a
descendant's visibility only where it differs from its parent's.
* test(registry): pin a copy keeping a descendant's own visibility
* fix(registry): keep a child's own hiding in the smear of a target hidden at attach
* refactor(registry): restore a lifted visibility in one call and lift any hidden form
* fix(registry): restore a lifted visibility even when copying styles throws
* fix(registry): let motion-blur copies hide with a clip that has ended
The smear group set an explicit visibility on every moving frame, and
copies used to freeze an explicit visible, so an ancestor clip hiding
after the last update could not hide them: copies from a 0-4 s
sub-composition stayed on screen at 16.4 s. The group now sets a
visibility only where the element's differs from its parent's.
Also regenerates skills-manifest.json for the motion-blur skill doc.
* feat(cli): stream heard words on the transcribe --json progress stream
* test(cli): share the streaming transcribe helper
* fix(cli): keep install and download stoppable and whisper errors short while streaming
* fix(cli): let parakeet-mlx detect the language and name the Parakeet install under auto
* docs(cli): say what --language does under each engine
* docs(cli): whisper transcribes as the --language, it does not filter
* chore(cli): drop a comment the install-hint condition already says
* feat(cli): catch up on what the desktop app did since a project was handed over
The desktop app records each chat turn in .hyperframes/app-history.jsonl.
`hyperframes catch-up [dir]` prints the turns since the hand-off or the last
catch-up (what the person asked, what Framey did, which files changed) plus
the project files changed since, by the app or by hand, then marks them seen.
`hyperframes open` and Studio's Edit with Framey mark the hand-off moment.
Any command run on a project with unseen turns ends with a line naming
catch-up, so an agent back in the terminal hears about it even when it
forgets. /hyperframes and /hyperframes-cli tell the agent to catch up as
soon as the person writes again.
* feat(cli): hand a Grok terminal session over to the desktop app
Grok gives every tool command its session as GROK_SESSION_ID (grok 1.0.4),
so `hyperframes open` and Edit with Framey now name a Grok session in
.hyperframes/agent-handoff.json, after Claude Code and Codex. The desktop
app reads its turns from Grok's own session files.
* feat(cli): tell a new project's agent to catch up after the desktop app
Scaffolded projects' CLAUDE.md and AGENTS.md now say to run
`npx hyperframes catch-up` before the next change once the project was
opened in the desktop app. Claude Code, Codex and Grok read these files on
their own, so a resumed session catches up even when it never loaded a
HyperFrames skill: a resumed Claude Code session that had not, asked to
change the title, ran catch-up first.
catch-up also prints each reply on one line.
* fix(cli): read and write .hyperframes records without following links, mark seen after showing
Review (Jerrai, Rames):
- One helper (projectRecords.ts) now reads and writes every record in
.hyperframes/. A linked folder is refused. A read opens once with
O_NOFOLLOW and checks the open file is a plain file, which clears the
CodeQL race. A write goes to a temp file renamed over the record, so a
link is replaced, never written through. agent-handoff.json, the seen
record and the history all go through it.
- catch-up marks a turn seen only after showing it, and only up to the
newest turn shown, so a turn written just after the read shows next
time. Changed files have their own "checked" time.
- Turns show only once the project was handed over from this machine, so
a history file shipped in a cloned repo is never shown as the person's
words. The end-of-command check skips reading when the history is
older than the seen record.
- The skills mention catch-up only when the CLI printed a line naming it,
so an older CLI never meets an unknown command.
- The notice test uses basename, which fixes Windows.
* feat(cli): add the catch-up line to an older project's instructions on hand-off
A project scaffolded before catch-up existed has no line telling its agent
to catch up, and a resumed session that never loaded a HyperFrames skill
then edits without knowing what happened in the app. When `hyperframes open`
or Edit with Framey hands such a project over, its scaffolded CLAUDE.md and
AGENTS.md (they start "# HyperFrames Composition Project") gain the
template's line, in the template's place. A file of the person's own, a
link, a missing file, or one that has the line is left alone. The file is
rewritten through a temp file with its mode kept. A test keeps the line the
same as the template's.
* fix(cli): keep catch-up's cursor in the person's home, never reset it, read without links on Windows
Review (Rames, Jerrai):
- The seen record moved from the project to ~/.hyperframes/catch-up/,
keyed by the project's real path. A seen file a cloned repo commits
can no longer make its history read as the person's words.
- A second hand-off (`open` again, or another Edit with Framey click)
keeps the cursor, so turns the app recorded meanwhile still show.
- Reads lstat the path, open it once, and refuse a file whose dev/ino
differs from what lstat saw. That holds where O_NOFOLLOW doesn't
exist, fixing the Windows test. The note writer reads the same way,
which clears the new CodeQL alert.
- Writes use core's replaceFileAtomically, which retries a rename that
Windows briefly refuses.
- catch-up names every changed file, and the cursor is the newest `at`
shown, not the last line's.
- `open --json` carries `catchUp` after a hand-off.
- The bin scripts are back to mode 644.
* fix(cli): open before checking for CodeQL, never show a turn dated ahead
Review (Rames, Jerrai):
- readPlainFile opens the file first, then requires lstat to find the same
plain file (dev/ino) at the path, and reads only through the fd. That is
just as safe, and it clears CodeQL's check-then-use alert.
- catch-up skips turns dated after the moment it reads. So a cloned history
can't show a far-future turn as the person's words or park the cursor
past real ones.
- The seen key uses core's realpath (realpathSync.native), so letter case
can't split a project in two. ~/.hyperframes/catch-up is made 0700, and
a hand-off that can't note itself says so.
- Tests stub HOME with vi.stubEnv and restore it.
* docs(cli): map the pinned Sherpa runtime loading contract
* fix(cli): use a healthy pinned Sherpa copy beside the CLI
* chore(cli): keep runtime loader comments within the ratchet
* docs(cli): wrap the runtime contract palette declarations
* chore(cli): keep the Sherpa contract note out of the repo