fix(deployment): await trial-wallet activation before creating a deployment - #3542
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3542 +/- ##
==========================================
- Coverage 74.79% 73.93% -0.86%
==========================================
Files 1155 1069 -86
Lines 30092 27784 -2308
Branches 7509 7054 -455
==========================================
- Hits 22508 20543 -1965
+ Misses 6701 6390 -311
+ Partials 883 851 -32
*This pull request uses carry forward flags. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughTrial activation now runs through background jobs and server-side wallet provisioning. Spending endpoints wait for activation. The deploy-web app removes client-side trial gating, polls for wallet readiness, and retries wallet-provisioning errors. ChangesTrial activation flow
Deployment wallet readiness
Estimated code review effort: 4 (Complex) | ~65 minutes Possibly related PRs
Suggested reviewers: ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts (1)
52-74: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy liftSeparate “activation in progress” from completed activation.
claimActivationmarksactivatedAtbefore grants and event delivery finish. A worker crash after the claim, or aTrialStartedenqueue failure, leaves the wallet appearing activated; later jobs exit at Line 52 and never complete the missing grant/event. Persist a separate claim/lease state and setactivatedAtonly after the full activation workflow completes, with retryable event delivery.🤖 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 `@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts` around lines 52 - 74, Update the wallet activation flow around claimActivation and the activatedAt guard to track an independent activation-in-progress claim or lease, rather than marking activatedAt before completion. Set activatedAt only after grant creation, repository updates, and TrialStarted delivery succeed; ensure crashes or enqueue failures leave the claim retryable and prevent duplicate concurrent activation.
🧹 Nitpick comments (1)
apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts (1)
17-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the repository alias for this API import.
Replace the relative import with the equivalent
@src/billing/services/activate-trial/trial-activation-instrumentation.servicealias.As per coding guidelines, files under
apps/apishould use the repository TypeScript path aliases@src/*where applicable.Proposed fix
-import { TrialActivationInstrumentationService } from "../activate-trial/trial-activation-instrumentation.service"; +import { TrialActivationInstrumentationService } from "`@src/billing/services/activate-trial/trial-activation-instrumentation.service`";🤖 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 `@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts` at line 17, Update the TrialActivationInstrumentationService import in wallet-initializer.service.spec.ts to use the repository alias `@src/billing/services/activate-trial/trial-activation-instrumentation.service` instead of the relative path.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.
Inline comments:
In
`@apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts`:
- Around line 80-82: Update the trial activation scheduling flow around
TrialActivationJobService.schedule so enqueue failures are not swallowed:
preserve the error after logging and fail or retry the verification flow before
it completes, ensuring users are not left verified without a queued activation
job.
In `@apps/api/src/billing/services/managed-signer/managed-signer.service.ts`:
- Around line 195-200: Update `#assertActivatedForSpending` so failures from
trialActivationJobService.schedule are caught and logged, then always throw the
existing 409 wallet_provisioning error. Preserve the current behavior for
non-spending transactions and already activated wallets.
---
Outside diff comments:
In
`@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts`:
- Around line 52-74: Update the wallet activation flow around claimActivation
and the activatedAt guard to track an independent activation-in-progress claim
or lease, rather than marking activatedAt before completion. Set activatedAt
only after grant creation, repository updates, and TrialStarted delivery
succeed; ensure crashes or enqueue failures leave the claim retryable and
prevent duplicate concurrent activation.
---
Nitpick comments:
In
`@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts`:
- Line 17: Update the TrialActivationInstrumentationService import in
wallet-initializer.service.spec.ts to use the repository alias
`@src/billing/services/activate-trial/trial-activation-instrumentation.service`
instead of the relative path.
🪄 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: 518e099b-f97e-48c6-9d73-323fc53d5e0c
⛔ Files ignored due to path filters (1)
apps/api/test/functional/__snapshots__/docs.spec.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (36)
apps/api/src/app/providers/jobs.provider.tsapps/api/src/auth/services/email-verification-code/email-verification-code.service.spec.tsapps/api/src/auth/services/email-verification-code/email-verification-code.service.tsapps/api/src/billing/controllers/wallet/wallet.controller.spec.tsapps/api/src/billing/controllers/wallet/wallet.controller.tsapps/api/src/billing/events/activate-trial.tsapps/api/src/billing/http-schemas/wallet.schema.tsapps/api/src/billing/routes/index.tsapps/api/src/billing/routes/start-trial/start-trial.router.tsapps/api/src/billing/services/activate-trial/activate-trial.handler.tsapps/api/src/billing/services/activate-trial/trial-activation-instrumentation.service.tsapps/api/src/billing/services/managed-signer/managed-signer.service.spec.tsapps/api/src/billing/services/managed-signer/managed-signer.service.tsapps/api/src/billing/services/trial-activation-job/trial-activation-job.service.tsapps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.tsapps/api/src/billing/services/wallet-initializer/wallet-initializer.service.tsapps/api/src/routers/open-api-handlers.tsapps/api/src/user/services/user/user.service.integration.tsapps/api/src/user/services/user/user.service.spec.tsapps/api/src/user/services/user/user.service.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/AutoDeployFlow/AutoDeployFlow.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/AutoDeployFlow/AutoDeployFlow.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.spec.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.tsapps/deploy-web/src/components/onboarding-picker/OnboardingPickerPage.spec.tsxapps/deploy-web/src/components/onboarding-picker/OnboardingPickerPage.tsxapps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.spec.tsapps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.tsapps/deploy-web/src/hooks/useEnsureTrialStarted.spec.tsapps/deploy-web/src/hooks/useEnsureTrialStarted.ts
💤 Files with no reviewable changes (7)
- apps/api/src/billing/routes/start-trial/start-trial.router.ts
- apps/api/src/billing/routes/index.ts
- apps/api/src/billing/http-schemas/wallet.schema.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.spec.ts
- apps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.spec.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.spec.tsx
- apps/api/src/routers/open-api-handlers.ts
55e9bdb to
aa77a8d
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts (1)
80-82: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winDo not swallow activation-enqueue failures. These paths report success after
JobQueueService.enqueue()fails, leaving a verified user without an activation job; job retries cannot recover work that was never queued. This repeats the prior email-verification finding and also affects the new registration and sync triggers.
apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts#L80-L82: log then rethrow, or persist a retryable handoff.apps/api/src/user/services/user/user.service.ts#L67-L71: preserve the scheduling failure rather than returning successful registration silently.apps/api/src/user/services/user/user.service.ts#L140-L144: preserve the scheduling failure rather than returning a successful sync silently.🤖 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 `@apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts` around lines 80 - 82, Do not swallow trial-activation scheduling failures: in EmailVerificationCodeService, log the failure and rethrow it or persist a retryable handoff; in UserService, preserve and propagate scheduling failures from both the registration and sync activation triggers so they cannot return success when enqueueing fails. Apply the required changes at apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts:80-82, apps/api/src/user/services/user/user.service.ts:67-71, and apps/api/src/user/services/user/user.service.ts:140-144.
🧹 Nitpick comments (2)
apps/api/src/billing/services/managed-signer/managed-signer.service.ts (1)
180-186: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDuplicated
hasSpendingTxderivation.Same
messages.some(... SPENDING_TXS...)expression appears in both#ensureAutoReloadScheduleand#assertActivatedForSpending. Consider extracting a small#hasSpendingTx(messages)helper.Also applies to: 195-197
🤖 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 `@apps/api/src/billing/services/managed-signer/managed-signer.service.ts` around lines 180 - 186, Extract the duplicated spending-transaction detection expression into a private `#hasSpendingTx`(messages) helper, then reuse it in both `#ensureAutoReloadSchedule` and `#assertActivatedForSpending`. Preserve the existing SPENDING_TXS matching behavior and each method’s current control flow.apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts (1)
76-99: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd assertions for the event publish + instrumentation side effects.
This test verifies the allowance persistence but not that
domainEvents.publish(new TrialStarted(...))ortrialActivationInstrumentation.recordActivated(...)were actually invoked — both are core outputs of this PR (event-driven consumers, activation latency metric). Worth locking these in given they're easy to silently regress.🤖 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 `@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts` around lines 76 - 99, Extend the test for WalletInitializerService.initializeAndGrantTrialLimits to assert that the trial-start event is published and activation instrumentation is recorded. Capture the relevant domain event publisher and trialActivationInstrumentation mocks from setup, then verify publish receives a TrialStarted event for the user and recordActivated is invoked with the activation result.
🤖 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.
Duplicate comments:
In
`@apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts`:
- Around line 80-82: Do not swallow trial-activation scheduling failures: in
EmailVerificationCodeService, log the failure and rethrow it or persist a
retryable handoff; in UserService, preserve and propagate scheduling failures
from both the registration and sync activation triggers so they cannot return
success when enqueueing fails. Apply the required changes at
apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts:80-82,
apps/api/src/user/services/user/user.service.ts:67-71, and
apps/api/src/user/services/user/user.service.ts:140-144.
---
Nitpick comments:
In `@apps/api/src/billing/services/managed-signer/managed-signer.service.ts`:
- Around line 180-186: Extract the duplicated spending-transaction detection
expression into a private `#hasSpendingTx`(messages) helper, then reuse it in both
`#ensureAutoReloadSchedule` and `#assertActivatedForSpending`. Preserve the existing
SPENDING_TXS matching behavior and each method’s current control flow.
In
`@apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts`:
- Around line 76-99: Extend the test for
WalletInitializerService.initializeAndGrantTrialLimits to assert that the
trial-start event is published and activation instrumentation is recorded.
Capture the relevant domain event publisher and trialActivationInstrumentation
mocks from setup, then verify publish receives a TrialStarted event for the user
and recordActivated is invoked with the activation result.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9e3e05b6-ecef-41ed-9f2f-68c97de1992f
⛔ Files ignored due to path filters (1)
apps/api/test/functional/__snapshots__/docs.spec.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (39)
apps/api/src/app/providers/jobs.provider.tsapps/api/src/auth/services/email-verification-code/email-verification-code.service.spec.tsapps/api/src/auth/services/email-verification-code/email-verification-code.service.tsapps/api/src/billing/controllers/wallet/wallet.controller.spec.tsapps/api/src/billing/controllers/wallet/wallet.controller.tsapps/api/src/billing/events/activate-trial.tsapps/api/src/billing/http-schemas/wallet.schema.tsapps/api/src/billing/repositories/user-wallet/user-wallet.repository.tsapps/api/src/billing/routes/index.tsapps/api/src/billing/routes/start-trial/start-trial.router.tsapps/api/src/billing/services/activate-trial/activate-trial.handler.spec.tsapps/api/src/billing/services/activate-trial/activate-trial.handler.tsapps/api/src/billing/services/activate-trial/trial-activation-instrumentation.service.spec.tsapps/api/src/billing/services/activate-trial/trial-activation-instrumentation.service.tsapps/api/src/billing/services/managed-signer/managed-signer.service.spec.tsapps/api/src/billing/services/managed-signer/managed-signer.service.tsapps/api/src/billing/services/trial-activation-job/trial-activation-job.service.tsapps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.tsapps/api/src/billing/services/wallet-initializer/wallet-initializer.service.tsapps/api/src/routers/open-api-handlers.tsapps/api/src/user/services/user/user.service.integration.tsapps/api/src/user/services/user/user.service.spec.tsapps/api/src/user/services/user/user.service.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/AutoDeployFlow/AutoDeployFlow.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/AutoDeployFlow/AutoDeployFlow.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.spec.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.tsapps/deploy-web/src/components/onboarding-picker/OnboardingPickerPage.spec.tsxapps/deploy-web/src/components/onboarding-picker/OnboardingPickerPage.tsxapps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.spec.tsapps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.tsapps/deploy-web/src/hooks/useEnsureTrialStarted.spec.tsapps/deploy-web/src/hooks/useEnsureTrialStarted.ts
💤 Files with no reviewable changes (7)
- apps/api/src/billing/routes/index.ts
- apps/api/src/billing/routes/start-trial/start-trial.router.ts
- apps/api/src/billing/http-schemas/wallet.schema.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.spec.ts
- apps/api/src/routers/open-api-handlers.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.spec.tsx
- apps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/api/src/billing/events/activate-trial.ts
There was a problem hiding this comment.
Beyond the inline findings, I also checked whether removing the client-side create() fallback in useEnsureTrialStarted drops the only retry path for a failed wallet/address derivation — it doesn't: WalletInitializerService.ensureWallet (address derivation) is now invoked server-side on registration and on syncEmailVerified, so the client no longer needs to drive it.
Extended reasoning...
Verified the candidate concern about useEnsureTrialStarted losing its wallet-creation retry path. Confirmed the hook now only reads wallet state (apps/deploy-web/src/hooks/useEnsureTrialStarted.ts) and that address derivation moved server-side into WalletInitializerService.ensureWallet, called from UserService.registerUser and UserService.syncEmailVerified in this diff — so a failed derivation is retried on the next verification/registration event rather than relying on a client-side mutation retry. Not a bug.
aa77a8d to
31856b2
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
apps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.spec.tsx (1)
852-853: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
mock()for the service mock.Line 852 uses
mockDeep(). Usemock<ReturnType<typeof DEPENDENCIES.useServices>>()to follow the required spec mock pattern.Proposed fix
- const services = mockDeep<ReturnType<typeof DEPENDENCIES.useServices>>(); + const services = mock<ReturnType<typeof DEPENDENCIES.useServices>>();🤖 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 `@apps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.spec.tsx` around lines 852 - 853, Replace the mockDeep call used to initialize services in the deployment flow spec with mock<ReturnType<typeof DEPENDENCIES.useServices>>(), while preserving the existing createDeployment mutation setup.Source: Coding guidelines
apps/deploy-web/src/components/billing-usage/RedeemCouponForm/RedeemCouponForm.spec.tsx (1)
76-76: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winKeep coverage for the readiness-independent submission path.
The deleted test covered the old disabled state. Add a test that enters a non-empty coupon and asserts that
RedeemCouponFormenables the button and invokesapplyCouponwithout anisWalletReadyinput. Keep polling-based disabling covered.🤖 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 `@apps/deploy-web/src/components/billing-usage/RedeemCouponForm/RedeemCouponForm.spec.tsx` at line 76, Restore coverage in the RedeemCouponForm tests for submission without an isWalletReady prop: enter a non-empty coupon, assert the submit button becomes enabled, and verify applyCoupon is invoked. Preserve the existing polling-based disabled-state coverage.apps/api/src/billing/services/managed-signer/managed-signer.service.ts (1)
180-186: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the duplicated spending-message check.
#assertActivatedForSpending(Line 196) and#ensureAutoReloadSchedule(Line 181) computehasSpendingTxwith the identical expression. Extract one private helper and call it from both sites. This keeps the activation gate and the auto-reload trigger in sync ifSPENDING_TXSchanges later.♻️ Proposed refactor
+ `#hasSpendingTx`(messages: EncodeObject[]): boolean { + return messages.some(message => SPENDING_TXS.some(msg => message.typeUrl.endsWith(msg.$type))); + } + async `#ensureAutoReloadSchedule`(userId: UserWalletOutput["userId"], messages: EncodeObject[]) { - const hasSpendingTx = messages.some(message => SPENDING_TXS.some(msg => message.typeUrl.endsWith(msg.$type))); - - if (hasSpendingTx) { + if (this.#hasSpendingTx(messages)) { await this.walletReloadJobService.scheduleImmediate({ userId }); } } ... async `#assertActivatedForSpending`(userWallet: UserWalletOutput, messages: EncodeObject[]) { - const hasSpendingTx = messages.some(message => SPENDING_TXS.some(msg => message.typeUrl.endsWith(msg.$type))); - if (!hasSpendingTx) return; + if (!this.#hasSpendingTx(messages)) return; await this.trialActivationJobService.assertActivated(userWallet); }Also applies to: 195-200
🤖 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 `@apps/api/src/billing/services/managed-signer/managed-signer.service.ts` around lines 180 - 186, Extract the duplicated spending-message detection expression into a private helper in the managed signer service, then use that helper from both `#ensureAutoReloadSchedule` and `#assertActivatedForSpending`. Preserve the existing boolean behavior and ensure both activation validation and auto-reload scheduling rely on the same helper.
🤖 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/onboarding-picker/OnboardingPickerPage.tsx`:
- Line 56: Update OnboardingPickerPage around useEnsureTrialStarted so
trial-start failures remain visible to the user instead of only gating the LLM
card through isWalletReady. Preserve the returned error state and render the
shared retry action or route failures to the existing provisioning error UI,
while keeping the successful loading and readiness behavior unchanged.
In
`@apps/deploy-web/src/context/PaymentPollingProvider/PaymentPollingProvider.tsx`:
- Around line 215-224: Update the polling logic around initialBalanceRef and
initialTrialingRef so initializing the balance baseline no longer returns early.
Continue evaluating wasTrialing in the same effect run, allowing
initialTrialingRef to be set when both balance data first arrives and the trial
has already completed.
In `@apps/deploy-web/src/queries/useManagedWalletQuery.ts`:
- Around line 21-22: Update the refetchInterval callback in the managed wallet
query so polling occurs only when query.state.data exists and its wallet address
is missing; return false for undefined data, including API or authorization
failures, while preserving the existing polling interval and stop condition when
an address is present.
---
Nitpick comments:
In `@apps/api/src/billing/services/managed-signer/managed-signer.service.ts`:
- Around line 180-186: Extract the duplicated spending-message detection
expression into a private helper in the managed signer service, then use that
helper from both `#ensureAutoReloadSchedule` and `#assertActivatedForSpending`.
Preserve the existing boolean behavior and ensure both activation validation and
auto-reload scheduling rely on the same helper.
In
`@apps/deploy-web/src/components/billing-usage/RedeemCouponForm/RedeemCouponForm.spec.tsx`:
- Line 76: Restore coverage in the RedeemCouponForm tests for submission without
an isWalletReady prop: enter a non-empty coupon, assert the submit button
becomes enabled, and verify applyCoupon is invoked. Preserve the existing
polling-based disabled-state coverage.
In
`@apps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.spec.tsx`:
- Around line 852-853: Replace the mockDeep call used to initialize services in
the deployment flow spec with mock<ReturnType<typeof
DEPENDENCIES.useServices>>(), while preserving the existing createDeployment
mutation setup.
🪄 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: f479cda6-3393-4972-aa50-177a22e5a729
⛔ Files ignored due to path filters (1)
apps/api/test/functional/__snapshots__/docs.spec.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (63)
apps/api/src/app/providers/jobs.provider.tsapps/api/src/auth/services/email-verification-code/email-verification-code.service.spec.tsapps/api/src/auth/services/email-verification-code/email-verification-code.service.tsapps/api/src/billing/controllers/stripe/stripe.controller.spec.tsapps/api/src/billing/controllers/stripe/stripe.controller.tsapps/api/src/billing/controllers/wallet/wallet.controller.spec.tsapps/api/src/billing/controllers/wallet/wallet.controller.tsapps/api/src/billing/events/activate-trial.tsapps/api/src/billing/http-schemas/wallet.schema.tsapps/api/src/billing/repositories/user-wallet/user-wallet.repository.tsapps/api/src/billing/routes/index.tsapps/api/src/billing/routes/start-trial/start-trial.router.tsapps/api/src/billing/services/activate-trial/activate-trial.handler.spec.tsapps/api/src/billing/services/activate-trial/activate-trial.handler.tsapps/api/src/billing/services/activate-trial/trial-activation-instrumentation.service.spec.tsapps/api/src/billing/services/activate-trial/trial-activation-instrumentation.service.tsapps/api/src/billing/services/managed-signer/managed-signer.service.spec.tsapps/api/src/billing/services/managed-signer/managed-signer.service.tsapps/api/src/billing/services/trial-activation-job/trial-activation-job.service.spec.tsapps/api/src/billing/services/trial-activation-job/trial-activation-job.service.tsapps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.tsapps/api/src/billing/services/wallet-initializer/wallet-initializer.service.tsapps/api/src/user/services/user/user.service.integration.tsapps/api/src/user/services/user/user.service.spec.tsapps/api/src/user/services/user/user.service.tsapps/api/test/functional/stripe-transactions-confirm.spec.tsapps/deploy-web/src/components/auth/AddCreditsSheet/AddCreditsSheet.spec.tsxapps/deploy-web/src/components/auth/AddCreditsSheet/AddCreditsSheet.tsxapps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.spec.tsxapps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.tsxapps/deploy-web/src/components/billing-usage/AddCreditsTabs/AddCreditsTabs.spec.tsxapps/deploy-web/src/components/billing-usage/AddCreditsTabs/AddCreditsTabs.tsxapps/deploy-web/src/components/billing-usage/RedeemCouponForm/RedeemCouponForm.spec.tsxapps/deploy-web/src/components/billing-usage/RedeemCouponForm/RedeemCouponForm.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/AutoDeployFlow/AutoDeployFlow.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/AutoDeployFlow/AutoDeployFlow.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigurationPane/HardwareSection/HardwareSection.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.spec.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.tsapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.spec.tsxapps/deploy-web/src/components/deployments/ConfigureDeployment/useDeploymentFlow/useDeploymentFlow.tsapps/deploy-web/src/components/layout/FundingBanner/FundingBanner.spec.tsxapps/deploy-web/src/components/layout/FundingBanner/FundingBanner.tsxapps/deploy-web/src/components/layout/HackathonCouponNavEntry/HackathonCouponNavEntry.spec.tsxapps/deploy-web/src/components/layout/HackathonCouponNavEntry/HackathonCouponNavEntry.tsxapps/deploy-web/src/components/onboarding-picker/OnboardingPickerPage.spec.tsxapps/deploy-web/src/components/onboarding-picker/OnboardingPickerPage.tsxapps/deploy-web/src/context/PaymentPollingProvider/PaymentPollingProvider.tsxapps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.spec.tsapps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.tsapps/deploy-web/src/hooks/useEnsureTrialStarted.spec.tsapps/deploy-web/src/hooks/useEnsureTrialStarted.tsapps/deploy-web/src/queries/useManagedWalletQuery.tsapps/deploy-web/src/queries/usePaymentQueries.tsapps/deploy-web/src/utils/walletProvisioning.spec.tsapps/deploy-web/src/utils/walletProvisioning.ts
💤 Files with no reviewable changes (6)
- apps/deploy-web/src/components/billing-usage/AddCreditsForm/AddCreditsForm.spec.tsx
- apps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.spec.ts
- apps/deploy-web/src/components/billing-usage/AddCreditsTabs/AddCreditsTabs.spec.tsx
- apps/deploy-web/src/components/deployments/ConfigureDeployment/useCreateDeployment/useCreateDeployment.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.spec.tsx
- apps/deploy-web/src/components/auth/AddCreditsSheet/AddCreditsSheet.spec.tsx
🚧 Files skipped from review as they are similar to previous changes (21)
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeployment/ConfigureDeployment.tsx
- apps/api/src/user/services/user/user.service.ts
- apps/api/src/billing/services/activate-trial/activate-trial.handler.spec.ts
- apps/api/src/user/services/user/user.service.spec.ts
- apps/api/src/billing/services/activate-trial/activate-trial.handler.ts
- apps/api/src/billing/repositories/user-wallet/user-wallet.repository.ts
- apps/api/src/billing/events/activate-trial.ts
- apps/api/src/auth/services/email-verification-code/email-verification-code.service.spec.ts
- apps/api/src/user/services/user/user.service.integration.ts
- apps/api/src/app/providers/jobs.provider.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/DeploymentFlowProvider/DeploymentFlowProvider.spec.tsx
- apps/api/src/auth/services/email-verification-code/email-verification-code.service.ts
- apps/deploy-web/src/hooks/useEnsureTrialStarted.spec.ts
- apps/deploy-web/src/components/onboarding-picker/OnboardingPickerPage.spec.tsx
- apps/deploy-web/src/hooks/useAutoDeploymentFlow/useAutoDeploymentFlow.ts
- apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.spec.ts
- apps/api/src/billing/services/wallet-initializer/wallet-initializer.service.ts
- apps/api/src/billing/services/activate-trial/trial-activation-instrumentation.service.spec.ts
- apps/deploy-web/src/components/deployments/ConfigureDeployment/ConfigureDeploymentForm/ConfigureDeploymentForm.tsx
- apps/deploy-web/src/hooks/useEnsureTrialStarted.ts
- apps/api/src/billing/services/activate-trial/trial-activation-instrumentation.service.ts
31856b2 to
e8b8c10
Compare
There was a problem hiding this comment.
Beyond the two nits already posted inline, this run also checked and ruled out three other candidates: a claimed permanent corruption of the top-up balance baseline in PaymentPollingProvider (it resets each poll session, not permanently); the CodeRabbit-flagged early return skipping trial-completion detection in that same effect (a later poll tick still catches the flip before the poll times out); and a missing error/retry affordance in OnboardingPickerPage if wallet provisioning stalls (the wallet-ready poll self-heals without one).
Extended reasoning...
This run's bug hunt found two nit-level issues (already posted as inline comments: the unbounded managed-wallet poll on persistent fetch errors, and a stale AddCreditsForm docstring) plus three candidate issues that finder agents raised and verifier agents examined and refuted: a permanent-corruption claim on PaymentPollingProvider's balance baseline, a claim aligned with CodeRabbit's 'Major' finding about the early return skipping trial-completion detection, and a claim that OnboardingPickerPage lacks an error/retry affordance for stalled provisioning. Given the PR is XL, touches security-sensitive billing/wallet-activation code, and inline comments already carry the actionable findings, this note is informational context for the author and any future review pass so these three items are not re-explored from scratch, not a restatement of the inline findings or a guarantee of correctness.
e8b8c10 to
7a40053
Compare
…spends A managed trial wallet gets its address at registration, but its on-chain deployment/fee grants were provisioned separately by a client-driven start-trial call. A spend firing before that finished failed terminally, so onboarding auto-deploy, the manual quote request, top-ups, and coupon redemptions all raced a wallet that was never ready. Trial activation moves to a background job triggered when the user registers or verifies their email, so it can't be aborted by a client reload and pg-boss retries it. Activation stamps activatedAt only after the on-chain grants land, so it doubles as the spend gate's ready signal. A shared assertActivated guard rejects an unactivated wallet with a retriable 409 (re-enqueuing activation as a self-heal) on every managed-wallet endpoint - create-deployment, top-up confirm, and coupon apply - and the client retries each with backoff until activation lands. The client-side wallet-ready gate is removed from the deploy, top-up, and coupon flows; readiness is enforced server-side. The client-driven POST /v1/start-trial is kept but reduced to ensuring the wallet and enqueuing activation, so an older client still works during a staged rollout. The managed-wallet query self-heals a stale no-address snapshot (polls until the address lands), so post-deploy pages, the onboarding gate, and the balance no longer need a reload; a successful deploy also refreshes the onboarding lease-existence cache. Activation job outcomes are recorded via OpenTelemetry so a failure is visible.
7a40053 to
1b213b3
Compare
There was a problem hiding this comment.
No new bugs found in this pass. This is a large (XL), security-sensitive change touching wallet provisioning, auth-adjacent access control, and payment flows across 64 files, so I'd still like a human to review before merge.
What was reviewed:
- Confirmed the previously-flagged IDOR on
POST /v1/start-trialis fixed —WalletController.createnow asserts the requestuserIdmatches the authenticated caller before calling the unscopedensureWallet. - Checked the new server-side activation job (
ActivateTrialHandler,TrialActivationJobService) and its wiring into registration, email verification, and the spend/top-up/coupon guards. - Checked wallet-provisioning retry plumbing on the client (
walletProvisioningRetry,useManagedWalletQuerypolling) for both the openapi-sdk and http-sdk error shapes.
Extended reasoning...
Overview
This PR moves trial-wallet activation from a client-driven call to a server-side background job (ActivateTrial / ActivateTrialHandler), gates managed-wallet spend/top-up/coupon actions behind a retriable 409 wallet_provisioning via TrialActivationJobService.assertActivated, and removes the corresponding client-side wallet-readiness gates across the deploy flow, top-up form, and coupon form. It also adds OpenTelemetry instrumentation for the activation job and changes managed-wallet query polling behavior. 64 files touched across apps/api (billing/auth/user services, routes, DI wiring) and apps/deploy-web (deployment flow, payment forms, onboarding).
Security risks
The main risk surface is access control around POST /v1/start-trial: an earlier version of this PR dropped the CASL per-user scoping when WalletController.create switched to the unscoped WalletInitializerService.ensureWallet, which would have let an authenticated caller read another user's wallet data and trigger their trial provisioning (IDOR). That regression has since been fixed with an explicit assert(userId === this.authService.currentUser.id, 403, ...) check in the controller before ensureWallet is called. I re-verified this fix is present and correctly scoped. No other injection/auth-bypass/data-exposure concerns stood out in this pass.
Level of scrutiny
This warrants a higher-than-default level of scrutiny: it changes production billing/payment code paths (Stripe top-up, coupon redemption, deployment creation) and touches wallet provisioning logic that previously had a real access-control bug during development. The change is also large (XL) and cuts across DI wiring, job queue handlers, and several React hooks/components, increasing the chance of a subtle interaction the automated pass didn't catch even though none was found this run.
Other factors
The PR has solid test coverage for the new pieces (handler specs, instrumentation specs, controller specs including a dedicated test for the fixed IDOR-adjacent ownership check), and CodeRabbit's own findings on this PR have been addressed in a follow-up commit. Given the size, security-sensitivity, and history of a real access-control bug on this PR, I'm deferring to a human reviewer rather than approving.
Why
A managed (trial) wallet gets its address at registration, but can only spend once its on-chain
grants are provisioned. That provisioning ("start trial") was driven from the client, kicked off
when the user landed on onboarding. Anything that interrupted the browser between landing and the
first spend — a reload, a navigation, a closed tab — abandoned the in-flight start-trial, so the
action raced ahead of an un-activated wallet and failed terminally. This wasn't only deploys:
top-ups and coupon redemptions hit the same un-provisioned wallet the same way. A manual retry
usually "fixed" it, because by then the grants had landed.
A spending action should never have to assume a client-side side-effect completed. Activation is now
the server's responsibility, and every managed-wallet endpoint waits for it.
What
Trial-wallet activation moves off the client and behind a server-side background job, and every
managed-wallet spend/credit awaits activation via a retriable
409 wallet_provisioninginstead offailing. No happy-path UX change; the action just occasionally runs a little longer while grants land.
flowchart TD subgraph server["Server-side — activation runs on its own, survives client reloads"] direction TB A[User registers or verifies email] --> B{Email verified?} B -- no --> Z[No activation yet] B -- yes --> C[Enqueue ActivateTrial job] C --> D[Grant on-chain trial limits, then set activatedAt] D -- transient failure: retry with backoff --> D D -- success --> F([Wallet activated]) end subgraph client["Client — deploy / top-up / coupon are managed-wallet actions"] direction TB G[User acts] --> I{Wallet activated?} I -- yes --> J([Action proceeds]) I -- no --> K[API returns 409 wallet_provisioning<br/>and re-enqueues activation] K --> L{Retries left?} L -- yes: wait backoff --> G L -- no --> M[Show 'try again later or contact support'] end F -. activation unblocks the gate .-> IServer-side activation (apps/api)
ActivateTrialjob the moment a user's email is verified —on registration, on email-verification sync, and on the passwordless code-verify path. The job grants
the on-chain limits then stamps
activatedAt; it retries with backoff, so a transient chain/queuehiccup self-heals without any client involvement.
activatedAtis set only after the grants land (same write as the allowances), so it doubles asthe spend gate's "ready" signal — an action arriving mid-provisioning can't slip past onto a bare
chain/funding error. No activation claim is needed: the queue is
policy: singleton(one job per user)and the grants are idempotent, so a retry just re-converges.
Shared spend/credit guard
TrialActivationJobService.assertActivatedbest-effort re-enqueues activation and throws theretriable
409 wallet_provisioningwhen the wallet isn't activated. It's applied to create-deployment(managed signer), top-up confirm and coupon apply (
StripeController). A failed re-enqueue islogged but the 409 is still returned, so the next client retry drives it.
Client retry + gate removal (apps/deploy-web)
walletProvisioningRetryretries thewallet_provisioning409 with backoff; wired into thecreate-deployment, top-up (
confirmPayment), and coupon (applyCoupon) mutations. The predicate matchesboth client error shapes (openapi-sdk
ApiErrorand the http-sdkAxiosError).form — they act immediately and let the server 409 + retry handle provisioning.
useEnsureTrialStartedis reduced to a pure reader;
useCreateDeploymentis gone (inlined into the flow). The now-deadisWalletReadyprop is removed all the way up the Add-Credits chain.shortly or contact support" message.
Stale-cache self-heal
lands (server-side provisioning can lag the first fetch), so post-deploy pages, the onboarding gate,
and the balance stop requiring a manual reload.
isn't bounced to
/onboarding. The top-up/coupon flows keep the existing balance-poll so funds areconfirmed before the user proceeds to deploy.
Backward compatibility
POST /v1/start-trialis kept but reduced to ensure the wallet + enqueue activation (returning thewallet), so an older deploy-web still works if the API ships first in a staged rollout. New clients
don't call it.
Observability (OpenTelemetry, not analytics)
classified reason (transient vs. terminal blocks like unverified email or a duplicate-fingerprint
block), job duration, and registration→activation latency. Failures also log
TRIAL_ACTIVATION_JOB_FAILED.Activation is expected to always succeed, so any failure is a real signal worth a dashboard alert.
Notes
?auth=password) stillexists, so the server must not assume every caller arrives already verified.
connectManagedWallet/ConnectManagedWalletButtoncluster that pointed at the old client-create endpoint.
Summary by CodeRabbit
New Features
Bug Fixes