Derek Gee
wV

wealthVault

Active build

Net worth, computed rather than copied. This is the one the rest of the fleet was in service of — the app I was really trying to build when I started learning to build things, and the only one whose problem I'd already been solving by hand for years.

StartedMay 2026
Commits79
Tests969 unit · 435 db · 33 e2e
Account classes9

Figures measured 2026-07-14.

Why it exists

the spreadsheet that wouldn't die

I have been tracking my net worth in an Excel workbook since 2018. Not casually — a real ritual, with real discipline, and it produced genuinely good data. It is also the most tedious thing I do. Every snapshot day starts by logging into fifteen-plus institutions one at a time and typing what each one says into a cell.

The typing isn't even the worst part. The worst part is the reconciliation. The workbook computes a single difference across the liability accounts, so when that number came out wrong, all it told me was that something, somewhere was off — and then I got to go find it by hand, account by account. It could tell me I was wrong. It could never tell me where.

Everything else was bolted on. Collectibles, crypto, and NFTs each got their own sub-ledger, wired into the balance sheet with formulas that drifted out of agreement with their own source data the moment either side was edited. And the history — the part I actually care about — was trapped: the trendline preserves a daily net-worth value going back to 2021, but there is no way to ask it a question. You cannot query a spreadsheet about which account did that, on that day, and why.

wealthVault is that ritual, rebuilt as software that can answer the question the spreadsheet couldn't.

What it does

one idea, then the machinery under it

Every dollar of balance change must be explained. If an account was worth $10,100 and is now worth $9,500, that $600 has to be accounted for — by transactions that add up to it, or by an explicit, recorded market adjustment saying "this moved because the market moved." Anything left over is a residual, and a residual is a question the app makes you answer rather than a rounding error it quietly absorbs. That single rule is the difference between this and a budgeting app, and everything below is in service of it.

Snapshot entry that knows what day it is. Balances are entered against a date, not against "now." The calendar marks which days actually have recorded data, and each account shows whether the number in front of you is one you recorded or one carried forward from the last day you did — so a value you never typed can never quietly masquerade as a value you did.

Transactions that understand transfers. Moving money between two of your own accounts is not income and not an expense, and treating it as either is how a ledger starts lying to you. Transfers are first-class and paired: both legs, matched, as one fact. A mortgage payment splits itself into principal and interest, and money moving into a retirement account is recognized as a contribution rather than a disappearance.

Reconciliation that names the account. This is the one the spreadsheet could never do. Instead of a single aggregate difference, every account reconciles on its own: here is the balance change, here are the transactions contributing to it, here is what they fail to explain. For a holding that moves because the market moved, one button records that drift as an explicit adjustment — so it is explained rather than ignored.

A net-worth trendline you can interrogate. The thing the spreadsheet had and couldn't answer questions about. Range presets, a filter by owner, and a stacked decomposition that shows which classes of asset and liability actually moved — plus a 4% sidebar that turns today's number into what it would pay out annually, which is the only reason any of this is worth tracking.

Also, in less detail:

  • Real estate carries a full valuation history, and a What-If Sale widget for the question you actually want answered.
  • Crypto is marked to market daily by a scheduled job, rather than whenever I remember to look.
  • Transfers whose two legs land on different dates still get matched to each other — money in flight is still money.

Currently building: the importer that finally lifts seven years of spreadsheet into the database — a two-phase one, which does a full dry run and hands back an audit report before it is allowed to write anything. Its acceptance gate is unforgiving on purpose: the imported history has to match the workbook cell for cell, within a cent. Until it clears that bar, I am still keeping the spreadsheet. Replacing it is the whole point, and I don't get to claim it early.

Walkthrough

real app · invented money

These are screenshots of the running app, captured against a database seeded with a household I made up. Every institution, balance, transaction, and total below is fictional — none of it is mine, and none of it is anyone's. The four screens are in the order the ritual runs: record the balances, enter what happened, reconcile the difference, then look at the line.

The snapshot entry screen for July 11, 2026, with accounts grouped by class: cash, taxable, tax-deferred and tax-free investments, and an alternative asset. Each row shows a balance carried forward from July 6 in italics with a 'carried from Jul 6' caption beneath it, and the crypto account instead takes a coin quantity of 0.42 BTC.
Snapshot entry — every value is dated, and a carried-forward number says so rather than pretending you typed it. Demo data — fictional household
The transactions ledger, filtered by date range, account, kind and category. Expense rows carry a category and a Need or Want tag; two rows dated July 1 form a matched pair of transfer legs into a 401(k), each badged 'Contribution', and a second pair dated June 30 shows minus $1,200 leaving a checking account and plus $1,200 arriving in savings.
Transactions — transfers are paired, both legs shown; a retirement contribution is recognized as one. Demo data — fictional household
The reconciliation drill-down for a single checking account over June 22 to July 6, badged 'Has diff'. A residual breakdown panel reads: computed delta minus $600.00, sum of transactions minus $460.00, residual minus $140.00. Below it the snapshot pair and the list of contributing transactions that explain the other $460.
The drill-down — the balance moved $600, the transactions explain $460, and the app will not let the missing $140 go unremarked. Demo data — fictional household
The net worth trendline from January to July 2026, drawn as a stacked area chart decomposed into seven bands — cash, investments, real estate, collectibles, crypto, NFTs and liabilities — with range presets and an owner filter above it. A sidebar headed '4% Safe Income' converts the total net worth into an annual and a monthly figure.
The trendline — decomposed by class, filterable by owner, and turned into the number that actually matters. Demo data — fictional household

How it's built

boring on purpose
FrontendNext.js · React · TypeScript
DataPostgreSQL
TestsVitest · Playwright
Runs onself-hosted app · managed Postgres
Sourceprivate repo

The repo is private, and for this one the reason is not subtle: it is the app that knows what I am worth. Even with the real numbers living in a database rather than the source tree, a personal-finance repo's history is not something I am going to open up casually. The screenshots above are the honest answer instead — the real app, filled with a household that does not exist.

Changes go through the same pipeline as the rest of the fleet — planned, built, reviewed by two independent AI reviewers, then opened as a pull request where an automated reviewer takes its own pass. That process is a project in its own right, and it has its own page.

What I learned

two that cost me something

The guard that couldn't fire, and the reviewer that cared. A function had a defensive check in it that could never trip — the condition it tested for was impossible by construction. Both of my AI reviewers looked at it and said the same reasonable thing: harmless dead code, leave it. The automated reviewer on the pull request looked at the same line and called it a data-integrity hazard, and it was right. A guard that cannot fire is not harmless; it is a claim that someone checked, sitting exactly where a real check should be, quietly discouraging anyone from adding one. Dead code is not neutral when it is shaped like a safety net. Delete it, or make it mean something — and for anything guarding data integrity, make it fail loudly.

The mapper that was correct in isolation and wrong at the seam. I wrote the piece that turns spreadsheet rows into database records. It was pure, it was thoroughly unit-tested, and it was correct: every record it produced was exactly what the specification said it should produce. Several kinds of them would have been thrown straight back out by the database, because the target table's constraints ruled out values the mapper considered perfectly legal — a transfer with only one leg and nothing to pair it to, a transfer carrying a spending category it had no business having, a not-applicable row turned into an expense that was neither a need nor a want. The tests never saw any of it, because they checked what the mapper emitted rather than whether anything downstream would accept it. Code can be provably right on its own and still be wrong at the seam where it meets something with its own opinions. Now I read the constraints of the thing I'm writing into before I write the thing that writes to it, rather than letting the commit be where I find them out.

Next in the fleet

the other three vaults