Commit Graph
715 Commits
Author SHA1 Message Date
James a9338a4e0f fix(cli): await error telemetry before finalization 2026-07-20 09:45:54 -07:00
James 73d3b4e1f4 fix(cli): report command failures once 2026-07-20 09:04:03 -07:00
James 619406a23b fix(cli): complete process lifecycle migration 2026-07-20 09:04:03 -07:00
James e0bda7a171 fix(cli): keep post-render exit reset root-owned 2026-07-20 09:04:03 -07:00
James 00b88abeb7 test(cli): align timeout failure with result contract 2026-07-20 09:04:03 -07:00
James 3aa2404747 refactor(cli): centralize process lifecycle 2026-07-20 09:04:03 -07:00
James 942db16fcd fix(cloudrun): publish adapter contract 2026-07-18 20:17:01 -04:00
Miguel Ángel e64a22893a fix(studio): preserve alpha proxy playback (#2625)
* fix(studio): preserve alpha proxy playback

* test(cli): pin VP8 alpha proxy pre-resolution

* fix(studio): emit textarea field-sizing CSS

* test(studio): verify textarea CSS generation
2026-07-17 21:56:27 -04:00
James e73304fb0e feat(cli): make cloud archives size-aware 2026-07-17 18:44:58 -04:00
James 65f2e2927c refactor(producer): unify render requests 2026-07-17 16:22:23 -04:00
Somansh Reddy 7acabbcdeb docs(guides): resolve the Send-to fidelity contradiction (preserve substance, adapt form) (#2620)
* docs(guides): resolve the fidelity contradiction (preserve substance, adapt form)

The guide called the rebuild 'lossy by nature' while the checklist demanded 'content
match the brief exactly', with no rule for what to preserve vs adapt. On a live import
CD kept fonts/palette/hero but genericized the source's real figures (2.4M signals/sec,
+240% spike) into vague phrases. Resolve around one principle: substance (real copy,
exact palette/fonts, distinctive figures/data, product names, signature visuals) is
preserved verbatim; only the form (static page to timed multi-scene motion) adapts.

* test(guides): pin the Send-to fidelity contract against silent regression

Address review on #2620: the resolved 'preserve substance, adapt form' instruction is
load-bearing LLM-facing prompt text, but validate-docs only proves syntax. Add a semantic
pin (mirroring packages/cli figma skillContent.test.ts) asserting the three positive
concepts are present and the two retired contradictory phrases ('lossy by nature',
'content match the brief exactly') cannot silently return.

* style(guides): oxfmt the Send-to fidelity-contract test

Wrap the readFileSync call to oxfmt form (printWidth 100); unblocks required Format + its
preflight/preview-regression cascade on #2620.
2026-07-17 12:30:48 -07:00
Vance Ingalls a7c0fa4fe5 Merge pull request #2564 from heygen-com/via/win-workdir-env
feat(cli): surface extract-cache dir in doctor + add --frames-cache-dir sugar
2026-07-17 00:43:06 -07:00
Vance Ingalls cf64f24e63 Merge pull request #2569 from heygen-com/via/macos12-dyld-crash-hint
fix(cli): surface HYPERFRAMES_BROWSER_PATH hint on macOS <13 chrome-headless-shell dyld crash
2026-07-17 00:40:14 -07:00
Vance Ingalls 8eccc6e9a3 Merge pull request #2599 from heygen-com/via/composition-structure-mandate
feat(skills): add COMPOSITION_STRUCTURE to feedback repro packet + soft-warn lint
2026-07-17 00:36:36 -07:00
Miguel Ángel e8371a7acc feat(media): alpha-capable authoring proxies (#2598)
* feat(media): alpha-capable authoring proxies

Alpha sources were refused a proxy before the codec map ever asked whether the
browser could decode them, so a ProRes 4444 alpha file (which no browser
previews at all) rendered black forever, while an alpha WebM (which previews
fine) was already covered by the browser-safe check on the next line. The alpha
veto earned nothing and cost the one case that needed help.

Alpha is now a target-codec choice rather than a veto: alpha sources transcode
to VP9 + yuva420p in WebM, everything else keeps the existing H.264/MP4 path
byte for byte. Only files no browser can preview are proxied, which is the rule
the runtime already followed everywhere else.

WebM cannot carry AAC, so the VP9 path uses Opus and drops the MP4-only
faststart flag. PROXY_PARAMS_VERSION moves to v3 so clients stop serving the
previously cached proxies.

Safari does not decode VP9 alpha and still shows black for alpha sources, as it
does today: this is better on Chromium and Firefox and no worse anywhere.

* fix(media): infer proxy variant for rescue

* fix(media): preserve alpha proxy hardening after restack
2026-07-17 03:26:55 -04:00
Miguel Ángel 35eff5038b feat(cli): bake proxies into published archives (#2595)
* feat(studio-server): serve H.264 proxies from the preview route

Wires the codec manifest and the transcoder into the preview surface: the route
negotiates a proxy via a query param and serves it through the existing range
and ETag machinery, composition HTML carries a codec map for the runtime, and
hostile assets pre-warm so a first play does not wait on a cold transcode.
Exposes the three subpath exports the CLI surfaces consume upstack.

Drops the TEMP fallow entry added with the transcoder: it has real importers now.

* fix(studio-server): publish media proxy exports

* fix(parsers): scan HTML comments linearly

* feat(cli): let projects opt out of automatic proxying

Adds media.autoProxy to hyperframes.json plus --proxy/--no-proxy flags, and
forwards the resolved value into the studio and preview servers and the vite
adapter. Lands before the runtime slice that turns auto-proxying on, so the
switch exists before there is any behavior to switch off.

* fix(cli): align media config schema

* feat(core): swap undecodable video to its proxy at runtime

Adds the browser-side half: before first load the runtime consults the injected
codec map and swaps a hostile source to its proxy, and if a video still reports
zero decodable width it rescues it reactively. An HEVC file carrying AAC fires
no error event, so zero videoWidth, not the error event, is the reliable signal.
Audio elements and alpha sources are never proxied, render mode never proxies,
and each swap evicts the element's stale sync state and reports once.

This completes the loop: auto-proxying is live for preview and studio from here.
The opt-out (media.autoProxy, --no-proxy) shipped in the previous slice.

* feat(cli): serve proxies from play, present, and the static project server

Adds proxy negotiation to the CLI-side servers and gives play byte-range
serving it never had, so a swapped video can seek. The static project server
behind check, snapshot, compare and friends injects the codec map once, so all
of its callers inherit the behavior; snapshot forwards its own proxy flag.

* fix(cli): serve proxies for camera formats

* feat(cli): resolve proxies before check's timed browser phase

check pre-resolves hostile assets so a cold transcode cannot exhaust the
render-ready budget, and surfaces the runtime's proxy diagnostics as findings
so a swap is visible rather than silent.

* feat(cli): bake proxies into published archives

Published pages are static, so there is no server to negotiate with: publish
transcodes proxies for hostile assets into the archive and rewrites the video
sources that point at them. Audio elements keep their originals, since audio
decodes independently of the video codec.

Splits the archive build from the zip step so publish can transform between
them. cloud render keeps calling the unchanged composition and still uploads
originals, which its regression test pins.

* fix(cli): harden proxy pre-resolution

* fix(cli): surface publish proxy outcomes

* test(cli): remove ffmpeg from archive guard

* test(cli): normalize publish fixture path
2026-07-17 02:50:28 -04:00
Via 490642b78a fix(feedback-lint): tighten census value scoping and defect-keyword matching
Addresses C1-C7 from Rames's adversarial review + N2/N3 nits:

- C1 (positionFixed): inline probe now value-scopes to 'fixed' — previously
  fired on any position value (absolute, relative, sticky), producing a
  false-positive anatomy that would mislead maintainers pattern-matching
  the 'sub-comp + position:fixed capture' bug family.

- C2 (overflowHidden): symmetric fix — inline path now catches
  style='overflow: hidden' via the value-scoped probe, matching the
  <style>-tag branch. Also handles overflow-x/overflow-y variants.

- C3 (VISUAL_DEFECT_KEYWORDS): drop 'render' — CLI's primary command is
  'hyperframes render', so build/perf/hang reports were triggering an
  inappropriate COMPOSITION_STRUCTURE: nudge on the most common failure
  mode. Rely on the more specific tokens (black, blank, flicker, corrupt,
  wrong frame) to identify actual visual defects.

- C4 (mentionsVisualDefect): compile keywords into a word-bounded regex.
  'blackboard', 'blanket', 'visualize', 'corruptible' no longer false-
  positive. Accepted tradeoff: plural forms ('flickers') don't match.

- C5 (marker case-normalization): REPRO COMMAND: / COMPOSITION_STRUCTURE:
  checks now case-insensitive, matching mentionsVisualDefect's
  normalization. Reporters using 'Repro command:' or lowercase
  'composition_structure:' get credit for compliance.

- C6 (background/mask shorthand): inline branch previously required the
  longhand 'background-image:' / 'mask-image:' — style='background:
  url(bg.png)' silently returned false. Now checks both longhand AND
  shorthand-with-url() inline forms.

- C7 (usesGsap docstring): trim promise of data-gsap-* attribute scanning
  that detectGsap never implemented — attribute scan lives outside the
  <script>-only detection path.

- N2 (EMPTY_VALUES): include 'inherit', 'revert', 'revert-layer' — a
  style='position: inherit' is authored intent to defer, not authored
  intent to place.

- N3 (input size cap): early-exit to a zero census on HTML > 20 MB
  rather than feeding linkedom a hostile input. Not expected in normal
  usage; guard for future callers that might pass raw user uploads.

Extends the test locks: 6 new census tests (value-scoping, size cap) and
5 new lint tests (word-boundary rejections, render-noise rejections,
lowercase-marker acceptance). All existing tests unchanged in intent —
only the 'flickers' plural in one test updated to 'flicker' to reflect
the new word-boundary rule.

No behavior change to the wire path: lint is still soft-warn, census is
still never called from feedback.ts, no new dependencies.
2026-07-17 06:22:00 +00:00
Miguel Ángel 0561adc11c feat(cli): resolve proxies before check's timed browser phase (#2594)
* feat(studio-server): serve H.264 proxies from the preview route

Wires the codec manifest and the transcoder into the preview surface: the route
negotiates a proxy via a query param and serves it through the existing range
and ETag machinery, composition HTML carries a codec map for the runtime, and
hostile assets pre-warm so a first play does not wait on a cold transcode.
Exposes the three subpath exports the CLI surfaces consume upstack.

Drops the TEMP fallow entry added with the transcoder: it has real importers now.

* fix(studio-server): publish media proxy exports

* fix(parsers): scan HTML comments linearly

* feat(cli): let projects opt out of automatic proxying

Adds media.autoProxy to hyperframes.json plus --proxy/--no-proxy flags, and
forwards the resolved value into the studio and preview servers and the vite
adapter. Lands before the runtime slice that turns auto-proxying on, so the
switch exists before there is any behavior to switch off.

* fix(cli): align media config schema

* feat(core): swap undecodable video to its proxy at runtime

Adds the browser-side half: before first load the runtime consults the injected
codec map and swaps a hostile source to its proxy, and if a video still reports
zero decodable width it rescues it reactively. An HEVC file carrying AAC fires
no error event, so zero videoWidth, not the error event, is the reliable signal.
Audio elements and alpha sources are never proxied, render mode never proxies,
and each swap evicts the element's stale sync state and reports once.

This completes the loop: auto-proxying is live for preview and studio from here.
The opt-out (media.autoProxy, --no-proxy) shipped in the previous slice.

* feat(cli): serve proxies from play, present, and the static project server

Adds proxy negotiation to the CLI-side servers and gives play byte-range
serving it never had, so a swapped video can seek. The static project server
behind check, snapshot, compare and friends injects the codec map once, so all
of its callers inherit the behavior; snapshot forwards its own proxy flag.

* fix(cli): serve proxies for camera formats

* feat(cli): resolve proxies before check's timed browser phase

check pre-resolves hostile assets so a cold transcode cannot exhaust the
render-ready budget, and surfaces the runtime's proxy diagnostics as findings
so a swap is visible rather than silent.

* fix(cli): harden proxy pre-resolution
2026-07-17 02:05:25 -04:00
Via 8f90fd9ec1 refactor(feedback): extract lint-warning loop to satisfy Fallow CRAP threshold
The added `for...of` loop over `lintFeedbackComment` warnings pushed the
`run` function's cyclomatic complexity from 4 to 5, landing the CRAP
score at exactly the 30.0 Fallow threshold. Extract the loop into
`printFeedbackLintWarnings` so `run` stays a flat driver — the helper
carries the incidental complexity.

No behavior change; all 29 unit tests + typecheck + oxlint + oxfmt +
local `fallow audit --base origin/main` pass clean.
2026-07-17 03:12:04 +00:00
Miguel Ángel 74b4f1e8c3 feat(cli): serve proxies from play and the static project server (#2593)
* feat(studio-server): serve H.264 proxies from the preview route

Wires the codec manifest and the transcoder into the preview surface: the route
negotiates a proxy via a query param and serves it through the existing range
and ETag machinery, composition HTML carries a codec map for the runtime, and
hostile assets pre-warm so a first play does not wait on a cold transcode.
Exposes the three subpath exports the CLI surfaces consume upstack.

Drops the TEMP fallow entry added with the transcoder: it has real importers now.

* fix(studio-server): publish media proxy exports

* fix(parsers): scan HTML comments linearly

* feat(cli): let projects opt out of automatic proxying

Adds media.autoProxy to hyperframes.json plus --proxy/--no-proxy flags, and
forwards the resolved value into the studio and preview servers and the vite
adapter. Lands before the runtime slice that turns auto-proxying on, so the
switch exists before there is any behavior to switch off.

* fix(cli): align media config schema

* feat(core): swap undecodable video to its proxy at runtime

Adds the browser-side half: before first load the runtime consults the injected
codec map and swaps a hostile source to its proxy, and if a video still reports
zero decodable width it rescues it reactively. An HEVC file carrying AAC fires
no error event, so zero videoWidth, not the error event, is the reliable signal.
Audio elements and alpha sources are never proxied, render mode never proxies,
and each swap evicts the element's stale sync state and reports once.

This completes the loop: auto-proxying is live for preview and studio from here.
The opt-out (media.autoProxy, --no-proxy) shipped in the previous slice.

* feat(cli): serve proxies from play, present, and the static project server

Adds proxy negotiation to the CLI-side servers and gives play byte-range
serving it never had, so a swapped video can seek. The static project server
behind check, snapshot, compare and friends injects the codec map once, so all
of its callers inherit the behavior; snapshot forwards its own proxy flag.

* fix(cli): serve proxies for camera formats
2026-07-16 23:02:01 -04:00
Miguel Ángel 6458807066 feat(cli): let projects opt out of automatic proxying (#2591)
* feat(studio-server): serve H.264 proxies from the preview route

Wires the codec manifest and the transcoder into the preview surface: the route
negotiates a proxy via a query param and serves it through the existing range
and ETag machinery, composition HTML carries a codec map for the runtime, and
hostile assets pre-warm so a first play does not wait on a cold transcode.
Exposes the three subpath exports the CLI surfaces consume upstack.

Drops the TEMP fallow entry added with the transcoder: it has real importers now.

* fix(studio-server): publish media proxy exports

* fix(parsers): scan HTML comments linearly

* feat(cli): let projects opt out of automatic proxying

Adds media.autoProxy to hyperframes.json plus --proxy/--no-proxy flags, and
forwards the resolved value into the studio and preview servers and the vite
adapter. Lands before the runtime slice that turns auto-proxying on, so the
switch exists before there is any behavior to switch off.

* fix(cli): align media config schema
2026-07-16 23:01:14 -04:00
Via 0aaac7aa30 feat(skills): add composition-structure block + soft-warn feedback lint
Extend the CLI feedback reproduction packet (#2498) with a fifth
mandated field, `COMPOSITION_STRUCTURE:`, and enforce presence of
`REPRO COMMAND:` / `COMPOSITION_STRUCTURE:` at feedback-submit time.

- Skill + reference now specify `COMPOSITION_STRUCTURE:` — a
  privacy-preserving structural anatomy (element census + attribute
  presence + timeline shape + delta + defect location) — required for
  any rating <=7 that describes a visual defect.
- `buildCompositionCensus()` + `renderCompositionCensusBlock()`
  auto-fill the block from composition HTML so agents don't ask the
  human user to hand-count `<video>` / `<img>` / sub-comp mounts.
  Counts + presence flags only — no file paths, no src URLs, no user
  text.
- `hyperframes feedback` soft-warns (never blocks) when a non-10
  `--comment` is missing `REPRO COMMAND:`, and when a rating-<=7
  visual-defect comment is missing `COMPOSITION_STRUCTURE:`. The
  warning points at the auto-census helper so agents remediate
  themselves.
- `coreSkillContent.test.ts` locks the new literal in both the skill
  and the reference file, following #2498's pattern.

Extends #2498. Follow-up: no change to `doctorSummary` generation, no
change to the feedback-submission API endpoint, no refactor of
#2498's doc-content Jest test.

Signed-off-by: Via
2026-07-17 02:58:11 +00:00
Via 9fc7390c2b fix(cli): import EXTRACT_CACHE_DIR_DISABLED_ALIASES in render.ts
Addresses Miga's SSOT review on #2564. The render command was
inlining the disable-alias list (["off","none","false","0"]) instead
of importing the exported constant, defeating the drift-safety the
constant exists to provide. Also switches the flag description string
to interpolate the alias set from the constant for consistency.

_— Via_
2026-07-17 02:52:04 +00:00
Miguel Angel Simon Sierra 88c049f525 refactor(parsers): single shared FFmpeg/FFprobe binary resolver
The cli, engine, and lint packages each carried their own copy of the
ffmpeg/ffprobe lookup, annotated fallow-ignore code-duplication, and the
copies had drifted: the engine copy handled Windows PATHEXT and executed
which/where without a shell but lacked the Homebrew-dirs fallback for
GUI-spawned processes; the cli copy had the opposite. One resolver in
@hyperframes/parsers (the dependency-graph bottom) now carries the union
of both hardenings, and all three packages delegate to it. Every
consumer gets strictly more robust resolution; env-override semantics
per call site are preserved via configuredMustExist.
2026-07-16 18:36:40 -04:00
Miguel Ángel 428e571914 fix(skills): align TTS docs with CLI contract (#2483) 2026-07-16 18:20:17 -04:00
Via 0d16f19b07 fix(cli): surface HYPERFRAMES_BROWSER_PATH hint on macOS <13 chrome-headless-shell dyld crash
Field feedback (#hyperframes-cli-feedback ts=1784227832, darwin/x64,
macOS 12, HyperFrames CLI 0.7.60) hit
`dyld: Symbol not found: _kVTCompressionPropertyKey_ReferenceBufferCount`
from VideoToolbox when launching the pinned chrome-headless-shell
mac-152.0.7928.2. The symbol is macOS-13-only, so older hosts abort
the binary at dyld load before any browser process starts.

The reporter recovered by installing an older shell
(`@puppeteer/browsers install chrome-headless-shell@150`) and pointing
`PRODUCER_HEADLESS_SHELL_PATH` at it. Their check/snapshot commands
accepted that older cached shell (they do not force the pinned build),
but the render command requires v152 via `preferManagedChrome: true`
and could not fall back on its own. The generic "Try --docker" hint
didn't name any of the browser-path env vars.

Sibling failure mode to the download-time hint added in #2443 and the
closed-with-invite #2078 (SIGTRAP at launch on macOS arm64), and the
in-flight #2481 (Windows STATUS_STACK_BUFFER_OVERRUN); same
`HYPERFRAMES_BROWSER_PATH` remediation, different trigger + platform.

The match is gated on:
1. Puppeteer launch-failure wrapper text
2. dyld Symbol-not-found signal
3. a macOS-13-only symbol OR the VideoToolbox framework

so unrelated darwin launch failures do not mis-fire the hint. The
symbol name is macOS-version-specific by construction — if a user's
dyld cannot find `_kVTCompressionPropertyKey_ReferenceBufferCount`
their host is <13, no separate `os.release()` gate needed.

- Signed-off-by: Via -
2026-07-16 19:25:45 +00:00
ViaandVia ca35227506 feat(cli): surface extract-cache dir in doctor + add --frames-cache-dir sugar
Windows users with the OS temp dir on a small system drive have hit
C: exhaustion mid-render (Slack ts=1784219488 · CLI v0.7.58 · win32
15 GB / 8-core, ~5500 frames). The engine already honors
HYPERFRAMES_EXTRACT_CACHE_DIR for relocation, but the knob was
undocumented and invisible in diagnostics — the reporter had to piece
together a 4-flag compound workaround including EXTRACT_CACHE_DIR=off.

Changes:
- Extract the env-var resolver into a public engine API
  (resolveExtractCacheDir, defaultExtractCacheDir,
  EXTRACT_CACHE_DIR_DISABLED_ALIASES) with a typed resolution shape
  distinguishing "disabled by user" vs "default" vs "env override".
- Add a Frames-cache check to `hyperframes doctor` that reports the
  effective directory, its free space, source (env or default), and
  fails with a relocation hint when <2 GB free at that mount.
- Add `hyperframes render --frames-cache-dir <path>` as discoverable
  CLI sugar for the env var, including the opt-out aliases
  (off/none/false/0) and CWD-safe absolute-path resolution.
- Document the flag in docs/packages/cli.mdx with the field-signal
  citation, and add a render example row for the Windows workflow.
- Cover both surfaces with unit tests (6 doctor cases + 4 engine
  cases including all disabled-alias variants).

Refs Slack #hyperframes-cli-feedback ts=1784219488 (win32 v0.7.58).

Co-authored-by: Via <via-heygen[bot]@users.noreply.github.com>
2026-07-16 18:24:43 +00:00
James 9b23c00237 refactor(engine): add fingerprinted browser leases 2026-07-16 12:58:10 -04:00
Miguel Ángel 771145a5c4 fix(registry): remove invalid media from caption components (#2454)
* fix(registry): remove invalid media from caption components

* test(registry): enforce nested media contracts
2026-07-16 12:16:45 -04:00
Miguel Ángel 9b17a5ad7e fix(cli): avoid Python whisper CLI collision (#2414) 2026-07-16 12:03:21 -04:00
Miguel Ángel e846cd6004 fix(cli): fail clearly on unsupported Node versions (#2388) 2026-07-16 12:03:13 -04:00
Miguel Ángel 584be6d64a fix(transcribe): select multilingual model before download (#2303) 2026-07-16 12:03:09 -04:00
Miguel Ángel 75eedf5cc1 fix(cli): time out stalled model downloads (#2415) 2026-07-16 11:45:49 -04:00
WaterrrForever f8c33cab72 feat(skills): act on stale CLI pin during project resume (#2540)
* feat(skills): probe and bump stale CLI pins during project resume

The entry skill now keeps a resumed project's pinned CLI current instead of
leaving that to a notice nobody acts on. On resuming a project with pinned
scripts, run the read-only probe 'npx hyperframes@latest upgrade --project
. --check'; when it (or the stale-pin stderr notice, or _meta.updateAvailable
from a pinned run) reports the project behind, apply the bump and verify
with 'hyperframes check'. A failed check reverts the bump and keeps the
project on its pinned version, preserving the reproducibility contract the
pin exists for.

The probe matters because the stale-pin notice only exists in >= 0.7.59:
a pinned run of an older CLI prints no warning at all, so a notice-only
trigger never fires for exactly the projects most behind. The probe runs
unpinned, so its behavior never depends on the project's CLI age.

Telemetry: the fleet converges to new releases within about a week via the
background auto-updater and ephemeral npx, but pinned projects form a
persistent stale tail (~10% of weekly actives, e.g. 6.3k users still on
0.6.x three weeks after 0.7.0).

Both skill surfaces now pass an explicit dir ('--project .') because a bare
'--project' followed by another flag consumes that flag as its directory
value and no-ops; the parsing fix is a separate CLI change.

* fix(cli): stop bare --project from eating the next flag as its directory

citty parses --project as a string option, so 'upgrade --project --check'
arrived with project="--check": the dir resolved to a nonexistent path and
the command no-opd with 'No package.json found' while --check was lost.
The documented default-cwd behavior only worked when --project was the
final token — and the trap-prone form is exactly what the scaffolded
template CLAUDE.md instructs.

A leading dash can never be a real directory argument, so resolveProjectArgs
now reclaims the eaten token as the flag the user wrote (--check / --json),
falls back to the current directory, and drops unrelated eaten flags rather
than treating them as paths. Templates and skill references switch to the
explicit-dir form ('--project .'), which behaves correctly on every release
including ones that predate this fix.

* feat(skills): report a successful pin bump in the run summary

Review follow-up on the stale-pin rule: 'hyperframes check' validates
composition structure, not render-output equivalence, so a check-passing
bump can still shift a project's rendered output. The bump stays the right
default for stale projects, but it must not be silent — the summary now
names the old and new version so the user knows the reproducibility
trade was made.
2026-07-16 22:28:11 +08:00
James Russo 1b4cf12cd3 refactor(cli): add stderr diagnostics logger + oxlint guard (#2551) 2026-07-16 09:35:27 -04:00
James Russo d92d1d4f51 fix(cli): keep diagnostics on stderr for --json commands (#2522) 2026-07-16 09:14:39 -04:00
Vance Ingalls ff26e5f2c8 Merge pull request #2529 from heygen-com/via/resolution-portrait-fix
fix(cli): accept portrait aspects for --resolution alias flag
2026-07-16 01:54:55 -07:00
ViaandClaude 2d398ed274 fix(cli): wire aspect-agnostic resolution through cloudrun/lambda/batch + preflight recompute
Addresses R2 CHANGES_REQUESTED from Miga + Rames on PR #2529:

1. Sibling-surface gap (blocker): `hyperframes cloudrun render{,-batch}`,
   `hyperframes lambda render{,-batch}` all advertised the same tier-only
   aliases (`1080p` / `hd` / `4k` / `uhd`) but normalized them to `landscape`
   and never set `outputResolutionAspectAgnostic`. The distributed plumbing
   PR #2529 added received `undefined` from those callers, so portrait `1080p`
   still hit the original aspect-mismatch on Cloud Run / Lambda.

   Fix: introduce `resolveResolutionFlagPair` in `@hyperframes/parsers` (the
   single source of truth for the two-step normalize + aspect-agnostic
   detect) and route every distributed entrypoint through a shared
   `parseOutputResolutionFlag` CLI util so the alias signal now reaches
   `SerializableDistributedRenderConfig`. Studio Server keeps its
   canonical-only HTTP contract; that intent is now pinned in tests.

2. Preflight recompute (hardening): the earlier "downgrade aspect-mismatch"
   preflight cleared un-remapped mismatches, so IG 4:5 (non-preset aspect,
   no sibling) and portrait-4K comp + `--resolution 1080p` (remap +
   downsample) both slipped through to fail late in `resolveDeviceScaleFactor`.
   Now `checkRenderResolutionPreflight` computes the effective preset via
   `suggestMatchingPreset` (mirroring the compile stage's
   `adaptAspectAgnosticResolution`) and re-checks against that — only
   genuinely-fixable mismatches clear early. New tests pin both regressed
   input classes.

3. Docker forwarding boundary test (Miga's important #2): pinned
   `1080p` survives verbatim as `--resolution 1080p` in the Docker args
   so the in-container CLI can re-run `isAspectAgnosticResolutionAlias`.

4. Doc-nit (Miga): parsers/src/types.ts no longer references the
   nonexistent `resolveResolutionForComposition` — points at the actual
   remap helpers.

Fallow: cloudrun.ts / lambda.ts share 390 lines of pre-existing structural
symmetry (parallel AWS + GCP dispatchers), and lambda/render.ts +
render-batch.ts declare parallel RenderArgs interfaces. Both re-flagged
after threading the aspect-agnostic field through each surface; ignored
with justification in .fallowrc.jsonc. lambda.ts's `run` and
lambda/render.ts's `waitForCompletion` are pre-existing CRAP-score
hotspots untouched by this PR — added under health.ignore.

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

— Via
2026-07-16 08:01:56 +00:00
Vance Ingalls 3e4c761842 Merge pull request #2527 from heygen-com/via/artifact-validated-exit-gate
fix(cli): don't override exit code after artifact validated
2026-07-16 00:35:30 -07:00
ViaandClaude f3b941f1e3 fix(cli): reduce CRAP score by extracting exit-path helpers; bump contactSheet Windows timeout
Fallow flagged the `uncaughtException` arrow at CRAP 30.0 (right at
threshold). Extracting the three exit paths — emitCliErrorEvent,
reportPostRenderTerminationEvent, exitAfterPostRenderTermination,
exitAfterCliFailure — pulls the two `_flushSync?.()` optional-chain
branches out of the arrow body and drops cyclomatic to 3. Same shape
already used on compileStage in a sibling PR. Behavior preserved:
EPIPE → exit(0), renderSucceeded → report + flush + exit(0),
default → commandFailed + track + flush + exit(1) for uncaught;
renderSucceeded → report + return, default → commandFailed + track
for rejection.

Windows contactSheet flake: `createContactSheet > writes PNG output`
timed out at 20025ms (default ceiling 20000ms) on Windows. Test does
milliseconds of real work — the wall-clock overhead is sharp's native
binary fork under runner I/O jitter. PR #2492 already tried lightening
the test; the flake persists at the default ceiling. Bumping the
per-test timeout to 60s absorbs runner jitter without hiding real
slowdowns (a genuine sharp regression would blow through 60s just as
readily as 20s). Targeted per-test bump, not a global suite bump.

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

— Via
2026-07-16 07:08:45 +00:00
ViaandClaude 46e9ecf3f2 fix(cli): accept portrait aspects for --resolution alias flag
The aspect-agnostic resolution aliases (`--resolution 1080p` / `hd` / `4k` / `uhd`) previously all normalized to a landscape preset, which rejected portrait 1080x1920 compositions with 'Output resolution incompatible'. Users had to specify the orientation-bearing alias (`1080p-portrait`) or render at native.

This threads two new fields (`outputResolutionAspectAgnostic` + `outputResolutionRaw`) through the render pipeline. At the CLI layer we detect whether the user's flag was an aspect-agnostic alias; at the compile stage we re-map the preset to the composition's orientation via the existing `suggestMatchingPreset` sibling-lookup (formerly private). Explicit orientation-bearing aliases and canonical presets stay strict.

Field signal: ts=1784176662 (darwin/arm64, CLI 0.7.59, `--resolution 1080p` on a 1080x1920 portrait comp).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

— Via
2026-07-16 06:23:14 +00:00
ViaandClaude 25a04d7fc8 fix(cli): don't override exit code after artifact validated
The render command's post-artifact-validated cleanup (telemetry flush,
feedback prompt, worker/browser teardown, stray promise rejections) can
throw AFTER the producer has committed a valid MP4 to disk. Field signal
ts=1784169760, ts=1784171150, ts=1784172467 (all win32/x64, CLI 0.7.58,
ffmpeg=no, 1080x1920): ffprobe + visual QA confirmed the outputs are
valid, but the CLI exited 1 after the terminal "artifact validated" log
with no final error message.

Introduce a `renderSucceeded` sentinel that flips after `executeRenderJob`
(or the Docker child render) resolves cleanly. From that point on:

  - Post-render steps in the render command (trackRenderMetrics,
    printRenderComplete, warnIfWebmAlphaDropped, maybePromptRenderFeedback)
    run through `runPostRenderStep`/`runPostRenderStepAsync` guards that
    swallow throws, log a compact warning to stderr, and sanitize a stray
    `process.exitCode` back to 0.

  - The CLI's top-level `uncaughtException` handler logs the throw for
    diagnosis but exits 0 instead of 1 when the render already succeeded.

  - The CLI's `unhandledRejection` handler stops flipping `commandFailed`
    (which drove the success:false telemetry field) when the render
    already succeeded.

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

🤖 Generated with [Claude Code](https://claude.com/claude-code)

— Via
2026-07-16 05:56:35 +00:00
ViaandClaude Opus 4.7 f8210d96da feat(cli): configurable transcribe timeout with duration-scaled default
Adds a `--timeout <ms>` CLI flag (and `HYPERFRAMES_TRANSCRIBE_TIMEOUT_MS`
env var) plus a model-slowdown factor in the auto-scaled default so
`hyperframes transcribe` doesn't hard-fail with `spawnSync ETIMEDOUT`
on slow CPUs running heavier whisper models.

Field-signal ts=1784165471 (win32/arm64 emulating x64 on Snapdragon,
CLI 0.7.59) reported the failure on a 63s wav with `-m medium` at ~13x
realtime — the historical 10x-realtime scale (PR #2463) gave 10.5 min
while the machine needed ~13.7 min. Splitting audio and merging offsets
was the manual workaround.

- Add `--timeout <ms>` and `HYPERFRAMES_TRANSCRIBE_TIMEOUT_MS` (min 5000).
  Explicit override bypasses auto-scaling; still capped at 12h.
- Add per-model slowdown factor (tiny 0.5, base 0.7, small 1, medium 2,
  large 4, large-v3-turbo 2). Multiplied into the 10s/audio-second
  baseline so medium/large get proportional headroom while `small.en`
  (the default) preserves the historical safety window.
- Wrap whisper's spawn error with a discoverability hint naming
  `--timeout`, the env var, and the effective timeout when the child
  was killed by SIGTERM/ETIMEDOUT (mirrors PR #2504 protocol-timeout).
- Docs: new `--timeout` row in `docs/packages/cli.mdx` Flags table.

Regression coverage in `packages/cli/src/whisper/transcribe.test.ts`
(56 tests) and `packages/cli/src/commands/transcribe.test.ts` (5 tests):
- Model factor per known name + case-insensitive + safe unknown fallback.
- 63s field-signal case on medium.en → 1_260_000ms (was 630_000ms).
- Explicit override honored below the auto floor + capped at 12h.
- Model factor ignored when overrideMs is set.
- SIGTERM/ETIMEDOUT detection + augmented message contract.
- CLI rejects below-minimum `--timeout` with error naming both the flag
  and the 5000ms floor.

— Via

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

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-16 05:49:46 +00:00
Miguel Ángel 2e8f871bc8 fix(video): hold final frame through composition 2026-07-16 02:15:01 +00:00
Miguel Ángel 3a71a03de5 fix(skills): require actionable CLI feedback repros (#2498) 2026-07-15 18:14:06 -04:00
James Russo 2417293dab fix(studio): enforce optimistic file concurrency (#2156)
* fix(studio): enforce optimistic file concurrency

* fix(studio): harden conditional file writes

* fix(studio): honor explicit file preconditions

* test(producer): allow zero-ms encode timing
2026-07-15 18:07:04 -04:00
Miguel Ángel 35e623b4f3 fix(cli): align video output boundaries (#2490)
* fix(cli): align video output boundaries

* test(cli): honor CI ffmpeg fixture path

* test(cli): decouple duration precedence from ffmpeg

* test(cli): pin half-open video boundaries

* test(producer): refresh style 7 boundary golden
2026-07-15 16:07:44 -04:00
Miguel Ángel 9e53c0f9b1 fix(cli): preserve snapshot timestamp precision (#2494) 2026-07-15 15:54:27 -04:00
James Russo dbdf03cd6b fix(ci): keep contact sheet test lightweight (#2492) 2026-07-15 14:38:57 -04:00
WaterrrForever c8d13af9b2 docs(registry,skills): surface code-highlight 0-based indexing and opacity-reveal sweep guidance (#2418)
* docs(registry,skills): surface code-highlight 0-based indexing and opacity-reveal sweep guidance

From the 2026-07-14 CLI feedback digest (skills-owner action): a user
building code teaching videos hit two authoring gaps.

1. code-highlight's `line` is intentionally zero-based (`line: 1` =
   second displayed line) but the warning lived only in pr-to-video's
   code-vocabulary reference — nowhere an author actually touches the
   value. Call it out at the block-use sites: the `__BLOCK` declaration
   itself, the registry-item description, and the motion-graphics
   catalog map.

2. Opacity-only code-typing tripped `sweep_static` for that user, who
   worked around it with a slow host y-drift. The sweep fingerprint
   does include per-element opacity, so document the actual trap (a
   reveal that settles before the sampled window, then holds a static
   frame) and the idiomatic fixes (spread the reveal / keep a blinking
   caret alive) in the check reference — and pin the fingerprint's
   opacity sensitivity with a regression test covering both the
   visibility-floor crossing and a mid-fade value change.


* docs(catalog): regenerate code-highlight page from updated registry-item description

Only the code-highlight page is committed: a full generate-catalog-pages
run also surfaces ~34 blocks missing from the git-tracked catalog index
(pre-existing drift on main), which belongs in its own chore PR.
2026-07-16 00:27:18 +08:00