Commit Graph
1072 Commits
Author SHA1 Message Date
Miguel Ángel 8e50e8477f fix(cli): skip contrast for intentionally covered text (#2366) 2026-07-13 18:22:30 -04:00
Vance Ingalls 56dcc4e47a fix(cli): accept 1–3-frame short WebMs + add direct pixel-probe tests
Address Miguel's R1 blocker + Rames/Miga's testability nit:

- The `-frames:v 3` decode samples AT MOST 3 frames; a legitimate 1- or
  2-frame WebM (256 or 512 bytes) was returned as `undefined` (probe
  failure), silently skipping the advisory even when every available
  pixel was opaque. Accept any positive whole-frame byte count ≤ 768
  (multiples of 256), distinguishing successful short-EOF from partial/
  malformed decode.
- Export `sampledAlphaIsFullyOpaque` and add 11 direct tests covering:
  3/2/1-frame opaque decodes → true; transparent pixel at pos 0 or
  final byte → false (guards the alpha-byte stride); non-frame-multiple
  / over-3-frame / zero byte counts → undefined; execFileSync throw →
  undefined; findFFmpeg missing → undefined; and one args-shape guard
  pinning the load-bearing `-c:v libvpx-vp9` before `-i` (without which
  the default decoder silently discards VP9 alpha and the whole check
  would false-positive on genuinely-transparent WebMs).
- Update advisory wording from "3 sampled decoded frames" to "every
  sampled decoded pixel" so the message is honest for short WebMs.

18/18 tests pass locally under `vitest run`.
2026-07-13 22:03:11 +00:00
Miguel Ángel 74fb4dbcc3 docs(cli): sync SRT upload docs from EF (#2364) 2026-07-13 17:51:25 -04:00
Vance Ingalls bd8ee05ae2 fix(cli): label the batch asset-import RENDER_FAILED with the images endpoint
Review feedback on #2358: the batch-miss RENDER_FAILED in
runAssetImportMany (asset.ts) throws the same typed error as
client.ts's single-node renderNode, but wasn't labeled — so
cli_error.endpoint would silently come back undefined for the
flow that most heavily exercises /v1/images.
2026-07-13 21:41:55 +00:00
Vance Ingalls 4ca1552e20 fix(core,cli): attribute figma REST failures to the endpoint that failed
cli_error had no way to tell which figma REST call (images, files_nodes,
variables_local, styles, ...) actually hit RATE_LIMITED/FORBIDDEN/etc, so
the dashboard could see failures spike but not which call caused them.

FigmaClientError now carries a low-cardinality endpoint label (never the
raw fileKey/nodeId), threaded through to cli_error's endpoint property.
2026-07-13 21:41:55 +00:00
Vance Ingalls b8562c91d5 fix(cli): detect WebM alpha lost when the ALPHA_MODE tag is written without side data
Extends webmAlphaCheck.ts (from #2044) with a pixel-level decode probe.
After the tag check passes, decodes 3 sampled frames via
`ffmpeg -c:v libvpx-vp9 -pix_fmt rgba -f rawvideo` at 8x8 and emits a
distinct advisory if every alpha byte reads 255.

#2044 detects the "tag absent" failure mode (ffprobe shows no
`alpha_mode` in stream tags). It doesn't catch a stricter case reported
on CLI 0.7.56 / Windows 11: ALPHA_MODE=1 present but BlockAdditional
alpha side data empty. Under current logic webmAlphaAdvisory sees
`alphaMode: true` and stays silent, so the render ships as opaque
without any signal.

The -metadata:s:v:0 alpha_mode=1 push is a muxer directive that some
ffmpeg builds write unconditionally, independent of whether libvpx-vp9
emitted the alpha plane. Tag presence is necessary but not sufficient
evidence of preserved alpha.

Advisory text names both possibilities (opaque composition OR silent
alpha drop) plus the concrete workaround (png-sequence + prores repack).
Fast path (no tag or missing tag) is unchanged. Probe adds ~1s per
WebM render only when the tag says alpha.
2026-07-13 21:38:12 +00:00
Vance Ingalls 9b71bc2103 Merge pull request #2361 from heygen-com/via/flake-fix-manager-progress-notice
test(cli): stabilize flaky install-lock wait-notice test with fake timers
2026-07-13 14:37:40 -07:00
Vance Ingalls e47af77c5a test(cli): stabilize flaky install-lock wait-notice test with fake timers
Adopt the fake-timer pattern the sibling "recovers when a crashed reclaimer
leaves both lock directories" test in the same file already uses. Without
fake timers, if the dynamic `import("./manager.js")` beat between
`installFsMocks({ initialMtimeMs: Date.now() })` and the `withInstallLock`
call exceeds `staleMs` (50 ms on a busy shared runner with `vi.resetModules()`
per beforeEach), the new immediate-stale short-circuit added in #2328 fires
on iteration 1, breaks out before `waitedMs` reaches `waitNoticeMs=20`, and
no "Waiting for another hyperframes process" warn ever emits — the assertion
at `manager.test.ts:428` (`expected false to be true`) then fails.

Under fake timers, `Date.now()` is frozen at the mtime seed, so the lock
stays non-stale across the dynamic import and the wait-notice branch
observes real polling; `vi.advanceTimersByTimeAsync(staleMs + pollMs * 5)`
then drives the loop past both the wait-notice threshold and the stale
deadline so the reclaim + acquisition still resolves.

Test-only change; no production-code diff. Verified 27/27 in
`packages/cli/src/browser/manager.test.ts` under `vitest run`.
2026-07-13 21:30:44 +00:00
Xuanru LiandClaude Fable 5 3e3b37d37f fix(cli): occlusion-probe false positives — pointer-events blindness, low-alpha gradients, not-yet-entered text (#2357)
* fix(cli): three occlusion-probe false-positive sources in text_occluded

- pointer-events:none text is invisible to elementFromPoint, so the probe
  always hit whatever paints beneath and misread visible text as buried;
  restore hit-testing on the element for the duration of the probe
- a backgroundImage counted as opaque regardless of alpha, so a 4%-alpha
  grid/scrim gradient qualified as an occluder; gradients now occlude only
  when their colours reach alpha > 0.6 (url() images unchanged)
- a visible container whose every text-bearing descendant is still at
  opacity 0 (entrance not started) was probed anyway; skip when no text
  ink is on screen

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

* fix(cli): address review — document-wide hit-testing restore, gradient compositing, whitespace ink

- restore hit-testing for ALL pointer-events:none elements during the text
  audit pass (not just the probed text): an occluder that itself carries
  pointer-events:none is invisible to elementFromPoint, which made truly
  buried text read as clean once the text alone became hittable
- hasVisibleTextInk ignores whitespace-only text nodes (indented markup
  defeated the gate) and uses a 0.05 floor so mid-fade text keeps its
  persistence occurrences
- hasOpaqueBackground composites gradient alpha with background-color
  (two 0.5-alpha layers paint at ~0.75); gradientMaxAlpha returns opaque
  for any colour function it cannot score (oklch/lab/...); percentage
  alpha values now parse as fractions

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

* fix(cli): walk the elementsFromPoint stack in occluderAt

A transparent layer that becomes hittable (pointer-events restored) must
not mask an opaque occluder painting beneath it — single-point
elementFromPoint returned the transparent top and dropped two genuinely
buried cases in the census acceptance run; the stack walk keeps 10/10.

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

* fix(cli): composite stacked background-image layers when judging occluder opacity

Two 0.5-alpha gradient layers paint at 0.75 combined; taking the max
color-stop alpha across the whole declaration under-counted them and
suppressed real text_occluded findings. Split layers at top-level commas
(paren-aware), score each, composite as 1-prod(1-a_i). Also pins the
0.05 text-ink floor with a boundary test (review feedback on #2357).

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

* fix(cli): keep walking the occlusion stack past pair-specific exemptions

sharedPreserve3d and isCrossSceneTransitionOverlap excuse one hit, not
the whole probe; returning null let a transparent decorative layer in
the text's 3D context mask a real occluder below it (review feedback).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 14:27:39 -07:00
Miguel Ángel 81a6edcb67 fix(browser): recover abandoned install locks (#2328) 2026-07-13 16:52:04 -04:00
Miguel Ángel 648e4e8c54 fix(cli): declare Apache-2.0 license (#2351) 2026-07-13 14:56:51 -04:00
Miguel Ángel 758a6d2152 test(ci): stabilize Windows suites after Studio cutover (#2320)
* test(cli): isolate browser check bundling

* test(studio): align timeline merge regressions
2026-07-13 03:24:02 -04:00
Miguel Ángel 94c2e0f6eb chore: release v0.7.56 2026-07-13 07:04:42 +00:00
Miguel Ángel 796d5df156 fix(render): allow explicit entry without index (#2307)
* fix(render): allow explicit entry without index

* fix(render): unify explicit composition handling

* test(cli): allow explicit render integration under CI load
2026-07-12 23:01:27 -04:00
Miguel Ángel 38badff820 fix(render): scope strict lint to composition (#2261)
* fix(render): scope strict lint to composition

* fix(lint): validate explicit composition scope
2026-07-12 22:18:11 -04:00
Miguel Ángel 44653b31da fix(cli): detect video motion in sweep guard (#2308)
* fix(cli): detect video motion in sweep guard

* docs(cli): clarify iframe fingerprint scope
2026-07-12 22:17:49 -04:00
Miguel Ángel b861afe454 fix(check): snapshot restored text after contrast audit (#2306) 2026-07-12 22:17:46 -04:00
Vance Ingalls c830aa83c5 chore: release v0.7.55 2026-07-12 11:52:53 -07:00
kiritowoo ccf5f20b3b fix(cli): stop losing render telemetry to the exit race (#2105) 2026-07-12 21:05:20 +08:00
James RussoandClaude Opus 4.8 f1f317a20c fix(cli): grant s3 encryption actions in lambda policies user output (#2289)
`hyperframes lambda deploy` runs `sam deploy --resolve-s3`, and SAM's
managed artifacts bucket (aws-sam-cli-managed-default) is created with
default SSE encryption. Setting that requires s3:PutEncryptionConfiguration,
which the generated deploy policy did not grant, so a first deploy by a
user provisioned exactly per `lambda policies user` 403s on the bucket and
the managed stack rolls back.

Add s3:GetEncryptionConfiguration and s3:PutEncryptionConfiguration to the
s3Bucket action set (Get pairs with Put for CloudFormation update/drift
reads, matching the existing Get/Put pairs in the list). Also add a hint to
the sam-deploy failure path pointing at the ROLLBACK_COMPLETE recovery step,
since first-time users hit the stuck-rollback error on their retry.

Fixes #2137

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 21:36:17 -07:00
Vance Ingalls ae4946e624 chore: release v0.7.54 2026-07-11 17:15:24 -07:00
Vance Ingalls bd86fb803d Merge pull request #2266 from heygen-com/07-11-fix-cli-check-detect-invisible-text
fix(cli): flag text with an effectively transparent fill in check
2026-07-11 17:10:44 -07:00
Vance Ingalls 1a9c1b66b0 Merge pull request #2264 from heygen-com/07-11-fix-cli-localize-remote-fonts-in-audit-capture
fix(cli): localize remote fonts in snapshot/check capture to match render
2026-07-11 17:05:35 -07:00
Vance Ingalls fa902400c9 fix(cli): only exclude clipped text when a background actually paints the glyphs
Addresses review on #2266: the gradient-text exclusion was too broad — any
background-clip:text skipped the invisible-text check, so a broken/missing
gradient (clip:text with no image and a transparent background, which paints
nothing) went unreported. Now exclude only when a real background fills the
glyphs (background-image != none, or an opaque background-color). Expands the
test suite to the reviewer's full case set: direct transparent fill, inherited
transparent fill over an opaque child color, color:transparent fallback, opaque
baseline, gradient-over-real-background exclusion, broken-gradient still flagged,
and empty-text no-op.
2026-07-11 17:04:30 -07:00
Vance Ingalls a8fd8562a8 fix(cli): read effective text fill by property, not getPropertyValue
The layout-audit test mock returns computed styles as plain camelCase
properties (no getPropertyValue), so the invisible-text detector threw
'cs.getPropertyValue is not a function' and broke the whole audit in CI. Read
webkitTextFillColor/webkitBackgroundClip/backgroundClip by property to match
the rest of the script (works in a real browser too). Adds tests: flags
transparent -webkit-text-fill-color, ignores opaque color, ignores gradient
text (background-clip:text).
2026-07-11 17:00:40 -07:00
Vance Ingalls 9ae84c652c fix(cli): flag text with an effectively transparent fill in check
Wild report (5th in cluster, CLI 0.7.53): snapshots omitted all text while
check passed — text painting with a transparent -webkit-text-fill-color (which
overrides `color` for the glyph fill AND inherits, so a parent's transparent
fill silently blanks descendant text that has its own opaque `color`) renders
invisible, but every geometry/occlusion/contrast audit missed it. Contrast in
particular reads `color`, not the fill that actually paints, so white-`color`
+ transparent-fill text scored as high-contrast and passed.

Add an invisible-text detector to the layout audit: flag any text element whose
effective fill (computed -webkit-text-fill-color, which already resolves to
`color` when unset) is transparent. Gradient/clipped text (background-clip:text)
legitimately uses a transparent fill and is excluded. Verified: check now fails
on an inherited-transparent-fill fixture (text_not_painted) while gradient text,
body-inherited color, sub-composition color, and real registry examples stay
clean.
2026-07-11 16:52:24 -07:00
Vance Ingalls fc2b905837 fix(cli): distinguish producer-absent from injector failure in font localization
Addresses review on #2264: the localization helper had one broad catch around
both dynamic producer resolution and injector execution, so it couldn't tell a
benign 'producer not in this environment' from a real injector/fetch failure,
and emitted no diagnostic. Split into loadFontInjector() (returns null when the
module is absent — silent fail-open) and localizeWithProducer() (warns ONCE per
distinct message when the injector itself throws, then fails open). Per-family
resolution failures remain the injector's own responsibility (producer's
warnUnresolvedFonts). The localizer seam is injectable; tests now cover success,
producer-unavailable, injector-throw, warn dedup, and call-site integration.
2026-07-11 16:39:50 -07:00
Vance Ingalls aa10d49234 fix(cli): resolve producer font-localization at runtime so vitest transform doesn't fail
The CLI test job builds with --filter '!@hyperframes/producer', and render.ts
imports producer only as a type — so a static import("@hyperframes/producer")
in the font-localization helper failed Vitest's transform-time module
resolution ("Failed to resolve entry for package"), breaking checkBrowser
tests and the helper's own test. Keep the specifier out of the static module
graph (@vite-ignore + variable specifier) so it resolves at runtime only:
production/installed CLI has producer in node_modules and localizes fonts;
the test env fail-opens to the plain bundle. Localizer is now injectable so
the helper's unit tests cover it without needing producer resolvable.
2026-07-11 16:30:13 -07:00
Vance Ingalls f53a75bfe8 Merge pull request #2224 from heygen-com/parallel-streaming
feat(producer): stream parallel screenshot/beginframe capture to the encoder
2026-07-11 16:15:49 -07:00
Vance Ingalls ad0d2393fe fix(cli): localize remote fonts in snapshot/check capture to match render 2026-07-11 16:14:36 -07:00
vanceingalls 591235f05c feat(producer): emit eligible_off cohort-sizing signal when parallel-stream router is off 2026-07-11 22:38:04 +00:00
vanceingalls 88c21ccd8b feat(cli): capture_parallel_stream telemetry on success and failure paths 2026-07-11 22:37:25 +00:00
Vance Ingalls 35c231e35f Merge pull request #2255 from heygen-com/07-11-fix_cli_surface_video_frame-injection_failures_in_snapshot_output
fix(cli): surface video frame-injection failures in snapshot output
2026-07-11 15:36:41 -07:00
Vance Ingalls fecd7dc1d3 Merge pull request #2248 from heygen-com/bf-reuse-telemetry
feat(producer): surface beginframe no-damage reuse counters in perf summary and telemetry
2026-07-11 15:31:58 -07:00
Vance Ingalls edc0a1624b fix(cli): surface video frame-injection failures in snapshot output 2026-07-11 14:42:44 -07:00
Vance Ingalls 3338a6f350 fix(cli): fold canvas pixel hashes into the frozen-sweep fingerprint 2026-07-11 14:42:43 -07:00
Vance Ingalls 3dc08c0868 feat(cli): preview --browser-no-gpu for gpu-unstable hosts 2026-07-11 14:14:08 -07:00
Vance Ingalls 8bbd5a6496 fix(cli): settle post-seek font subset loads before audit screenshots 2026-07-11 14:14:08 -07:00
Miguel Angel Simon Sierra 3b081a44ae chore: release v0.7.53 2026-07-11 15:59:07 -04:00
vanceingalls dde3afb72e feat(producer): surface beginframe no-damage reuse counters in perf summary and telemetry 2026-07-11 19:44:24 +00:00
Miguel Ángel 87618eef4c fix(telemetry): expose stalled render stages (#2220)
* fix(telemetry): expose stalled render stages

* fix(telemetry): preserve capture data on terminal stage events

* fix(telemetry): fix calibration TDZ crash, tag encode/assemble, extend heartbeat cadence

capture_calibration referenced captureStageObservationData before its
declaration (later in the same scope), which would throw a ReferenceError
for any render hitting the calibration path. Hoist the closure and split
workerCount's declaration from its resolution so calibration can safely
read it as undefined before capture strategy resolves worker count.

Also address the two non-blocking review items: wire encode/assemble
stages through captureStageObservationData for consistent tagging, and
extend the heartbeat schedule to repeat every 120s after the initial
30/60/120s ramp instead of going dark on stalls beyond two minutes.
2026-07-10 23:59:08 -04:00
Miguel Ángel 5f22209a82 fix(cli): correct strict warning hint (#2221) 2026-07-10 22:57:29 -04:00
Vance Ingalls 7498eb4a3a chore: release v0.7.52 2026-07-10 19:47:59 -07:00
Miguel Ángel b95ddd74d5 fix(cli): honor direct entries in keyframe shots (#2217)
* fix(cli): honor direct entries in keyframe shots

* fix(core): rebase entry-authored asset paths for direct-entry bundling
2026-07-10 22:32:26 -04:00
Miguel Ángel de4e85add6 fix(skills): align core contract with check (#2218) 2026-07-10 21:37:35 -04:00
Vance Ingalls 392dd410a5 Merge remote-tracking branch 'origin/main' into de-parallel-router-failure-telemetry
# Conflicts:
#	packages/cli/src/telemetry/config.ts
2026-07-10 18:15:12 -07:00
Miguel Ángel 0e7f40dfc0 fix(lint): ignore macOS AppleDouble HTML files (#2191) 2026-07-10 20:27:24 -04:00
Vance IngallsandClaude Opus 4.8 b02703b7c9 fix(cli,producer): opt-in trial polarity + narrowed-fallback-flag docs (review)
Two non-blocking review notes from Rames, both addressed:

1. Trial polarity inverted to OPT-IN: disableDeParallelRouterTrial →
   enableDeParallelRouterTrial. renderLocal is exported, so any programmatic
   consumer (future studio-server path, test harness, distributed runner)
   previously inherited the trial and its process-wide env-var/module-latch
   state without knowing to disable it — and concurrent invocation races
   that state. Now only the CLI's own sequential call sites opt in (the
   single top-level render, and batch at concurrency 1); everyone else gets
   no trial by default. The doc comment names the sequential-invocation
   assumption explicitly.

2. deSelfVerifyFallback semantic narrowing documented at both declarations
   (RenderCaptureObservability + RenderPerfSummary.drawElement): since the
   pinned-fallback retry was widened, the flag means verify-triggered
   SPECIFICALLY — OOM/capture_error fallbacks report false with
   deFallbackReason carrying the reason. Dashboards keyed on
   de_self_verify_fallback=true as "any fallback fired" must migrate to
   de_fallback_reason IS NOT NULL (also called out in the PR body for the
   observability rebuild to pick up).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 17:24:13 -07:00
Miguel Ángel 598dd8b350 chore: release v0.7.51 (#2188) 2026-07-10 20:16:15 -04:00
Miguel Ángel b1f1c0571e fix(cli): make skills update converge on a skill retired upstream (#2176)
`hyperframes skills update` failed hard or looped forever once a skill was
retired/renamed upstream while still installed locally (hyperframes-media folded
into media-use; hyperframes-captions/compose/tts consolidated earlier). Two
paths dead-ended:

- Install: target selection could trust a stale local skills-manifest.json
  (findRepoManifest) while `skills add` always installs from the canonical repo.
  isCoreSkill matches the `hyperframes-` prefix, so a retired skill was forced
  into the target set, `skills add` silently declined it (exit 0), and strict
  verifyInstalled threw "Skill(s) still missing after install".
- Prune: upstream `skills remove` scans on-disk directories, so a lock entry
  retired before it ever shipped a bundle has nothing to match — a silent
  exit-0 no-op that never clears the lock, so detectRemoved re-flags it on
  every run.

The stale-skills nudge compounded it: it fired even from `skills update` itself
(pointing users back at the failing command) and its count ignored the removed
bucket.

Resolve update targets against the canonical manifest (checkSkills({ canonical:
true })) so a retired skill is never targeted. Add pruneOrphanedLockEntries to
clear the orphaned lock entries the upstream remover can't (idempotent, so a
second run is a clean no-op). Exclude `skills` from the update-nudge gate and
thread the removed count through the nudge total.
2026-07-10 19:57:55 -04:00