SIGN IN SIGN UP

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