walletSyncPassesRunProvider top-level property

Provider<bool> walletSyncPassesRunProvider
final

SSOT for money copy that leans on a future SYNC PASS (#401 R5).

The core's durable money machinery — the §6.1 ReBroadcast arm that re-sends an already-signed-but-unbroadcast transaction, the §6.3 reconcile that re-queues a mid-signature row, the queue drain — is driven EXCLUSIVELY by after_synced. No completed pass, no correction. So any string that promises the wallet "will finish this on a later sync" is a claim about THIS provider, and must be qualified when it reads false.

BOTH TERMS (#403 R5 — the #401 version read the DRIVE alone).

!= failed is the term that does the work the policy cannot: a start command that FAILED means the loop never ran and after_synced never fires, WHILE the policy still reads true. Copy keyed on the policy alone tells exactly that user their payment completes on its own — the wait-forever this qualifier exists to prevent.

walletSyncPolicyProvider is the host's own switch and a plain Provider<bool> — it cannot lag and it has no in-flight command behind it. BE PRECISE ABOUT WHAT IT BUYS, because the review that proposed this conjunction credited it with more: today it is REDUNDANT with the drive's disabledByHost, since build returns that synchronously the moment the policy reads false, and every settle path is already policy-aware. It is kept as defence in depth — the host's explicit "off" should be authoritative for a money promise no matter what the drive is doing — not as a fix for a reproduced bug. It does NOT close the background/resume flicker either: on the flickering host the policy reads true, so the whole predicate rides on the drive. What closes that is the drive itself now PRESERVING failed across a background stop (see the settle in _command), which is where the bug was.

The two remaining drive states are deliberately not consulted:

  • WalletSyncDrive.suspended — the app is backgrounded, so nothing that reads this is on screen, and a resume restarts the loop. Qualifying it would be a pause the user can only ever be told about after it ended.
  • WalletSyncDrive.inactive — no session; every consumer here renders inside a live one.

TWO ROLES, one predicate (#405 sharpened the original "never a gate" claim, which this provider outgrew the moment a DESTRUCTIVE op was keyed on it):

  1. HONESTY QUALIFIER for money copy — the original role. A false hides and disables NOTHING: the affordances a committed payment needs stay available (the #400 R9 rule), the copy just stops promising a schedule it cannot keep.
  2. READINESS PREDICATE for the two ops that can only COMPLETE through a sync pass — the rescan (wipes the DB and rebuilds as sync scans) and the swap deep scan (marks ranges a later pass covers). Offering those while no pass will run is a destructive dead end, not a copy problem, so they are genuinely DISABLED on false and the rescan is refused again at its commit point. This is not an exception to rule 1 — neither op is an affordance a committed payment needs; both are user-initiated recovery.

The line between the two: never remove a way to FINISH or RESCUE money that is already committed; do refuse to START something that provably cannot end.

CAUSE-AGNOSTIC AT EVERY CONSUMER (#405). The predicate deliberately does not say WHICH term is false, and no consumer may re-derive it to word its copy: "turn syncing on in settings" is the right instruction for disabledByHost and the WRONG one for a failed start, and a family that words itself per cause drifts back into exactly the #401 R5 bug the SSOT replaced. Consumers state the CONDITION ("syncing isn't running"); the sync badge and the start-failed notice — both always on the wallet surface — own the cause and its remedy.

Implementation

final walletSyncPassesRunProvider = Provider<bool>((ref) {
  return ref.watch(walletSyncPolicyProvider) &&
      ref.watch(walletSyncControllerProvider) != WalletSyncDrive.failed;
});