Oliver Wehrens

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:

WhereWhat lives there
PRD.mdthe durable what & why — behaviour, screens (§9), reasoning, reversals
GitHub issuesthe done-when — per-feature acceptance criteria and delivery tracking
docs/adr/decisions with architectural weight, numbered, with the reasoning
UBIQUITOUS_LANGUAGE.mdevery 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)

SideGate
Backenduv run pytest · uv run ruff check app tests · uv run mypy app — all green
iOSxcodegen 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:

LayerRunsCatches
pre-commit hookevery commit, sub-secondcontract YAML parse errors; direct edits to the generated app spec; auto-mirrors root → app copy
pre-push hookevery pushbackend quality gate (pytest · ruff · mypy); mirror drift that slipped past --no-verify; warns on doc-less code pushes
backend-ci.ymlpushes/PRs touching cooked-backend/the same backend gate, server-side
ios-ui-tests.ymliOS changesCookedKit tests + simulator UI/E2E — the Swift build the hooks skip
contracts-ci.ymlcontract changesYAML 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

ToolPhasePurpose
/grillproductinterrogate an idea until no material ambiguity remains
/to-prdproductconversation → feature PRD issue + doc ripples
/to-issuesproductPRD → tracer-bullet issues with acceptance criteria
/qaproductconversational bug intake → durable issues
/implementdevelopmentbuild one issue to the definition of done
/tdddevelopmentred→green vertical slices at the agreed seams
/verifyverificationfresh-context, evidence-based acceptance check
git hooks (tools/git-hooks/)developmentfast local invariants + backend gate
GitHub Actions (.github/workflows/)verificationserver-side gates that can’t be skipped
GitHub issues (docs/agents/issue-tracker.md)allthe done-when: acceptance criteria + tracking

Skills are versioned with the repo: canonical files in .agents/skills/, symlinked from .claude/skills/.