Skip to content

feat(simplefin): discover new bridge accounts on sync - #1877

Merged
canalesb93 merged 6 commits into
mainfrom
worktree-simplefin-bridge-model
Jun 24, 2026
Merged

feat(simplefin): discover new bridge accounts on sync#1877
canalesb93 merged 6 commits into
mainfrom
worktree-simplefin-bridge-model

Conversation

@canalesb93

@canalesb93 canalesb93 commented Jun 17, 2026

Copy link
Copy Markdown
Owner

A SimpleFIN bridge spans every bank you link there with one token — and that set grows as you add banks at the bridge later. Breadbox synced transactions for those banks but never re-discovered the accounts, so a bank added after connect never showed up. This fixes that, on the normal per-connection model.

What it does

  • provider.SyncResult gains an Accounts field. A SimpleFIN connection returns its full current account set on every sync (captured once — the bridge lists the full set on every window). Fixed-account-set providers (Plaid, CSV) leave it nil.
  • Before processing transactions, the sync engine upserts that set via a new UpsertAccountMetadata query — metadata only, never balances (those are owned by the balance-refresh path), connection_id set only on INSERT — and refreshes its account caches. So a bank linked at the bridge after connect appears automatically on the next sync, and its transactions resolve in that same sync instead of being dropped.

Model: per-connection (not a singleton)

An earlier revision of this PR reworked SimpleFIN into a settings-managed singleton (one token, managed in Settings). That was rolled back: SimpleFIN stays a normal per-connection provider like Plaid/Teller — each token creates its own connection, attributed to a household member, and a household can run multiple SimpleFIN bridges. One token already covers every bank at a bridge, so the singleton added a constraint that blocked multi-bridge households for no benefit. The account-discovery fix above is model-agnostic and is the keeper.

Per-account owner editing (to split a single shared bridge across household members) is a separate, provider-agnostic follow-up.

Tests

  • Unit: SimpleFIN surfaces its account set across multi-window backfills (TestSyncTransactions_ReturnsDiscoveredAccounts).
  • Integration: a new account discovered mid-sync is created on the connection and its transaction resolves onto it (TestSync_DiscoversNewAccountsMidSync).
  • go build/vet ./... + unit + integration all green on a tree merged with latest main.

Docs: docs/simplefin-integration.md + .claude/rules/providers.md document the on-sync account discovery.

🤖 Generated with Claude Code

canalesb93 and others added 4 commits June 16, 2026 18:17
…ounts discovered on sync

SimpleFIN is a bridge: one access URL spans every bank linked at the
bridge. The prior integration modeled it like a per-bank provider — it
prompted for a setup token in the Add-connection flow and never
re-discovered accounts after connect, so banks added at the bridge later
never appeared. This reworks it to reflect what SimpleFIN actually is.

Sync-time account discovery (the real backend gap):
- Provider.SyncResult gains an Accounts field. SimpleFIN populates the
  full deduped account set each sync; fixed-set providers (Plaid, CSV)
  leave it nil.
- The sync engine upserts discovered accounts (metadata only, never
  balances) via a new UpsertAccountMetadata query before processing
  transactions, refreshing its account caches. Banks linked at the
  bridge after connect now flow in automatically.

Settings-managed singleton token:
- The token is pasted in Settings → Providers → SimpleFIN (side drawer),
  not the per-bank flow. At most one active connection per household:
  GetActiveConnectionByProvider resolves the singleton, so the handler
  creates on first claim or rotates the credential in place — re-pasting
  can no longer orphan accounts.
- Add-connection shows SimpleFIN as a non-selectable informational row
  linking out to the bridge and to Settings, reinforcing the one-token
  mental model.

Tests: unit coverage for deduped account discovery across sync windows;
integration coverage for mid-sync account creation + transaction
resolution onto the new account. Docs and provider rules updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ect path + collapse dedup

Addresses two review findings on the bridge-model PR:

1. Singleton enforced in one place. ProvidersSaveSimpleFINHandler creates
   or rotates the household's single SimpleFIN connection, but the generic
   /-/exchange-token handler still accepted provider=simplefin and called
   RegisterNewConnection unconditionally — a second creation path that
   bypassed the GetActiveConnectionByProvider singleton and left duplicate
   connections the Settings drawer (LIMIT 1) couldn't see or manage. Reject
   simplefin there (Settings is the one connect surface; relink rotates in
   place via reauthSimplefin). This also lets us delete the now-unreachable
   SimpleFIN-specific branches in ExchangeTokenHandler (institution-name
   override + daily-schedule assignment).

2. One dedup authority. The provider re-collected the full account set on
   every sync window and deduped it with a map, duplicating the engine's
   generic cross-result dedup. The bridge returns the full set on every
   window (the date range only bounds nested transactions), so capture it
   once and let the engine remain the single dedup point.

Adds a unit test asserting /-/exchange-token rejects SimpleFIN with 400.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…er-connection + sync-discovery

Reverses the singleton bridge model from this PR's earlier commits and
returns SimpleFIN to the normal per-connection flow (like Plaid/Teller):
each pasted setup token creates its own connection, attributed to a
household member, and a household can have multiple SimpleFIN bridges.
The singleton bought nothing that per-connection didn't already give —
one token already covers every bank at a bridge — while blocking
multi-bridge households and fighting the connections model.

Kept (the genuinely valuable half): sync-time account discovery. Each
SimpleFIN connection re-reads its full account set every sync and the
engine upserts new accounts (metadata only, never balances) before
processing transactions, so banks added at the bridge after connect flow
in automatically. This is model-agnostic and works the same per
connection.

Reverted to pre-PR state: the Settings token drawer + singleton
create-or-rotate handler, the GetActiveConnectionByProvider singleton
query, the /-/exchange-token SimpleFIN reject, and the connect-flow UI
(restores the inline token-paste step). Docs updated to describe
per-connection + sync discovery.

Per-account owner editing (for a single bridge shared across members) is
tracked as a separate, provider-agnostic follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@canalesb93 canalesb93 changed the title feat(simplefin): bridge-aggregator model — one token in Settings, accounts discovered on sync feat(simplefin): discover new bridge accounts on sync Jun 17, 2026
canalesb93 and others added 2 commits June 23, 2026 20:17
Address code-review findings on the discovery path:
- engine: reset pendingAccounts + seen-set on ErrSyncRetryable so a
  retried sync rebuilds the discovery buffers from scratch
- simplefin: restore union-across-windows account dedup (a single
  window can omit accounts; capture-once dropped them)
- integration test: assert a pre-existing account stays exactly one
  row after discovery, guarding against duplication

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EtFunz2hhqiMwRnWk6gdvE
@canalesb93
canalesb93 merged commit b3ceb48 into main Jun 24, 2026
8 checks passed
@canalesb93
canalesb93 deleted the worktree-simplefin-bridge-model branch June 24, 2026 03:25
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.

1 participant