Skip to content

Add ad-hoc AI credit pack option to onboarding "Choose how to start" - #14605

Open
warp-agent-staging[bot] wants to merge 6 commits into
masterfrom
factory/rev-1886-onboarding-credit-packs
Open

Add ad-hoc AI credit pack option to onboarding "Choose how to start"#14605
warp-agent-staging[bot] wants to merge 6 commits into
masterfrom
factory/rev-1886-onboarding-credit-packs

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Free users who don't want a subscription had no way to get AI credits during onboarding. The Choose how to start offer (OfferVariant::ChooseHowToStart) only forked between subscribing and "Set up AI later", so anyone who just wanted to try Warp had to either commit to a plan or start with no AI at all.

This adds a third option that buys a one-time add-on credit pack.

Buy AI credits card. A new OfferChoice::BuyCredits sits between the subscribe and skip cards and presents the server's credit packs as a single horizontal row of tiles — credit count, the price actually charged, and the volume savings vs the smallest pack. Tiles are click-selectable and the three cards are arrow-key navigable. The layout follows the Billing & Usage page's add-on credit denominations row (compact credit chips 8px apart, with the same credits icon); the tiles are Expanded so the packs always stay on one line rather than wrapping.

Pricing is sourced at runtime, never hardcoded. current_credit_pack_options builds the list from pricingInfo.addonCreditsOptions and the viewer's PurchaseAddOnCreditsPolicy::effective_premium_bps, reusing price_usd_cents_with_premium so the displayed price matches what the server charges (free plan: $12 / $24 / $60 / $120 for the 400 / 1,000 / 3,000 / 6,500 packs). The option is hidden entirely when pricing hasn't loaded or the plan can't buy packs, and the packs refresh when pricing or the user's teams change.

Purchase and checkout return. "Get Warping" on that option calls purchaseAddonCredits with no teamUid, so the server resolves or creates a personal team for a brand-new free user. A PurchaseAddonCreditsCheckoutOutput opens the checkout URL and leaves the user on the slide with a "finish in the browser" note; onboarding advances only once the purchased credits are observed to have landed on an AIRequestUsageModel refresh — the balance is snapshotted before checkout opens and must actually grow, so unrelated AI availability can't advance anyone. Abandoning checkout therefore never advances anyone; "Set up AI later" stays available as the explicit escape hatch even mid-checkout. A synchronous charge advances immediately; a rejection surfaces a retryable error.

Copy. The subscribe card is titled "Subscribe to a Warp plan" and says monthly credits are the best value and that you save 20% on add-on credits with any Build plan — savings-framed, matching the web wording, with no "surcharge"/"premium" language.

Demo binary. crates/onboarding's standalone demo can now preview the post-auth offer slides via ONBOARDING_DEMO_OFFER=choose_how_to_start|head_start. It also had to start initializing feature flags — a debug build previously panicked on the first FeatureFlag::is_enabled check before any window rendered, so the demo was unusable for these slides.

Scoping note on ai_access_slide

Triage flagged whether "Get AI access" (ai_access_slide.rs) needs the same third option. It does not, and this PR deliberately leaves it alone: that slide is only constructed on the non-account-first path (agent_onboarding_view.rs builds ai_access_slide when AccountFirstOnboarding is disabled and offer_slide when it is enabled), so the two are alternative flows rather than forks that must stay in sync. The requester's screenshot is the offer slide. Adding packs to the legacy DES-816 slide would be net-new surface on a path that is being replaced.

Rework changes

