peterfrey.app

This site. Written as a full specification before any code existed, then handed to AI builders to construct.

The specification took longer to write than the site took to build. I used Lovable to iterate the product specification and fine-tune design, and what it produced is what you are reading. Codex and Claude Code take over for the production build.

  • Lovable
  • React
  • TypeScript
  • MDX
  • Claude
  • Codex

Background

Everything on this page was built the same way, and this site is the version where I wrote the method down.

Claude runs as the orchestrator. It holds the specification, writes the prompts for each build phase, reviews what comes back, and folds the durable decisions into the spec rather than into a chat thread that gets lost. I make the product decisions and approve every phase. The builders build.

Then a discovery build, whose only job was to render the spec so I could see what it got wrong. It was meant to be thrown away. You are reading it: it turned out good enough to publish while the production build gets written, so it went live instead of into the bin. What survives either way is a harder specification, and the production build starts from there.

The pattern underneath it is simple: the specification is the thing that carries between tools, and the code is not. Swap the builder and the spec still stands. Lose the spec and the next builder starts from a description in my head.

How it works

It's a static site. Every piece of content lives in the repository as JSON or MDX: the article list, the career timeline, the project pages you're reading now, and the standing copy for each page. Nothing is fetched at runtime, nothing renders from a database, and no page depends on an external service being up.

Technology

What you are on right now is the discovery build: React and TypeScript, built and hosted by Lovable. The production version is specified as Next.js with the App Router, TypeScript in strict mode, on Vercel, and it is being written. Either way: no CMS, no database, no cookies, and no consent banner, because there's nothing to consent to.

How it was built

The specification came first: purpose, audience, routes, content model, every requirement with its own acceptance criteria, and a release plan. I wrote it with Claude, then had two other models read it cold and argue with it. Some of what they found was right and got applied. Some of it was wrong and got logged as rejected, with the reason.

The copy came next, all of it, before any builder saw the repository. That's a deliberate rule: builders receive finished content and never write, reword, or improve a sentence.

Only then did the building start. The discovery build ran through several phases, fine-tuning the product spec, and is what shipped. The production build will work against the same spec, which lives in its own repository as the single source of truth and reaches each builder by a one-way sync.

Three things I did not expect:

A specification can be internally consistent and impossible to build. Twice now the spec has described a component that no implementation could produce, and both times it had passed the reviews I ran against it. What caught them was a builder planning the work against a real browser and stopping to say so. Reading a spec against itself finds contradictions. It does not find the ones that only exist between the document and reality.

Design decisions arrive by looking. Almost every visual change on this site came from seeing a rendered page rather than from re-reading the design section. So I now build a throwaway page with the real content before writing that part of the spec, which turned a week of build cycles into an afternoon.

Fixing a review's findings introduces new ones. One design revision was audited, corrected, and audited again: the second pass found about as much as the first, and roughly half of it had been introduced by the corrections. One finding repeated the exact mistake it had just fixed, a section away.

All projects