refactor(kyc): render the backend ResolvedRail verdict (step 4)#2418
Conversation
the capability gate, provider-rejection surfaces, qr-pay and ActivationCTAs now branch on rail.resolved - the BE-derived verdict - instead of re-deriving fixable/terminal from status + reason codes + action kinds. a local fallback mirroring the BE collapse rules covers rails from older/cached responses and gets deleted with the BE's step-5 cleanup. gate priority order is unchanged; all 32 pre-existing gate tests pass through the new path unmodified, plus 7 new tests pinning that the verdict wins over contradictory legacy fields. hygiene riding along: api.generated.ts regenerated from the PR-2 openapi snapshot (resolved on both capability routes, provide-email/ restart-identity kinds now present); dead tosStatus/tosAcceptedAt types deleted (zero live reads); InitiateKycResponse.tosStatus dropped (BE never emits it).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughChangesThe PR makes Verdict-driven capability gating
Additional API contract changes
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Backend
participant CapabilityGate
participant ProviderRejection
participant KYCUI
Backend->>CapabilityGate: resolved rail verdict
CapabilityGate->>ProviderRejection: normalized gate state
ProviderRejection->>KYCUI: rejection status and user message
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Code-analysis diffPainscore total: 6186.26 → 6176.09 (-10.17) 🆕 New findings (381)
…and 361 more. ✅ Resolved (379)
…and 359 more. 📈 Painscore deltas (top movers)
|
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/app/`(mobile-ui)/qr-pay/page.tsx:
- Around line 195-233: Exclude rails whose resolved blocking selfHealKind is
"provide-email" from the fixableRail selection in the QR payment flow. Preserve
the existing PROVIDER_REJECTION_FIXABLE handling for other fixable rails,
allowing provide-email cases to fall through to the existing
PROVIDER_REJECTION_BLOCKED/contact-support path.
In `@src/utils/provider-rejection.utils.ts`:
- Around line 66-104: The verdict resolution logic is duplicated and incomplete
across two consumers. In src/utils/provider-rejection.utils.ts lines 66-104,
update deriveProviderRejection and its verdictOf fallback to preserve
selfHealKind, including provide-email, by reusing or extracting the shared
resolver used by capability-gate.ts or ActivationCTAs.tsx. In
src/app/(mobile-ui)/qr-pay/page.tsx lines 195-233, update the CTA selection to
exclude provide-email blocks on both resolved and legacy-derived verdicts,
routing them according to the existing provide-email handling rather than
document upload; have all affected consumers use one shared rail-verdict
resolver where practical.
- Around line 66-104: Update deriveProviderRejection and its verdictOf fallback
to detect legacy email-required rails using the same action lookup approach as
ActivationCTAs.tsx, such as a by-key or next-actions lookup threaded into the
function. Ensure the fallback sets or exposes provide-email classification so
isProvideEmail routes these rails through the dedicated fixable email path,
while preserving current handling for resolved verdicts and other legacy
statuses.
🪄 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: 88e64553-3d67-4137-850e-5f5cf8b49c28
⛔ Files ignored due to path filters (1)
src/types/api.generated.tsis excluded by!**/*.generated.*
📒 Files selected for processing (9)
src/app/(mobile-ui)/qr-pay/page.tsxsrc/app/actions/types/users.types.tssrc/components/Home/ActivationCTAs.tsxsrc/interfaces/interfaces.tssrc/types/api.openapi.jsonsrc/types/capabilities.tssrc/utils/capability-gate.test.tssrc/utils/capability-gate.tssrc/utils/provider-rejection.utils.ts
💤 Files with no reviewable changes (2)
- src/interfaces/interfaces.ts
- src/app/actions/types/users.types.ts
…view round) /code-review (47-agent verified pass) confirmed 8 findings; all fixed: fallback fidelity (the live path until the BE ships resolved): - railVerdict keys on STATUS first - action kinds only refine within a status. a blocked rail carrying a stale sumsub/tos/wait action stays blocked (the old gate never offered those CTAs on blocked rails); requires-info keeps its wait/self-serve/document-punt tiers - ladder parity restored: the blocked-family CTA is decided on the FIRST blocked rail only (a terminal block beats a sibling's restart-identity, which cannot unblock it and burns Sumsub attempts); fixable-with-action outranks pending, action-less fixable ranks below it (the old 7b placement); requires-info + contact-support no longer outranks a fixable sumsub sibling one collapse, one copy source (was four diverging hand-rolled copies): - railVerdict + railUserMessage are exported; provider-rejection.utils, qr-pay and ActivationCTAs consume them (deriveProviderRejection gains an optional nextActions param for the fallback's action-kind refinement - callers without it keep pure status semantics) qr-pay: a provide-email verdict maps to the unavailable modal, never the document-upload flow (that surface has no email form; the upload flow dead-ends an email-blocked user) tests: +6 fallback-fidelity pins on the gate, +11 new provider-rejection.utils suite, +1 qr-pay provide-email pin
Summary
PR-3 of the KYC single-source-of-truth refactor (TASK-20629) — the FE renders the backend's per-rail verdict instead of re-deriving it. Companion to peanut-api-ts #1177 (step 1) + #1181 (steps 2/2b/3/5); all three merge as one train, reviewed together.
capability-gate.ts:deriveGatebranches onrail.resolved(the BE-derivedResolvedRail) viarailVerdict(). A local fallback mirroring the BE collapse rules covers rails from older/cached responses — it's deleted when the BE's step-5 cleanup makesresolvedguaranteed. Gate priority order is unchanged; all 32 pre-existing gate tests pass through the new path unmodified, plus 7 new tests pinning that the verdict wins over contradictory legacy fields.provider-rejection.utils.ts/qr-pay/ActivationCTAs: fixable-vs-terminal / restart-identity / provide-email decisions now readresolved.blocking.selfHealKindinstead of sniffing reason codes and action kinds.ResolvedRailadded to the hand-written contract;api.generated.tsregenerated from fix: properly format currency code when fetching exchange rate #1181's OpenAPI snapshot (resolvedon both capability routes;provide-email/effectiveDate/requirementKey/hintActionsnow present — the old snapshot lagged). DeadtosStatus/tosAcceptedAttypes deleted (zero live reads);InitiateKycResponse.tosStatusdropped (BE never emits it).Deliberate behavioral deltas (small, flagged)
resolved: pending+ wait action) no longer surface a fixable CTA on provider-rejection surfaces — there is nothing user-actionable; the legacy status check offered a flow that dead-ended.country_not_supportedrestart-identity branch is preserved verbatim viaresolved.blocking.code, but note: today's BE never emits that code — the branch predates the current resolver and appears dead. Kept for parity; worth a product decision on whether Manteca country-ineligibility should re-emit it (BE change, not this PR).Not in scope
general.utils.tspayerAccount.user.bridgeKycStatusread: keeps working — fix: properly format currency code when fetching exchange rate #1181 derives and re-emits the same wire field. TheisVerified-boolean migration for the charges flow remains its own follow-up (pre-existing note in the code).bridgeCustomerIdreaders (withdraw/bank, BankFlowManager, Crisp deep-link): unchanged — the column and wire field stay this release (dual-homed; retirement is the BE's next-release item).SelfHealResubmissionResponse.requiredActiondrift flagged in the plan: already consistent with the BE union — nothing to fix.Risks / breaking changes
QA
Summary by CodeRabbit
New Features
Bug Fixes