perf(paywalls): warm a paywall's first page before its later ones (#3966)
- Warms a workflow's pages in the order a customer reaches them, breadth first from its entry steps, so a warm cut short covers what is seen first. - Warming is bounded and gets cut short by a paywall opening or a memory trim, and map order is not visit order. - Screens no step reaches are appended rather than dropped: `WorkflowTriggerAction` has an `Unknown` variant and `WorkflowStep.type` is an open string, so a navigation this SDK version cannot read must not silently stop a page that really is shown from warming. - `singleStepFallbackId` counts as an entry step alongside `initialStepId`, since `dismissExitOffer` reads it and nothing guarantees triggers reach it. ### Checklist - [x] If applicable, unit tests - [ ] If applicable, create follow-up issues for `purchases-ios` and hybrids <details> <summary>Agent description</summary> ### Motivation Which pages are warm when warming stops depends entirely on the order they are visited, and the order screens appear in the response has nothing to do with the order a customer reaches them. ### Description - `PublishedWorkflow.screensInVisitOrder()`: breadth-first from `initialStepId` and `singleStepFallbackId`, following each step's trigger actions in the order the components firing them appear, then appending any screen the walk never reached. - An action no trigger references still counts as an edge, so an entry point this SDK version does not model cannot hide a step. - Cycle-safe through a visited set, and a missing step or a screen id with no screen is skipped rather than throwing. ### Regression gates `orders screens breadth first from the initial step` and `follows trigger declaration order for sibling steps` are the ordering gates, and their fixture deliberately puts the second page first in response order so map order alone cannot produce the expected result. `appends screens no step reaches` and `walks a step whose action no trigger references` cover the forward-compatibility cases, and `terminates on a cycle` covers a self-referential workflow. </details> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Internal prefetch ordering only; no purchase, auth, or rendering path changes. Worst case is still warming the same assets in a different sequence. > > **Overview** > Paywall asset prewarm now follows the order a customer actually reaches screens, not the order they appear in the workflow payload. Warming is often cut short, so the first page’s images and web-view bundles get queued first. > > `screensInVisitOrder()` walks steps breadth-first from `initialStepId` and `singleStepFallbackId`, following trigger actions (including actions no trigger references). Unreachable screens are appended rather than dropped so unknown navigation types cannot skip a real page. Cycles and missing steps/screens are skipped safely. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 958593a40039f74fb04ee91bc88fb5574ef217cc. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
Á
Álvaro Brey committed
54274becc16c635d8cc9edfe5c7c412f0b095d3c
Parent: 57f5b18
Committed by GitHub <noreply@github.com>
on 8/24/2026, 1:14:44 PM