Records why the brand teal is --primary rather than --accent, why @font-face
cannot live in the bundled stylesheet (measured: 70 KB -> 303 KB of
render-blocking bytes, and unicode-range subsetting defeated), why the theme
preference is not in the dataset, and the carry-meter regression that earned
the undefined-token guard.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Six tasks. Task 1 is deliberately large because the model change is atomic
under TypeScript: Post.standingRule -> Post.rules will not compile as a partial
change, so everything referencing it moves together, with the settings UI
getting only a read-only summary until Task 5.
Self-review closed two spec requirements that had no test: a version dated
before the fold start is legal but inert, and an archived post still resolves
its rules because it still folds.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
A post's allocation rule becomes a dated series: a version takes effect in its
month and continues until the next replaces it. Per-month overrides survive
alongside as one-month exceptions, because collapsing the two loses the
distinction between a permanent change and a temporary one.
A post with no version yet allocates zero rather than applying its earliest
rule backwards, which would invent budget for months never budgeted and let
the rollover fold carry that fiction forward.
Requires the project's first real migration (schemaVersion 1 -> 2). It is a
pure shape change and behaviour-preserving by construction, and the spec makes
that a tested property rather than an assumption: every month's allocation must
be identical before and after.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Three entry-form changes specified together because they share the same tab
order: keyboard-driven bulk rows, purchases that no longer demand a day, and
an optional note per purchase.
Records why the date change widens IsoDate rather than splitting it into
month + optional day: the split is the better model but changes a type every
layer depends on and needs a migration run in the browser against the user's
only copy of their data, for an identical user-visible outcome. Verified that
monthOf() already parses both date shapes and still rejects out-of-range
months.
Time-versioned allocation rules are explicitly out of scope and get their own
cycle.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
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]>