Domi, four months in: by the numbers


Three months ago I posted one month in, then two, then three. Here’s the four-month mark, same method: every number comes from the GitHub API, git, the filesystem, and ccusage, with the commands at the bottom.

Month one built a shippable system. Month two made it safe to let other people in. Month three let them in and grew a second codebase. Month four is the first one where the factory changed more than the product did — so this post has two parts: the numbers, and then what actually happened in August.

The headline

One-hundred-twenty-three calendar days. Sprints S0 through S47, with S48 opening — six sprints closed in August on a week-long cadence, which is a rhythm, not an acceleration. Across both repos: 1,861 commits and 1,456 pull requests merged.

Merged pull requests per month, May–August 2026 Stacked bars. May 312 (domi). Jun 357 (domi). Jul 366 total: 328 domi plus 38 domi-mobile. Aug 421 total: 271 domi plus 150 domi-mobile. 0 100 200 300 400 500 May: 312 domi PRs 312 May Jun: 357 domi PRs 357 Jun Jul: 328 domi PRs Jul: 38 domi-mobile PRs 38 mobile 366 Jul Aug: 271 domi PRs Aug: 150 domi-mobile PRs 150 mobile 421 Aug domi domi-mobile
Merged pull requests per month. August's 421 is the first month above 400, and the first where the mobile repo is more than a third of it.
Monthdomidomi-mobileCombinedPer calendar day
May31231210.1
June35735711.9
July3283836611.8
August27115042113.6

August is the first month to break out of the ten-to-twelve-a-day band: 421 merged PRs, 15% more than July, on 29 active days. The mix is the story. domi had its lowest month — 271, down from 328 — while domi-mobile went from 38 to 150, four times its first month, in the four sprints (M3–M6) that took it from a capture client to an app that works on a real phone. The web app didn’t run out of work; the attention moved to the phone, and the numbers followed.

The shared PR/issue numbering pool in domi climbed from #1431 to #1882 — 451 new numbers, against 383 in July.

Week by week

Merged pull requests per week, May–August 2026 Stacked bars over 18 weeks. Weekly totals after the bootstrap week range from 50 to 155. The peak is the week of Jul 27 (Jul 27–Aug 2) at 155: 111 domi plus 44 mobile. August weeks: 118 (65 domi + 53 mobile), 75 (66 + 9), 124 (76 + 48), 96 (62 + 34). 0 40 80 120 160 Week of 4/27: 1 domi PRs 4/27 Week of 5/4: 76 domi PRs 5/4 Week of 5/11: 70 domi PRs 5/11 Week of 5/18: 62 domi PRs 5/18 Week of 5/25: 107 domi PRs 107 5/25 Week of 6/1: 114 domi PRs 114 6/1 Week of 6/8: 84 domi PRs 6/8 Week of 6/15: 57 domi PRs 6/15 Week of 6/22: 76 domi PRs 6/22 Week of 6/29: 55 domi PRs 6/29 Week of 7/6: 84 domi PRs 7/6 Week of 7/13: 50 domi PRs 7/13 Week of 7/20: 52 domi PRs 7/20 Week of 7/27: 111 domi PRs Week of 7/27: 44 domi-mobile PRs 155 7/27 Week of 8/3: 65 domi PRs Week of 8/3: 53 domi-mobile PRs 118 8/3 Week of 8/10: 66 domi PRs Week of 8/10: 9 domi-mobile PRs 8/10 Week of 8/17: 76 domi PRs Week of 8/17: 48 domi-mobile PRs 124 8/17 Week of 8/24: 62 domi PRs Week of 8/24: 34 domi-mobile PRs 8/24 May June July August domi domi-mobile
Merged pull requests per week, Monday-start, through the week of August 24. The week of July 27 is now shown in full (seven days), which is why it reads 155 here and 147 in the previous post.

Eighteen weeks, and after the first one the range is 50 to 155. The week of July 27 is now the full seven days rather than the five the last post could see, and it’s the peak of the build: 155 merged PRs, 44 of them mobile, the mobile repo’s first full week of existence. August then settled into the same sawtooth as June — 118, 75, 124, 96 — with the heaviest week (August 17) being the one in which the factory got its registry, and carrying 48 mobile PRs on its own. Monday, August 31 added 37 more, off the end of the chart.