Two requester adjustments after the first round of screenshots:

  • Card renamed. "Use Warp with AI" no longer distinguished that card once two of the three options were ways to use Warp with AI, so it is now "Subscribe to a Warp plan". The Recommended badge and the body copy are unchanged and still read correctly under the new title. The telemetry action name (use_warp_with_ai) is deliberately not renamed so existing dashboards keep continuity — called out in a code comment.
  • Canceled checkout could still advance. The checkout-pending state advanced whenever has_any_ai_remaining() became true, which is broader than "the pack the user just tried to buy was granted" — it is also true for base plan requests, BYOK credentials, overages and auto-reload, so a user who already had AI availability could open checkout, cancel, and still be advanced. AwaitingCheckout now carries the purchased-credit balance observed just before checkout opened, and the purchase completes only when that balance has actually grown; the app supplies it from the user- and workspace-scoped bonus grants a pack purchase lands in. Covered by canceled_checkout_does_not_advance_a_user_who_already_had_credits (a user holding 250 credits who cancels — and even spends some — stays on the slide) and observing_a_credit_balance_outside_checkout_does_nothing.
  • Packs on one line. The pack selector previously stacked four full-width rows, which made the slide tall enough for the onboarding container to scroll. The packs now render as one horizontal row of equal-width tiles modelled on Billing & Usage. Each tile still shows all three data points (credit count, premium-adjusted price, savings badge); the only detail shed is the redundant "credits" word after each number, which the card title and the credits icon already supply — so CreditPackOption::credits_label() now returns just the thousands-separated count ("6,500"), matching how the Billing & Usage denominations are labelled.

Verification

Regression tests (new; the pricing math test is the guard on the confirmed server SKUs):

  • app/src/pricing/pricing_tests.rs — 5 tests pinning the premium math ($10/$20/$50/$100 at list, $12/$24/$60/$120 at 2000 bps), volume savings 0/20/33/38%, empty input, and the no-negative-savings edge case.
  • crates/onboarding/src/model_tests.rs — 9 tests over the purchase state machine, including abandoned_checkout_leaves_the_purchase_in_flight (checkout alone must not advance) and that completion callbacks are inert with no purchase in flight.
  • crates/onboarding/src/slides/offer_slide_tests.rs — 6 tests over the slide: the third option appears only for the free-standard offer and only with packs, arrow-key navigation across all three options, get_warping_buys_credits_when_the_credit_option_is_selected, "Set up AI later" still emitting while checkout is pending, savings-framed copy, and pack-list truncation.

Repository checks: ./script/format; cargo clippy -p onboarding --all-targets --tests -- -D warnings; cargo clippy -p warp --all-targets --tests -- -D warnings; cargo test -p onboarding (42 passed); cargo test -p warp --lib pricing:: (5 passed); cargo test -p warp --lib root_view (11 passed). All re-run after the rework and after merging the current master into this branch.

Visual verification (full-desktop screenshots and a recording below), run against the real UI via ONBOARDING_DEMO_OFFER=choose_how_to_start ./target/debug/onboarding:

  • The three cards read "Subscribe to a Warp plan" (with the green Recommended pill), "Buy AI credits", and "Set up AI later".
  • The four packs sit side by side on one horizontal line: 400 / $12, 1,000 / $24 Save 20%, 3,000 / $60 Save 33%, 6,500 / $120 Save 38% — i.e. the free-plan premium and volume savings as computed.
  • The entire slide — heading, all three cards and the bottom button row — is visible at once with clear empty space below the buttons, no scrollbar and nothing clipped; scrolling over the panel moves nothing.
  • Clicking the right-most pack moves the tile highlight and selects the credit card.
  • "Get Warping" on the credit option stays on the same screen, the button becomes a disabled "Waiting for checkout…", and the "Finish your purchase in the browser…" note appears — the abandoned-checkout requirement.

Originating thread: https://warpdev.slack.com/archives/C0BDQDW8V5E/p1785523238898989

Computer-use screenshots

Full-desktop, uncropped captures (1429x804 display, app window ~940x590 with desktop margin on all four sides), so the absence of clipping and of any scrollbar is visible rather than asserted. The demo runs under Xvfb with no window manager, so the window has no OS-drawn title bar; the surrounding desktop and taskbar serve the same purpose of showing the window's full extent.

