Skip to content

fix(deployment): lock GPU and CC-GPU for trials on the configure page - #3435

Merged
baktun14 merged 5 commits into
mainfrom
fix/deployment-trial-gpu-lock-configure
Jul 16, 2026
Merged

fix(deployment): lock GPU and CC-GPU for trials on the configure page#3435
baktun14 merged 5 commits into
mainfrom
fix/deployment-trial-gpu-lock-configure

Conversation

@baktun14

@baktun14 baktun14 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Why

On the new Configure your deployment page (flag onboarding_redesign_v1), a free-trial user who enables Confidential Compute → CPU-GPU — or leaves a GPU card on the default empty "Any model" selection — requests a GPU that trials cannot use. The deployment is created and enters quoting, but no usable bid ever satisfies allPlacementsHaveBids (the only TEE-capable GPU, nvidia/pro6000se, is on the trial blocklist), so the CTA stays on "Requesting…" forever with no explanation. The issue asks that trial users be told upfront, with an Add Funds path, instead of hitting an endless spinner.

Fixes CON-660

What

Lock the unusable GPU options upfront and guard submission, mirroring the existing GPU/Presets trial locks from #3409:

  • v1beta3 — new UI-only predicates isTrialBlockedGpuSelection / hasTrialBlockedGpu. They treat an empty ("any") model as blocked when the vendor exposes any blocked model.
  • ConfidentialComputeCard — locks the CPU-GPU radio (lock icon) with a warning + Unlock high-end GPUs CTA; keeps CPU-only TEE selectable (the backend never blocks it); defensively rejects cpu-gpu in setTee.
  • GpuCard — locks the "Any model" empty option; specific allowed models stay selectable.
  • SearchableSelect — supports a disabled empty option.
  • HardwareSection — routes the new predicate and passes isGpuBlocked / onUnlock to the confidential-compute card.
  • ConfigureDeploymentHeader — a load-bearing submit guard: enabling the GPU card leaves the model at the empty default without ever opening the (locked) picker, so the presentational lock alone can't stop an empty-model submission. On a trial with a blocked GPU selection it appends a clear message to the request-quotes error snackbar and blocks the request.

Intended behavior change (flag for review)

Locking "Any model" makes the front-end intentionally stricter than the backend. The backend leaves "any nvidia" to per-bid enforcement (validateLeaseGpuModels), and the SDL policy applyTrialGpuPolicy deliberately leaves "any" as null — both are unchanged. "Any" can succeed if an allowed-model provider happens to bid, but it's an unreliable gamble: if none bids, the deployment spins with no explanation. Forcing a trial to pick a specific allowed model (which stays selectable) or add credits gives a deterministic outcome. This rationale is documented in code comments (isTrialBlockedGpuSelection) so the divergence reads as deliberate.

Out of scope

  • Backend enforcement (accept-bid 402) is unchanged; this is a proactive front-end gate.
  • A trial that picks a specific allowed model no provider currently offers still hits the pre-existing generic 60s "No providers available" timeout — a separate UX gap.

Testing

  • New/extended specs across all six files (139 tests in the affected set). Full deploy-web suite: 2646 passed. Lint clean; no new type errors.
  • Manual E2E not run in this session (needs a dev server + a fresh $1 trial wallet with NEXT_PUBLIC_UNLEASH_ENABLE_ALL=true). Recommended before merge: verify CPU-GPU locks with CPU still selectable, "Any model" locks, Request quotes on the empty model is blocked with the trial message, an allowed model proceeds, and a funded wallet is unaffected.

Summary by CodeRabbit

  • New Features
    • Trial users now see blocked GPU options clearly marked and disabled, with guidance to unlock access.
    • Added unlock actions for high-end GPUs and prevented quote requests when selected GPUs require credits.
    • “Any model” GPU selections now reflect trial availability and credit requirements.
  • UI Improvements
    • Added disabled-state support for empty selections.
    • Improved loading buttons with accessible indicators and consistent disabled behavior.
    • Added a prominent style option for GPU unlock actions.

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.48%. Comparing base (f6540ea) to head (59c7c8b).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3435      +/-   ##
==========================================
- Coverage   72.58%   71.48%   -1.11%     
==========================================
  Files        1166     1076      -90     
  Lines       29510    27189    -2321     
  Branches     7308     6866     -442     
==========================================
- Hits        21421    19437    -1984     
+ Misses       7111     6798     -313     
+ Partials      978      954      -24     
Flag Coverage Δ *Carryforward flag
api 85.98% <ø> (ø) Carriedforward from f6540ea
deploy-web 61.34% <100.00%> (+0.05%) ⬆️
log-collector ?
notifications 91.44% <ø> (ø) Carriedforward from f6540ea
provider-console 81.38% <ø> (ø)
provider-inventory ?
provider-proxy 86.42% <ø> (ø) Carriedforward from f6540ea
tx-signer ?

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...ts/billing-usage/AddCreditsForm/AddCreditsForm.tsx 94.65% <100.00%> (-0.05%) ⬇️
...onfidentialComputeCard/ConfidentialComputeCard.tsx 100.00% <100.00%> (ø)
...reDeployment/ConfigurationPane/GpuCard/GpuCard.tsx 99.06% <100.00%> (+0.02%) ⬆️
...figurationPane/HardwareSection/HardwareSection.tsx 100.00% <100.00%> (ø)
...gurationPane/UnlockGpusButton/UnlockGpusButton.tsx 100.00% <100.00%> (ø)
...gureDeploymentHeader/ConfigureDeploymentHeader.tsx 100.00% <100.00%> (ø)
...nents/shared/SearchableSelect/SearchableSelect.tsx 100.00% <ø> (ø)
...pps/deploy-web/src/utils/deploymentData/v1beta3.ts 65.13% <100.00%> (+3.91%) ⬆️

... and 93 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds trial GPU selection blocking across deployment configuration and quote requests, including confidential compute gating and unlock CTAs. It also extends SearchableSelect and LoadingButton, and migrates the add-credits form to the updated loading control.

Changes

Trial GPU gating