The pattern hasn’t changed in four months: a heavy week, then a lighter one where the heavy week’s work gets reviewed, fixed and documented. What changed is that the lighter weeks got heavier — August’s floor was 75, June’s was 55.

The code

The authored surface — code plus docs, excluding generated JSON like lockfiles and i18n bundles — is now about 387,000 lines across both repos, up 34% on the month. August added 86,681 lines and removed 7,481 in domi, and 21,102 / 2,395 in the mobile repo, which tripled in size.

Languagedomi (files / LoC)mobile (files / LoC)Combined LoC
TypeScript1,174 / 227,79298 / 18,264246,056
React TSX268 / 57,45123 / 5,91363,364
Markdown200 / 39,28023 / 3,32342,603
MDX (help site)356 / 14,62914,629
SQL migrations179 / 6,4336,433
YAML + OpenAPI34 / 13,7087 / 43214,140

The code subset — TypeScript, TSX, and SQL — is 315,853 lines across 1,742 files combined (291,676 / 1,621 in domi alone, up from 221,553 / 1,333 at the end of July). The authored surface grew 83% in June, 39% in July, and 34% in August — 29% in domi alone, still decelerating, with the mobile repo tripling on top of it. August was a construction month again, on the phone and on expenses, after July’s hardening.

Tests and gates

3,939 test cases across 414 files combined — 3,576 in domi (up from 2,303) and 363 in the mobile repo (up from 14). Tests grew 70% against the code’s 37%, the fourth month in a row the test count has outrun the thing it tests.

179 Drizzle migrations (was 154). And the number that moved most in the whole post: 28 GitHub Actions workflows combined, up from 15 — 25 in domi (was 13), 3 in mobile. Twelve new gates in one month. The pattern in them is one gate per way a thing has silently failed: a cross-model AI review on every pull request; a family of real-LLM eval gates, one per assistant behaviour — chat, document extraction, obligation extraction, transcription, contact classification, MCP write routing; browser end-to-end runs against each PR’s own preview deployment and a liveness check against production; a visual-regression gate; a credential-scope check that proves a database key cannot read another project; a production error-triage sweep that files its own tickets; and a docs-only merge path that doesn’t wait for a human. Two of July’s new gates existed because a convention had lapsed. August’s exist because I stopped being the reviewer of last resort.

The bilingual surface

English and French from day one, still CI-hard-failed on drift: 2,752 translation keys in each locale, exactly matched (up from 2,396), and 354 help pages split evenly — 177 each (up from 268). The help site grew another third; most of the new pages are the ones the assistant retrieves from when it answers “how do I…?”.

What August actually shipped

Thirty-one production promotes in thirty-one days — about one a day, against 42 in July and 8 in June. What reached production, from the promote tags rather than the merge list:

  • The mobile app went from nothing to real. Sign-in on a real iPhone, an offline-capable sync protocol, capture, chat with photo upload — built across two agent terminals over a local bridge. In the app itself: weather end to end, with hourly and daily forecasts and precipitation amounts that tell “confirmed dry” apart from “we don’t know”; every icon a vector, where the tab bar had been rendering literal text characters in every screenshot to that point; the brand typefaces shipped over the air to phones already installed, with no new build; auth that fails closed three separate ways — a revoked session signs out in seconds instead of up to fifteen minutes, a locked Keychain reads as unknown rather than signed-out, a reinstall no longer inherits the credentials iOS keeps behind a deleted app; and sync that stopped lying — a change that hasn’t left the device now says so on the row itself.
  • Expenses became a real money surface. Orders, subscriptions and other transactions as one ledger where every charge appears exactly once, with filters that survive a tab switch.
  • Weather shipped twice in one week — MET Norway first, then migrated to Open-Meteo on a direct call four days later, the contract frozen for mobile both times.
  • The app learned to look like itself: a design token system, light/dark/system themes, and a persisted per-user appearance preference.
  • Recurring events grew a memory — occurrence expansion at the sync boundary, skip-this-one, and rules like “third Thursday” that calendars actually use.
  • Household calendars stopped lying about moved appointments: recurrence overrides and cancelled single instances now import correctly from Google, iCloud and Microsoft.
  • Every pull request now gets an adversarial AI review before a human sees it, and CI opens the app in a real browser against each PR’s own preview.

What it cost to build

This is the cost of the coding agents working alongside me, measured with ccusage against local session logs — not the product’s own runtime LLM usage, which is a different and much smaller number.

August cost $8,765.93 across 29 active days: 27.16M billed input-plus-output tokens (4.39M in, 22.78M out), plus 10.9 billion cache-read tokens. That’s about $302 per active day. The heaviest single day was $1,061.10 on August 22, the day before the factory registry landed.

MonthCostBilled in+outActive days
May$4,418.00~13.0M29
June$4,935.75~20.9M30
July$5,724.9014.07M22
August$8,765.9327.16M29

August’s bill rose 53% on July. All-in token volume — cache included, which is where an agentic workload actually lives — went from about 3.5 billion to 11.1 billion, more than three times. So the factory ran three times the tokens for half again the money, and the per-model table says how.

Model (August)CostShare$ per 1M all-in tokensCache-read share
Claude Opus 5$4,914.1556.1%$0.72497.8%
Claude Fable 5$2,745.5931.3%$1.50497.3%
Claude Opus 4.8$645.587.4%$0.70198.1%
Claude Sonnet 5$460.575.3%$0.29697.6%
Claude Haiku 4.5$0.040.0%

In July, Fable 5 was 86% of the bill. In August it’s 31%, and Opus 5 — at less than half Fable’s unit price, and within a few cents of Opus 4.8’s — carried 56%. That’s the factory: each station is pinned to a model — half of them to Opus 5, the other half to Sonnet 5 at a fifth of Fable’s rate. The mix moved to cheaper models while the volume tripled, and the two roughly cancelled into “half again”. The unit prices themselves barely moved — Opus 4.8 has been $0.68–$0.70 per million for three months running, which is what makes it a usable control.

One line item excluded on purpose: the committed snapshot’s August total is $8,828.05, of which $62.12 is Codex CLI sessions whose working directories are personal folders, not Domi. The $8,765.93 above is the Claude-only figure and the one comparable with the earlier months.

Which repo spent it

Same line-level split as last time — every session-log line carries its own working directory, so tokens are summed per repo and each model’s cost allocated in proportion. It reconciles to ccusage’s per-model token totals exactly.

Repo (August)All-in tokensCostShare
domi8.04B$6,502.6174.2%
domi-mobile1.32B$1,106.3112.6%
software-factory0.99B$593.306.8%
sessions outside a repo0.33B$363.574.1%
this website0.35B$174.752.0%
domi-design64M$25.390.3%

Two new rows since July, and they are the factory’s own overhead made visible: the orchestration repo itself cost $593 to build, and the design-system repo $25. Neither ships to a user. Together with the website they’re 9% of the month.

Cost per merged PR: $20.82 overall — up a third from July’s $15.64. Split by repo it stops looking like one number: $24.00 in domi, $7.38 in domi-mobile. The web repo merged fewer PRs for roughly the same money, because that’s where the factory’s coordination — the bridge traffic, the review rounds, the stations reading each other’s contracts — actually runs. The mobile repo, a month old with one owner and no legacy, is where a merged PR is cheapest it has ever been.

Four months in, the build has cost $23,844.58 — about $194 a day across 123 days, for every repo on the machine.

The retention caveat from last time still stands, with a happier ending: ccusage prunes after about 40 days, so July can no longer be re-derived from the logs — the figure above is the one published at the time. August is captured in the committed monthly snapshot, taken on the first of the month by the runbook written for exactly this. Measured while it could be.

Four months at a glance

Flow — what each month produced:

Per monthMayJuneJulyAugust
PRs merged312357366421 (271 + 150 mobile)
Commits696348390427 (271 + 156 mobile)
Production releases84231
Active working days29302229
Build cost$4,418.00$4,935.75$5,724.90$8,765.93
Cost per merged PR$14.16$13.83$15.64$20.82