Default state: heading, all three cards ("Subscribe to a Warp plan" with the Recommended pill, "Buy AI credits", "Set up AI later") and the Back / Get Warping buttons all inside one frame, with empty space below them. The four pack tiles sit on one row: 400/$12, 1,000/$24 Save 20%, 3,000/$60 Save 33%, 6,500/$120 Save 38%.
After clicking the right-most pack: the "Buy AI credits" card is selected and the 6,500/$120 tile carries the highlight instead of 400/$12.
Contrast frame: the "Subscribe to a Warp plan" card is selected and the "Buy AI credits" card is unselected.
After scrolling down over the left panel: the frame is unchanged, so there is nothing to scroll to.

Computer-use video recording

Continuous full-screen recording panning through the states above: default, pack tiles, selecting a different pack, selecting the top card, and the scroll attempt.

Conversation: https://staging.warp.dev/conversation/0bec429a-a2ca-46af-892b-4ce333e400ee
Run: https://oz.staging.warp.dev/runs/019fb990-9952-7194-b171-5995e49cc9de

This PR was generated with Oz.

Free users who don't want a subscription had no way to get AI credits
during onboarding: the "Choose how to start" offer only forked between
"Use Warp with AI" (subscribe) and "Set up AI later".

Adds a third option that buys a one-time add-on credit pack:
- `OfferChoice::BuyCredits` renders a card with the server's credit
  packs (credits, premium-adjusted price, volume savings), keyboard
  navigable alongside the other two options.
- Pack prices come from `pricingInfo.addonCreditsOptions` plus the
  viewer's `PurchaseAddOnCreditsPolicy` premium at runtime; nothing is
  hardcoded, and the option is hidden when pricing is unavailable.
- "Get Warping" on that option calls `purchaseAddonCredits` with no
  `teamUid`, so the server resolves or creates a personal team.
- A checkout hand-off opens the URL and leaves the user on the slide;
  onboarding advances only once the granted credits are observed on a
  usage refresh, so abandoning checkout never advances anyone.
- The subscribe card now states the savings-framed add-on discount.

The onboarding demo binary can now preview the post-auth offer slides
via ONBOARDING_DEMO_OFFER, and initializes feature flags so its debug
build can render them at all.

Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Jul 31, 2026
oz-agent and others added 2 commits July 31, 2026 20:19
The post-auth offer slide is only constructed on the account-first
onboarding flow, so previewing it from the demo binary panicked at the
`expect("offer slide exists")` in the render match. Turn the flag on
when ONBOARDING_DEMO_OFFER selects an offer to preview.

Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
@warp-agent-staging
warp-agent-staging Bot marked this pull request as ready for review July 31, 2026 20:24
@warp-agent-staging
warp-agent-staging Bot requested a review from jefflloyd July 31, 2026 20:24
oz-agent and others added 3 commits July 31, 2026 20:50
Requester feedback on the onboarding offer slide:

- "Use Warp with AI" no longer distinguishes the card now that two of
  the three options are ways to use Warp with AI, so it is now
  "Subscribe to a Warp plan". The telemetry action name is deliberately
  unchanged so existing dashboards keep continuity.
- The stacked pack rows made the slide tall enough to scroll. The four
  packs now sit on a single horizontal row of equal-width tiles,
  following how the Billing & Usage page presents add-on credit
  denominations (compact credit chips 8px apart, with the credits
  icon). Each tile keeps its credit count, premium-adjusted price and
  savings badge; the count drops the redundant "credits" suffix, which
  the card title and icon already supply.

Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
The checkout-pending onboarding state advanced whenever
`has_any_ai_remaining()` became true. That predicate is broader than
"the pack the user just tried to buy was granted" — it is also true for
base plan requests, BYOK credentials, overages and auto-reload. A user
who already had AI availability could open checkout, cancel, and still
be advanced, which breaks the requirement that abandoning checkout
leaves them on the slide.

`AwaitingCheckout` now carries the purchased-credit balance observed
just before checkout opened, and the new `on_credit_balance_observed`
completes the purchase only when that balance has actually grown. The
app supplies the balance from the user- and workspace-scoped bonus
grants that a pack purchase lands in, rather than any-AI-availability.

Co-Authored-By: Warp <agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant