How I ship with Claude Code: my spec-driven workflow

How I build this site with Claude Code: specs and tickets as GitHub issues, one ticket per session, and a stop at every decision that's mine to make, not the agent's.

#1
4 min
Claude Code, GitHub Issues, Supabase, Nuxt UI

The problem

An AI coding agent works fast, and it fills every gap with a guess: a fact about me, a component prop, a change to the live database. On my own site, each of those guesses ends up in public. I wanted Claude Code to do most of the typing while every decision that matters stays with me.

What I built

A workflow made of two parts: rules that live in the repo, and a fixed order of skills that stops wherever a decision is mine. The skills come from Matt Pocock's skills collection. Everything below is how I built this site, including the Lab you're reading.

The rules live in the repo

Claude Code reads CLAUDE.md at the start of every session. Mine is short. It points to the documents that win when sources disagree: a project rules file, a facts file and a writing style guide. The workflow is one line of it:

**Workflow:** `/grill-with-docs` → `/research` (if needed) → `/prototype` (UI questions) → `/to-spec` → `/to-tickets` → `/implement` (one ticket per session) → `/code-review`. Stop at every owner gate in `PROJECT-RULES.md` §3.

The facts file is the only source for anything about me: roles, dates, projects, numbers. If a fact isn't in it, the agent writes TBD. It doesn't guess.

The steps and where they stop

  • /grill-with-docs questions me until a fuzzy plan turns into decisions. They're saved as architecture decision records (ADRs) and glossary terms.
  • /research checks an outside fact against primary sources and saves a cited note, when a decision waits on one.
  • /prototype builds throwaway variants on their own branch when a screen can't be settled by talking. I pick one, and the branch is never merged.
  • /to-spec writes one spec as a GitHub issue. I approve it.
  • /to-tickets splits the spec into tickets with dependencies, each sized for one session. I approve the list.
  • /implement builds one ticket in a new chat and makes one commit on the feature branch.
  • /code-review checks the changes against the repo's standards and against the spec, before I approve a merge.

Nothing is pushed, merged or deployed without my OK.

One ticket, one session

Each ticket says what to build, which documents it comes from, and how to check it's done. These acceptance criteria are from the ticket that built the Lab's RSS feed:

- [ ] Every fact traces to `brand-facts.md`; anything unknown is `TBD` and listed in the report.
- [ ] Typecheck and lint pass.
- [ ] `/code-review` has run; findings fixed or recorded.
- [ ] One commit on `feature/lab`. No push, merge or deploy.

Before it writes any code, Claude Code reads the ticket and asks every open question in one message, with its recommendation for each. I answer once, and it builds. At the end it reports what it couldn't match and what's still TBD. Anything only I can do, like a secret in the hosting dashboard or a check on the live site, goes into one launch checklist issue.

The database is production

The site's Supabase project is linked to the repo, and it's the live database. So the schema changes only through migration files, and Claude Code shows me a dry run before anything is applied:

npx supabase migration new <name>
npx supabase db push --dry-run

I run the real push myself. Claude Code reads the result, and it never writes test rows to production: pages that need data are checked against mock data.

What I learned

  1. One message of questions beats questions as they come up. Every open question, each with a recommendation, in one message. I answer in one pass, and the work doesn't stop halfway through a ticket.
  2. A throwaway prototype is cheap to reject. I rejected all four variants in the first homepage round and started the second round from reference sites I picked. Nothing had to be unpicked from the main branch.
  3. Check the component library, not the model's memory. The rules make Claude Code check every Nuxt UI component against the installed version. Building the Lab signup form, it found that the text input drops to 14px on wider screens unless it's set to fixed. The approved prototype had that bug.

Privacy

One email per entry. Unsubscribe any time.