Layer / File(s) Summary
GPU blocklist helpers
apps/deploy-web/src/utils/deploymentData/v1beta3.ts, apps/deploy-web/src/utils/deploymentData/v1beta3.spec.ts, apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
Adds GPU selection, restriction-active, and form-level blocked-GPU helpers with coverage for vendor, model, service, and trial cases.
GPU picker disabled state
apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/GpuCard/*, apps/deploy-web/src/components/shared/SearchableSelect/*
Disables the “Any model” option when trial restrictions apply and adds disabled empty-option support to SearchableSelect.
Confidential compute trial gate
apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ConfidentialComputeCard/*, apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/*, apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/UnlockGpusButton/*
Blocks CPU-GPU selection during restricted trials, displays an unlock warning, and wires the unlock callback through HardwareSection.
Quote request trial guard
apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/*
Prevents restricted users from requesting quotes when form services contain blocked GPU selections and adds corresponding test coverage.

Loading controls

Layer / File(s) Summary
LoadingButton behavior
packages/ui/components/loading-button.tsx, packages/ui/components/loading-button.spec.tsx
Adds an internal accessible spinner and ensures the button remains disabled while loading.
Add credits submission control
apps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.tsx
Migrates the submit control from Button plus inline Spinner to LoadingButton.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: ygrishajev

✨ 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/deployment-trial-gpu-lock-configure

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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

@coderabbitai coderabbitai Bot 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.

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
`@apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/ConfigureDeploymentHeader.tsx`:
- Around line 72-78: Update the trial-blocked GPU validation error in
ConfigureDeploymentHeader to include an actionable “Add Funds” CTA or link that
opens the existing funding flow or credits sheet, rather than only plain
snackbar text. Reuse the established funding action and preserve the validation
behavior for blocked submissions.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3eb05e76-6222-4d1f-96df-8e5351b8c1f3

📥 Commits

Reviewing files that changed from the base of the PR and between 7101d26 and 4259f69.

📒 Files selected for processing (13)
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ConfidentialComputeCard/ConfidentialComputeCard.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ConfidentialComputeCard/ConfidentialComputeCard.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/GpuCard/GpuCard.gated.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/GpuCard/GpuCard.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/ConfigureDeploymentHeader.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/ConfigureDeploymentHeader.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/components/shared/SearchableSelect/SearchableSelect.spec.tsx
  • apps/deploy-web/src/components/shared/SearchableSelect/SearchableSelect.tsx
  • apps/deploy-web/src/utils/deploymentData/v1beta3.spec.ts
  • apps/deploy-web/src/utils/deploymentData/v1beta3.ts

@github-actions github-actions Bot added size: L and removed size: M labels Jul 15, 2026
baktun14 added 5 commits July 16, 2026 09:28
On the new "Configure your deployment" page (onboarding_redesign_v1), a
free-trial user who enabled Confidential Compute → CPU-GPU (or left a GPU
card on the empty "Any model" default) requested a GPU trials cannot use.
The deployment was created and entered quoting, but no usable bid ever
arrived, so the CTA stayed on "Requesting…" forever with no explanation
(CON-660).

Lock the unusable options upfront and guard submission:
- New UI-only predicates isTrialBlockedGpuSelection / hasTrialBlockedGpu in
  v1beta3 treat an empty ("any") model as blocked when the vendor exposes any
  blocked model. This is intentionally stricter than the SDL trial policy and
  the backend, which leave "any" to per-bid enforcement — "any" is an
  unreliable gamble that only draws a usable bid if an allowed-model provider
  happens to bid. The rationale is documented in code to preempt review flags.
- ConfidentialComputeCard: lock the CPU-GPU radio with a warning + add-credits
  CTA, keep CPU-only TEE selectable, and defensively reject cpu-gpu in setTee.
- GpuCard: lock the "Any model" empty option; allowed specific models stay
  selectable.
- SearchableSelect: support a disabled empty option.
- HardwareSection: route the new predicate and pass isGpuBlocked/onUnlock to
  the confidential compute card.
- ConfigureDeploymentHeader: add a load-bearing submit guard — enabling the GPU
  card leaves the model at the empty default without opening the locked picker,
  so the presentational lock alone can't stop an empty-model submission.

Fixes CON-660
The CPU-GPU trial warning on the configure page rendered at base font size
with heavy padding and a muted ghost unlock button that read as disabled
text, and its copy implied all GPU access was blocked rather than only
high-end models.

- Shrink the alert (text-sm, tighter padding) so it reads as a hint.
- Add a `prominent` variant to UnlockGpusButton and use it here, so the
  add-credits CTA is a filled primary button instead of a ghost link.
- Reword to "High-end GPUs aren't available on a free trial", matching the
  unlock tooltip and the actual high-end-only blocklist.
- Drop the em dash in the CPU-GPU option description.
isGpuBlocked was derived from isBlockedModel("nvidia", ""), so the
confidential-compute CPU-GPU lock only worked as a side effect of the
stricter "any model" UI rule. Gate it directly instead: a restricted trial
while the GPU restriction is switched on.

- Add isTrialGpuRestrictionActive() (true when the blocklist is non-empty).
- HardwareSection: isGpuBlocked = isRestricted && !locked && isTrialGpuRestrictionActive().
- Behavior-preserving in production (all-nvidia blocklist); if the blocklist
  is emptied the whole restriction is a no-op, so CC-GPU correctly unblocks
  along with every other GPU.
LoadingButton renders the default filled-button spinner head as fill-primary,
the same color as the button's bg-primary, so it was invisible while a coupon
redemption was in flight. Pass a text-current LoaderCircle as the loading
indicator (the in-button spinner idiom already used elsewhere in deploy-web) so
it matches the label color and shows.
…buttons

Two bugs in the shared LoadingButton surfaced in the add-credits flow:

1. Double-submit: LoadingButton spread `{...props}` (which carries `disabled`)
   after its own `disabled={loading || disabled}`, so a caller's `disabled`
   clobbered the combined value and `loading` never disabled the button. The
   "Purchase Credits" button stayed clickable while a charge was in flight.
   Destructure `disabled` so the loading state always blocks clicks. This also
   closes the same latent hole in TrialStartButton and DeploymentAlerts.

2. Invisible spinner: the default filled-button spinner used a Spinner variant
   whose arc is fill-primary, identical to bg-primary. Replace it with a ring
   tinted by the button's own foreground (border-current), visible on every
   variant.

- AddCreditsForm: switch the raw Button + Spinner to LoadingButton.
- RedeemCouponForm: drop the one-off loadingIndicator now that the default is
  correct.
- Add a LoadingButton spec (none existed), including a regression test for the
  disabled-while-loading behavior.
@baktun14
baktun14 force-pushed the fix/deployment-trial-gpu-lock-configure branch from 4d40f01 to 59c7c8b Compare July 16, 2026 13:29

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
packages/ui/components/loading-button.spec.tsx (1)

1-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Sort the imports with simple-import-sort.

@testing-library/react must be grouped and sorted with the other package imports before the relative ./loading-button import.

Proposed fix
+import { render, screen } from "`@testing-library/react`";
 import type { ReactNode } from "react";
 import { describe, expect, it } from "vitest";
 
 import { LoadingButton } from "./loading-button";
-
-import { render, screen } from "`@testing-library/react`";
🤖 Prompt for 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.

In `@packages/ui/components/loading-button.spec.tsx` around lines 1 - 6, Reorder
the imports in the loading-button spec so the `@testing-library/react` package
import is grouped and sorted with the other external imports, before the
relative ./loading-button import, following simple-import-sort conventions.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@packages/ui/components/loading-button.spec.tsx`:
- Around line 1-6: Reorder the imports in the loading-button spec so the
`@testing-library/react` package import is grouped and sorted with the other
external imports, before the relative ./loading-button import, following
simple-import-sort conventions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9a1195be-2025-476c-8903-89ba0237e078

📥 Commits

Reviewing files that changed from the base of the PR and between d3a5f82 and 59c7c8b.

📒 Files selected for processing (17)
  • apps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ConfidentialComputeCard/ConfidentialComputeCard.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ConfidentialComputeCard/ConfidentialComputeCard.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/GpuCard/GpuCard.gated.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/GpuCard/GpuCard.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/UnlockGpusButton/UnlockGpusButton.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/ConfigureDeploymentHeader.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/ConfigureDeploymentHeader.tsx
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/components/shared/SearchableSelect/SearchableSelect.spec.tsx
  • apps/deploy-web/src/components/shared/SearchableSelect/SearchableSelect.tsx
  • apps/deploy-web/src/utils/deploymentData/v1beta3.spec.ts
  • apps/deploy-web/src/utils/deploymentData/v1beta3.ts
  • packages/ui/components/loading-button.spec.tsx
  • packages/ui/components/loading-button.tsx
🚧 Files skipped from review as they are similar to previous changes (14)
  • apps/deploy-web/src/components/onboarding/OnboardingContainer/OnboardingContainer.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/UnlockGpusButton/UnlockGpusButton.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/GpuCard/GpuCard.tsx
  • apps/deploy-web/src/components/shared/SearchableSelect/SearchableSelect.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.spec.tsx
  • apps/deploy-web/src/components/shared/SearchableSelect/SearchableSelect.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/GpuCard/GpuCard.gated.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ConfidentialComputeCard/ConfidentialComputeCard.spec.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/ConfigureDeploymentHeader.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.tsx
  • apps/deploy-web/src/utils/deploymentData/v1beta3.ts
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/ConfidentialComputeCard/ConfidentialComputeCard.tsx
  • apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentHeader/ConfigureDeploymentHeader.spec.tsx
  • apps/deploy-web/src/utils/deploymentData/v1beta3.spec.ts

@baktun14
baktun14 added this pull request to the merge queue Jul 16, 2026
Merged via the queue into main with commit 5339649 Jul 16, 2026
59 checks passed
@baktun14
baktun14 deleted the fix/deployment-trial-gpu-lock-configure branch July 16, 2026 14:29
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.

2 participants