Sep 2026 · 3 min read
PublishedBuild Log #8: The Backend Had Been Invisible for a Month
By this point the backend could take a real bank statement in, validate it, extract it, parse it, deduplicate it, categorize it, and analyze it — 221 tests proved all of it worked. None of it had ever been on a screen. Every check had gone through curl or pytest.
This entry is the first slice of frontend, and it's deliberately narrow: open the app, import statements, watch the backend process them, see the ones you've already imported. No Dashboard, no Review yet — those routes exist as honest "lands in the next update" placeholders instead of 404s, because a missing link in the sidebar is worse than an admitted gap.
Import shows the backend's real answer, not a summary of it
A client-side check catches the obvious problems — wrong extension, too large — before anything uploads. Past that, the screen renders whatever the backend actually decided about each file individually: accepted, or rejected with the specific reason. Not one error for the whole batch.
I dropped the browser's own PDF file-type filter on the upload input. It made the file picker quietly hide non-PDFs before they ever reached my rejection screen — which meant the one state that screen exists to demonstrate could never actually appear.
A rule locked before writing more frontend
Server data lives in TanStack Query. UI-only state — is a panel open, which tab is active — lives in local React state. Anything about navigation or filtering lives in the URL's search params. No Redux, no Zustand. Small as this slice is, it's the rule every later screen has to fit into, and it's easier to hold a line like that from the first component than to retrofit it after five screens already disagree.
Next
An off-plan detour first — a real bug found by using the app, not by testing it — then back to build-plan #9 for the Dashboard and Review.