Bank Transactions stats cache key ignores the date range, so tiles disagree with the table
Why (Accounting (Diagnostics), 2026-06-29)¶
Found while reviewing the Codex commit batch (owner asked: "look thru the
GitHub commits β¦ fix possible bugs that were over looked"). T-131 made the
Bank Transactions table follow the Accounting page's global Period (or the
local date picker), passing startDate / endDate into both the list and the
stats fetch. The stats summary tiles could then show numbers for a different
period than the table beneath them.
Diagnosis¶
getTransactionStats({ startDate, endDate, subsidiaryId })
(lib/accounting/transactions.ts) filters the transaction set by the date
range via listTransactions(options), but built its cache key with
CacheKeys.transactionStats(options?.subsidiaryId) β date range omitted.
So for one subsidiary, the first range computed in a 60s window wins, and every other range gets that cached result until the TTL expires. Switching the Period dropdown (or the local date filter) updated the table but could leave the tiles on the previous range's totals.
What shipped¶
lib/cache/index.tsβCacheKeys.transactionStatsnow takes optionalstartKey/endKeyand appends them after thetx-stats:<sub>prefix:tx-stats:<sub>:<start>:<end>. The date segment is only added when present, so the all-time key staystx-stats:<sub>.lib/accounting/transactions.tsβgetTransactionStatspasses the range (startDate.toISOString()/endDate.toISOString()) into the key.
The 20+ invalidateCache(CacheKeys.transactionStats(sub)) /
invalidateCache(CacheKeys.transactionStats()) mutation hooks are unchanged:
invalidateCache matches by substring (key.includes(pattern)), and the
range-less prefix tx-stats:<sub> is a prefix of every date variant, so a
mutation still purges all of that subsidiary's cached ranges.
Verification¶
- β
Attestation (Accounting (Diagnostics)): read
AGENTS.md; checked the board by scope, not UID β T-131 shipped the period-follow behavior but no task covered the stats-cache key collision it exposed. Tracking T-138. - Source: Accounting (Diagnostics) Β·
https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea - Unit test added asserting different ranges produce different keys and that the range-less invalidation prefix is a substring of every date variant.
NODE_OPTIONS=--max-old-space-size=4096 npx tsc --noEmitclean;npx vitest rungreen.
Blast Radius¶
Stats-cache keying only. The compute path, the API contract, and all invalidation call sites are unchanged; the fix narrows cache identity so tiles and table always reflect the same date scope.
Commit index (backfilled 2026-07-01, best-effort Β· Coaching (Diagnostic))¶
Candidate related commits, auto-backfilled from git on main: commits whose message references this task's UID or a PR number it cites. Not verified β this squash-merged history can't yield a precise per-task list, so rows tagged (mentions only) name the task in passing (may be tangential) and untagged work commits may be missing. Treat as a starting point: verify, prune tangential rows, and append any real ones per the AGENTS.md "record every related SHA" policy.
- (no git-discoverable commit references this UID or its PRs β append real SHAs here as identified.)