Skip to content

fix(cli): bug-bash fixes for pull, offline, preview, check, cloud aspect, sfx search, and media-use avatars - #7170

Merged
WaterrrForever merged 17 commits into
mainfrom
miao/cli-bugbash-a
Nov 4, 2026
Merged

WaterrrForever merged 17 commits into
mainfrom
miao/cli-bugbash-a

Conversation

@WaterrrForever

Copy link
Copy Markdown
Collaborator

What

CLI bugs from the style-sampling bug bash (Jake Moran's five-template run on 0.9.9, Miguel's checklist on 0.9.10):

  • An interrupted templates pull no longer leaves a templates/<slug>.part-* folder (bc3b2c4).
  • Offline, auth status and templates pull say HeyGen can't be reached instead of waiting ~72 s and printing "fetch failed" (c1a8112).
  • preview leaves the project's HTML files as they are; it no longer stamps data-hf-id into every file (26e8891).
  • check runs the browser audits past a lint error; it used to skip layout/motion/contrast whenever lint failed (a275f04).
  • No "Keep editing … in the desktop app" line in CI or when a script reads render output (aaa290e).
  • cloud render takes a composition at any aspect ratio (2:1 included) without the misleading 16:9 warning (18605b9).
  • A weak or cache-only sound-effect match says so (score, low-confidence flag) instead of passing silently (e4ac55c).
  • New media-use avatars: the HyperFrames account's own avatar looks, mirroring media-use voices, so templates stop listing avatars through the separate heygen sign-in (a13908c).

Draft while the full-suite fixes land.

Related work

Refs #6842. Companions: #7164 (style-sampling skill + packs), #7168 (render watchdog, lock, fonts), #7169 (audio attribute lint).

Test plan

  • Each fix has a test that fails without it
  • Full suite green (in progress)

🤖 Generated with Claude Code

WaterrrForever and others added 8 commits November 4, 2026 15:21
Unpacking now runs tar asynchronously and removes its .part folder on
SIGINT/SIGTERM/SIGHUP before re-raising the signal. Part folders carry the
owning pid, and the next pull clears any whose process is gone.

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

Offline, both commands waited on unbounded requests and then printed only
undici's 'fetch failed'. A new networkFailure helper names the host, the
reason (DNS lookup, no route, refused, no answer in time, dropped) and the
next step. The account and token-refresh requests now time out after 15 s,
the template link after 15 s, and auth status reports an offline refresh at
once instead of retrying with the stale token. describeCauseChain moves into
the helper so the registry and auth share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a project in preview stamped data-hf-id into every composition on
disk. The preview route and every source mutation already mint the same
content-keyed ids in memory, so the startup pass is dropped: files gain ids
only when an edit to them is saved. The .hyperframes folder is still made
before the watcher starts, so the preview cache creating it is not reported
to Studio as a manifest change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lint error made check skip runtime, layout, motion and contrast, so one
known lint error hid every browser finding. The browser pass now runs
whenever there is a composition to load, and the lint error still fails the
check. Only a root with no data-composition-id skips it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 'Keep editing by chatting with Framey' line printed on every render and
preview, including CI and piped runs where nobody can open the app. It now
prints for a person at a terminal, and for a coding agent, which the
hyperframes skill tells to pass the line on; CI and other non-TTY runs get
nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 1920x960 composition got 'server will default aspect_ratio to 16:9'
although the render kept 1920x960: at 1080p the cloud renders at the
composition's own size and aspect_ratio only picks a 4K preset. The CLI now
names the detected ratio (2:1) and says the size is kept, takes
--aspect-ratio as any W:H checked against the composition, sends only the
16:9, 9:16 and 1:1 the API accepts, and refuses a 4K render of any other
ratio before upload, where no preset fits.

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

resolve --type sfx returned a HeyGen sound at any score down to 0.4, and a
project file that shared one word of its name with the intent, with nothing
to tell them from a real match. Such a result now carries a match object in
the JSON ({score, min_score, low_confidence} or the matched words and the
intent the file was resolved for) and a low-confidence line on stderr. A
HeyGen search with no hit says so with the min score it used, and a sound
effect nothing matches fails with 'no sound effect matched'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Templates had to reach for 'heygen avatar list', a separate sign-in that can
be another account. 'media-use avatars' lists the private looks on the
HyperFrames account making the film (GET /v3/avatars/looks?ownership=private,
following next_token up to --limit) as {avatars: [{avatar_id, name, ...}]},
the id that 'generate --type avatar --avatar' takes.

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

