Skip to content

fix(kyc): keep financial-data answers on submit failure (retryable)#620

Open
joshuakrueger-dfx wants to merge 2 commits into
RealUnitCH:stagingfrom
joshuakrueger-dfx:joshua/fix-financial-data-submit-dataloss
Open

fix(kyc): keep financial-data answers on submit failure (retryable)#620
joshuakrueger-dfx wants to merge 2 commits into
RealUnitCH:stagingfrom
joshuakrueger-dfx:joshua/fix-financial-data-submit-dataloss

Conversation

@joshuakrueger-dfx
Copy link
Copy Markdown
Collaborator

Summary

A failed financial-data submit (_submitAll catch) replaced KycFinancialDataLoadedSuccess — which holds the user's responses — with a terminal KycFinancialDataFailure, rendered by an AppBar-only failure page with no retry. The collected answers were lost and the user stranded.

Fix:

  • New KycFinancialDataSubmitFailure state that subclasses KycFinancialDataLoadedSuccess (retains responses/url/index). The page's BlocBuilder keeps rendering the questions page (it matches the LoadedSuccess arm) and the cubit's is! KycFinancialDataLoadedSuccess guards still allow a retry.
  • _submitAll emits it instead of KycFinancialDataFailure.
  • The page listener surfaces the error as a transient red snackbar.
  • The load-failure path is unchanged (still a terminal failure page — nothing to retain).

Test

  • Updated the prior test (which asserted the buggy dead-end) to the recoverable behavior.
  • Added: submit failure retains the answers and stays a LoadedSuccess (not a Failure); a retry submits the same answers and reaches SubmitSuccess.

Verification (local, CI-equivalent, Flutter 3.41.6)

  • flutter analyze on the 4 changed files: No issues found.
  • Tests pass with the fix; fail when the cubit change is reverted (answers dropped), confirming the regression is caught.

Fixes the K2 item of #613.

@joshuakrueger-dfx
Copy link
Copy Markdown
Collaborator Author

Why this PR shows a red ✗ (it is not this change)

The functional checks for this PR pass: Analyze & Test ✅, Coverage Floor Gate ✅, BitBox quirks audit ✅. The red is the Visual Regression job, and it is a pre-existing baseline drift, not caused by this PR.

It fails on exactly one golden:

test/goldens/screens/home/home_golden_test.dart
  "HomePage loaded state with price data (variant: macOS)"   → 101 passed, 1 failed

Evidence that it is unrelated to this change:

Resolution: the drifted baseline needs to be regenerated via the golden-regenerate.yaml workflow (a deliberate baseline change, separate from this fix). Once that runs, Visual Regression goes green here too. This PR should be reviewed on its functional checks, which are green.

@TaprootFreak
Copy link
Copy Markdown
Contributor

Per global repo convention (CLAUDE.md):

Don't reference the current task, fix, or callers ("used by X", "added for the Y flow", "handles the case from issue #123"), since those belong in the PR description and rot as the codebase evolves.

