Skip to content

fix(core): settle slow documents against their own frame cadence - #5168

Merged
miguel-heygen merged 4 commits into
mainfrom
fix/core-document-idle-readiness
Oct 7, 2026
Merged

miguel-heygen merged 4 commits into
mainfrom
fix/core-document-idle-readiness

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

A steadily slow document no longer has to reach the paint wait's 1.5-second cap merely because its frames are more than 50 ms apart.

The shared core readiness input preserves the existing strict < 50 ms fast-page path. Slower gaps qualify when they are at most 1.5 times the shortest gap observed in the current readiness attempt. Two consecutive qualifying gaps are required; a stall resets the streak. The existing paint cap, shared timeout and cancellation behavior remain.

Media, compute, loader and generation checks retain their roles. Studio still promotes the picture and commits its timeline together. The same input serves the player, runtime-reported first-frame readiness and Studio's Play gate.

What I measured

Controlled Linux/Chromium fixture with sixfold CPU throttling, inline content and 60 ms of main-thread work per frame. The burst case adds one 240 ms workload before returning to the ordinary cadence. Timing begins before the first workload and includes scheduling. Each row has three runs. Measurements use main's core input and executable candidate 23b8c9c8; final cc07ff1a changes only comments and documentation. These are fixture measurements, not production latency estimates.

Fixture Main paint input Final branch paint input
Steady slow 1682 to 1701 ms 352 to 429 ms
Small workload jitter 1652 to 1703 ms 368 to 376 ms
Burst then recovery 1637 to 1686 ms 603 to 622 ms

Ordinary observed gaps were approximately 50 to 100 ms; the injected burst produced a 250 ms gap. Before implementation, I replayed 183 overlapping nine-gap windows that did not contain the injected burst:

Relative tolerance Windows still waiting after nine gaps Worst settlement among resolved windows
1.1x 2 500 ms
1.25x 2 500 ms
1.5x 0 350 ms
1.75x 0 233 ms

I chose 1.5x as the smallest tested tolerance that covered all ordinary windows. The windows overlap and are not independent trials. Absolute timing is affected by shared-machine activity. The 1.5-second cap was retained, not retuned.

Review exposed a compatibility problem in using a running minimum alone: 8 ms followed by 16 ms frames, alternating 16/33 ms frames, or zero timestamps could delay fast pages. The final rule retains the old fast path exactly. Deterministic tests cover those sequences and ensure later stalls cannot raise the shortest-gap baseline.

At 23b8c9c8, the readiness file passes 35 tests with no skips in three consecutive runs. The player asset-ready group passes 32 selected tests, with 216 intentionally excluded by the name filter. Studio Player, shadow reload and player store pass 21, 37 and 70 tests respectively, with no skips. Builds, repository lint, formatting and both core typechecks pass.

Nine deliberate mutations each fail one targeted test, with the other 34 excluded by the filter: removing relative readiness, narrowing tolerance, losing streak reset, removing the fast path for variable-refresh/alternating/zero cases, losing baseline ownership in either direction, and removing the cap. Source is restored before the passing runs.

At final cc07ff1a, the 35-test readiness file passes three more consecutive runs with no skips. Repository lint, formatting, core build and both core typechecks pass with exit 0.

The readiness contract records the producer and consumer boundaries.

The CI timeline viewport check currently fails: interaction p95 is 72.1 and 72.4 ms against a 58.3 ms ceiling. A serial, same-harness diagnostic with only the core readiness source changed fails on both the unmodified base and this head. That diagnostic also fails the frame budget, unlike CI, and ran under shared load, so it does not establish CI attribution or clear the failed check.

What I did NOT exercise

A full Studio reload in a real browser, macOS/Windows, or a physical variable-refresh display. The browser fixture executes the real core input; consumer tests use their existing DOM harnesses. A steady cadence can still represent unfinished work, so compute and media readiness remain separate gates. No downstream timeline commit was moved earlier.

@mintlify

mintlify Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

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

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Oct 7, 2026, 7:44 AM

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

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

Review at cc07ff1a7fb1b9504a40916ba9dc7058414774e1 — approved.

packages/core/src/compositionReadiness.ts:278-292 adds the relative-cadence path without replacing the existing strict < 50 ms fast-page qualification: two consecutive qualifying gaps are still needed, and a stall resets the streak. The shortest gap is retained for the attempt rather than increased after a stall. A focused timestamp replay showed steady 66 ms frames settling early, while 8/16 ms, alternating 16/33 ms, and zero-timestamp fast sequences settle at their previous frame. The 1.5-second cap and timeout/abort behavior remain; media and compute readiness, player generation checks, runtime first-frame reporting, and Studio's Play/shadow-promotion gates remain separate. The effective merge-base diff is the core implementation, its focused tests, and the readiness contract. Required checks passed at this head. I did not independently reproduce the PR body's browser timing distributions or mutation runs, nor run a full real-browser Studio reload.

Nonblocking limitation: An initial outlier-fast frame can anchor the minimum below the eventual slow cadence. With timestamps 0,16,82,148,214,…, later 66 ms gaps fail both < 50 and <= 1.5 × 16, so this attempt reaches the existing cap despite the subsequent regular cadence. An alternating 60/120 ms cadence likewise never makes two consecutive qualifying gaps and reaches the 1.5-second paint cap; a focused replay confirmed that delay reaches the player loader/assetsready and Studio Play readiness when the other gates have cleared. The steady-slow tests begin with a slow gap and do not exercise the first transition. These cases are conservative and no worse than the former cap behavior, but worth measuring and testing if either pattern is common on target pages. Likewise, a permanently steady cadence alone cannot prove content completion; the separate media/compute gates remain important.

— tai

Verdict: APPROVE
Reasoning: The new slow-cadence qualification improves the measured case without delaying any previously qualifying fast sequence or weakening the existing cap, cancellation, and consumer gates. The short-initial-gap case limits the improvement but does not introduce a correctness regression.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Edit accuracy: accurate 2059 (base branch 2059), smooth 1669 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 (0)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit da62598 Oct 7, 2026
188 of 189 checks passed
@miguel-heygen
miguel-heygen deleted the fix/core-document-idle-readiness branch October 7, 2026 09:00

This branch was successfully deployed

1 active deployment
staging - docs — cc07ff1a Deployed Oct 7, 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.

2 participants