mintlify Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Nov 4, 2026, 4:12 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

…s that skill

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@WaterrrForever
WaterrrForever marked this pull request as ready for review November 4, 2026 16:15

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested. Reviewed at 20619362e98db3467133684925ef923a28322bd1, including the two-file description/manifest delta from requested a13908c50429997f5c45c3c26ee6192598ed3ddb. Miao re-pinned this revision as ready during review.

Important — abandoned-part cleanup deletes unrelated user files (packages/cli/src/commands/templates.ts:143–152). removeAbandonedParts() removes every sibling starting <slug>.part- unless its next token happens to be a live PID. Pulling sample deleted my templates/sample.part-notes/my-notes.txt, sample.part-reference.txt, and a sibling symlink, then successfully installed the template. A prefix and a dead/unparseable PID do not establish ownership. Restrict cleanup to positively identified CLI-owned temporary directories (strict generated name plus an ownership marker); preserve unknown files/directories/symlinks and unmarked legacy entries. Add preservation tests alongside the abandoned-pull case. Repro used the real pullTemplate and real local tar, with only fetch stubbed: unrelatedDirectorySurvived:false, unrelatedFileSurvived:false, unrelatedSymlinkSurvived:false, templateInstalled:true; the external symlink target survived.

Nit — retain offline recovery when avatar credential refresh fails (packages/cli/src/heygen/avatars.ts:75–77). This catch changes a refresh NetworkFailure into “no HeyGen sign-in” / auth login, although the stored login can still exist. Convert it to Fix(error.message, error.hint) as fetchPage does; reserve login recovery for actual credential failures. The shared resolver is correctly the same HyperFrames chain used for film generation, not the separate HeyGen CLI store; API-key and OAuth/host-token precedence remain that resolver's policy.

Nit — confidence metadata disappears on reuse (packages/cli/src/media-use/resolve.mjs:361–363,580–616). The first weak provider/name match gets match, but it is not stored on the record and exact-prompt cache hits call result() without it. Preserve/reconstruct the advisory from provenance and test a second identical resolve. Asset selection itself is unchanged, so this is a follow-up.

Validation: bun run build and bun packages/cli/scripts/gen-skills-manifest.ts --check passed. At a13908c, the nine focused CLI suites passed 272 tests (214 preview/cloud/check/desktop in the independent pass; 58 template/avatar/auth/network here), and the resolve engine reported 67 passed / 0 failed. Those code/test paths are identical at this reviewed successor; only the style guide sentence and its generated hash changed. The independent real-tar preservation probe reproduces the Important issue. No live sign-in, paid render, cloud render or packaged Desktop walk was performed.

Audited: new avatar/network modules and template unpack/cleanup; credential refresh integration; cloud aspect submit logic, check continuation, preview-ID lifecycle and SFX reuse paths; changed tests/docs. Trusting: unchanged bulk command/server/check/resolve machinery outside these integration paths. Preview still mints IDs in memory; edit paths persist them. Executed preview tests cover untouched source and first element save; GSAP save behavior was source-traced against existing tests. I did not exercise repeated saves/reopen or concurrent editor changes.

No existing review or inline finding was present at acquisition; this is the first review round.

Cloud contract: source-read EF master c1dcf19e1e73924e609903f162b1d24e5f00ca2b: request DTO permits only 16:9/9:16/1:1, which this CLI respects by omitting other ratios. hyperframes_api_render.py:867–872 and hyperframes_render_parameters.py:1–5 forward supersampling only for *-4k; 1080p leaves native composition dimensions. This is source verification, not a deployed-image/paid-render claim.

Need: yes · Size: reviewable, eight independent fixes across 36 files · Reuse: shared HyperFrames credentials and network-cause helper · Simpler: cleanup must prove ownership before deleting · Structure: fine
Risk: medium — new recursive cleanup can remove user data.

Verdict: REQUEST CHANGES
Reasoning: Preserve unrelated sibling entries during cleanup. The remaining notes are nonblocking.

— hf-review