Please drop the issue refs before merge:

  • lib/screens/kyc/steps/financial_data/cubits/kyc_financial_data_cubit.dart — the (issue #613 K2) trailing reference in the catch comment.
  • lib/screens/kyc/steps/financial_data/cubits/kyc_financial_data_state.dart — the two (issue #613 K2) references in the docstring of KycFinancialDataSubmitFailure.
  • test/screens/kyc/steps/financial_data/kyc_financial_data_cubit_test.dart — the // Regression for issue #613 K2: … block.

The state class docstring also re-explains implementation details (super.props, the is! KycFinancialDataLoadedSuccess guards) that the code already shows; consider trimming it to one line that just describes the role of the state.

Fix itself looks correct — the extends KycFinancialDataLoadedSuccess pattern keeps the questions UI alive and the retry path works.

@TaprootFreak
Copy link
Copy Markdown
Contributor

Re API as Decision Authority (CONTRIBUTING.md:23–67):

  • Answer retention in state: pure UX concern (display state, not a business decision) — OK in the app per rule.
  • Failure rendering: now surfaces the API's error message via SnackBar instead of swallowing it into a dead-end page — that's literally the "call the API, render its error" line in the rule.
  • Retry path: the user re-submits via the existing button, which calls the API again. No client-side retry/backoff logic, no local interpretation of error meaning. ✓

Fix lands in the correct layer.

@joshuakrueger-dfx
Copy link
Copy Markdown
Collaborator Author

@TaprootFreak done — dropped the issue #613 K2 refs from the cubit catch comment, the state docstring, and the test block, and collapsed the KycFinancialDataSubmitFailure docstring to one line describing its role (no more re-explaining super.props/the guards). HomePage golden regenerated on dfx01, Visual Regression green.

@joshuakrueger-dfx
Copy link
Copy Markdown
Collaborator Author

Status — ready for review

  • Review feedback addressed: dropped the three #613 K2 issue refs (cubit catch comment, state docstring, test block) and trimmed the KycFinancialDataSubmitFailure docstring to its role.
  • Repo rules: no issue/caller refs in the diff; no platform-channel path, so no @no-integration-test needed.
  • CI fully green on d25c61d: Analyze & Test, Visual Regression, Coverage Floor Gate, BitBox quirks audit.

Open gate: formal maintainer approval (currently REVIEW_REQUIRED).

@TaprootFreak TaprootFreak changed the base branch from develop to staging June 1, 2026 14:36
TaprootFreak added a commit that referenced this pull request Jun 1, 2026
## Summary

Scopes a `diffThreshold: 0.005` only on the `home_page_loaded` golden
via `AlchemistConfig.runWithConfig`. Every other golden stays at the
default `0.0` (exact-match).

## Why

The home_page_loaded golden renders a stroked price-chart path.
Sub-pixel anti-aliasing along that path produces coverage values that
jitter across runs even on the locked dfx01 renderer — hardware-lock is
not enough for stroked-path AA. Evidence:

- `044852b` (28.05.26, bot regen) refreshed `home_page_loaded` *and*
`buy_registration_required`
- `d25c61d` (01.06.26, #620) refreshed `home_page_loaded` again — only 4
days later, no UI change in scope
- Pixel diff between the two `home_page_loaded` versions: **0.24% of
pixels** changed, clustered in the chart-curve region only (rows
129–244, cols 252–385).

Both regens were the *correct* fix in the moment (a real pixel drift had
to be reconciled), but they're paying recurring cost for a structural
property of the chart widget.

## Why scoped, not global

A global `diffThreshold` would weaken the gate for **every** screen —
including 93 static-content goldens where 0.0 is exactly right. A small
icon removal (~256 px on 329 160-pixel surface) is well under 0.5% and
would be silently swallowed.

Local scope is the surgical fix: the chart-drift screen tolerates ~0.5%,
everything else stays strict.

## Why 0.005 (not lower or higher)

- Current chart drift: 0.238% of pixels → 0.005 absorbs it with ~2×
headroom for next time Skia jitters slightly more.
- Sanity ceiling: any meaningful UI regression in a 390×844 viewport
(icon, button, label) covers >2% — well above the threshold. The gate
still catches real changes.

## Alternative considered — not chosen

Hardening the chart with `StrokeJoin.bevel` + `StrokeCap.butt` in the
production code would remove drift at the source, but at the cost of
making the chart visually less smooth for end users to satisfy a test
concern. Wrong direction.

## Verification

- `flutter analyze test/goldens/screens/home/home_golden_test.dart` → No
issues found.
- Threshold is read at goldenTest registration time via `Zone.current`
(see `alchemist/src/golden_test.dart:163`), so the zone-based override
correctly reaches the comparator's `variantConfig.diffThreshold`
(`golden_test.dart:195`).
- The wrapper `goldenTest` from
`test/helper/golden_test_with_assets.dart` is preserved (alchemist
import uses `show AlchemistConfig, PlatformGoldensConfig` to avoid
ambiguous import on `goldenTest`).
A failed financial-data submit replaced KycFinancialDataLoadedSuccess (which
holds the user's answers) with a terminal KycFinancialDataFailure rendered by an
AppBar-only failure page — no retry, all answers lost. Introduce
KycFinancialDataSubmitFailure (a LoadedSuccess subtype) that retains the
responses so the questions page keeps rendering and the user can retry; surface
the error as a transient snackbar instead of a dead-end. The load-failure path
keeps its terminal failure page.

Updates the prior test that asserted the dead-end behavior and adds coverage for
answer-retention + a successful retry.

Refs RealUnitCH#613 (K2)
@TaprootFreak TaprootFreak force-pushed the joshua/fix-financial-data-submit-dataloss branch from d25c61d to 2ac9aad Compare June 2, 2026 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants