Idea to TestFlight in Three Weeks
Three weeks ago Tobi was an idea in my head. Yesterday it hit feature complete on TestFlight. Here's the AI workflow that got it there.
Three weeks ago, Tobi was an idea in my head. Yesterday, it became feature complete on TestFlight. Tobi is a shared household maintenance app: everyone in a home shares one view of what needs upkeep and a log of what was done, so the knowledge of how to care for the house doesn’t live in just one person’s head.
This post is about how it got built that fast: a workflow that leans on Claude, ChatGPT, and Greptile, each doing the thing it’s best at. I’m not claiming this is the way to build with AI. It’s the way that’s working for me, and pieces of it might be useful in yours.
Start with a PRD, not with code
Before writing any Swift, I sat down with Claude Opus on high reasoning and worked out a PRD. I knew what I wanted in general, and I used the conversation to force out the details. Here’s the prompt that started it:
Can you help me create a PRD for a house maintenance app in swiftui with a
backend that allows for people to share the household with others?
Here's the features I want:
* Panel layout like the image attached
* A view to list projects, who they were performed by and any notes
* Maintenance log
* Item
* task
* How often
* Last performed
* Next due date
* Manual links
* Notes
* Replacement links if needed like links to air filters when they
need replacement
I would like to have notifications when maintenance is due like the day
it's due and then the day after and an option to choose when to remind
me again
I had to stop Claude pretty quickly. It started making product decisions for me, filling gaps with assumptions instead of flagging them. I told it to ask me before assuming anything, because I wanted to be the one designing the product. That one correction changed the whole conversation. Instead of getting a plausible-looking document full of choices I never made, I got questions, and answering them is where the actual design work happened.
Even then, the first PRD draft had open questions in it, so we worked through those until nothing was left unresolved. Once the PRD said what I meant, I asked Opus to break it into phases that made sense to build in order.
From phases to a Linear-driven loop
I took that PRD into Claude Code and worked through it one phase at a time. That was enough to get a working app, but over time the process tightened into a loop that runs through Linear:
- Capture. Every idea goes into Linear the moment it happens, straight from my phone, no AI involved.
- Define. Back at the computer, a Claude Code skill turns that rough capture into a fully specified ticket.
- Build. I tell Claude Code to work the ticket.
- Ship. The finished work goes up as a PR.
The skill in step two is where most of the leverage lives, so here are the exact rules it enforces:
- Read the codebase before asking me anything. Questions are only allowed when the answer isn’t inferrable from the code, the ticket, or the conversation.
- Always ask at least one question. The discovery conversation is never skippable.
- One open-ended question per message. No multiple choice, no suggested answers, because those smuggle the AI’s assumptions back in.
- If the ticket involves new UI, where it lives is a required question. The skill never invents a location.
- Keep asking until it’s roughly 95% confident the ticket is unambiguous, then create it directly.
- Work with a clear finish line goes in a scoped project. Work that never finishes, including all post-ship improvements, gets no project at all.
- More than about three independently closeable acceptance items means splitting into sub-issues.
- Every ticket leaves with a priority and labels for type and area.
- Same body structure every time: context paragraph up top, an
## Acceptancesection of verifiable done states at the bottom, and any “how to build it” notes in named sections between so they never get confused with “whether it’s done.”
That structure is what pushed Tobi from “usable by me” toward “usable by anyone.” The skill conversation is where I make decisions. The implementation run is where Claude executes them. When those two got blurred early on, I ended up reviewing code and product choices at the same time, and both suffered.
Never trust a single model’s spec
Between defining a ticket and building it, there’s one more step I’ve come to rely on: I don’t treat one model’s output as settled.
When I built Tobi’s feature flag service, the Linear skill produced a ticket that looked complete. I pasted it into ChatGPT anyway and asked it to evaluate the ticket and question any gaps.
It caught that the ticket quietly assumed a flag would already exist in PostHog before the service ever asked for it. In reality the service needed to accept any key or variant from day one, configured or not. It also pushed on a person property race the ticket had listed as a known issue, and working through it ended with the ticket redesigned so the race couldn’t happen at all.
The two models don’t share assumptions, so the second one catches what the first one silently baked in.
Review every PR, even as a team of one
ChatGPT checks the spec on the way in. On the way out, once the ticket becomes a PR, the review is Greptile’s job. Mid-project, Greptile was releasing a new model, so I gave it a try, since I wanted the whole workflow to be AI driven, including the part where someone checks my work.
Every review comes with confidence scores and a sequence diagram of what the change actually does. For the Firestore functions especially, that high-level view is worth a lot. Here’s the diagram it generated for my notification scheduler PR:
It catches real bugs, too. On one PR it flagged a P1 in my subscription reconciliation:
API failure incorrectly deactivates active subscriptions
revenueCatEntitlementActivereturnsfalsefor both “subscriber has no entitlement” and “network/API error”.reconcileUserSubscriptionwrites thatfalseunconditionally tousers/{uid}.subscriptionActiveand then fans out to every owned household. A transient RevenueCat outage silently deactivates a subscriber’s household access until the next successful reconciliation.
That’s not linting noise. That’s a paying subscriber losing access to their household because RevenueCat had a slow morning, and I would not have caught it reading the diff myself. Greptile is a paid service, so the deal is that Tobi’s revenue has to cover it. If it can, Greptile stays in my toolbox, especially once Tobi starts expanding to other platforms.
Docs as part of the work, not after it
Everything above follows one ticket from capture to merged PR. Two habits run alongside that loop, and the first is documentation. Claude Code writes docs as part of the work itself, never as a cleanup task afterward.
The doc that matters most describes the Firestore data models. Those same models have to exist in the Cloud Functions and in iOS, and eventually on other platforms too, so there has to be one written description that every implementation answers to.
The rest cover the day-to-day. The analytics doc means I always know exactly what’s being measured without spelunking through code. An App Store Optimization (ASO) doc keeps store copy easy to produce whenever it’s needed. And when I settle on a pattern I want followed, that becomes a doc as well, so future Claude Code sessions can reference the convention instead of reinventing it.
None of these docs are for some hypothetical future team. They pay off now, because every new Claude Code session starts from written-down truth instead of re-deriving it.
Use the better model for the creative work
The second habit is handing creative output to ChatGPT, which I think is simply better at it. The app icon on TestFlight right now came from one sentence about what I wanted plus the app’s color scheme, first try. It wrote the App Store copy too, which the ASO doc makes quick to put together.
What didn’t go smoothly
Three weeks also included the failures, and the AI workflow caused some of them.
The biggest one got its own post: the emails my app swore it sent. An AI assistant confidently told me that verifying a root domain in Resend covers its subdomains. It doesn’t, and because my email logging recorded attempts instead of outcomes, that wrong answer hid for weeks inside a log that said everything was fine.
The other time sink was the paywall. My monetization model is a little unusual: only the owner of a household needs a subscription, so I can’t gate access on RevenueCat entitlements alone. Whether you get in depends on RevenueCat and on Firestore entries about your household.
Those two load at different speeds, so I put up about four different PRs circling around how and when the paywall shows. Fix the entitlement path, and the Firestore path lags and flashes the paywall at people who should never see it. Fix that, and something else regresses.
The eventual answer was boring: one loading flag, and nothing renders until both RevenueCat and Firestore have loaded.
No model was going to hand me that on the first try, because the problem wasn’t in any single PR. It was in my design splitting the truth across two systems. AI makes the write-code part fast, and that speed made it very tempting to keep patching instead of stopping to notice the architecture underneath.
The workflow from a high level
Here’s the whole thing in one view:
- A PRD conversation where the AI asks and I decide, broken into buildable phases
- Ideas captured in Linear the moment they happen, defined into real tickets with a Claude Code skill
- ChatGPT cross-examining Claude’s specs
- Claude Code implementing one well-scoped ticket at a time, ending in a PR
- Greptile reviewing every PR
- Documentation written continuously by Claude Code, so new sessions start from what’s already decided
- ChatGPT handling the creative output, from the app icon to the App Store copy
You don’t have to adopt all of this at once. If I were adding one piece to an existing project, I’d start with ticket definition and continuous documentation: one settles the decisions before any code exists, and the other keeps those decisions from getting lost between sessions.
And if a shared view of your home’s maintenance sounds like something your household needs, Tobi is feature complete and on TestFlight. Sign up at gettobi.com.