Comment thread packages/cli/src/commands/templates.ts Outdated
if (!name.startsWith(partPrefix(dest))) continue;
const pid = Number(name.slice(partPrefix(dest).length).split("-")[0]);
if (Number.isInteger(pid) && pid > 0 && processIsAlive(pid)) continue;
rmSync(join(parent, name), { recursive: true, force: true });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important — user-data deletion. Prefix matching is not ownership: a real pullTemplate("sample", dir) removed my unrelated templates/sample.part-notes/my-notes.txt, sibling sample.part-reference.txt, and a prefix-matching symlink before successfully installing the template. Their suffix is not a PID, so they fall into recursive deletion. Please remove only positively identified CLI-owned temporary directories (exact generated naming plus ownership evidence), preserve unknown/unmarked siblings including files and symlinks, and add these preservation regressions. Keep live pulls protected.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 23d3f70 (head bb849a43). Each pull writes .hyperframes-pull.json (owner tag + pid) into its own part folder when it creates it. Cleanup removes an entry only if it is a real directory (not a symlink), its name matches the generated <slug>.part-<pid>-XXXXXX exactly, its marker has the owner tag and the same pid, and that pid is dead. Everything else is left alone: your files, folders and symlinks, unmarked folders, and legacy part folders. a9531be reads the marker through one O_NOFOLLOW descriptor and takes entry types from readdir, so there is no check-then-use. The tests use the real pullTemplate and tar: sample.part-notes/my-notes.txt, sample.part-reference.txt, an unmarked legacy folder, an unmarked dead-pid name, and a generated-name symlink to a marked outside folder all survive, as does its target. They fail without the fix.

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 122 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

Unstable (1)

  • seqab-none-center-r0-root-z100: tracking 0.03, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.03, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true / tracking 0.03, pressJump 0, drop 0, reload 0, render 0.02, renderKey -, undo true, teleport true

WaterrrForever and others added 6 commits November 4, 2026 16:25
…ts own

The abandoned-part cleanup deleted any sibling named '<slug>.part-*' whose
next token was not a live pid, so a person's own 'sample.part-notes/',
'sample.part-reference.txt' or a symlink went with it. A pull now writes an
ownership marker (owner tag + pid) into its part folder, and the cleanup
removes only a real directory with the generated '<slug>.part-<pid>-XXXXXX'
name whose marker names a dead pid. Files, symlinks, unmarked folders and
older unmarked parts are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… cannot reach HeyGen

A network failure while refreshing the stored login was reported as 'no
HeyGen sign-in' with 'auth login' as the fix, though the login was still
there. It now keeps the network failure's message and next step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first resolve of a weak HeyGen sound or a name-matched project file
flagged it, but an identical second resolve hit the project manifest and
returned it with no match object. The advisory is now rebuilt from the
record's provenance (the stored score, or the adopted file's name) on reuse.

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r written to disk

CodeQL flagged the downloaded bundle written to a file before tar read it,
and a stat-then-read in the preview test. The verified archive now streams
into tar's stdin, so no network bytes reach a file the CLI writes. The
abandoned-part scan takes the entry type from readdir instead of an lstat,
reads the ownership marker through one O_NOFOLLOW descriptor it fstats, and
the preview test reads each file once instead of stat-checking it first.

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

main's @hyperframes/cli was already about 0.99% over its 23722874-byte
unpacked budget, so this branch's additions (networkFailure, the avatars
command, the larger skill references) tip it past the 1% tolerance. This is
the same value #7169 sets, 25159107 bytes (5% headroom over the measured
size), so the two PRs merge cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WaterrrForever added a commit that referenced this pull request Nov 4, 2026
…ck and font-lint additions

Main's CLI unpacked size was already ~0.99% over its budget; the render lock and
owner markers push it past the 1% tolerance. The CLI unpacked budget takes the same
value as #7169 and #7170 (25,159,107) so the PRs merge cleanly. The engine's packed
size grows by about 4.7 KB for the effect-scan and session-open watchdogs; its packed
budget moves to 5% above the measured 467,097, as the budget file's headroom rule sets it.

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

