Files
budget2.0/TODO.md
T
Lucas WintherandClaude Opus 5 53707b14ca feat(tooling): drive headless Chrome, and see the app for the first time
CLAUDE.md, AGENTS.md, ARCHITECTURE.md and TODO.md all said no browser exists
in this environment, so appearance had never been verified by anyone. Chrome
is on PATH. Its own --screenshot flag is what fails: it fires on the load
event, before this app's async IndexedDB read resolves, and captures a blank
page — which is presumably how the claim took hold.

scripts/screenshot.ts drives CDP instead: waits past the load event, seeds
IndexedDB via --eval-file and reboots the app with --reload, clicks with
--click, applies shadcn's class-based dark palette with --dark, and reports
console errors and uncaught exceptions. A page that renders blank from a
thrown error looks identical to an empty one, so the console output is half
the value.

Two defects found by simply looking:

- The month table's numeric columns had no horizontal padding, so figures ran
  into each other: "+7,175.00 DKK4,200.00 DKK". Now padded.
- Dev logged "The script has an unsupported MIME type ('text/html')" on every
  boot, because registerServiceWorker asks for sw.js and the "/*" catch-all
  answered with the HTML shell. Dev now serves an inert worker.

Confirmed working, first time seen: the carry meter (including red where a post
is overspent), the Task 5 rule-history editor, the Task 6 "change from here"
control, and baked FX rates showing as "manual - 2026-09-01" in Settings.

Recorded in the docs, with the limits stated: this container's fontconfig maps
every generic family to a monospace face, so screenshots need a FONTCONFIG_FILE
override or you will chase a font bug that does not exist; Chrome starts from
an empty profile each run; and no screenshot can judge how a design feels.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 14:46:59 +02:00

5.4 KiB

TODO

Work queued for whoever picks this up next, human or agent. Ordered. Each item says enough to act on without re-deriving the reasoning.

Current state: branch feature/versioned-rules, 9 commits ahead of main, not merged, not pushed. 288 tests passing, bunx tsc --noEmit clean, bun run build succeeds. Versioned allocation rules are COMPLETE, all six tasks. Neither new editing surface has been seen in a browser — see section 5.


1. Time-versioned allocation rules — DONE, all six tasks

Spec: docs/superpowers/specs/2026-09-02-versioned-allocation-rules-design.md Plan: docs/superpowers/plans/2026-09-02-versioned-allocation-rules.md Ledger + extracted briefs: .superpowers/sdd/2026-09-02-versioned-allocation-rules/

Task What Commit
1 Post.rules: RuleVersion[] replaces standingRule 68712e7
2 v1 -> v2 migration 15dcf93, 1214d2b
3 setRuleFrom / removeRuleFrom actions de128e3
4 JSON import validation for the rule series 1cb91d3
5 Rule-history editor in Settings e3023d0
6 "Change from here" control in the month view f94e458

Tasks 3-6 were executed by hand rather than dispatched. Every deviation from the briefs is recorded in the ledger's task log with its reasoning; four of them fixed real defects in the brief code (JSX that would not compile, a component that pre-filled the previous post's rule, a number field that could not be cleared, and missing validation that let a NaN allocation into the fold). Worth a read before reviewing the diff.

Not yet done for this feature: nobody has independently reviewed tasks 3-6, and no browser has seen the two new editing surfaces.

2. Baked FX rates — designed and approved, not built

So a fresh install can convert a EUR purchase on day one instead of erroring.

  • build.ts fetches DKK/USD/EUR at build time and embeds them via define; on any network failure it falls back to committed constants rather than failing the build. Fetched rates get source: "api" with the build date; the fallback constants get source: "manual", so Settings never claims a hardcoded number came from an API.
  • They seed a new dataset only. They must NOT act as a fallback whenever a rate is missing — that would undo a deliberate decision: clearing a rate removes the row so MissingRateError surfaces, instead of silently converting money at a number the user never chose.
  • Domain purity forces the shape: createSeedDataset takes the rates as an argument; the env read lives in a new src/store/bakedRates.ts, in the store layer, using the try/catch accessor pattern (see CLAUDE.md gotchas).

3. Visual redesign

The owner's words: "right now it looks a bit boring, simple and ugly. We want sleek, modern, and nice to use." Sequencing was their explicit choice: finish all six rules tasks first, then redesign everything.

PRODUCT.md exists and holds product truth. There is no DESIGN.md; the incumbent visual world is the --budget-* tokens in src/index.css plus the design contract summarised in ARCHITECTURE.md. A redesign should treat that as evidence and anti-reference, not as something to preserve.

4. Smaller items, in no particular order

  • confirmImport has no .catch (src/ui/routes/settings/DataSection.tsx). A failed import write is an unhandled rejection with no error shown. One line.
  • PNG icons 192/512 are not shipped, so Chrome's install prompt is incomplete. Binary assets cannot be generated in the agent environment; a human needs to add them.
  • round2 in src/store/actions.ts hardcodes 2 decimals rather than reading CURRENCY_DIGITS. Correct for DKK/USD/EUR; silently wrong the day a non-2dp currency is added. Same latent issue in sliceTotal (src/domain/plans.ts).
  • EMPTY_FIGURES (src/domain/fold.ts) is exported non-frozen and returned by shared reference from figuresFor. No consumer mutates it today; Object.freeze is a one-line hardening.
  • buildWorkbook calls monthView per (post, month), making a full ODS export O(posts x months^2). Fine at personal-budget scale.
  • The dev-only src/manifest.webmanifest placeholder hardcodes scope: "/". Harmless in production, misleading if you dev-serve under a subpath.

5. Still needs a human

scripts/screenshot.ts drives headless Chrome, so the list here is much shorter than it was. Seen and confirmed working: the carry meter (it renders, and shows red where a post is overspent), the rule-history editor in Settings, the "change from here" control, the baked FX rates arriving in a fresh dataset, and every route at desktop and mobile widths.

Genuinely outstanding:

  • How it looks and feels. A screenshot proves a layout is not broken. It cannot tell you the design is good, and the container's fonts are not the owner's fonts.
  • Offline behaviour, the install prompt, and the update prompt. The dev server now serves a deliberately inert sw.js, so dev exercises registration but nothing else.
  • the generated .ods opening in a real spreadsheet (validated structurally: mimetype first and stored, well-formed XML, numeric cells).
  • keyboard flow in fast entry — Tab traversal, Backspace-to-remove, and focus placement after a row auto-appends. Drivable over CDP in principle; not yet done.
  • GitHub Actions has never been executed.