From idea to launch
This page is the mental model of a whole run (that is: one site being taken from nothing to live), for someone who has never watched one. A site passes through five phases: decide, design, build, test, ship. For each phase it shows who works, what they leave on disk, and what the gate (that is: the program site-step, which runs the checks and decides whether a step is finished) checks before the next phase may begin. The step-by-step tables are on The workflow, step by step; the states a run can be in are on Life-cycle of a run.
Four names for who does the work, because the page uses all four. The agent is the AI assistant working in a Claude Code session; it drives the run. A skill is a written set of instructions the agent follows for one creative step, such as the brief or the review. When the agent follows a skill, we call it that step's actor. When it only runs commands and lets the machine judge, we call it the orchestrator. Same agent, different hats.
Why it matters
An agent can build a website in an afternoon. The same agent will tell you the site is accessible, fast and legal, and it will be wrong about at least one of those. The site still looks finished.
The machine exists so you never have to take that word. Every phase ends in a check that code can run, and the next phase does not open until the check is green. Understanding the phases lets you predict where a run will stop, why, and what the fix is. A refusal (that is: the gate saying no) stops being a mystery.
The mental model in one sentence
A site is a sequence of fifteen claims, each proven by a program before the next claim may be made; the agent makes the claims true, and only the gate says they are.
Everything below elaborates that sentence. "Phase" is a reading aid — the machine knows only the fifteen steps in workflow.yaml and which step requires which. Those relationships are the arrows in the pipeline diagram further down. The phases group those steps by what kind of work happens in them.
The gate cycle: how every step closes
Before the phases, the loop that runs inside each one. It is the same for every step, whether the step takes a minute or a day.
Four things to notice, because every phase depends on them:
- The agent never marks a step done. It runs
site-step done(written asdone <step>below, for short), and the gate runs the step's exit predicates (that is: small programs that answer yes or no about the site). Each of those reads files, the build, a browser or the network — never the agent's report. - Two hooks watch the work. A hook (that is: a small program Claude Code runs on its own before or after the agent acts) needs no invitation. After every tool call,
site-logappends what happened to the ledger (that is: the site's append-only log,run-log.jsonl). Before every shell command,site-guardrefuses a deploy while a halt (that is: the stopped state a refusal puts the run in) stands. The two fail in opposite directions on purpose: if logging breaks, work continues; if the gate breaks, nothing passes. - A refusal is a halt, not a note. The run stops, the reason and the remedy are printed, and only succeeding on the same step lifts it. There is no override flag.
- Two other ways a step can count as satisfied, and both leave a trace. A blocker (that is: a recorded reason a step genuinely cannot be done) needs evidence on disk, and a small program per blocker checks that evidence before it is accepted. A sign-off (that is: a named person's approval, recorded with
site-step sign --by "Name") is required by two steps in addition to their checks. Neither is a way to skip a check quietly.
The five phases, named
The diagram below is the whole pipeline with the predicates each step runs. Blue boxes are steps where a skill does creative work. White boxes are steps where the orchestrator (that is: the agent driving the run) runs commands and the machine judges. The pen marks the two steps a human signs. In words: the skill steps are brief, design, scaffold, ux-review, commercial-review, publish-preview, legal-check and publish-live; the two signed steps are commercial-review and legal-check.
| Phase | Steps | What exists afterwards | Who does the work |
|---|---|---|---|
| 1 Decide | preflight, brief, repo, mwg-install | brief.md, a private repo, Modern Web Guidance installed and pinned | the site-brief skill, then the orchestrator |
| 2 Design | design, scaffold | tokens.css, declared colour pairs, a project that builds | design-system, site-scaffold |
| 3 Build | build | every page implemented; the ledger proves guidance came first | the orchestrator |
| 4 Test | security, flows, ux-review, ship-check, commercial-review | a frozen flow, a review, three ballots, a measurement; the phase ends when commercial-review closes | orchestrator, ux-review, commercial-jury, the owner |
| 5 Ship | publish-preview, legal-check, publish-live | a preview URL, a legal inventory, the live domain | site-publish, legal-check, a legal reviewer |
Phase 1 — Decide: preflight, brief, repo, guidance
Claim. Most bad websites are bad because nobody decided what they were for. So the first phase produces a decision, not code: one page, brief.md, that names the single action a visitor should take.
How it goes. The run opens with preflight, which runs the doctor (that is: a check of the machine the build will run on). It confirms Node, Chrome, a logged-in gh (GitHub's command-line tool), a logged-in wrangler (Cloudflare's deploy tool) and a current operator attestation. The attestation is a record, renewed yearly, in which a named person confirms two-factor login on both accounts and a review of the deploy token's permissions. A machine that cannot finish a build is refused before the build starts. Then the site-brief skill interviews you, one question at a time, with a recommended answer. It searches whatever prior material exists before it asks. It pins seven things:
- the one action a visitor should take
- the audience, in one sentence
- what that audience needs to know before acting
- the proof that exists today — not the proof that would be nice
- the pages, honestly; usually one to four
- tone and language
- constraints: domain, deadline, maintainer, legal needs
It also records what is out of scope, and whether any AI-generated likeness is involved.
What the gate checks. brief-complete.mjs parses the file: every required field non-empty, and exactly one primary action. Two equal calls to action fail the step, because a page with two equal buttons converts on neither. Then repo asks GitHub, not the agent, whether the site's repository is private. Finally mwg-install copies Modern Web Guidance (that is: Google Chrome's maintained set of current web-platform practice, packaged as a skill the agent searches before building anything) into the site's repo and pins its version in skills-lock.json. The version is read out of the installed skill; the tool refuses to invent one.
So what. When a run stalls in this phase, the fix is a decision, not a file. Ask what the visitor should do. If the answer has an "and" in it, the brief is not done.
Phase 2 — Design: tokens, pairs, a scaffold that builds
Claim. Colour, type and spacing are decided once, as tokens, before a single page exists. The contrast of those tokens is proven in both colour schemes before anyone may use them.
How it goes. The design-system skill searches Modern Web Guidance first, which the ledger records. It creates one Claude Design project for this site (Claude Design is Anthropic's design tool; the tokens are exported from that project) and writes src/styles/tokens.css. Each colour token uses light-dark(), a CSS function that carries a light and a dark value in one token. Nothing is shared between sites; the guidance is the shared knowledge, not the code. Then it declares which text token meets which background token, through site-context.mjs design, the only writer of site-context.json. A stylesheet does not say which colours ever meet on screen; the declaration does.
What the gate checks. contrast.mjs resolves both themes from the CSS and demands 4.5:1 for every declared pair. The usual allowance of lower contrast for large text does not apply, because a token gets reused at sizes nobody predicted. A coverage rule refuses a declaration that leaves a colour token out, so the cheapest way past the checker is closed. design-project.mjs confirms the recorded project still exists and is editable. Then site-scaffold stands up Astro with TypeScript. It brings a base layout, canonical and Open Graph tags, a JSON-LD stub, sitemap, robots, favicon and a 404 page. The tokens are imported once and the routes declared. The step closes when npm run build exits 0.
So what. The most common halt here is one pair a few tenths short in dark mode. The fix is the token, not the check. Declaring the routes now is what makes phase 3 auditable: the build must produce exactly the declared pages, no more, no fewer.
Phase 3 — Build: pages with guidance in front of them
Claim. The build step is where most refusals happen, and each one is a to-do line. Its exit proves two different things: that current guidance was consulted before every component, and that every route renders clean in a real browser.
How it goes. The orchestrator implements the pages. Before writing a new component, it runs npx modern-web-guidance search for the thing it is about to build. The site-log hook records that search as an mwg_search line, and every file write as a write line. At the end, done build serves dist/ and opens every declared route in headless Chrome. It never uses the dev server, which carries a hot-reload socket and unminified output the live site never has.
What the gate checks. First the ledger claim: for every component on disk, a guidance search must precede its first write. A component the ledger never saw is unproven, and the message names the uninstalled hook as the likely cause. Then ten predicates run on every route:
axe-routes, an accessibility scanner, in both colour schemes. A result axe cannot decide fails; the page must be made decidable, for instance by putting a solid surface behind text over an image.a11yfor what axe does not decide: the skip link driven with a real Enter and Tab, motion over five seconds pausable, fragments that resolve, no placeholder-only labels.console-cleanon three error channels: console errors, uncaught exceptions, failed requests. A missing favicon is a real 404 on every load.bfcache, the browser's back/forward cache: one real back-navigation is observed restoring the page instantly instead of reloading it.forms,fonts,markupandcontent, plus the parts ofseoandlegalthat can be judged from the built files alone. Their remaining checks need a live site and run in phase 5.
So what. An empty result is never a pass here. Every route carries proof it really loaded: a status, a complete document, something rendered. A route or scheme that never ran is refused rather than skipped. Treat the FAIL lines as the list, fix, and run done build again.
Phase 4 — Test: four checks in parallel, then a jury
Claim. Once the site builds clean, the pipeline fans out into four independent checks: defences, the critical flow, honesty, speed. It joins again only after three blind scorers and the owner have said the site is worth paying for.
How it goes. The four can run in any order:
- security reads
_headers(the file Cloudflare Pages reads to set response headers) anddist/for 19 declared defences: security headers such as a content security policy without inline-script exceptions, forced HTTPS, no content sniffing, referrer and permissions policies; integrity hashes on external scripts; no secrets and no source maps in the output.deps.mjsadds "no high or critical advisory";supply-chain.mjsadds lifecycle scripts off, lockfile URLs on the public registry, signatures verified. The step reports "declared, not proven live". Proving the host sends the CSP is phase 5's job. - flows replays the critical user journey, such as booking or contacting, from a frozen script under
flows/(that is: a script written once againstflow-kit.mjsand then locked, so the same journey can be replayed on every later build). The predicate servesdist/on its own port and counts what its server was asked for. A flow that exits 0 having requested nothing did not visit the site. Anybudgetline the flow prints is judged: INP (how quickly the page reacts to a tap or click) within 200 ms, zero layout shift, a busy state shown within 300 ms of any longer wait. - ux-review is the one check where a skill judges. The
ux-reviewactor opens every route at phone width in both themes. It files a verdict with a reason for each of the 31 mandatory dark patterns (that is: manipulative interface tricks such as a pre-ticked consent box or a fake countdown) and legal patterns. That file is its testimony (that is: a record written by the actor, which the gate corroborates but never trusts on its own).ux-review.mjsthen checks the mechanical half: every pattern judged, every route named, the review newer than the build. A small, deliberately strict scanner reads the rendered pages and must find no trace of a pattern the review called absent. - ship-check runs Lighthouse five times through
measure.mjsand grades the median againstbudgets.json. A spread of more than 5 points between runs, or runs that disagree about whether a budget is met, is refused rather than averaged into a pass.
Then commercial-review. The commercial-jury skill puts screenshots of every route in front of three blind scorers. They are separate agent sessions, or people, that have not seen the brief or each other's ballots, and they score eleven strategic dimensions from 1 to 5. jury.mjs reads the ballots and checks their shape: three distinct scorer names, the same eleven dimensions on every ballot, whole numbers from 1 to 5. It computes the median per dimension; every median must reach 3. Then the owner signs, by name, with site-step sign commercial-review --by "Name". The skill never fills in a score itself.
So what. This phase separates the floor from the ceiling. Everything mechanical is the floor: fast, accessible, honest. The jury and the owner are the ceiling: would a stranger who lands here understand what the page wants and do it? A 100 Lighthouse score is the entry price, not the product.
Self-check: which of the four parallel checks rests on a judgement rather than a measurement?
Only ux-review, and the judgement is the skill's, not a person's. Even there it is corroborated: the scanner refuses a review that the build contradicts. The other three, security, flows and ship-check, are decided entirely by code. The first human judgement comes after all four, in the owner's sign-off on commercial-review.
Phase 5 — Ship: preview, legal inventory, live
Claim. A deploy is a thing you did. A step closes on what is true afterwards, measured against the live origin. This is where the temptation to loosen a check is strongest, and it is the phase the machine was built for.
How it goes. publish-preview requires ship-check, commercial-review and security; commercial-review in turn required flows and ux-review, so all of phase 4 is behind it. The site-publish skill deploys dist/ to Cloudflare Pages on the preview branch. It then declares three things no predicate can discover: the preview URL; the site's mail posture (that is: a declaration of whether the domain sends mail at all, and from where), because a domain that never sends mail must say so or anyone can spoof it; and licences.json, an entry with source, licence and proof for every licensable asset. Then legal-check runs on that live preview, and publish-live moves the site to its own domain.
What the gate checks.
- publish-preview: the preview URL answers with
x-robots-tag: noindex, because a preview that can be indexed is a live site nobody decided to launch. It also answers with acontent-security-policyheader that was really sent, which is the proof step security could not give.assets.mjsproves the licence register is complete; whether a licence is valid stays a human question. - legal-check:
legal.mjsreports 25 labelled checks. Eight are re-run from the build: the Impressum and Datenschutz routes (the legal-notice and privacy pages German law requires), no pre-ticked boxes, no hotlinked fonts, price display. The rest run against the live origin, using the site's declarations (that is: facts stated once throughsite-context.mjs, such as hosting processor, jurisdiction, legal form and accessibility scope). They cover no cookie or third-party request before consent, a refusal as prominent as the accept, and a policy that names every processor observed. The actor reports "required elements present", never "compliant". That distinction is what the named sign-off is for, and it is risk-scaled: a client site must name the person who actually read the legal texts, not the person who ran the check. - publish-live:
live.mjsconfirms four things. The domain answers; the receipt (that is: the recordverify-receipt.mjs auditwrites from a real Lighthouse run, separate from ship-check's measurement) is newer than the newest source file; noindex is gone; the preview is noindexed or provably torn down.seo.mjschecks canonicals, sitemap and robots on the live site.dns.mjschecks that SPF, DMARC and MX match the declared mail posture, that no CNAME dangles, that CAA tolerates Cloudflare's certificate authorities, and that the registration is not about to lapse.
So what. Edit one caption before launch and publish-live refuses, because the receipt now describes an older build. That is freshness (that is: the rule that a record about a build must be newer than the build) working as designed: run verify-receipt.mjs audit again, then done publish-live. You never edit a predicate to make a deploy pass. Either the site is wrong or the check is wrong. If the check is wrong, the run stops and the check is fixed first, in its own commit, with its own test. It is never edited just to get this deploy through.
After launch: monthly re-verification
Every gate above answers at publish time. The most common way a live site goes wrong afterwards is "verified once, drifted since". tools/reverify.mjs is the answer, and it is deliberately not a step. Run monthly, it repeats the checks that look at the live site, for every site: the domain, DNS, security.txt, the consent behaviour against the real origin. It writes a report under the site's .claude/state/reverify/. Drift is reported in both directions, a pass turning into a fail and a fail turning back into a pass, because a check that flips back and forth hides if only one direction is reported. It refuses nothing, sets no halt and does not advance the run: a drift finding is a report, and the gate belongs to that site's next real step.
Common misconceptions
The skills are prose for seven creative steps. The pipeline is workflow.yaml, read by site-step.mjs. When the two disagree, the YAML wins, because the YAML is what the gate reads.
A skill does a step's work and files its testimony. Whether the step is done is decided by the exit predicates, and only by them.
Fourteen of the fifteen steps prove a floor: fast, accessible, honest, legal elements present. None of them can tell whether a stranger would act on the page.
"Good" is the ceiling, and it is decided by the jury and the owner in commercial-review, on purpose the one place the verdict is human.
Four halts in an ordinary run is normal: a token a shade too light, a missing favicon, a policy that does not name the host. Each is lifted by evidence, not by explanation.
The refusal prints the reason and the remedy. Fix, rebuild, run the same step again. The one thing a halt never accepts is a more confident report.
Step security reads files on disk, _headers among them. A host can silently drop a declared header, and a file check cannot see that.
publish-preview fetches the live URL and proves the CSP was sent. That is why both steps exist and why passing one is no evidence for the other.
The same run, seen by phase
The photographer's site from Life-cycle of a run, compressed to the five phases. The full walk lists every command; this view shows where the phases start and end.
- Decide, one hour. The doctor passes. The brief interview settles on "book a shoot" as the one action, after the owner first wanted booking and a newsletter.
brief-complete.mjsfinds one primary action. The repo is private on GitHub; the guidance is pinned. - Design, one hour, one halt. The tokens are written and four pairs declared. The caption colour is 4.1:1 in dark mode; the run halts; the token is darkened; the step closes. The scaffold builds.
- Build, two hours, one halt. Two guidance searches precede the gallery and the booking dialog, so the ledger claim holds.
console-cleanfinds a missing favicon on every route. Fixed, closed. - Test, one hour, no halt. Headers declared, dependencies clean. The booking flow freezes and replays green. Thirty-one verdicts filed. Five Lighthouse runs, median 96, spread 3, well inside the limit of 5. Three blind scorers: lowest median 3. The owner signs.
- Ship, one hour, two halts. The preview sends noindex and a CSP; twelve photos are in the licence register.
legal.mjsfinds the privacy policy does not name Cloudflare, which the hosting declaration does. Halt, text fixed, redeployed, re-observed; a reviewer signs. One caption edited before launch makes the receipt stale: audit rerun, then live. Fifteen of fifteen.
Six hours, four halts, none of them a judgement call. The agent never marked anything done; it only made the predicates true.
Reference card
A site is a sequence of fifteen claims, each proven by a program before the next claim may be made; the agent makes the claims true, and only the gate says they are.
| Phase | Ends when | Typical halt | The fix is |
|---|---|---|---|
| Decide | brief.md names one action; repo private; guidance pinned | two primary actions | a decision |
| Design | every colour pair ≥ 4.5:1 in both themes; npm run build exits 0 | one pair short in dark mode | a token |
| Build | ledger proves guidance first; ten predicates green on every route | a console error, an axe finding | the page |
| Test | defences declared, flow replays, review corroborated, median in budget, jury ≥ 3, owner signs | a pattern the review missed | the pattern, then a fresh review |
| Ship | preview sends noindex + CSP; legal elements present and signed; domain live, receipt fresh, DNS sane | a stale receipt, a policy missing a processor | re-audit, or the text |
Numbers that matter: 15 steps, 23 predicate programs (listed on the Architecture page), 7 skills, 31 mandatory patterns, 3 blind scorers scoring 11 dimensions to a floor of 3, 5 Lighthouse runs to a median, 4.5:1 contrast in both themes, 2 human sign-offs, 1 monthly re-verify.
Self-check: without looking up, what are the two things the build step proves, and what proves each?
That guidance was consulted before every component — proven from the ledger, never from a claim. And that every declared route renders clean — proven by ten predicates driving a real Chrome over dist/.
Next steps
To watch one of these phases happen, follow Drive a run, which walks the commands with a check after each. To see what every predicate in a phase actually inspects, read the tables on The workflow, step by step. To learn what a specific file or folder in the diagrams is for, Architecture names every one.