@jerrai-bot-heygen jerrai-bot-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At ready head bb849a43, the earlier template-cleanup user-data deletion appears repaired: the reaper now requires a real directory with an exact generated name and a matching CLI-owned marker/dead PID, and the avatar-refresh/cache-confidence fixes have focused tests. Rames's original change request is still a separate gate. I found one remaining custom-aspect correctness failure in this PR's new 4K path (inline): a noncanonical authored ratio within the inherited 0.05 detector tolerance is returned as a supported 16:9 preset and skips the new pre-upload custom-ratio refusal. A 1920×1055 composition is a concrete input. Current-head Fallow audit also failed on pullOwner complexity and duplicate OAuth blocks; other hosted checks were still running, so this is not a green-CI claim. The CodeQL warnings on the earlier head were addressed by streaming the archive to tar and removing check-then-use in the test/marker path, but no cloud render or paid path was exercised by me. No merge or queue action. — Jerrai

);
failCommand();
}
if (detection.kind === "matched") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The detector's inherited ±0.05 band treats a custom 1920×1055 composition (ratio 1.8199, ≈0.042 from 16:9) as matched:16:9, so this new branch returns that preset and bypasses refuseUnsupportedAt4k. A cloud render --resolution 4k then sends aspect_ratio:16:9 for a composition that is not authored at 16:9, contrary to the new 'custom ratios refused before upload at 4k' contract. Please compare exact authored dimensions against a valid 4K preset (e.g. integer cross-products), or explicitly reject tolerated-but-noncanonical dimensions for 4K; add a near-preset custom-ratio regression.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c17dc90. A 4k cloud render now runs the producer's own checkOutputResolutionCompatibility (from @hyperframes/core) before upload, on the composition's exact size against the 4K preset the API would pick (landscape-4k / portrait-4k / square-4k). The ±0.05 band now only chooses which preset to ask for; whether it fits is the producer's exact check, run locally. An incompatible size is refused with the producer's reason and a 1080p or exact-size suggestion. Tests: 1920×1055, 1000×980 and 1055×1920 are refused at 4k (they fail without the fix), 1920×1080 / 1080×1920 / 1080×1080 are still sent as 16:9 / 9:16 / 1:1, and 1080p keeps 1920×1055 at its own size.

…nt POST

Fallow flagged pullOwner (CRAP 49.5, cyclomatic 13) and a 28-line clone
between refreshOnce and exchangeCodeForTokens. pullOwner is now the name
match, the O_NOFOLLOW marker read and the marker check as three small
functions, with the same behaviour. Both token requests go through one
postToTokenEndpoint, which keeps the 15 s timeout and the named network
failure.

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

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 3d36d71b283ea426e2f7b4f65e93e79865c30f6e, including the complete delta from 20619362e98db3467133684925ef923a28322bd1.

My cleanup blocker is fixed: cleanup now requires a real, exactly named directory with a matching CLI owner marker and dead PID. The real-tar preservation probe now keeps the original notes folder/reference file, unmarked/legacy entries, wrong-owner/PID markers, entry symlinks, marker symlinks and external targets, while removing a marked abandoned pull and retaining a live pull. Offline avatar refresh and repeated weak-match warnings are also fixed.

Important — Jerrai's near-preset 4K finding remains. packages/cli/src/commands/cloud/render.ts:492–494 still returns the detector's approximate preset before enforcing the custom-ratio refusal. Actual helper calls at this head accept 1920×1055 as 16:9 and 1000×980 as 1:1 for 4K; a true 2:1 input is refused. The producer's shared checkOutputResolutionCompatibility rejects both accepted near-preset examples as aspect-mismatch, so these inputs can pass the CLI's before-upload gate only to fail downstream.

Reuse that existing compatibility check for known local dimensions and the resolved 4K preset before returning/submitting, rather than treating the loose detector tolerance as compatibility. It also covers supersampling constraints. Add near-wide, near-square and near-tall negative controls alongside canonical successes. I am confirming the existing discussion with a second dimension class and the downstream predicate, without duplicating its inline.

Validation at this head: workspace build passed; template/avatar/OAuth/network suites passed 76 tests, resolve engine 67 passed/0 failed. Independent real-tar cleanup, repeated-cache advisory, aspect-selection and producer-compatibility probes passed their controls and reproduced the remaining aspect gap. No live sign-in, paid/cloud render or packaged Desktop run.

