Sprint M2 — the app becomes Domi
Sprint M1 ended with a walking skeleton on my phone: four empty tabs and a supply chain that worked. M2’s job was to make it Domi. It took about thirty hours of calendar time, thirty merged PRs, and a working pattern I haven’t seen written up much anywhere: two AI agents, one per repository, negotiating a contract over a message bridge while I made only the calls that were actually mine to make.
Two arcs, one day each
The mobile app needed two things from the backend: a way to sign in, and a way to sync. Each became a formal contract arc with the same shape — the mobile agent sends requirements, the backend agent drafts a provider-owned spec, mobile reviews it (three engine-blocking fixes caught in the sync review: client-generated IDs for offline chains, pull pagination, the visibility-gain case everyone forgets), I answer the open decisions, then phased implementation with a gate on every security-relevant step.
The OAuth arc ran Wednesday: static PKCE client, fifteen-minute tokens, rotating one-time refresh with family revocation, device sessions that show up in the web app’s settings. The sync arc ran Thursday: a trigger-fed change-log (triggers, because a database trigger fires on every write path — chat, cron, connector, script — so coverage is structural rather than a convention someone forgets), an opaque cursor with a commit-order watermark, idempotent capture push. Spec to shipped to validated on my phone, each inside a day.
The decision that cut the hardest problem
Sync systems die on conflict resolution. My call, mid-sprint: Tier 1 — offline you can capture (add a task, a note, a shopping item, complete, snooze — all idempotent, no conflicts possible by construction) but not edit existing rows; editing offline waits until dogfooding proves a real collision ever happens. The whole conflict engine now sits behind a demand gate instead of being built on speculation. The payoff was immediate: when the backend later shipped its third phase (archives and revocations arriving as deletes), the client needed zero changes — it had been shaped to the contract, not to the server of the moment.
The moment
Last night: my actual household on the Today screen — overdue in red, the day’s events, the week ahead — rendered entirely from an encrypted database on the phone. Airplane mode on, add a task, complete another: everything instant, because no gesture ever waits on a network. Airplane mode off: both changes on the web within seconds. That render-from-local-always rule isn’t an optimization, it’s the architecture — and it’s why the app feels fast in a way a spinner-driven app never can.
The two bugs no CI could see
Honesty section. My first device run found two real problems, and neither was catchable by either repo’s CI. Tasks vanished from Today — the server’s projections carry completion as a lifecycle state, not the timestamp field my cache indexed, so every synced task looked eternally active while my date filters saw NULLs. And my email disappeared from Settings — which I reported as a scary regression, and which turned out to be a stale fifteen-minute token making a guarded UI row hide itself silently. Both fixed within the hour (authoritative field mapping; retry-once-on-401 plus a rule that guarded rows must log, never vanish quietly). The structural lesson: unit CI on two sides of a contract is blind to the seam between them. A cross-system smoke test — a real authenticated probe against staging in CI — is now a named gap with a plan, not a surprise waiting to recur.
Also in the honesty section: one agent briefly pushed an empty branch to the other repo thanks to a working-directory reset, confessed, cleaned up completely, and wrote itself a permanent rule. And the trust boundaries got exercised for real — twice, the backend agent refused a “JF approves” relayed through its peer and waited for me to say it directly. Security-critical work on a relayed authorization is exactly how agent systems go wrong; watching the refusal happen at the fastest moment of the sprint, and cost nothing, was the best evidence yet that the posture is right.
By the numbers
Thirty merged PRs in ~30 hours (plus four closed on policy — native-module version bumps that only a device build could validate, now excluded by rule). Two provider arcs spec-to-device-validated same-day. Seventy messages over the agent bridge. Eight decisions that came to me — scheduling, an auth amendment, the Tier-1 cut, an architecture choice, five sync parameters — which is the working model in one sentence: the agents move the work; the human moves the risk. Twenty-five tests, ~1,800 lines of app code, one phone that now runs the household from its pocket.
What’s next
M3 is the product build on top of the contract stack: the calendar surface, camera capture into the document pipeline (the onboarding flow where you photograph a utility bill and Domi does the typing), chat on the phone. The plumbing era is over; what’s left is the part you can see.