Reference
Decision records
Twenty-one ADRs, and what each one settles.
The full text lives in docs/adr/ in the repository. This is the index, so the shape of the
argument is visible without opening twenty-one files.
Build and platform
| # | Decision |
|---|---|
| 0001 | Record architecture decisions |
| 0002 | Nx over Gradle + Bun, from day one |
| 0003 | PostgreSQL over MySQL, because this is a money system |
| 0004 | No :default on any config placeholder — fail loud at boot |
| 0005 | CI owns deploys; Cloudflare Workers Builds is off |
| 0018 | Env lives in exactly two places, and never in a root .env |
Identity and access
| # | Decision |
|---|---|
| 0006 | A thin IAM — no roles, no 2FA, no directory |
| 0007 | Backend-for-frontend auth, not SPA + PKCE |
| 0008 | scope == audience == app id — audience is the whole authorisation model |
Money
| # | Decision |
|---|---|
| 0009 | Transaction + postings, with transaction fees as the residual |
| 0010 | Prepaid vendors, debts and money lent out are all accounts |
| 0011 | Cached balances, proved by reconcile() |
| 0012 | Envelopes are virtual claims on the pooled balance |
| 0013 | Allocation order on income: tithe on gross, first |
| 0014 | Recurring bills create a pending item; they never auto-post |
| 0016 | Money is DECIMAL(18,2) in TZS; timestamps UTC stored, EAT displayed |
| 0017 | Corrections are edit-in-place with an append-only change log |
| 0020 | Open at today's balance; reconcile instead of backfilling |
| 0021 | A transaction has postings against accounts and lines against categories |
Scope and process
| # | Decision |
|---|---|
| 0015 | Run 1 stops before any finance entity exists |
| 0019 | Notifications go through a port: email now, in-app feed, Telegram never assumed |
What an ADR is for
Not to prove a decision was right — to record what was known when it was made, what the alternatives were, and what it cost. A decision whose reasoning is only in someone's memory gets re-litigated every six months, usually badly.