Scope: all 14 delta files and the affected caller paths; the unchanged preview-ID/check/desktop/cloud-auth surfaces retain my initial review scope and limitations. No additional introduced Important issue was verified.

Need: yes · Size: ok · Reuse: existing output-resolution compatibility predicate · Simpler: share the producer's precondition · Structure: fine
Risk: medium — unsupported 4K inputs reach the cloud instead of failing locally.

Verdict: REQUEST CHANGES
Reasoning: The cleanup and recovery/advisory fixes are sound, but the existing 4K compatibility blocker remains at this head.

— hf-review

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE · 0 blocker · 1 important (follow-up) · 0 nits · head 3d36d71b

Rames's three findings are fixed. The cleanup only removes exactly-named, marked part folders whose pid is dead; avatar refresh stays offline-aware; and a reused weak sound keeps its warning. CI is green at this head.

  • Follow-up for Jerrai's open 4K thread (render.ts:492), not a blocker. The ±0.05 match predates this PR: main also sent 16:9 at 4K for 1920×1055. The server doesn't letterbox it; the producer's checkOutputResolutionCompatibility throws aspect-mismatch after upload. An exact 16:9 at a non-integer scale (1440×810) throws non-integer-scale the same way. Running that check against the chosen preset before upload at 4K would close both. Until then, "4k needs 16:9, 9:16 or 1:1" is incomplete.
Scope

I read the changed source and tests end-to-end, then ran 219/219 tests across templates, cloud/render, networkFailure, check, auth and avatars. Also checked:

  • Reaper races: the gap before the marker is written, pid reuse and own-pid all keep the folder (at worst a leak, never a deletion).
  • Bundle install: the hash and byte count are checked before unpack, and the bundle is renamed into place only after a full unpack.
  • oauth.ts refactor: a pure extraction; behaviour is unchanged.
  • preview: no HTML writes are left on open.
  • check: still exits 1 on a lint error.

I ran no paid, cloud or live sign-in path.

…before upload

The aspect detector matches a ratio within ±0.05, so at 4k a 1920x1055
composition was sent as 16:9 and 1000x980 as 1:1, and the producer then
refused both as an aspect mismatch. At 4k the matched ratio's preset
(landscape-4k, portrait-4k, square-4k, the ones the API maps it to) now goes
through the producer's own checkOutputResolutionCompatibility, and a
composition it does not fit is refused locally with the producer's reason
and the 1080p alternative. 1080p, where the render keeps the composition's
own size, is unchanged.

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

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at c17dc902c4aea3c42c199d152191701dd81503b0, complete two-file delta from 3d36d71b.

The remaining 4K compatibility blocker is fixed. Known local dimensions now run the producer's shared checkOutputResolutionCompatibility against the actual landscape/portrait/square 4K preset before auth, archive preparation or upload. The approximate detector only selects a candidate; it no longer establishes compatibility. Explicit aspect-ratio flags cannot bypass the check.

Validation at this head: workspace build passed; the cloud-render suite passed 26/26. An independent adversarial probe passed 50 controls: near-wide/square/tall and non-integer upscales are refused; canonical ratios, integer 3× upscale and downsample pass; 1080p preserves native behavior. Actual CLI dry-run controls pass, and six nondry-run negative commands hit the compatibility error before auth/upload. No live account, paid/cloud render or packaged Desktop check was run.

The cleanup-preservation, offline avatar recovery and cached-confidence repairs are carried forward from my 3d36d71b review after confirming this two-file delta leaves them unchanged. No new substantive finding in this delta.

Need: yes · Size: ok · Reuse: producer's existing compatibility predicate · Simpler: one shared precondition · Structure: fine
Risk: low — local refusal now agrees with downstream constraints.

Verdict: APPROVE
Reasoning: The prior custom-ratio and adjacent non-integer-scale failures are stopped before upload while the tested valid and 1080p controls retain their behavior.

— hf-review

@jerrai-bot-heygen jerrai-bot-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at c17dc902, superseding my prior 4K aspect-ratio change request. The CLI now uses the producer's own checkOutputResolutionCompatibility on exact composition dimensions and the chosen 4K preset before auth/upload; near-preset custom and non-integer upscale cases are refused, while exact presets pass and 1080p preserves native dimensions. I also verified the prior cleanup, avatar auth/offline and cached-confidence repairs in the composed head; Rames approved this exact revision. Hosted checks on this head report no failed or pending jobs at click time. No paid call, live cloud render, merge or queue action from me. — Jerrai

