feat(kyc): skippable advisory pre-empt at add/withdraw (Bridge future-dated requirements)#2255
Conversation
…ed requirements A Bridge bank rail can be ENABLED now but carry a future-dated requirement (the BE surfaces it as a non-blocking hintAction whose NextAction has an effectiveDate — the 2026-06-29 sof_individual_primary_purpose cohort, ~148 users). Today the FE shows nothing until it becomes blocking; this lets the user resolve it early, skippably, so they're never interrupted. - types: mirror the BE contract additions — RailCapability.hintActions, NextAction.effectiveDate/requirementKey. - capability-gate: the `ready` state now carries an optional `advisory` (earliest future-dated hint among ready rails). No new gate kind, no priority change — advisory strictly rides on `ready`; a current blocker still wins. When the date passes the BE reclassifies it to blocking and the gate becomes fixable-rejection on its own (the existing non-skippable modal) — no FE cutover logic. - useAdvisoryPreempt + AdvisoryPreemptModal: intercept the proceed step once per session with a skippable 'Complete now / Not now' modal. 'Complete now' launches the RFI early (handleInitiateKyc with the advisory's levelKey); 'Not now' continues the transaction. - wired at add-money/[country]/bank (intercept after amount validation) and withdraw/[country]/bank (thin wrapper around the untouched money handler). Tests: gate advisory derivation (ready+advisory, back-compat, cap-nudge-is-not- advisory, earliest-date-wins, blocker-still-wins) + the hook (defer, skip-runs- proceed, dismiss-once, complete-launches). Pairs with peanut-api-ts #1033 (BE deploys first). Follow-up: same pattern for the claim→bank flow.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughThe PR adds a non-blocking "advisory pre-empt" step to the bank deposit and withdraw flows. Capability types and the gate derivation logic are extended to surface hint-based advisory requirements on a ChangesAdvisory Pre-Empt KYC Flow
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
Code-analysis diffPainscore total: 5762.99 → 5778.82 (+15.83) 🆕 New findings (54)
…and 34 more. ✅ Resolved (44)
…and 24 more. 📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
…, UTC date Audit fixes: - 'Complete now' dispatched via handleInitiateKyc -> /users/identity, which ignores the level and no-ops for already-approved users (the only cohort with an enabled advisory rail) -> SDK never opened. Add startKycAction + handleStartAction to POST the NextAction key to /users/kyc/start-action (the capability-native path that resolves key -> RFI level + mints a token). GateAdvisory now carries the action key (not levelKey). - onClose (X/backdrop/Escape) stranded the user + re-prompted every click: now dismisses for the session without auto-triggering the money action. - effectiveDate is date-only YYYY-MM-DD -> format in UTC, else Americas timezones show the day before the deadline. - move add-money's DEPOSIT_AMOUNT_ENTERED into the proceed callback so an X-then-reclick can't double-count it.
Advisory Complete now called handleStartAction (start-action), whose
submission never round-trips to Bridge. Switch both add-money and withdraw
bank pages to handleSelfHealResubmit('BRIDGE', advisory.requirementKey),
the path whose webhook completion relays answers to Bridge. Thread an
optional requirementKey through initiateSelfHealResubmission and
handleSelfHealResubmit to target the future-dated advisory requirement.
handleStartAction/startKycAction are now unused (kept for a focused
follow-up cleanup to avoid the isActionFlow cascade in this diff).
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/utils/capability-gate.ts (1)
32-33: ⚡ Quick winUpdate stale
actionKeycontract comment to match current flow.Line 32 still says
actionKeyis forPOST /users/kyc/start-action, but the wired "Complete now" path uses self-heal resubmit keyed byrequirementKey. This doc drift can mislead future edits.Suggested doc-only diff
- /** the NextAction `key` to start (POST /users/kyc/start-action) if the user completes it now. */ + /** the originating NextAction `key` for analytics/debug correlation of the advisory hint. */ actionKey: string🤖 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/utils/capability-gate.ts` around lines 32 - 33, The JSDoc comment for the actionKey field in capability-gate.ts is outdated and references the old POST /users/kyc/start-action endpoint, but the current implementation uses a self-heal resubmit flow keyed by requirementKey instead. Update the comment for the actionKey field to accurately reflect its actual role in the current self-heal resubmit flow rather than the deprecated endpoint reference, ensuring future maintainers understand the correct flow.src/hooks/useAdvisoryPreempt.ts (1)
39-44: ⚡ Quick winAdd a regression test for rapid double-invocation of
onCompleteNow.A small test here would protect the new flow from accidental duplicate submissions in future refactors.
🤖 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/hooks/useAdvisoryPreempt.ts` around lines 39 - 44, Add a regression test for the useAdvisoryPreempt hook that verifies the completeNow callback properly handles rapid double-invocation. The test should call the completeNow function twice in quick succession and assert that onCompleteNow is only called once and that the state variables (dismissed, visible, and pendingProceed.current) are set correctly to prevent duplicate submissions. This protects the callback logic from accidental duplicate executions in future refactors.
🤖 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/hooks/useAdvisoryPreempt.ts`:
- Line 25: The completeNow function (lines 39-44) allows re-entry and multiple
submissions when clicked rapidly before parent isLoading disables the button.
Use the pendingProceed useRef to track whether a request is already in flight.
In the completeNow function, check if pendingProceed is already set to a pending
callback before executing onCompleteNow, and if it is, return early to prevent
duplicate submissions. Set pendingProceed when starting the self-heal/KYC
request and clear it when the request completes to allow subsequent submissions
only after the current one finishes.
---
Nitpick comments:
In `@src/hooks/useAdvisoryPreempt.ts`:
- Around line 39-44: Add a regression test for the useAdvisoryPreempt hook that
verifies the completeNow callback properly handles rapid double-invocation. The
test should call the completeNow function twice in quick succession and assert
that onCompleteNow is only called once and that the state variables (dismissed,
visible, and pendingProceed.current) are set correctly to prevent duplicate
submissions. This protects the callback logic from accidental duplicate
executions in future refactors.
In `@src/utils/capability-gate.ts`:
- Around line 32-33: The JSDoc comment for the actionKey field in
capability-gate.ts is outdated and references the old POST
/users/kyc/start-action endpoint, but the current implementation uses a
self-heal resubmit flow keyed by requirementKey instead. Update the comment for
the actionKey field to accurately reflect its actual role in the current
self-heal resubmit flow rather than the deprecated endpoint reference, ensuring
future maintainers understand the correct flow.
🪄 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: 893da4fb-a129-40a2-80d4-24f3b2c5224b
📒 Files selected for processing (11)
src/app/(mobile-ui)/add-money/[country]/bank/page.tsxsrc/app/(mobile-ui)/withdraw/[country]/bank/page.tsxsrc/app/actions/sumsub.tssrc/components/Kyc/AdvisoryPreemptModal.tsxsrc/hooks/useAdvisoryPreempt.test.tssrc/hooks/useAdvisoryPreempt.tssrc/hooks/useMultiPhaseKycFlow.tssrc/hooks/useSumsubKycFlow.tssrc/types/capabilities.tssrc/utils/capability-gate.test.tssrc/utils/capability-gate.ts
…bbit) onCompleteNow now fires a real network call (self-heal resubmit), so rapid clicks before isLoading disables the CTA could launch duplicate requests. Add a completingRef in-flight guard.
kushagrasarathe
left a comment
There was a problem hiding this comment.
@jjramirezn reviewed mainly on code quality side, dont see anything concerning, tho i dont see any screenshots to judge/review the ui, please test it once you merge it
Why
A Bridge bank rail can be ENABLED today but carry a future-dated requirement. The backend (peanut-api-ts #1033, deploys first) now surfaces it as a non-blocking
hintActionwhose NextAction has aneffectiveDate. This FE lets the user resolve it early and skippably, so the ~148 users hitting the 2026-06-29sof_individual_primary_purposecliff are never interrupted. (Notion TASK-20162.)What
RailCapability.hintActions,NextAction.effectiveDate/requirementKey.readynow carries an optionaladvisory(earliest future-dated hint among ready rails). No new gate kind, no priority change — advisory strictly rides onready; a current blocker still wins. When the date passes, the BE reclassifies it → the gate becomesfixable-rejection(the existing non-skippable modal) with zero FE cutover logic.handleInitiateKycwith the advisory'slevelKey); Not now continues the transaction.add-money/[country]/bank(intercept after amount validation) andwithdraw/[country]/bank(thin wrapper around the untouched money handler).Tests
Gate advisory derivation (ready+advisory, back-compat, cap-nudge-is-not-advisory, earliest-date-wins, blocker-still-wins) + the hook (defer / skip-runs-proceed / dismiss-once / complete-launches). Full unit suite green (the lone failing suite is the pre-existing worktree
public/flagsartifact).Scope / follow-up
Covers add + withdraw (the stated scope). The claim→bank flow (
BankFlowManager) is a 3rd Bridge offramp surface — same pattern applies, deferred to a fast-follow to keep this PR tight.Cross-repo: contract is fully back-compat (all new fields optional); BE #1033 deploys first.