Product Development Workflow
Intro
I’m writing a meal planning, cookbook, shopping list app for just my family. Sharing, real time sync, photo and URL and recipes, ratings and so much more. Many ideas I had in mind for years. Having all the access to to my own data, sharing it with other tools. Amazing. So I never did it since it was way to much work. Not anymore.
I wanted to learn and get my hands dirty how this can be done. Yes, it is a green field project but I need to start somewhere.
Tech Stack is simple, iOS App (Apple watch + TV coming later) and Python backend. Claude Code + Github.
I spent a 5h train ride to write the prd. I defined an openapi.yaml to be the contract between backend and frontend. After that I toyed around with skills and just the last two - three days I got to a point where I had the Aha moment. I checked with Fable the flow I had, did improvements, and it wrote the workflow down below - partially using Matts great skills.
I have a few hundreds tests for frontend, backend, UI E2E, contract tests. I reviewed architecture and code, Fable did this as well. We did some improvments, but so far, not too shabby.
Now I ‘just’ talk about a feature, like I want to make sure the App works even if I have no network connection. This is 10-15 Minute discussion with claude, describing my ideas, answering questions. After that the prd is in Github and broken down in the correct order as a bunch of issues.
Then just /implement in auto mode and it knows about my workflow, does the tickets in the right order and does the /verify as background agent at the end. Sometimes it runs for 60-90 minutes, while I dream up more features.
Since the Claude Sandbox Runtime can’t do iOS development / compiling I isolate backend tickets for ‘ralph.sh’. I spend 1h to refine them, bring them in the right dependency order and then fired the script.
The following is my WORKFLOW.md which is linked in my CLAUDE.md. And yes, I develop on main :).
It is addictive, and I guess thats called honeymoon.
Product development workflow
How a feature travels from idea to verified delivery in this repo. Three phases, each with
its own owner, tools, and exit condition. CLAUDE.md holds the one-screen summary; this is
the full description.
The core principle: ambiguity is removed before code exists, and delivery is proven by someone who didn’t build it. The human decides in phase 1; agents execute in phases 2–3; deterministic gates (hooks, CI) backstop everything.
PRODUCT WORK DEVELOPMENT VERIFICATION
(human decides) (agent builds) (fresh agent proves)
idea ─► /grill ─► /to-prd ─► /to-issues ─► /implement ─► /verify ─► closed issue
│ │ │ │
▼ ▼ ▼ ▼
decisions PRD.md / ADRs / quality gates evidence per
recorded glossary updated + doc ripples acceptance criterion
▲
bug reports ──► /qa ────────────────────────────┘
Phase 1 — Product work
Owner: the human. Agents challenge, structure, and record — they do not decide scope.
Exit condition: issues on the tracker with acceptance criteria, labelled ready-for-agent.
1a. Discover — /grill
A relentless interview that attacks the idea before it becomes a spec: the premise itself (is this worth building? is there a simpler non-feature?), conflicts with existing ADRs / PRD decisions / v1 scope, undefined terms, and the Cooked hard cases (offline, realtime, owner-vs-member, contract impact, ingest/LLM failure paths). One question per turn, concrete scenarios over abstractions.
Output: a numbered decision list, rejected alternatives with reasons, and deliberately deferred open questions. Killing ambiguity here is ~10x cheaper than finding it in review.
1b. Specify — /to-prd
Synthesizes the grilled conversation into a feature PRD — no re-interviewing. It proposes the test seams (existing seams preferred, as high as possible, ideally one) and checks them with the human, then publishes the PRD as a GitHub issue.
It also ripples the durable parts into the repo docs, respecting the split that keeps each document honest:
| Where | What lives there |
|---|---|
PRD.md | the durable what & why — behaviour, screens (§9), reasoning, reversals |
| GitHub issues | the done-when — per-feature acceptance criteria and delivery tracking |
docs/adr/ | decisions with architectural weight, numbered, with the reasoning |
UBIQUITOUS_LANGUAGE.md | every domain term — issues and code use these words, no synonyms |
Never blur the split: acceptance criteria don’t creep into PRD.md; what/why doesn’t live
only in an issue.
1c. Slice — /to-issues
Breaks the PRD into tracer-bullet issues: thin vertical slices that cut through every
layer (contract, backend, iOS, tests) and are individually demoable. The human approves
granularity and dependency order before anything is published. Issues get acceptance
criteria and the ready-for-agent label (vocabulary: docs/agents/triage-labels.md).
Bug intake — /qa
Bugs skip the pipeline’s front half: an interactive QA session where the human describes
problems conversationally and the agent files durable, user-language issues (no file paths,
reproduction steps mandatory). Small fixes go straight to phase 2; anything that smells like
a feature goes back through /grill.
Phase 2 — Development
Owner: the implementing agent, one issue at a time.
Exit condition: the definition of done in /implement — gates green, docs rippled, committed to main.
Build — /implement (+ /tdd)
Work happens at the pre-agreed seams from the PRD, test-first where possible (/tdd:
vertical red→green slices, behaviour through public interfaces, never implementation
details). Typecheck and targeted tests run continuously; the full suite once at the end.
The contracts are the source of truth
openapi.yaml / asyncapi.yaml define the system; the Swift client is generated from
them and the backend is validated against them. A contract change ripples both ways in the
same change — regenerate/adjust the app, confirm the backend still satisfies it. Never edit
generated Swift. The app’s bundled copy of the spec is a byte-exact mirror maintained by the
pre-commit hook.
Quality gates (from CLAUDE.md)
| Side | Gate |
|---|---|
| Backend | uv run pytest · uv run ruff check app tests · uv run mypy app — all green |
| iOS | xcodegen generate succeeds · swift test --package-path app/CookedKit green · LiveBackendE2ETests when the contract changed |
Docs are part of the change, not a follow-up
The consistency rules in CLAUDE.md apply inside the change: new/changed behaviour →
PRD.md; architecture → BACKEND.md; new terms → UBIQUITOUS_LANGUAGE.md; dev-loop
changes → DEV_ENV.md; weighty decisions → an ADR. The pre-push hook nudges (warn-only)
when code or contracts push without any .md alongside.
Git
Straight to main — no branches, no PRs. #NN in commit messages are GitHub issue numbers.
Phase 3 — Verification
Owner: a fresh agent that did not build the work. Exit condition: every acceptance criterion demonstrated with evidence; the issue closed on proof.
Prove — /verify
The implementer never verifies its own work — shared assumptions hide shared mistakes. A
fresh-context agent gets only the issue number and must demonstrate each acceptance
criterion against the running system: curl against the seeded dev backend, targeted
end-to-end tests, contract/mirror checks. “The code looks right” is explicitly not evidence.
Criteria only a human on a device can check are tagged MANUAL with a one-line script —
never silently passed.
The verdict lands as a per-criterion table on the issue: all ✅ (plus listed MANUAL items) →
criteria checked off, issue closed; any ❌ → issue stays open with a reproduction and goes
back to phase 2 as ready-for-agent.
The deterministic safety nets
Layered so that skipping one layer is caught by the next:
| Layer | Runs | Catches |
|---|---|---|
pre-commit hook | every commit, sub-second | contract YAML parse errors; direct edits to the generated app spec; auto-mirrors root → app copy |
pre-push hook | every push | backend quality gate (pytest · ruff · mypy); mirror drift that slipped past --no-verify; warns on doc-less code pushes |
backend-ci.yml | pushes/PRs touching cooked-backend/ | the same backend gate, server-side |
ios-ui-tests.yml | iOS changes | CookedKit tests + simulator UI/E2E — the Swift build the hooks skip |
contracts-ci.yml | contract changes | YAML parse + OpenAPI mirror invariant, server-side (hooks are opt-in) |
Hooks are activated per clone with git config core.hooksPath tools/git-hooks
(tools/git-hooks/README.md).
Derived documentation
docs/product/ is a plain-language rendering of what PRD/ADRs/specs define — refreshed at
feature milestones, never authoritative, never an implementation source. On conflict, the
PRD/ADRs/specs win.
Tools at a glance
| Tool | Phase | Purpose |
|---|---|---|
/grill | product | interrogate an idea until no material ambiguity remains |
/to-prd | product | conversation → feature PRD issue + doc ripples |
/to-issues | product | PRD → tracer-bullet issues with acceptance criteria |
/qa | product | conversational bug intake → durable issues |
/implement | development | build one issue to the definition of done |
/tdd | development | red→green vertical slices at the agreed seams |
/verify | verification | fresh-context, evidence-based acceptance check |
git hooks (tools/git-hooks/) | development | fast local invariants + backend gate |
GitHub Actions (.github/workflows/) | verification | server-side gates that can’t be skipped |
GitHub issues (docs/agents/issue-tracker.md) | all | the done-when: acceptance criteria + tracking |
Skills are versioned with the repo: canonical files in .agents/skills/, symlinked from
.claude/skills/.