@WaterrrForever
WaterrrForever added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit fb73801 Nov 4, 2026
86 checks passed
@WaterrrForever
WaterrrForever deleted the miao/cli-bugbash-a branch November 4, 2026 18:39
ameyamg pushed a commit to ameyamg/hyperframes that referenced this pull request Nov 4, 2026
…t a time, and fonts the cloud can't resolve are flagged (heygen-com#7168)

* fix(producer): a wedged effect scan or screenshot-session open trips the 60 s stall watchdog

The sequential streaming watchdog raced only the capture call. Session init
(where the fast-capture effect scan runs) and opening the screenshot session
for effect frames waited on CDP's protocol timeout instead, which scales with
output area up to 30 minutes, so a starved page hung a render for ~25 minutes
before the watchdog saw the next frame. The effect scan now gives up when a
chunk outlasts its 120 s budget, opening the effect-frame session gets its own
no-progress window, and session init gets a 300 s one; each trip retries on a
fresh session like any other stall.

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

* feat(cli): one local render at a time per machine; a second waits and says for whom

Renders started side by side from different sessions starve each other until
frames stop landing. `hyperframes render` now takes a machine-wide slot (a lock
file in the HyperFrames cache directory, held by pid) after its preflight and
holds it for the render or the whole batch. A second render waits and prints
the pid and project it is waiting behind; `--no-wait` fails at once instead.
A holder whose process is gone is taken over, and HYPERFRAMES_RENDER_LOCK=off
restores side-by-side renders. `--workers` is unchanged.

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

* fix(lint): flag fonts a cloud render cannot resolve, including the font shorthand

Lint read only `font-family`, so `font: 500 16px "PingFang SC", sans-serif`
passed while the renderer, which reads the shorthand too, drew the installed
Mac font locally and the cloud refused it. The shorthand now counts for
font_family_without_font_face (its parser moves to @hyperframes/parsers so
core and lint share one), and a new font_face_local_only warning names an
@font-face whose only sources are local(): it renders here and falls back in
the cloud. The rule's hint and the format reference stop recommending local()
for OS fonts.

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

* fix(cli): a cloud render that refuses a font says which project files use it

The cloud's "fonts that could not be resolved: PingFang SC" named no
composition. For a render submitted from a local project, the failure now
lists each refused family with the project files that name it, and says how
to ship the font so the cloud can use it.

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

* chore(cli): keep comment shares and exports within the gates, and split the single-render notes out of executeHeldRender

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

* fix(parsers): read a spaced line-height slash and var() before the size in the font shorthand

`font: 16px / 1.5 Inter` was read as the family `/ 1.5 Inter`, and
`font: var(--weight) 16px Inter` as `16px Inter`, because a var() counted as
the size. The parser now follows the shorthand grammar: optional style,
variant, weight and width keywords (or var()), then a length, percentage,
keyword or calc() size, then a `/ line-height` in any spacing. When no size can
be told apart from a var(), it says so: lint skips that declaration instead of
reporting a missing font, and the renderer keeps its best guess.

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

* fix(cli): say the render slot is one per user account, not per machine

The lock lives in the account's HyperFrames cache folder, so renders by two
user accounts never queued behind each other while the messages, flag help and
docs said "this machine". They now say "per user account" and that
HYPERFRAMES_CACHE_DIR set to a shared folder shares the slot across accounts.

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

* fix(cli): reclaim a render lock or takeover file left half-written

A lock or `.break` file that exists but cannot be parsed (a crash mid-write
where hard links are unavailable) was never reclaimed: a waiting render waited
forever and --no-wait refused forever. Once such a file is older than any real
write (5 s by mtime) it counts as abandoned: the lock goes through the same
`.break` takeover as a dead holder's, and a takeover file is removed.

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

* fix(producer): opening the capture session counts against the setup stall budget

The sequential streaming attempt opened its own capture session (Chrome launch
and new page) before the setup watchdog and outside the attempt's cleanup, so a
wedged launch still waited for the protocol timeout. Opening now shares the
300 s setup window with session initialisation, a trip fails the attempt like
any other sequential stall, and a session that finishes opening after its
attempt gave up is closed instead of leaking a browser.

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

