Skip to content

fix(cli): Studio renders take the same one-render-slot lock as the CLI - #7459

Merged
miguel-heygen merged 2 commits into
mainfrom
dexportfix/studio-render-lock
Nov 9, 2026
Merged

miguel-heygen merged 2 commits into
mainfrom
dexportfix/studio-render-lock

Conversation

@miguel-heygen

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

Copy link
Copy Markdown
Collaborator

What

Studio's render route now takes the CLI's per-account render slot before it runs a render, and releases it when the render completes, fails or is cancelled.

Why

The CLI runs one local render at a time per user account, but Studio's route called the producer directly, so Studio renders ran side by side with each other and with CLI renders, each sizing its workers to the whole machine.

Related work

Fixes #7457.

How

acquireRenderLock in utils/renderLock.ts already owns the slot policy (the HYPERFRAMES_RENDER_LOCK=off switch, the HYPERFRAMES_CACHE_DIR location, stale-holder takeover, unwritable-folder errors). The CLI's acquireRenderSlot only adds console text around it, so Studio calls acquireRenderLock directly and no second copy of the policy exists. While it waits the job's stage reads "Waiting for another render ()"; Cancel passes the job's abort signal, so it ends the wait and the job reads as cancelled. A stale-break or unwritable error fails the job with the lock's own message.

Test plan

fails without the fix: packages/cli/src/server/studioServer.renderLock.test.ts "makes a second render wait, and say so, until the first ends" and "lets Cancel end a wait without freeing the slot the first render holds" fail on main's studioServer.ts (both renders run at once and no stage says "Waiting for another render"); with the fix 4 of 4 pass. The "until the first ends" test also fails if the wait words are left on the job after the slot is taken.

  • Unit tests added: studioServer.renderLock.test.ts (second render waits and says so until the first ends; Cancel while waiting leaves the first render's slot held; a failed render frees the slot; HYPERFRAMES_RENDER_LOCK=off runs both side by side). 4 pass, 3 runs in a row. With the old studioServer.ts restored, the first two fail.
  • studioServer.test.ts (44) and studioServer.renderBrowser.test.ts (5) still pass; oxlint and oxfmt are clean on the changed files.
  • Manual testing performed: not done; no real Studio render was run against a second process.
  • tsc --noEmit reports 5 errors, all missing @hyperframes/inspector/live (a package not built in my checkout); none in the changed files.

@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.

Requesting changes at c1d2da9c for one reason: the PR body is missing the required line.

Blocker: this is a fix PR (fix(cli), Fixes #7457), so its body needs a fails without the fix: line that names a test. The Test plan says "With the old studioServer.ts restored, the first two fail", but it doesn't say which tests or put them on that line. For example:

fails without the fix: packages/cli/src/server/studioServer.renderLock.test.ts "makes a second render wait, and say so, until the first ends" and "lets Cancel end a wait without freeing the slot the first render holds" fail on main's studioServer.ts (both renders run at once and no stage says "Waiting for another render"); with the fix, 4 of 4 pass.

The code looks correct (no blocker):

  • acquireRenderLock is called after the producer loads and the browser resolves, and before executeRenderJob. The release is in finally, so the slot is freed on complete, fail and cancel.
  • Cancel while waiting: pause rejects with the signal's reason. The existing catch sees signal.aborted and marks the job cancelled instead of failed. RenderLockUnwritable and stale-break errors reach state.error with the lock's own message.
  • Calling acquireRenderLock directly keeps a single copy of the HYPERFRAMES_RENDER_LOCK=off and cache-dir policy. Two Studio renders in the same process still queue, because the holder's pid is alive and isn't treated as stale.

Optional (not blocking): after the slot is acquired, state.stage keeps showing "Waiting for another render (…)" until the producer reports its first currentStage. Clearing it, or setting a "Starting render" stage, right after acquireRenderLock returns would stop the UI from looking stuck behind a render that has already finished.

CI at this head: regression, Player perf, PR captures and Comments passed. CI, CodeQL and preview-regression were still running, and Windows render verification was queued.

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

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

Approving at 622a1eb1. Both review points are resolved.

  • Blocker resolved: the fails without the fix: line now names the file and the two tests ("makes a second render wait, and say so, until the first ends" and "lets Cancel end a wait without freeing the slot the first render holds").
  • Optional point addressed: state.stage = undefined right after acquireRenderLock returns clears the wait text as soon as the slot is taken. The new assertion in the first test checks this with a mocked producer that never reports a stage, so the assertion would fail without that line. packages/cli/tsconfig.json doesn't set exactOptionalPropertyTypes, so assigning undefined type-checks.
  • The interdiff from c1d2da9c is just these two lines plus the assertion. The rest of the change is what I reviewed before and has no blocker.

CI at this head: regression, Player perf and PR captures passed. CI, CodeQL, Comments, preview-regression and Windows render verification were still running. Merge only after they're green.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit 4b3f70f Nov 9, 2026
88 of 89 checks passed
@miguel-heygen
miguel-heygen deleted the dexportfix/studio-render-lock branch November 9, 2026 21:37
@miguel-heygen miguel-heygen mentioned this pull request Nov 9, 2026
3 of 4 tasks
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 9, 2026
…heygen-com#7456 and the heygen-com#7439 docs

heygen-com#7459: Studio renders take the same one-render-slot lock as the CLI and say
they are waiting; Cancel ends the wait.
heygen-com#7463: deleting an animated group parses its script once instead of once per
tween.
heygen-com#7454: seam-gate awaits the async player seek (both skill mirrors).
heygen-com#7456: the engine browser test project gets 30 s per test.
heygen-com#7439: current ChatGPT and Claude setup steps in the MCP guide.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
SanHsien added a commit to SanHsien/hyperframes that referenced this pull request Nov 9, 2026
…ve more; advance watermark

Twelve commits, PRs 7453-7468, issues 7457-7465 and eleven pin moves are
recorded with their evidence in the continued ledger, DECISIONS and UPSTREAM.
Adopted: heygen-com#7338, heygen-com#7459, heygen-com#7463, heygen-com#7454, heygen-com#7456 and the heygen-com#7439 docs; heygen-com#6732 was
already ported. Deferred: heygen-com#7455, heygen-com#7437. Not applicable: heygen-com#7453, heygen-com#7269, heygen-com#7464.
The watermark moves to 9e346f9, PR heygen-com#7468, issue heygen-com#7465; four new PRs are
pinned and the eleven moved pins re-pinned at their current heads. FORK.md
records the fork-only differences in the heygen-com#7338 port; the roadmap tracks the
standalone scripts that keep a bare mkdtemp and issue heygen-com#7458.

Co-Authored-By: Claude Sonnet <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.

Studio bypasses HyperFrames existing cross-project render lock.

1 participant