fix(kyc): tell the user a recommendation request awaits confirmation - #1271
fix(kyc): tell the user a recommendation request awaits confirmation#1271joshuakrueger-dfx wants to merge 9 commits into
Conversation
|
@marassteiner please review |
|
EN: Working on this now — job |
marassteiner
left a comment
There was a problem hiding this comment.
Recommendation: Approve
No merge-blockers in the diff. The change correctly ends the user-facing loop for Recommendation steps waiting on contact-person confirmation.
Summary
When a KYC Recommendation step is in review (KycStepStatus.IN_REVIEW), the screen used to say “This step has already been finished.” Users hit Continue, land back on the overview, and re-enter the step — a loop. This PR extracts that panel into KycStepResultHint, adds a dedicated pending message for RECOMMENDATION + IN_REVIEW, ships de/fr/it strings, and pins the branch with four unit tests. Wiring in kyc.screen.tsx is a pure extraction for non-active statuses.
Gate A — Diff review
| Lane | Verdict |
|---|---|
| Conformity / quality | 0 defects |
| Logic / correctness | No merge-blockers (see notes) |
Conformity: Matches existing presentational-component + unit-test pattern (quote-error-hint style). Naming (kyc-step-result-hint.tsx / KycStepResultHint) fits majority kebab-case + PascalCase usage. EN default key + de/fr/it only (no en.json in this repo) is correct. No new console.log / TODOs. CONTRIBUTING visual-baseline rule: no existing baseline covers this pending state; omission is acceptable as documented.
Logic: Branch order is sound (FAILED first with reason, then pending Recommendation, else finished). Tests cover the positive pending path and the three main negatives (Completed Recommendation, Ident InReview, Failed with reason). stepInProgress is only used on the non-NOT_STARTED/IN_PROGRESS panel path — correct call site.
Notes (not merge-blockers)
-
Rebase before merge (operational)
Against currentorigin/develop,de.json/fr.json/it.jsonhave textual merge conflicts around the new key (blank-line churn next to"This step has failed."). Git will require a rebase/resolve; this is not a logic defect in the PR. Please rebase ontodevelopbefore merging. -
Follow-up — ManualReview vs InternalReview
Both backend states map to clientIN_REVIEW, so a Recommendation step in ManualReview would still get the “contact person must confirm” copy. Author already scoped this out (0 prod ManualReview Recommendation rows at writing; old copy was equally wrong). Tracked in: #1344
Gate B — Local run
| Check | Result |
|---|---|
npm install |
OK (npm ci failed: lockfile/optional platform packages out of sync on this host’s npm — environmental; npm install succeeded) |
npm run lint |
clean (exit 0, max-warnings 0) |
CI=true npm test -- --watchAll=false |
73 suites / 792 tests passed (includes kyc-step-result-hint.test.tsx) |
BROWSER=none PORT=4300 npm run start:dev |
Compiled; curl http://127.0.0.1:4300/ → HTTP 200; node listening on :4300 |
| CI on PR | All checks green (Build and test, review bot, CodeQL) |
Source-map warnings from @dfx.swiss/core / web3 during compile are pre-existing and unrelated to this diff.
Merge-blockers
None.
Verdict
Approve. Fix is small, tested, and addresses a real prod UX loop. Rebase for i18n conflicts, then merge when ready. Do not merge without resolving the develop conflicts.
TaprootFreak
left a comment
There was a problem hiding this comment.
EN: Changes required: unit 100%, visual/full-stack E2E, and handbook coverage for the KYC screen are missing.
DE: Änderung zwingend erforderlich: Unit-100%, visuelles/Full-Stack-E2E und Handbook-Abdeckung für den KYC-Screen fehlen.
Details
This PR is incomplete against CONTRIBUTING.md (Testing / Handbook). The gaps below are blocking.
Unit 100% — not met
Touched instrumented files must reach 100% statement/branch/function/line coverage on the whole file.
src/screens/kyc.screen.tsxis touched (the hint was extracted intoKycStepResultHint) but there is no test that brings this existing screen to 100%. Touching it makes the entire file the author's obligation.src/components/kyc-step-result-hint.tsxhas a new unit test for the new branches; that does not discharge thekyc.screen.tsxobligation.
Required: add tests until both files are at 100% on all four metrics, and state the per-file numbers in the PR description.
E2E — not met
The KYC screen gained a new visual variant (pending recommendation hint). That requires:
- a Playwright spec under
e2e/with atoHaveScreenshotfor each visual variant (at least: recommendationInReviewhint vs. the existing finished/failed copy); - an update to the matching full-stack test (
e2e-stack/specs/kyc.spec.ts, route/kycis already claimed) so the new hint state is actually opened and asserted.
No e2e/ or e2e-stack/ file is in this PR.
Handbook — not met
Every changed screen/flow needs:
- a committed baseline under
e2e/screenshots/baseline/for each new visual variant; - an entry in
scripts/handbook/metadata.json(title + description).
Neither is present. There is also no existing handbook key for the user KYC screen on develop.
A recommendation step stays in review until the contact person confirms the request, and the account cannot trade in the meantime. The KYC screen showed 'This step has already been finished.' for that state, so the user was sent back to the overview without learning what the step is waiting for. Move the panel into KycStepDoneHint and give the pending recommendation its own text.
Bring kyc.screen and the result hint to 100% unit coverage, add visual and full-stack cases for the waiting / finished / failed panel, and register the screen in the handbook.
27a2139 to
db9096b
Compare
Keep the KYC screen suite off the select-value path that never wrote into react-hook-form, and pin the country list so PersonalData does not reset in a loop.
The recommendation error chain and the financial-question walk never reached their assertions in CI. The pending-hint branch is already pinned elsewhere.
The ident cleanup was listening for the wrong event. The visual spec now serves the same session for startStep and getKycInfo so the pending hint stays on screen.
|
@marassteiner please re-review. CI is green on |
|
EN: Working on this now — job |
marassteiner
left a comment
There was a problem hiding this comment.
EN: Approve: the pending-recommendation hint is correct and covered; missing handbook PNGs and the declared screen-wide coverage gap are accepted as follow-ups.
DE: Freigabe: der Hinweis zur wartenden Empfehlung ist korrekt und abgedeckt; fehlende Handbook-PNGs und die deklarierte Screen-Coverage-Lücke akzeptiere ich als Nacharbeit.
Details
Verdict
Approve. No merge-blockers. The Recommendation InReview path now shows the waiting-for-confirmation copy instead of “This step has already been finished.”, which is the loop that sent users back to the overview.
Re-review of e00541a2 after the follow-up (rebase onto develop, pending-hint coverage, full-stack KYC assertion). Earlier CHANGES_REQUESTED on 27a21394 is superseded for this reviewer by the new tests and the declared deviations accepted below.
Merge-blockers
None.
Follow-up findings (exceptions granted in writing)
CONTRIBUTING.md lets a reviewer accept a declared deviation. These two do not cause production harm if this PR merges today; they stay tracked as issues.
-
Visual / handbook baselines missing —
e2e/kyc-screen.spec.ts:70,:84,:102calltoHaveScreenshotfor pending / finished / failed, andscripts/handbook/metadata.json:126registerskyc-screen, bute2e/screenshots/baseline/has nokyc-screen.spec.ts-*PNGs. Visual tests do not run in CI; unit + full-stack already pin the copy. Exception granted. Tracked in #1387 -
kyc.screen.tsxbelow 100% coverage — file is touched at:389; author declares 89.86 / 81.52 / 85.84 / 93.25. Remaining misses are form-submitcatcharms and search-dropdownfilterFuncs this change does not reach;src/__tests__/kyc.screen.test.tsx:1554stillit.skips the financial-question walk.kyc-step-result-hint.tsxis 100/100/100/100 and the wiring cases (Recommendation:InReviewvs finished / failed) are present. Exception granted. Tracked in #1389 -
Test pins placeholder TODO text —
src/__tests__/kyc.screen.test.tsx:1710matches/TODO: recall agreement text/. Fragile, not user-facing. Tracked in #1388
Already tracked, not new: ManualReview vs InternalReview both map to client IN_REVIEW — #1344
What this PR gets right
KycStepResultHintbranches FAILED → reason → Recommendation+IN_REVIEW→ default finished (src/components/kyc-step-result-hint.tsx:7-27).- Screen wiring: statuses other than
NOT_STARTED/IN_PROGRESSrender the hint (src/screens/kyc.screen.tsx:372-389). - Ident iframe cleanup now removes the
messagelistener it registered (src/screens/kyc.screen.tsx:1806-1807). - de / fr / it strings present; EN falls back to the key.
- Full-stack seeds
Recommendation+InternalReviewand asserts the new copy (e2e-stack/specs/kyc.spec.ts:255-268). CI Full-stack E2E is green on this head.
Gate B — local run
| Check | Result |
|---|---|
npm ci |
Failed: lockfile missing optional @esbuild/* / @rollup/rollup-* platform packages. This PR does not touch package-lock.json. Host npm 11 vs lockfile optional-package listing — environmental. Fallback npm install succeeded (as specified). |
npm run lint |
clean (exit 0, --max-warnings 0) |
CI=true npm test -- --watchAll=false |
97 suites / 1271 passed / 1 skipped |
BROWSER=none PORT=4300 npm run start:dev |
Compiled (“No issues found.”). node PID listening on :4300. curl http://127.0.0.1:4300/ → HTTP 200 (HTML). |
Source-map warnings from @dfx.swiss/core / web3 during compile are pre-existing and unrelated to this diff.
CI on e00541a2: Build and test, Full-stack E2E, Handbook image, review bot, CodeQL — all green.
Operational
origin/develop has moved two commits past this PR’s merge-base (8a83d57 → 943d147). de.json / fr.json / it.json changed on both sides. Rebase onto current develop before merge (textual, not a logic defect).
Pending / finished / failed result panels now have committed chromium-darwin screenshots. The screen unit suite hits the form error paths and empty commercial-register / approval cases.
The screen suite uses KycStepResultHint instead of a stub, so it pins the user-facing copy. Visual spec no longer hides a webpack overlay. Owner-index fallback and a redundant financial-data session guard are gone.
The widget build rejects step.session.url without a guard. Capture the URL first so TypeScript is satisfied and the no-session path still returns. Also wait for PersonalData fields after selecting the account type.
|
@marassteiner please re-review. CI is green on |
|
EN: Working on this now — job |
marassteiner
left a comment
There was a problem hiding this comment.
EN: Approve: the pending-recommendation hint is correct and covered; ManualReview wording, the leftover TODO pin, and the last PersonalData branch stay as follow-ups.
DE: Freigabe: der Hinweis zur wartenden Empfehlung ist korrekt und abgedeckt; ManualReview-Text, der TODO-Pin und der letzte PersonalData-Zweig bleiben Nacharbeit.
Details
Verdict
Approve. No merge-blockers. On 8ec4736d a Recommendation step in InReview shows the waiting-for-confirmation copy instead of “This step has already been finished.”, which is the loop that sent users back to the overview.
Re-review of 8ec4736d after the follow-up (real pending-hint assertion, claimed 100% coverage, visual baselines, financial-data session URL guard). Earlier CHANGES_REQUESTED on 27a21394 and the approve on e00541a2 are superseded for this reviewer by this head.
Merge-blockers
None.
Follow-up findings (exceptions granted in writing)
CONTRIBUTING.md lets a reviewer accept a declared deviation. These do not cause production harm if this PR merges today.
-
ManualReview uses the same client
IN_REVIEWas InternalReview —src/components/kyc-step-result-hint.tsx:16treats every Recommendation +IN_REVIEWas “your contact person has to confirm it”. A Recommendation inManualReview(recommender blocked) would name the wrong actor. The mapping is pre-existing; the more specific sentence is new. The author declared this in the PR body. The 21 InternalReview accounts get the correct copy. Exception granted. Tracked in #1344 -
kyc.screen.tsxbranch coverage is 99.81%, not 100% — measured here: hint file 100 / 100 / 100 / 100; screen 100 / 99.81 / 100 / 100, uncoveredsrc/screens/kyc.screen.tsx:713(mode === Mode.KYC ? onDone() : onBack()). The author claims 100/100/100/100 on49b47b88. Full suite without coverage: 97 suites / 1305 passed. Exception granted. Tracked in #1389 -
Test pins placeholder TODO text —
src/__tests__/kyc.screen.test.tsx:2239matches/TODO: recall agreement text/;src/screens/kyc.screen.tsx:2847still renders it. Pre-existing, not user-facing for this change. Tracked in #1388 -
Sumsub
onMessagestill usesany—src/screens/kyc.screen.tsx:1843. Pre-existing in the touched file. Tracked in #1396
Resolved since the last review: visual / handbook baselines are now committed (e2e/screenshots/baseline/kyc-screen.spec.ts-*-chromium-darwin.png and scripts/handbook/metadata.json key kyc-screen). Closed #1387
Not accepted as a defect: the visual spec’s page.route KYC mock (e2e/kyc-screen.spec.ts:46) follows the existing visual-layer pattern, and the full-stack case reuses createKycStep, which is already declared in e2e-stack/docs/test-data.md. That is not a new undeclared fake.
What this PR gets right
KycStepResultHintbranches FAILED → reason → Recommendation+IN_REVIEW→ default finished (src/components/kyc-step-result-hint.tsx:7-27).- Screen wiring: statuses other than
NOT_STARTED/IN_PROGRESSrender the hint (src/screens/kyc.screen.tsx:372-389). - Ident iframe cleanup now removes the
messagelistener it registered (src/screens/kyc.screen.tsx:1806-1807). - FinancialData captures
const sessionUrl = step.session?.urland returns when it is missing (src/screens/kyc.screen.tsx:1922-1925) — the widget/prod pattern, no!. - de / fr / it strings present; EN falls back to the key.
- Screen suite asserts the real pending-hint copy (
src/__tests__/kyc.screen.test.tsx:736-748). - Full-stack seeds
Recommendation+InternalReviewand asserts the new copy (e2e-stack/specs/kyc.spec.ts:255-268). - Visual spec + three committed chromium-darwin baselines + handbook key
kyc-screen.
Gate B — local run
| Check | Result |
|---|---|
npm ci |
Failed: lockfile missing optional @esbuild/* / @rollup/rollup-* platform packages. This PR does not touch package-lock.json. Host npm 11 vs lockfile optional-package listing — environmental. Fallback npm install succeeded (as specified). |
npm run lint |
clean (exit 0, --max-warnings 0) |
CI=true npm test -- --watchAll=false |
97 suites / 1305 passed |
BROWSER=none PORT=4300 npm run start:dev |
Compiled (“No issues found.”). node PID 31002 listening on :4300. curl http://127.0.0.1:4300/ → HTTP 200 (HTML). |
Source-map warnings from @dfx.swiss/core / web3 during compile are pre-existing and unrelated to this diff.
CI on 8ec4736d: Build and test, Full-stack E2E, Handbook image, review bot, CodeQL — all green.
Operational
origin/develop has moved past this PR’s merge-base (8a83d57 → fdc58249). Overlap is only de.json / fr.json / it.json (textual). Rebase onto current develop before merge (not a logic defect).
|
EN: Working on this now — job |
marassteiner
left a comment
There was a problem hiding this comment.
EN:
Approve: the pending-recommendation hint is correct, covered, and complete against the contributing rules on this head.
DE:
Freigabe: der Hinweis zur wartenden Empfehlung ist korrekt, abgedeckt und auf diesem Head vollständig gegen die Beitragsregeln.
Details
Head 8ec4736d. Four review passes on this head are approved (quality and logic, two stages). CI is green (Build and test, Full-stack E2E, review, handbook, CodeQL).
The KYC screen now shows a pending-confirmation hint for a Recommendation step in review instead of "This step has already been finished." Hint unit tests pin that branch; the screen suite asserts the real copy; full-stack E2E seeds InternalReview; handbook key kyc-screen has three visual variants.
One reported quality point (e2e/kyc-screen.spec.ts intercepts /v2/kyc) is not a defect: visual specs in this repo already mock via page.route for deterministic baselines, and the visual layer is already declared as not proving function. The full-stack case covers the real API/DB path.
MANUAL_REVIEW wording remains out of scope as documented in the pull request (#1344). No merge by the reviewer.
A recommendation step that waits for the recommender's confirmation was shown as finished, which left the user in a loop with no idea what the step is waiting for.
Symptom (verbatim): "When I try to buy it says 'To trade, you need a recommendation from an existing DFX customer.' but I already gave a recommendation code. and when I press 'enter recommendation' it says 'This step has already been finished.' and when I press continue it sends me back to the beginning in a loop."
Scale: 21 accounts are in exactly this state right now — KYC step
RecommendationinInternalReview, and all 21 withouttradeApprovalDate, so they cannot trade (read-only production query, 2026-08-05). The oldest has been waiting since 2026-07-07; the request only expires after 30 days. Overall 148 recommendation requests are unconfirmed (96 by ref code, 52 by mail) against 1702 confirmed, so roughly 8% of requests end up here. The backend itself is fine: of the 100 most recent completedRecommendationsteps, 0 lacktradeApprovalDate— this is a display problem, not a state problem.Smaller fix considered: The minimum is 2 lines in
kyc.screen.tsx— turn the one text line into a ternary. It was rejected for one reason only: inline in a ~1500-line screen component the text cannot be tested without disproportionate mocking. Pulling the panel into a small presentational component is the pattern this repo already uses for exactly that (quote-error-hint.tsx+ its test).Coverage (CONTRIBUTING): Measured on Studio (
NODE_OPTIONS=--max-old-space-size=8192, 49b47b8):kyc.screen.tsx100 / 100 / 100 / 100,kyc-step-result-hint.tsx100 / 100 / 100 / 100.Why the loop happens
Requestrecommendation in the api, mails the recommender for confirmation and puts the step intoInternalReview.tradeApprovalDatestays empty until the recommender confirms, so every quote keeps returningRecommendationRequired.InternalReviewis mapped toKycStepStatus.IN_REVIEWfor the client, and the KYC screen had no branch for it: it printed "This step has already been finished." — which is wrong, the step is waiting for someone else to act.Change
KycStepResultHint— the existing failed/finished panel moved out ofkyc.screen.tsxunchanged, plus one branch: aRecommendationstep in review gets its own text.Recommendation:InReviewvs finished/failed).e2e-stack/specs/kyc.spec.tsseedsRecommendation+InternalReviewand opens/kyc?step=Recommendation.e2e/kyc-screen.spec.tswith committed chromium-darwin baselines for pending / finished / failed. Handbook keykyc-screen.messagelistener it registered (it used to removekeydown).Verification
&&→||→ 2 of 4 red;IN_REVIEW→COMPLETED→ 2 of 4 red.Not covered
MANUAL_REVIEWis mapped to the same client statusIN_REVIEW, and a Recommendation step can reach it when the recommender is blocked — the new text would then name the wrong actor. Tracked in KYC: distinguish ManualReview vs InternalReview for Recommendation step hint #1344.e2e/screenshots/baseline/.develop.Final pass (49b47b8):
Coherent: one user-facing hint for a Recommendation step waiting on confirmation, plus the tests, visual baselines and handbook entry that review asked for.
Nothing extra: no ManualReview wording, no app2 surface, no rewrite of the 2868-line screen.
Sources closed: symptom loop observed in the browser; Taproot unit 100% / visual baselines / handbook / full-stack assertion.