* chore(parsers): keep the shorthand result type module-private

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

* fix(cli): reclaim an unreadable render lock only when its writer is provably gone

Where hard links are unavailable the lock is created by an exclusive open and
then written, so a contender paused between the two leaves an unreadable lock
while it is still alive. The 5 s mtime grace took such a lock over, and both
processes then held the slot. Each contender now writes an owner marker under
a name only it can take (pid plus random suffix, holding its process identity)
before it creates the lock, keeps it while it holds the slot, and removes it
otherwise. An unreadable lock or `.break` is reclaimed only when no other
contender's marker belongs to a live process; markers of dead processes, or of
a reused pid, are deleted. No reclaim is decided by time alone.

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

* refactor: split acquireRenderLock and the streaming-stage session stub under the complexity gate

acquireRenderLock hands contender setup and a single marked claim to their own
functions, and the test stub for createCaptureSession splits its held-open
cases from the fake session it builds, so neither exceeds Fallow's cognitive
or CRAP thresholds. Behaviour is unchanged.

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

* chore(ci): raise the CLI and engine size budgets for the watchdog, lock and font-lint additions

Main's CLI unpacked size was already ~0.99% over its budget; the render lock and
owner markers push it past the 1% tolerance. The CLI unpacked budget takes the same
value as heygen-com#7169 and heygen-com#7170 (25,159,107) so the PRs merge cleanly. The engine's packed
size grows by about 4.7 KB for the effect-scan and session-open watchdogs; its packed
budget moves to 5% above the measured 467,097, as the budget file's headroom rule sets it.

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

* fix(parsers): a var() size glued to its line height is the shorthand's size

`font: 700 var(--size)/1.1 "Inter"` read as no family at all: the token was
tested whole against var(), and the slash after it hid that it was the size,
so the renderer injected no face for Inter. A var() followed by `/ line-height`
(glued or spaced) can only be the size, and is now read as one.

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

* refactor(parsers): split the shorthand size search out of parseFontShorthand

Keeps parseFontShorthand under Fallow's cognitive-complexity gate; behaviour is unchanged.

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

* fix(cli): render lock never removes another render's takeover, knows a reused pid, works in a shared folder and on Windows

- A `.break` takeover file is removed only by the render that wrote it. One
  whose writer is gone stops the render with the file to delete, instead of
  being removed on an earlier reading, which could remove a live `.break`
  written since and let two renders hold the slot. The takeover re-reads the
  lock under its own `.break` right before replacing it.
- The lock record keeps its holder's process start time, so a pid that now
  belongs to another process (an OOM-killed render in a container with a
  persisted cache) no longer holds the slot forever.
- The `locks` folder is created writable for every account, so accounts
  sharing HYPERFRAMES_CACHE_DIR take turns instead of the second failing with
  EACCES; a folder this account cannot write in says how to fix it, and
  another account's file that cannot be removed is left, not fatal.
- Windows: no PowerShell start-time lookup per check (pid liveness only), a
  file another handle holds (EPERM/EBUSY/EACCES) reads as held, a lock that
  vanished is retried once instead of in a tight loop, and a wait stops when
  its signal aborts. The tests end every acquire they start before removing
  their folder, and the paused contender writes through its open descriptor.

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

* chore(ci): raise the lint packed size budget for the font-shorthand rule

With main's audio-attribute rule (heygen-com#7169) merged in, the font-shorthand and
local()-only font findings take @hyperframes/lint's packed tarball to
181925 bytes, over the 179338 budget. The new budget is that measurement
plus the usual 5% headroom.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 5, 2026
…licable heygen-com#7170

- heygen-com#7170 (applicable parts): CLI reports offline/network failures as
  NetworkFailure (host only) across auth, status, check and cloud render;
  registry reuses describeCauseChain; check pipeline and studio server
  stamp-on-open follow upstream. Desktop hint, SFX and templates/avatars
  parts deferred with prerequisites.
- heygen-com#7148: X and Instagram links in docs.
Watermarks: commit 06722eb, PR 7164, issue 7223; monitored 91.

Co-authored-by: WaterrrForever <miao.yang@heygen.com>
Co-authored-by: Ular Kimsanov <ularkimsanov7@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants