fix(card): key card flows off findActiveCard, never cards[0] + un-trappable setup modal#2334
Conversation
…ppable setup modal 2026-07-02 dup-card incident: the setup modal read cards[0] (newest) while the backend stored the session-key grant on a different card — every tap 'succeeded', the modal never dismissed, and the error-gated Skip never rendered. All card flows now use the existing findActiveCard helper, and a grant that succeeds without flipping hasWithdrawApproval reveals the escape hatch and pages Sentry (console.warn + captureMessage).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 4 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughThis PR replaces first-card selection ( ChangesActive card selection and escape hatch
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
Code-analysis diffPainscore total: 5856.19 → 5827.36 (-28.83) 🆕 New findings (28)
…and 8 more. ✅ Resolved (25)
…and 5 more. 📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/components/Home/__tests__/EnableAutoBalanceBanner.test.tsx (1)
89-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLGTM! Good coverage for the two documented regressions.
Consider adding a follow-up test once the
grantSucceededstaleness issue is fixed: render with card A, succeed a grant, then swapmockCardsto a fresh card B needing setup, and assert the escape hatch/Sentry message do not fire until a grant is actually attempted for card B.🤖 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 `@src/components/Home/__tests__/EnableAutoBalanceBanner.test.tsx` around lines 89 - 127, The current tests in EnableAutoBalanceBanner cover the documented regressions, but they do not guard against stale grantSucceeded state leaking across card changes. Add a follow-up test in EnableAutoBalanceBanner.test.tsx that renders with one ACTIVE card, completes a successful grant, then updates mockCards to a fresh card that still needs setup and verifies the modal behavior resets correctly without showing the escape hatch or calling Sentry until a new grant is attempted. Use the existing EnableAutoBalanceBanner, mockGrant, and Sentry.captureMessage assertions to locate and extend the coverage.
🤖 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 `@src/components/Home/EnableAutoBalanceBanner.tsx`:
- Around line 40-42: The success flag in EnableAutoBalanceBanner is not scoped
to the active card, so it can stay true across card swaps and falsely trigger
the lockout path. Update the grant-success tracking in EnableAutoBalanceBanner
to remember the specific card identity used for the successful attempt (based on
the card id from findActiveCard/overview), and reset or ignore it when that id
changes. Make sure stuckAfterSuccess only becomes true when the current active
card matches the one that was actually granted, so Sentry.captureMessage and the
“Skip for now” state only happen for a real duplicate-card case.
---
Nitpick comments:
In `@src/components/Home/__tests__/EnableAutoBalanceBanner.test.tsx`:
- Around line 89-127: The current tests in EnableAutoBalanceBanner cover the
documented regressions, but they do not guard against stale grantSucceeded state
leaking across card changes. Add a follow-up test in
EnableAutoBalanceBanner.test.tsx that renders with one ACTIVE card, completes a
successful grant, then updates mockCards to a fresh card that still needs setup
and verifies the modal behavior resets correctly without showing the escape
hatch or calling Sentry until a new grant is attempted. Use the existing
EnableAutoBalanceBanner, mockGrant, and Sentry.captureMessage assertions to
locate and extend the coverage.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a2f1c252-ba13-4200-bfdb-072e396845f1
📒 Files selected for processing (6)
src/app/(mobile-ui)/dev/card-session-approve/page.tsxsrc/components/Home/EnableAutoBalanceBanner.tsxsrc/components/Home/__tests__/EnableAutoBalanceBanner.test.tsxsrc/hooks/wallet/useGrantSessionKey.tssrc/hooks/wallet/useSignSpendBundle.tssrc/hooks/wallet/useSpendBundle.ts
…bit) — a re-issued card must not inherit an old card's grant success
- overviewFresh: a grant whose overview refetch failed is STALE, not stuck — don't false-page Sentry on flaky connections (grant() now reports it) - per-card Skip dismissal: skipping stuck card A must not suppress card B's legitimate prompt in the same session - stuck state now explains itself (error copy + Try again CTA) instead of happy-path text with an unexplained Skip - Sentry warn dedupe uses a Set — alternating active cards (A→B→A) no longer re-page for an already-warned card
…-review findings 4+5) A hard failure on card A no longer leaks 'Try again' copy and the skip escape into a re-issued card B's first prompt — same stale-signal class as the grantSucceededFor fix, applied to the hook's un-scoped error.
Summary
Companion to peanutprotocol/peanut-api-ts#1107 (2026-07-02 duplicate-card incident). The "Finish setting up your card" modal — and the grant pre-flights in
useGrantSessionKey/useSpendBundle/useSignSpendBundle— readcards[0](newest row, including CANCELED ones) while the backend stored the session-key grant on a different card. With duplicate ACTIVE cards every passkey tap returned 200, the non-dismissible modal never cleared, and the error-gated "Skip for now" never rendered: a hard app lockout with zero Sentry signal.Fixes:
cards[0]call sites now use the existingfindActiveCard(overview)helper (cardState.utils — "the one place that decides which card entry the UI operates on"). Pure reuse, no new abstraction.hasWithdrawApproval(the lockout shape) now reveals the "Skip for now" escape anyway, fires aconsole.warn, and pages Sentry viacaptureMessage(level: error).Risks / breaking changes
QA
EnableAutoBalanceBannertest extended: CANCELED-newest shapes (modal hidden when the active card is granted, still shown when not), stuck-after-success reveals Skip + console.warn + Sentry capture. Existing escape-hatch regressions kept.