Sprint 33 — The extra-large jumbo, on a foundation that held
I called Sprint 33 for one feature: the objective layer — Areas, Projects, Goals — so a task in Domi could finally roll up to a why. That was the plan. What actually happened is that the plan finished early, the foundation kept holding weight I threw at it, and I just… kept going. Three days, twenty-three PRs, four genuinely new capabilities. An extra-large jumbo.
The lesson of the sprint isn’t any single thing I shipped. It’s that none of them were expensive — because each one leaned on something built in an earlier sprint that I’d half-forgotten was load-bearing.
The objective layer
Tasks had no why. “Rotate the winter tires” is a chore; it’s more legible if it lives under Vehicles, and a goal like “spend less on the car this year” lives there too. So Domi now has three concepts:
- Areas — permanent life domains (Home, Health, Finances, Kids, Vehicles, Pet, Sports, Wellness, Hobby, and now Travel). Ten seeded by default, extend with your own, shown alphabetically.
- Projects — bounded initiatives that complete (“renovate the basement”).
- Goals — measurable targets attached to exactly one Area or Project.
The nice part: predicted tasks file themselves. When the engine predicts “book the dental cleaning,” it lands in Health on its own, by category. You don’t sort anything. The whole thing — schema, chat tools, the Settings manager, auto-filing — went in across four PRs and mirrored the exact applicator + RLS pattern the entity-relationships work laid down two sprints ago. I was mostly filling in a stencil.
Onboarding that knows what it doesn’t know
Then I built guided onboarding — a “Get started” thread that walks you through telling Domi about your household. The trick that makes it not-annoying is that it’s count-aware: it tracks coverage per topic, and “we don’t have a boat” is a complete answer, not a skipped one. It stops asking once a topic is genuinely settled, including when you’ve said none. Conversational, not a form.
A real MCP server — and ChatGPT and Claude logging into it
This is the one I’m still a little giddy about. Domi now runs a live Model Context Protocol server at domiapp.ai/api/mcp. You add it as a custom connector in ChatGPT or Claude, log in, and ask your assistant “what tasks do I have this week?” or “what’s left to set up for my household?” — and it answers from your real Domi data.
Two decisions made it tractable:
- It rides on Vercel, as a Next.js route handler, instead of the standalone Fly.io process the original architecture imagined. One fewer deploy surface for the dogfood; the standalone option is still open if it ever earns its keep.
- Auth is OAuth 2.1 via WorkOS AuthKit. The MCP route verifies a bearer token against WorkOS’s public keys — no API key on the hot path — resolves your tenant from the token, and every tool call runs under the same row-level security as the web app. The assistant is acting as you, scoped to your data, audited like everything else.
V1 is read + discovery only — the assistants can read your household and help run onboarding, but they can’t mutate anything; risky writes stay in the app behind confirmation cards. I tested it end-to-end with both ChatGPT and Claude against the live endpoint. Watching ChatGPT walk through a WorkOS login screen and then list my actual tasks back to me was the best fifteen minutes of the sprint.
That same WorkOS decision turned into an ADR for making it Domi’s primary login — one provider for Google, Apple, and passwordless email — with a careful note that authentication is not key custody, so the zero-knowledge path I keep promising myself stays open.
The dogfood tax, paid in small change
Living in the app keeps handing me a punch list, and I cleared a chunk of it:
- Members got email and phone — and the phone formats itself for the country of your main residence.
- Deleting your account now actually logs you out and drops you at the front door, instead of leaving you in a half-authenticated limbo.
- An expired magic link used to dump you on an ugly API error page; now it sends you home.
- Small copy and ordering fixes, and retiring a manual reorder control that alphabetical sorting made pointless.
Each of these was a paragraph of code. Each one removes a daily wince.
The actual theme
I keep writing sprint posts about the foundation — Sprint 31 was patching it, Sprint 32 was discovering an unenforced role column hiding in it. This one was the opposite: the foundation paid out. Areas reused the relationships pattern. The MCP server reused the auth, the RLS, the audit log, the list queries — all of it already there. I added four capabilities and barely wrote any genuinely new infrastructure.
That’s what a foundation is for. You spend the boring sprints building it so that one day you get an extra-large jumbo for the price of a stencil. 23 PRs merged in three days.
Next sprint: keep dogfooding, and start measuring the week-4 numbers for real.