Engineering
Next.js 15 on the App Router, TypeScript in strict mode, Postgres via Supabase, Tailwind against a small set of design tokens. No component library. The interesting parts are not the stack.
The Two-Person Rule Lives in the Database
The central invariant is that the person who proposes something is not the person who confirms it. It would have been easy to enforce in the interface by hiding a button. It is enforced in Postgres instead, as a column constraint, so no code path can get around it and no future feature has to remember.
check (confirmed_by is null or confirmed_by <> proposed_by)Resolutions, meetings, schedule swaps, support payments and the untangling checklist all carry a version of that line. An assistant acting on one person’s behalf runs into the same wall a browser does, which is the point: the confirm step is meaningless if either side can supply both halves.
Privacy is Row-Level Security, Not Filtering
Almost everything is shared on purpose. Four things are not: the other person’s budget line items until they share them, their statement of net worth until it is exchanged, their comments during a cool-down hold, and everything at all for anyone who is not one of the two allowlisted addresses.
Those boundaries are row-level security policies rather than conditions in application code. The difference matters when a second client appears: the answer to “can this new thing see her private budget” is decided by the database, and the new client is not in a position to argue.
The Interface Never Does Arithmetic
Child support, maintenance, the marital portion of a retirement account, the separate-property house claim and the settle-up ledger are pure functions in one directory, each with exported test vectors. Components read results. They never compute.
The statutory caps and rates sit in one constants file with a comment noting the date they next adjust, because a number that changes on a schedule and is scattered across a codebase is a bug with a start date.
The Documents Fill Themselves
Both draft agreements are markdown containing tokens where decisions go. Resolving a question fills its clause. Changing a worksheet figure changes every number derived from it. The alternative, keeping prose in sync with data by hand, does not survive contact with a real negotiation: the documents drift, and nobody notices until a lawyer reads them.
An MCP Server That Runs as a Normal User
More than sixty tools let an assistant read and write the app as one of the two people, over a local stdio process or over HTTP behind OAuth. The design decision worth naming is what it does not have: no service role key in the tool path, no elevated query, no bypass. The server holds an ordinary user session and every request goes through the same row-level security a browser does.
Over HTTP the bearer token is a Supabase JWT rather than a token the app invented, so there is no second permission model to drift out of sync with the first, and identity is resolved per request rather than per process, because one process answers both people.
Every write logs to the activity feed marked as assisted. A change made through an assistant shows up in the other person’s feed like any other. A private edit to shared state is the one thing the app exists to prevent.
Two Guards That Exist Because of Near Misses
A demo fixture is generated from a fictional persona for screenshots and prototypes, and a test fails the build on any trace of real personal data reaching it. Hand-editing anything it produces is forbidden, because a hand-edit is how a real name gets back in after the guard has already passed.
The seed script refuses to run against a database that has answers in it. It only ever upserts content, but it runs with elevated credentials and it is the script most likely to be edited while changing question text, and overwriting a question changes what a recorded answer was an answer to.