The OBSERVED arm of bankAccountView — accounts the feed has seen
that no first-class table registers.
Every other arm starts from a registered account and asks what moved through
it. This one starts from the feed and asks which accounts were never
registered, and that question is only meaningful because of what the feed
already excludes: victorTransactionSyncActivities resolves each side of a
transaction against victor_account ∪ Victor-provider virtual_account and
writes no row for a side it does not recognise. bank_transaction rows are
therefore only ever written for accounts on CFX's side, so a
(routing_number, account_number) pair here that matches nothing is not a
stranger — it is a CFX or customer account nobody registered.
It doubles as the detector for the one failure mode bankAccountKeySql
cannot defend against: a producer writing the same account under a
differently-formatted string (0001234567 for victor_account's 1234567)
splits one balance across two keys, and it surfaces here as a row that looks
suspiciously like a known one. Reporting the fact of a mismatch is
deliberately all this does — normalising the account number to "fix" it would
merge genuinely distinct accounts into one balance, silently, across two
customers (§ 4.4). The remedy is upstream either way: register the account,
or fix the producer.
Aggregation is a GROUP BY, not the per-row lateral the other four arms use.
This arm is already scanning bank_transaction to find its rows, so grouping
costs one scan rather than one indexed aggregate per row — the expressions
are the same ones, from bankAccountBalanceAggregatesSql.
bank_id is MIN(bank_id) rather than a GROUP BY term. Grouping on it
would let one account appear as two rows sharing a key, each holding a
partial balance, which breaks the correspondence between a key and its
figures. One account belongs to one bank in practice; if that stops being
true, it is a finding, not a rendering problem.
dbmate owns the DDL: the body below is mirrored by
20260814200000_bank_account_view.sql.
The
OBSERVEDarm of bankAccountView — accounts the feed has seen that no first-class table registers.Every other arm starts from a registered account and asks what moved through it. This one starts from the feed and asks which accounts were never registered, and that question is only meaningful because of what the feed already excludes:
victorTransactionSyncActivitiesresolves each side of a transaction againstvictor_account∪ Victor-providervirtual_accountand writes no row for a side it does not recognise.bank_transactionrows are therefore only ever written for accounts on CFX's side, so a(routing_number, account_number)pair here that matches nothing is not a stranger — it is a CFX or customer account nobody registered.It doubles as the detector for the one failure mode bankAccountKeySql cannot defend against: a producer writing the same account under a differently-formatted string (
0001234567forvictor_account's1234567) splits one balance across two keys, and it surfaces here as a row that looks suspiciously like a known one. Reporting the fact of a mismatch is deliberately all this does — normalising the account number to "fix" it would merge genuinely distinct accounts into one balance, silently, across two customers (§ 4.4). The remedy is upstream either way: register the account, or fix the producer.Aggregation is a
GROUP BY, not the per-row lateral the other four arms use. This arm is already scanningbank_transactionto find its rows, so grouping costs one scan rather than one indexed aggregate per row — the expressions are the same ones, from bankAccountBalanceAggregatesSql.bank_idisMIN(bank_id)rather than aGROUP BYterm. Grouping on it would let one account appear as two rows sharing akey, each holding a partial balance, which breaks the correspondence between a key and its figures. One account belongs to one bank in practice; if that stops being true, it is a finding, not a rendering problem.dbmate owns the DDL: the body below is mirrored by
20260814200000_bank_account_view.sql.