Domi, two months in: by the numbers
A month ago I posted Domi, one month in: by the numbers — a measured snapshot of a solo, ten-hours-a-week household-management build after 30 days. Here’s the two-month mark, same method: every number comes straight from git and the filesystem, commands at the bottom, no rounding-up from memory.
Month one was about getting a shippable system standing. Month two was about making it safe to let other people in — encryption, an in-house front door, a real staging environment — and making the house genuinely answerable.
The headline
Sixty calendar days. Sprints S0 (bootstrap) through S38 — eight more sprints closed in June (S31–S38). Cumulatively, 1,041 commits and ~652 pull requests merged to main.
A wrinkle worth being honest about: month two changed how PRs land. May merged through merge commits (299 of them); in June the repo moved to squash-merge, which collapses each PR to a single commit on main. That’s why raw commits dropped — 687 in May, 354 in June — even though throughput went up. The comparable metric is PRs, and June closed 353 of them versus May’s 299: about 11.8 merged PRs a day, the pace holding (and slightly rising) into month two.
June’s Monday-to-Sunday cadence:
| Week (2026) | Dates | PRs merged |
|---|---|---|
| Week 5 | Jun 1–7 | 114 |
| Week 6 | Jun 8–14 | 84 |
| Week 7 | Jun 15–21 | 57 |
| Week 8 | Jun 22–28 | 76 |
| Week 9 | Jun 29–30 | 22 |
The shared PR/issue numbering pool climbed from #592 to #1048 — 456 new numbers in a month.
The code
The authored surface — code plus docs, excluding generated JSON like lockfiles and i18n bundles — is now about 200,500 lines, up from ~109,700 a month ago. It nearly doubled: June alone added ~91,000 lines and removed ~11,000 (net +80k) across code and docs.
| Language | Files | LoC | Share | vs. month 1 (files / LoC) |
|---|---|---|---|---|
| TypeScript | 687 | 117,585 | ~59% | 375 / 62,210 |
| React TSX | 155 | 33,855 | ~17% | 73 / 17,141 |
| Markdown | 134 | 30,678 | ~15% | 87 / 20,525 |
| MDX (help site) | 146 | 7,835 | ~4% | 74 / 4,558 |
| SQL migrations | 111 | 3,962 | ~2% | 48 / 2,159 |
| YAML + OpenAPI | 19 | 6,609 | ~3% | 16 / 3,070 |
The code subset alone — TypeScript, TSX, and SQL — is 155,402 lines across 953 files (was 81,510 across 496). Markdown is still doing load-bearing work: the spec folder remains the source of truth, and month two added a stack of design docs (the encryption spec, three AI-principles distillations reconciled, the auth ADRs, the roadmap).
Tests and gates
1,519 test cases across 184 Vitest files — up from 889 across 89. Tests grew faster than the code did; the encryption work in particular is the kind of thing you don’t ship without a test proving a round-trip decrypt.
111 Drizzle migrations (was 48). Ten GitHub Actions workflows standing guard on every PR — lint and typecheck, a real-database integration suite on Neon ephemeral branches, a schema-drift gate that blocks a merge when the migration journal is ahead of the deployed DB (now checking the runtime role’s grants too), a real-LLM eval gate, OpenAPI/MCP-catalog sync, English/French key parity, and a closes-an-issue check. That posture is what lets one person add 91,000 lines in a month without it quietly rotting.
The bilingual surface
Still English and Quebec French from day one, still CI-hard-failed on drift: 1,651 translation keys in each locale, exactly matched (up from 790), and 144 help-site pages split evenly across the two languages — 72 each (up from 74 total). Every feature shipped in June — encryption, connectors, recurrence — landed with both its English and French help page in the same PR.
The AI surface
This is new enough to deserve its own line. Domi now routes 14 workload roles (chat, document extraction, classification, prediction, entity resolution, embeddings, reranking, and more) across three providers — Anthropic, OpenAI, and Cohere — through a single model gateway. The rule (locked as ADR-0007 in June) is that internal callers ask for a task, never a provider, and the catalog maps each task to the best model for it. A per-tenant “pick your provider” switch was built in June and then deliberately deleted for violating exactly that principle — a good reminder that a month of output includes the things you take back out.
What June actually shipped
The numbers don’t capture the shape of the month, so: the marquee work of June was encryption at rest — every household’s data sealable under a key unique to it (notes, contacts, finances, chat, document filenames and the original files in storage, and the search index), built across the entire data surface and validated end-to-end on a real environment. Alongside it:
- The front door went fully in-house. A week spent adopting a managed auth provider ended with it deleted; Domi’s sign-in is now its own (Google + email magic link) with its own OAuth server for the AI-assistant connectors, and identity never leaves our database.
- A real staging environment. For its whole life Domi had exactly one environment; June split staging from production, so changes get a place to be wrong before they reach real data.
- The house became answerable. Retrieval over documents and notes got materially smarter (entity-scoped search, parent-document fetch, reranking, hybrid keyword+semantic), and the assistant can now traverse the household graph to answer relationship questions.
- Multi-user became real. Invitations, server-side role enforcement, per-member notifications and digests, and 1Password-style sharing groups — the machinery for a second human to hold an account.
- Recurring events done properly, an AI-SDK major upgrade, guided onboarding, and a long tail of dogfood fixes.
What it cost to build
Building Domi still isn’t free. This is the cost of the Claude Code coding agent working alongside me — measured with ccusage against the local session logs — not the product’s own runtime LLM usage.
Month one burned about 13.0M tokens at $4,418. June ran hotter: ~20.9M billed input-plus-output tokens (7.7M in, 13.1M out) at $4,935.75 — roughly $165 a day, with activity on every one of June’s 30 days (May had two idle days). Against 353 merged PRs that’s about $14 of model spend per merged PR, down from $15.40 — more PRs absorbed a similar daily spend.
The shape of the spend changed. May had spiky fan-out days — its heaviest single day hit 2.71M tokens and $446 in one subagent blitz. June was remarkably flat: the priciest day was $433 and none strayed far from the ~$165 average — the signature of a steady grind rather than big-bang rewrites. The work ran almost entirely on Claude Opus (now with a 1M-context variant), with Haiku on the lighter tasks. (Billed input+output only; cache reads aren’t in the token count, though the dollar total includes them.)
Two months in, the build has cost ~$9,354 in Claude Code tokens (~33.8M billed in+out combined). Set against ~652 merged PRs, ~200,000 lines, and 1,519 tests, that’s the price of a second pair of hands that never got tired — on nights and weekends, for less than a contractor-week a month.
What it adds up to
Two months in, Domi is no longer just a shippable surface — it’s one I’ve handed real trust: my own family’s data, sealed under its own key, on an app with a staging tier to catch my mistakes and an in-house front door I fully understand. Month one built the system; month two made it safe to open. The remaining work before beta is validation, not construction — onboarding a real second person, measuring against the success bars, and a couple of auth-hardening steps.
What I take from the numbers is the same lesson, louder: a small, sustained cadence with strong guardrails compounds. Sixty quiet evenings turned into ~200,000 lines, 1,519 tests, encryption at rest, and a household app I’ve stopped demoing and started living in.
Same commands next milestone. That’s the whole point of measuring instead of guessing.
Month two vs month one, at a glance
Everything below comes straight from git and the filesystem.
| Metric | Month 1 (May) | Month 2 (cumulative) | June delta |
|---|---|---|---|
| PRs merged | 299 | ~652 | +353 |
| Commits | 687 | 1,041 | +354 (squash era) |
| Authored LOC | ~109,700 | ~200,500 | +~91k added |
| Code (TS+TSX+SQL) | 81,510 | 155,402 | — |
| Test cases / files | 889 / 89 | 1,519 / 184 | +630 / +95 |
| Migrations | 48 | 111 | +63 |
| i18n keys (each locale) | 790 | 1,651 | +861 |
| Help pages | 74 | 144 | +70 |
| GHA workflows | 9 | 10 | +1 |
| PR/issue pool | #592 | #1048 | +456 |
Reproducible from git: git log --oneline | wc -l for commits, --merges (May) and (#N)-in-subject squash commits (June) for PRs, and null-delimited git ls-files -z … | xargs -0 cat | wc -l for lines of code (null-delimited because the spec filenames contain spaces). Full methodology in the repo’s docs/metrics/ snapshots.