You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(backup): simplify reminder engine to posture cadences
The engine asked one question in several voices and tracked state it did
not need. It now answers "could you get your money back?" with one verb
per posture.
Delete the balance-tier machinery (BackupBalanceTier, the 1,000,000-sat
tier, highestHandledBalanceTier) and the pending-action bookkeeping. The
record is now the acknowledgement time plus a one-time milestone flag;
keys written by the earlier shape are ignored rather than rejected, so no
migration is needed for an unreleased feature.
Make the cadence posture-dependent: 90 days for a vault-only wallet,
365 for one that already holds words. Anchor the schedule on the clock of
the thing being urged rather than on the most recent backup of any kind —
a both-backups wallet with a fresh vault and a two-year-old physical test
was never reminded, which was the whole point of the reminder.
Fire the 10,000,000-sat notice at most once per wallet, inclusive of the
threshold, retired by either dismissing it or acting on it. Merge the
both-backups posture into "test your backup" and drop the "review your
backups" nag, which named no action. There is deliberately no vault or
PIN freshness reminder: the fix for a single server-dependent recovery
path is a physical backup, not a PIN rehearsal.
The popup now asks one question and offers one action, with a
low-emphasis dismissal that snoozes a full cycle and never records a test
the app did not observe.
Copy file name to clipboardExpand all lines: docs/backup-health-reminders.md
+48-27Lines changed: 48 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,42 +1,57 @@
1
1
# Backup health reminders
2
2
3
-
Backup health reminders encourage users to periodically confirm that their wallet can still be recovered. They supplement the existing no-backup warning; they do not replace it.
3
+
Every backup surface answers one question: **if this phone vanished right now, could you get your money back?** Each state has exactly one honest answer and exactly one thing to do about it.
4
+
5
+
| Situation | Honest answer | The one action |
6
+
| --- | --- | --- |
7
+
| Nothing backed up | No — the money is gone | Back up, urgently |
8
+
| Encrypted Vault only | Probably — it needs the server up and your PIN | Add a physical backup |
9
+
| Physical backup done (with or without a vault) | Yes — if the words are still findable and correct | Occasionally confirm that is still true |
10
+
11
+
## One surface per state
12
+
13
+
Three surfaces, three tenses, no overlap:
14
+
15
+
- the **every-launch warning** (`backup_warning_overlay.dart`) means "you are unprotected right now". Zero-backup only, unchanged by this feature.
16
+
- the **reminder popup** means "it has been a while, or something changed". Every posture except zero-backup.
17
+
- the **Backup Settings hero** means "here is your standing situation". Always available on the screen you visit deliberately.
18
+
19
+
So zero backup gets the warning and the hero, never the popup; every other state gets the popup and the hero, never the warning. One popup asks one question and offers one action.
4
20
5
21
## Reminder matrix
6
22
7
-
The reminder is evaluated for the default mainnet hot-wallet seed after at least one backup method has been verified. Eligible balances are aggregated separately as described below.
23
+
The reminder is evaluated for the default mainnet hot-wallet seed once at least one backup method has been verified.
| Recoverbull only | Explain that automatic recovery normally depends on the Recoverbull key server and recommend an independent physical backup. | Create and test a physical backup | Acknowledge the dependency risk |
13
-
| Physical only | Ask the user to perform a health check of the physical backup they already have. Recoverbull is available only through a neutral link to other backup options. | Test the physical backup | Remind in three months |
14
-
| Recoverbull and physical | Ask the user to review and test their available backups. | Review backup options | Remind in three months |
25
+
| Verified backup posture | Reminder | Action |
26
+
| --- | --- | --- |
27
+
| No verified backup | None. The existing no-backup warning owns this state. | — |
28
+
| Encrypted Vault only | Every 90 days: without a physical backup you cannot recover if the vault server is unavailable. | Add a physical backup |
29
+
| Physical backup, with or without a vault | Every 365 days since the physical backup was last tested. | Test the backup |
15
30
16
-
The reminderis shown only on the wallet home screen and only after higher-priority no-backup and legacy-storage warnings are clear.
31
+
There is deliberately **no vault-freshness or PIN reminder**. A vault-only wallet's real exposure is depending on someone else for its only recovery path, and the fix for that is a physical backup, not a PIN rehearsal. Once a physical backup exists, PIN rot cannot cost the user their money.
17
32
18
-
## Timing and balance milestones
33
+
## Timing
19
34
20
-
A scheduled reminder becomes due 90 full days after the most recent of:
35
+
A scheduled reminder is anchored on the clock of the thing being urged:
21
36
22
-
- the latest completed physical backup;
23
-
- the latest completed Recoverbull backup; or
24
-
- the last time the user acknowledged the reminder.
37
+
- vault only — the latest vault backup, or the last acknowledgement;
38
+
- physical backup present — the latest *physical* backup test, or the last acknowledgement. A fresh vault does not buy silence about words that were last read two years ago.
25
39
26
-
A verified backup record with no completion timestamp is treated as due immediately. Acknowledging a reminder starts a new 90-day interval.
40
+
A verified backup with no completion timestamp is due immediately.
27
41
28
-
Balance milestones can show the reminder before the scheduled date:
42
+
Dismissing a reminder snoozes it for a full cycle (90 or 365 days). It never writes a tested timestamp, so the Backup Settings screen keeps saying how long ago the backup was really tested while the popup is quiet. Only completing a verification flow resets that date; creating a backup counts as tested on day zero, because the creation flow tests it.
29
43
30
-
- more than 1,000,000 sats; and
31
-
- more than 10,000,000 sats after the first milestone has been handled.
44
+
The popup is shown on the wallet home screen only, and only once the higher-priority no-backup and legacy-storage warnings are clear. A deep link or payment intent lands on its own route, so nothing ever stands between a payment and its completion.
32
45
33
-
The comparisons are strict: exactly 1,000,000 or 10,000,000 sats does not trigger a milestone. Each milestone is recorded after the user acknowledges the reminder or completes the selected backup action, so ordinary balance fluctuations do not repeatedly trigger it.
46
+
## Balance milestone
34
47
35
-
Starting a backup action records a pending action. On the next evaluation, a backup completion timestamp at or after the action start handles the associated balance milestone. Cancelling the flow leaves the milestone due for the next app session.
48
+
The first time an evaluation observes an eligible balance of **10,000,000 sats or more**, the reminder is shown once, whatever the schedule says, with the posture-appropriate ask. The comparison is inclusive: exactly 10,000,000 sats counts.
49
+
50
+
That notice is retired for the lifetime of the wallet as soon as the user either dismisses it or acts on it. A balance later dropping below the threshold and crossing it again changes nothing — a wallet is told once.
36
51
37
52
## Eligible wallets and balances
38
53
39
-
Only mainnet wallets whose keys are held on the device participate in reminder evaluation and balance milestones.
54
+
Only mainnet wallets whose keys are held on the device participate in reminder evaluation and in the balance total.
40
55
41
56
Included:
42
57
@@ -65,15 +80,21 @@ The overlay evaluates when wallet data or the Ark balance changes, after its fir
65
80
66
81
Reminder state is stored locally in a versioned SharedPreferences record keyed by master fingerprint. It contains only:
67
82
68
-
- the last acknowledgement time;
69
-
- the highest handled balance tier;
70
-
- a pending action start time; and
71
-
- the pending action balance tier.
83
+
- the last acknowledgement time; and
84
+
- whether the balance milestone has been shown.
72
85
73
-
No mnemonic, seed, private key, vault key, or other secret is stored or logged by the reminder. Malformed, unsupported, or unreadable reminder records are replaced with an empty in-memory record, which favors showing another reminder over suppressing one indefinitely. The underlying read failure is logged without exposing it to the user.
86
+
No mnemonic, seed, private key, vault key, or other secret is stored or logged by the reminder. Keys written by an earlier shape of the record are ignored rather than rejected. Malformed, unsupported, or unreadable records are replaced with an empty in-memory record, which favors showing another reminder over suppressing one indefinitely. The underlying read failure is logged without exposing it to the user.
74
87
75
88
If a reminder action cannot be persisted, the overlay stays visible so the user can retry. A localized "close for now" action is then available as a session-only escape; it does not acknowledge the reminder, so the app evaluates it again on the next launch.
76
89
90
+
## Backup Settings screen
91
+
92
+
The screen is composed top to bottom as status rows, then at most one hero, then the settings menu:
93
+
94
+
1.**Status rows** — Physical Backup and Encrypted Vault, each Tested or Not tested, with a muted "Last tested …" line under a tested physical backup.
95
+
2.**Hero** — the single most useful action right now, or nothing at all: an urgent *Back up your wallet* card when nothing is backed up, *add a physical backup* for a vault-only wallet, *test your backup* once the physical test is over a year old, and no card when the physical backup is fresh. The hero derives its posture from the same domain code the reminder uses, so the two can never disagree.
96
+
3.**Menu** — Encrypted vault settings, Labels, Transaction History, plus the vault-key and test-backup entries when they apply. There is no "Start Backup" row: the zero-backup hero is the way in.
97
+
77
98
## Architecture
78
99
79
100
The feature follows the repository flow described in `ARCHITECTURE.md`:
@@ -86,7 +107,7 @@ The repository boundary uses a domain entity and a separate persistence model. R
86
107
87
108
## Tests
88
109
89
-
The focused suite covers the posture matrix, exact 90-day boundary, strict balance thresholds, testnet and non-local-signer exclusions, pending actions, persistence corruption, physical completion, and Recoverbull completion ordering.
110
+
The focused suite covers the posture matrix, both cadences and their exact boundaries, the anchor rule (a fresh vault must not silence a stale physical backup), the inclusive milestone threshold and its once-per-wallet guarantee, dismissal snoozing without recording a test, testnet and non-local-signer exclusions, persistence corruption, and each hero the screen can render.
0 commit comments