Distribution computes the remainder part by subtraction so float splits
always sum exactly to the total. Omits money() and addAmounts() from the
brief since no downstream task consumes either and the brief's own tests
don't cover them.
bun init --react=shadcn, with the demo API routes removed since this app
has no backend. Adds project invariants to CLAUDE.md and a smoke test
proving the domain layer imports with no DOM.
25 TDD tasks across five phases: foundation, pure domain core, IndexedDB
persistence, JSON/ODS export, UI, and shipping (PWA + Docker + CI).
Self-review caught five defects, fixed inline:
- distributeByWeight divided by zero on all-zero weights, which a
zero-total purchase reaches and which would take the whole fold down
- summaryView's fold binding needed an explicit type or CI typecheck fails
- a duplicated module import in registerSw.ts
- a shell render test that would break once its routes read the store,
since happy-dom has no IndexedDB
- an unreadable assertion in the ODS numeric-cell test
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Design for a personal budgeting PWA replacing a spreadsheet workflow.
Key decisions captured:
- Static SPA, no backend in the data path, so it deploys to both GitHub
Pages and a self-hosted Docker image from one build artifact
- Bun toolchain (bun init --react=shadcn); no Vite, no Electron
- Pure derivation core over an IndexedDB fact store; envelope rollover
makes caching worthless, so every figure is folded from raw facts
- One Purchase entity covers simple, split, and financed purchases, which
is what lets splits and finance plans compose without special-casing
- Money is a float, with mandatory rounding at boundaries and last-part-
by-subtraction so distributed parts sum exactly to the whole
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>