Skip to content

feat(cli): check --budget stops the whole check in time and reports what it found - #7245

Merged
miguel-heygen merged 3 commits into
mainfrom
fix/check-budget
Nov 5, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
fix/check-budget

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

hyperframes check --budget <ms|90s|8m> stops the whole check that long after the command starts, keeping 10 s back to write the report, and prints the report with what it found so far. A check_budget_reached error names the phase it was in and the seconds spent in each phase. Video copies get only what is left of the budget when that is under their own 2 minutes. Without --budget nothing changes.

Why

A host with its own time limit (Desktop allows a check 8 minutes from the moment it is asked) gets no report when that limit is hit. The CLI cannot answer the host's stop: on Windows the stop is taskkill /F, and a host that is stopping a command does not read what it printed. The check already ends itself after 2 minutes with no progress and reports its partial results then, but a check that keeps making progress runs until the host kills it. Only the CLI can stop in time on every OS, so it needs to be told how much time it has.

A 22 s project with live WebGL canvases and per-frame SVG displacement: the full check takes 71 s on a 32-core Linux box, 56 s of it in the craft pass, and each --range run takes 6 s because --range skips craft. On a slower Windows machine with time already spent in a queue, that can run past the host's limit.

How

CheckOptions.budget is { ms, stopAt }, with stopAt set when the flag is parsed, so time spent waiting for the project lock counts. CheckDeadline takes the budget beside its idle window and expires at stopAt with CheckBudgetError, through the same path an idle expiry takes: deadline.run rejects, the partial results the passes keep are merged into the report, and the browsers are closed. Its phase summary (where()) is shared with the idle message, whose text does not change. runBrowserCheck gives the copies min(2 minutes, remainingMs()). Crops are skipped once the deadline has expired, as finding frames already were, so an expiry is reported once; both read one isCheckExpiry test. The expiry promise is marked handled, so a budget that runs out before any step is waiting (a long wait for the project lock) is not an unhandled rejection. parseTimeoutMs takes the flag name for its warnings.

Test plan

  • Unit tests added/updated:
    • fails without the fix: check.test.ts "--budget ends a check that is still within its idle window and keeps what its finished samples found": idle window 410 s, budget at 60 s; the report has check_budget_reached naming the overlap pass and per-phase seconds, ok: false, and the finished samples' layout finding. It fails with the budget not passed to the deadline.
    • fails without the fix: check.test.ts "--budget sets when the whole check stops, its report time kept back, counted from the command's start": --budget 8m gives ms 480000 and stopAt 470 s from the start; no flag gives none. It fails with the option not parsed.
    • fails without the fix: checkBrowser.test.ts "gives the copies only what is left of the check's --budget when that is under the copy budget": a copy that never finishes is stopped at 30 s with 30 s of budget left, not at 2 minutes. It fails with the copies given their full budget.
    • check.test.ts, checkBrowser.test.ts, checkDeadline.test.ts, timeoutArg.test.ts and checkPipeline.reportingProgress.test.ts (256) pass three runs in a row. tsc on the CLI: no errors.
  • Manual testing performed: the built CLI, check --json --budget 40s on the 22 s WebGL and SVG project above (71 s without it): it exits at 30.9 s with the full JSON report, check_budget_reached "while running the overlap pass (craft frame at 10.375s)", seconds per phase, and the layout finding already made.
  • Documentation updated (if applicable): the check flag table in docs/packages/cli.mdx.
  • Comments follow CONTRIBUTING.md "Comments": no new comments, so no file's comment share rises. stopAt is a Date.now() time with the 10 s report margin already taken off; remainingMs() is undefined without a budget.

Not handled here: Desktop passing --budget with what is left of its own limit is the next change, on the Desktop side.

Size

About 55 lines of source in six files, about 110 of tests and one doc row. One change: check can be told its whole budget.

@mintlify

mintlify Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

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

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Nov 5, 2026, 2:16 PM

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

@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 113 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)

@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: REQUEST CHANGES · 2 blocker · 0 important · 0 nits · head d438a69e

The design holds up: the budget expires through the idle-expiry path, partial results are kept, and the copies get min(2 min, remaining). CI is red for two reasons caused by this diff:

  • blocker: unhandled rejection from the budget timer. ↓ inline
  • blocker: Fallow audit fails: withOptionalFindingFrames (checkPipeline.ts:1863) now has a CRAP score of 31.6, over the 30 threshold, because of the third instanceof in its catch. One fix is to move CheckDeadlineError | CheckStoppedError | CheckBudgetError into a shared isCheckExpiry(error) helper that withFindingCrops also uses.
Scope

Read all 6 source files at head, plus CheckDeadline and how runCheckPipeline calls it. No earlier reviews. CI run 42038862063: Test (cli 2/2), Flake gate (checkBrowser.test.ts failed 10/10) and Fallow audit failed; Comments, Docs, regression and preview-regression passed.

Comment thread packages/cli/src/utils/checkDeadline.ts Resolved
@mintlify

mintlify Bot commented Nov 5, 2026

Copy link
Copy Markdown

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

Project Status Preview Updated
hyperframes 🟡 Building – Nov 5, 2026, 1:47 PM

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

@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Both fixed at 915b0c8:

  1. Unhandled rejection: the expiry promise is marked handled in the constructor, as suggested; run() still races it. With that line removed, the copies test reports vitest's Unhandled Errors again; with it, checkBrowser.test.ts, check.test.ts and checkDeadline.test.ts (248) pass three runs in a row with no unhandled errors.
  2. Fallow: isCheckExpiry(error) is shared by withFindingCrops and withOptionalFindingFrames. fallow audit --fail-on-issues against the merge base: no issues.

@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 · 2 resolved, 0 open, 0 new · d438a69 → 915b0c8

CI note

The checks this diff could affect all pass in both CI runs (Flake gate, Test cli 1/2 and 2/2, Fallow audit). The remaining red is unrelated to the change: Catalog payloads failed bun install 3 times, Preview parity failed downloading ffmpeg-static, and Studio: edit accuracy gate failed on seqplainkeys-none-px-r0-root-z100 in run 42042798353 but passed in run 42042800367 on the same head.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit b7d72c9 Nov 5, 2026
202 of 210 checks passed
@miguel-heygen
miguel-heygen deleted the fix/check-budget branch November 5, 2026 15:05
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 6, 2026
…eview

Adopts or partially adopts 11 groups: multi-worker tween replay behind
the timeline stand-in (heygen-com#7231), template entries in the shared compile
stage (heygen-com#7217), HYPERFRAMES_CACHE_DIR and HYPERFRAMES_CONFIG_DIR for
every per-user cache and settings file (heygen-com#7224, heygen-com#7229; HeyGen
credentials stay under HEYGEN_CONFIG_DIR), WebKit audio clock (heygen-com#7225),
packet-based video length (heygen-com#7233), HLG tone mapping (heygen-com#7234), fetch
network cause (heygen-com#7236), .hyperframesignore in lint (heygen-com#7239), info
--media (heygen-com#7235), lint priority (heygen-com#7244), check --budget (heygen-com#7245) and the
unknown-command answer (heygen-com#7247). bun.lock gains only the lint `ignore`
edge from heygen-com#7239, already resolved. Watermark advances to 0a6746e.

Co-authored-by: Jerrai <[email protected]>
Co-authored-by: Miguel Ángel <[email protected]>
Co-authored-by: WaterrrForever <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>

This branch was successfully deployed

1 active deployment
staging - docs — 915b0c83 Deployed Nov 5, 2026 by mintlify[bot]
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.

1 participant