Skip to content

fix(pdf): budget readiness from the load allowance, return it to the caller#841

Merged
Kikobeats merged 2 commits into
masterfrom
fix/readiness-budget
Jul 18, 2026
Merged

fix(pdf): budget readiness from the load allowance, return it to the caller#841
Kikobeats merged 2 commits into
masterfrom
fix/readiness-budget

Conversation

@Kikobeats

@Kikobeats Kikobeats commented Jul 18, 2026

Copy link
Copy Markdown
Member

Problem

The readiness gate waits for a page to settle — page-load work — but was budgeted from timeouts.action (timeout/11), then halved by READY_BUDGET_RATIO: ~1.2s. A hydrating document needs 2-3s, so the gate timed out on every tall page and the zero-capture fast path added in #837/#839 has never fired in production.

Every production split trace for a 46-page document shows it:

ready height=76210 images=29 painted=2 resets=0 timedOut=true  duration=1207   <- probe
ready height=71325 images=4  painted=2 resets=0 timedOut=false duration=844    <- sibling

duration=1207 is exactly the cap, every time.

Two consequences:

Change

Budget the gate from the load allowance goto assigns to this phase, at a quarter share (~3.9s at the default request budget). Observed settle times are 0.6-3.3s, and the gate returns as soon as the page is quiet — so this caps only a page that never settles, it is not a cost added to healthy pages.

The blank-page screenshot poll keeps its own action budget, now measured from after the gate, so a slow gate cannot starve it.

Also return the readiness result from prepare. A caller reusing one load across page-ranges can then skip that reuse when readiness could not be confirmed, instead of printing a page it never verified had settled. prepare previously returned undefined; the non-auto branch still does.

Verification

Against the local checkout, both previously timedOut=true in production:

ch-806 prepare=2595ms timedOut=false height=38034 painted=2 | render OK
ch-804 prepare=1400ms timedOut=false height=71325 painted=2 | render OK

chapter-806's height now matches the settled-context value (38034, not 36149).

Caveat: on native GL the old budget may not have timed out either, so this run is consistent with the fix rather than isolating it. The timedOut=true evidence is from the GPU-less fleet, and that is where it needs confirming after release.

waitForReady itself is unchanged; its 8 unit tests pass.

🤖 Generated with Claude Code


Note

Medium Risk
Changes core PDF auto-wait timing and prepare’s return value for waitUntil: 'auto', which can affect production render speed, fast-path behavior, and downstream split sizing for callers that consume readiness.

Overview
Fixes waitUntil: 'auto' PDF prep so the readiness gate gets enough time to finish on slow/hydrating pages instead of almost always hitting timedOut=true.

The gate’s cap is now 25% of the phase load timeout goto passes into waitUntilAuto (not the tiny timeouts.action slice that capped it around ~1.2s). That should let the zero-screenshot fast path run when the page actually settles, and yield more accurate height for split/page-count logic. The blank-page screenshot poll still uses the action budget, but its clock starts after the gate so a long settle doesn’t eat that budget.

prepare in auto mode now returns the readiness result (including timedOut) so callers reusing one load across page ranges can bail out and reload when readiness wasn’t confirmed.

Reviewed by Cursor Bugbot for commit f0db59d. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes
    • Improved automatic page-readiness handling during PDF generation.
    • Reduced the time allocated to readiness checks, helping the process move more efficiently to subsequent rendering steps.
    • Improved timing separation and diagnostics for readiness checks and blank-page detection.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PDF preparation flow reduces the waitUntil: 'auto' readiness budget, accepts an optional timeout override, returns readiness data after navigation, and separates readiness timing from blank-page polling timing.

Changes

PDF readiness budgeting

Layer / File(s) Summary
Auto readiness timeout flow
packages/pdf/src/index.js
waitUntilAuto allocates 25% of the applicable timeout to readiness checks, tracks readiness separately from screenshot polling, and returns the readiness result through prepare.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: readiness budgeting and returning readiness to the caller.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/readiness-budget

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

coveralls commented Jul 18, 2026

Copy link
Copy Markdown

Coverage Status

Coverage is 80.796%fix/readiness-budget into master. No base build found for master.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/pdf/src/index.js`:
- Around line 122-124: Update the timeout calculation in the waitForReady call
to use nullish coalescing instead of logical OR, so an autoTimeout value of 0 is
preserved and only undefined falls back to timeout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b9a66df7-7156-48a9-a34a-faca89f36522

📥 Commits

Reviewing files that changed from the base of the PR and between c9d9abe and 25c4e59.

📒 Files selected for processing (2)
  • packages/function/README.md
  • packages/pdf/src/index.js

Comment thread packages/pdf/src/index.js
Kikobeats and others added 2 commits July 18, 2026 21:03
…caller

The readiness gate waits for a page to settle — page-load work — but was
budgeted from `timeouts.action` (timeout/11), halved by READY_BUDGET_RATIO:
~1.2s. A hydrating document needs 2-3s, so the gate timed out on every tall
page and the zero-capture fast path never fired in production.

Production, chapter-804 (46 pages), every split trace:

  ready height=76210 images=29 painted=2 resets=0 timedOut=true  duration=1207  <- probe
  ready height=71325 images=4  painted=2 resets=0 timedOut=false duration=844   <- sibling

`duration=1207` is exactly the cap. The starved gate also under-measured the
document (36149 vs 38034 from a settled context), which feeds the page count
and mis-sizes the split.

Budget it from the load allowance goto assigns to this phase instead, at a
quarter share (~3.9s at the default request budget). Pages settle in 0.6-3.3s,
and the gate returns as soon as the page is quiet, so this caps only a page
that never settles. The blank-page poll keeps its own action budget, measured
from after the gate so a slow gate cannot starve it.

Also return the readiness result from `prepare`. A caller reusing one load
across page-ranges (microlink-api's parallel renderer) can then skip that reuse
when readiness could not be confirmed, rather than printing a page it never
verified had settled.

Verified against the local checkout:

  ch-806 prepare=2595ms timedOut=false height=38034 | render OK
  ch-804 prepare=1400ms timedOut=false height=71325 | render OK

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`||` treats an explicit `autoTimeout` of 0 as absent and grants a fresh action
budget instead. `??` falls back only when the caller passed no budget at all.

Not reachable today — goto computes `gotoTimeout` once from the request timeout
and never decrements it, so it cannot pass 0 — but `??` is the operator the
intent calls for, and correct if goto ever passes a remaining budget.
@Kikobeats
Kikobeats force-pushed the fix/readiness-budget branch from 9847c07 to f0db59d Compare July 18, 2026 19:04
@Kikobeats
Kikobeats merged commit c7fcf15 into master Jul 18, 2026
22 checks passed
@Kikobeats
Kikobeats deleted the fix/readiness-budget branch July 18, 2026 19:07
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