Stock — where things stood at each month’s end:

At month endMayJuneJulyAugust (domi)August (combined)
Authored LOC~109,700~200,500~288,000~359,000~387,000
Code (TS+TSX+SQL)81,510155,402229,843291,676315,853
Test cases8891,5192,3173,5763,939
Test files89184272372414
Migrations48111154179179
i18n keys (each locale)7901,6512,3962,7522,752
Help pages74144268354354
CI workflows910152528
Cumulative PRs3126691,0351,2681,456

The trend across four months: throughput up for the first time — 13.6 merged PRs per calendar day against three months at 10–12, all of the increase on the phone; code growth holding at a third a month after decelerating through July; tests outrunning code for the fourth month straight; gates nearly doubling in one month; and cost per unit of work up a third, for a reason that is about coordination, not model price — the model mix got cheaper, the amount of talking between agents got larger.

August, in brief

The numbers above are the product’s. This is what happened around it — the part of the month I’d remember if the tables were lost.

  • The factory became a factory. Five specialised agents — web, mobile, design, devx, marketing — had been coordinating over a local message bridge since July 30, informally. On August 23 that got a spine: an agent registry, a launcher, a card per station, and a sixth station to watch the other five. Two days later, a product station (acceptance) and a security station (reads everything, writes only security documents, can hold a release without asking, can never clear the hold). Two days after that, a chief — a proxy that carries my words to the stations, never my authority. Nine stations by August 27. On August 31 the conductor was retired, eight days old, because everything it watched for turned out to be a query rather than a judgement — that story is its own post. Eight stations today. How it’s wired is on the machinery page; what it’s doing right now is on the live board.
  • Review stopped being me. Every PR now gets an adversarial review from a different model family before a human sees it — that’s the workflow count going from 13 to 25 — and the measurement that came out of it is the one I’d point to first: across 150 merged PRs the median is fast and the tail is the whole problem, a quarter of the PRs eating about half the elapsed time, and review rounds rather than authorship being where it goes. Where the factory’s time actually goes has the numbers; When the factory argues with itself has the five-round case that made them concrete.
  • My decisions got a channel. Work for the stations now goes into one append-only queue, in my own words, and my rulings on it — approve, reject, a one-line answer — are recorded on the task itself, where every station reads them first-hand. A message from a station claiming I approved something is still just a message. Last week’s refresh of the factory page shipped through that channel: the station opened the PR, I read the preview, I typed one line onto the task.
  • The phone caught up. Four mobile sprints, 150 merged PRs, and a mobile repo that went from 8,967 lines to 27,932 — the whole visible app, from icons to auth, rebuilt in a month on top of the same backend the web app uses.
  • A release a day. Thirty-one production promotes, each through the same automated staging-to-production path, with release notes published behind it. In June that number was eight.

What it adds up to

Month one built the system. Month two made it safe to open. Month three opened it. Month four is the first one where more of the interesting decisions were made about the machine that builds Domi than about Domi — and the product numbers went up anyway.

What I keep relearning is the same thing with more agents in it: the guardrails are what make the pace survivable. Twenty-eight gates, 3,939 tests, and a reviewer that doesn’t share the author’s blind spot aren’t overhead on a team of eight agents and one person; they’re the only reason 421 pull requests can land in a month and the tenth one is still trustworthy.

One-hundred-twenty-three evenings. $23,845 and counting. Two repos, one household, eight stations.

Same commands next milestone.


Reproducible: the GitHub search API for PR counts (is:pr is:merged merged:2026-08-01..2026-08-31, UTC month boundary — the git-based counts in the earliest posts are superseded), git log --format=%cd bucketed by calendar month for commits (a bare --until=<date> leaks the next month’s first day, which is why the earlier posts’ monthly commit counts shift by a handful), null-delimited git ls-files -z … | xargs -0 cat | wc -l at the August 31 commit for lines of code, grep -c of it(/test( across test files, gh run list --workflow=promote-prod.yml --status success for releases, and ccusage daily --json for build cost, split per repo by reading the cwd on each session-log line. The committed monthly cost snapshot and its runbook live in the repo’s docs/metrics/usage/.