The web factory
Today you ran every station of a professional website by hand, one Role per chat. A web factory runs the same stations as one loop, ticket after ticket: you decide what is built and what goes live, and agents do the rest. The closing slide called it "A code factory for the web."; a web factory is the same thing. You can run one at home: web-factory is a free, open-source web factory (MIT licence) that runs the stations this page describes. For each station, this page says what you did today and what web-factory does.
A web factory is today's Roles as a loop: you decide at both ends, and in the middle agents build and review each change until it passes, with no person in it.
The parts
- Kickoff Once per site, before the first request: the architecture decisions. Which stack the site is built with, which host puts it online, which rules apply where your visitors are. You decide; every later station builds on it.
- Request One idea, one goal: a feature, a bug, a visitor's email. At home it is a GitHub issue, a to-do note in your repo on GitHub: one ticket, small enough for one fresh chat.
- Model, context and tools What every agent works with: the right model, the context of AGENTS.md, its skill and Modern Web Guidance, and DevTools as its tool. You set it up once, in Setup; every ticket uses it.
- Spec The Analyst's station, in three steps: it asks you questions until the request is precise, writes the spec, what the change must do and how to tell it works, and splits it into tickets. You answer, and you confirm the tickets.
- Design The Designer's station: how it looks. The Designer writes it down in the design brief; you pick the style. A ticket that changes no look skips it.
- The ticket loop Build, Review and one decision, Pass?, running by themselves: Build (the Developer) writes the change, Review (QA's job) checks it, and if it does not pass, it goes back to Build. It runs without you watching, and stops to ask you when it needs a decision.
- Approve You read the change and the evidence, then you accept it by merging it: you add it to
main, the version of your site that is live. You decide what goes live. - Publish GitHub Actions builds the site from
mainand puts it online. Today, the Ops Role did this. - Measure Two things are measured. The site: real visitors use it, and their numbers and errors are collected by themselves. And the factory: every run of the ticket loop leaves a report of what went well and what got stuck. What matters becomes the next request, for the site or for the factory itself. The loop starts again.
Get started with web-factory
web-factory lives inside your site's own repo, one repo per site, and you run all of it by talking to your coding agent. You type the few commands in a terminal: the Terminal app on a Mac, or the terminal inside a Codespace.
- Two ways in
- Open the template from the web-factory repo in a Codespace, an editor that runs in your browser, so nothing is installed on your computer: the way in on Windows or a Chromebook. Or, on a Mac, on Linux or on Windows with WSL (Linux inside Windows), run the install command from the repo's README: it installs Node, Git, the GitHub CLI (GitHub's own terminal tool) and your coding agent if they are missing, and running it twice changes nothing. Then
npm create web-factory@latest my-sitecreates your site's repo in a new folder,my-site. GitHub asks you to sign in only when the factory first needs it, and says why. - Your agent, your plan
- It asks which coding agent you use, and pushes none: Antigravity CLI (free with a Google account, with weekly limits Google does not publish), Codex (works on ChatGPT Free and Go), Claude Code (needs a paid Claude plan) or Gemini CLI (needs a paid Gemini key). These prices change often; the repo's README has the dated table.
- Your language
- The agent answers you in your language; the repo stays in English, so its code stays shareable.
Kickoff: the architecture decisions
Before the first request, a site needs a few decisions that every later ticket builds on: what it is built with, where it runs, and which rules apply. They are architecture decisions: expensive to change later, so they are made once, on purpose, and written down.
Today: they were made for you. The Template fixed the stack (plain HTML and CSS, built by node tools/build.mjs), the host (GitHub Pages) and the legal page for a personal site in the EU.
In web-factory: open the my-site folder in your coding agent and ask it for next: it starts Kickoff, which first explains why it exists and then asks one thing at a time. Who is the site for, what must it do, how often does its content change, and where are you and your visitors? From your answers it recommends a stack, the languages and tools your site is built with, and a host, the service that puts it online, each with the case for and against, and you choose. Firebase, Google's hosting service, is the recommended host. Your choices are saved as ADRs, short notes on what was decided and why, and as a Stack section in AGENTS.md with your site's test command and host, so every later chat and every coding agent builds the same way. If you or your visitors are in Germany, the German Locale pack switches on: checks for the Impressum, the privacy policy, prices and no third-party requests before consent. It is not legal advice, and it says so. Kickoff also creates a label for every station on GitHub.
So what: a stack you chose and understand is one you can still change. And when a later request touches the architecture, the spec raises a new decision for you, so the site changes on purpose, not by drift.
You decide at both ends
Four stations are blue: Request, Spec, Design and Approve. At the start you decide what is built; at the end you decide what goes live. Everything between them can run without you.
Today: the Analyst wrote a first draft of your content, and you corrected it. The Designer asked about your taste, and you picked the style. Before every Role ended, you read what it changed.
In web-factory: you describe an idea or a bug in your own words, and the request skill files it as a GitHub issue. For the design you use a design tool, Stitch, Figma or Claude Design, or no tool at all: an import skill fetches your design, or the agent writes one and builds a sample page for you to approve. Every design is saved in one design folder, with a DESIGN.md, the design tokens (its colours, fonts and spacing), and a reference screenshot for every page; a check catches text without enough contrast before any ticket is built.
So what: a factory is only as good as your spec and your design. An agent cannot know what you want; it fills every gap with a guess. A clear spec makes the loop short, and a vague one makes it run in circles. And the agents never decide alone what your visitors see.
Spec: questions, the spec, the tickets
The spec turns a request into work an agent can build. It does three things, in this order.
- Ask The agent questions you until the request is precise, and says why each question matters. A vague request makes a vague spec, and the loop then runs in circles.
- Write The spec says what the change must do, and how to tell the site does it: the test that decides Pass, agreed with you before any code.
- Split The spec is cut into tickets, each small enough for one fresh chat and buildable on its own. Each ticket lists its blockers, the tickets that must be built first.
Today: the Analyst asked about your work and your goal, wrote your content and a short docs/spec.md, and you corrected it. The tickets were already cut for you: one Role per Block.
In web-factory: the spec skill does all three. Tickets become sub-issues of the spec, with GitHub's own blockers. A ticket too big for one session is split again before you see it. Then the skill shows you the cut in plain words, what each ticket delivers, what blocks it, and why it is cut as a thin slice through the whole site instead of one layer at a time, and asks you to confirm or change it. Until you confirm, the ticket loop leaves the tickets alone. Confirming moves the spec on to Design, and its tickets reach Build only once you have approved the design.
So what: the split is where you steer the loop. Each ticket is one goal, so each review checks one thing, and a ticket that fails is small enough to fix.
Model, context and tools at every station
Every agent starts in a fresh chat, so nothing from an earlier task gets in its way, and every chat starts with the same three things. The model: the smallest one that does the job well. The context: the rules in AGENTS.md, the station's own skill, and Modern Web Guidance for today's web. The tools: programs the agent runs, such as DevTools, which measure instead of guessing.
Today: you picked Gemini Flash in Setup. AGENTS.md was in every chat, each Role was one skill, and the Developer read the matching Modern Web Guidance before it wrote code. Inside your repo shows every file.
In web-factory: one AGENTS.md is the rulebook for every coding agent, with a Stack section that Kickoff writes. Modern Web Guidance comes with it at a fixed version, and the rules make it required reading in Spec, Design and Build. Versions move only when you accept an update: factory update opens a pull request, a proposed change on GitHub that waits for your review, with new skills, checks and guidance.
So what: you write this setup once, and every ticket profits from it. Write few rules, and delete the ones the model no longer needs: models get better every few months, and rules written for last year's model can hold this year's back.
The ticket loop: build, review, decide
The green frame is the part that runs by itself. Build writes the change for one ticket. Review checks it. Then one decision: does it pass? If not, the ticket goes back to Build with the finding, and the whole review runs again. If it passes, it leaves the loop for you to approve.
Today: you were the loop. QA found a problem, you said "fix it", the agent fixed it, and the Check, the program in your repo that checks your site and prints one line per item, ran again until every line said PASS.
In web-factory: one command, factory loop, builds your spec's tickets while you do something else, lowest unblocked ticket first. It does not start when your site's tests are red. Each ticket is built in its own copy of the code, a worktree, with your agent's sandbox on, a fence that keeps the agent inside the project folder, and nothing is ever pushed to main. Before Review, the program itself checks the work: a clean folder, at least one commit, your test command green, and no tests deleted or skipped, so a pass cannot be bought by removing checks. A test that fails once and passes on a rerun is marked flaky, not failed. A ticket that fails gets a new Build with the failing check and its reason attached. Hitting your plan's rate limit, the cap on how much your plan lets the agent do in an hour or a week, is not a failure: the loop waits until the limit resets and continues with the same ticket. The tickets that pass are collected into one pull request per spec.
So what: two rules keep the loop honest. First, the review is a different agent from the builder, in a fresh chat: a reviewer that did not write the code sees what the builder missed. Second, let it try more than once, but not forever. More rounds often get there. A ticket that stays stuck comes to you: web-factory parks it after its attempt limit, with the reason in plain words, and a ticket whose change would contradict the spec stops at once and asks you to decide. The other tickets continue. And a loop that keeps failing often has a problem in the spec, not in the code. On the slide, that is the red line marked "Stuck", from Pass? to Approve.
What runs the loop
Something must start each step, so that you do not have to. That is the orchestrator: an agent or a program, in charge of the agents. It takes the next ticket, starts Build in a fresh chat, hands the result to the reviewers, reads their verdicts, and either sends the finding back to Build or opens the pull request. It also counts the rounds. The agents it starts are subagents: helpers with a fresh chat of their own, which report back to it.
Today: there was no orchestrator: you were it. You started each Role in a fresh chat and told it what to do next.
In web-factory: the orchestrator is the factory loop command, a program, not an agent: a program does the same thing every time. It starts each Build and each code review as a session of your coding agent, the same way for Antigravity CLI, Gemini CLI, Codex and Claude Code, and it decides Pass from the checks' results.
So what: you can also build an orchestrator yourself: in a coding agent such as Claude Code it is usually one skill that tells the agent how to run the loop and which subagents to start. Deep dive: building blocks explains skills and subagents.
Review: five checks, one decision
Review is one box with five checks inside, one for each thing that makes a good website. They run side by side, each as a program or as a reviewer agent of its own, and the ticket passes only when all five pass.
- Performance
- The page on a mid-range phone with a slow 4G connection. Two numbers: LCP, how fast the main content shows (Google's line: 2.5 seconds), and CLS, how much the page jumps (0.1). Today:
node tools/look.mjs, the tool QA ran, which loads your home page on an emulated slow phone. - Accessibility
- Contrast, alt text, real headings and buttons, checked with the axe rules that Lighthouse scores. Today: a line in the Check.
- Privacy & legal
- A legal page with no blanks left, linked from your home page's footer, and nothing loaded from other servers, because every server your page loads from sees your visitors. Today: a line in the Check, and you reading the page.
- Visibility
- Your content in the HTML itself, so search engines and AI agents that run no JavaScript can read it. Today: the Check's line "What an agent sees".
- Code & security
- A reviewer agent reads the change against the spec: no secrets in the code, no errors in the browser console, nothing the ticket did not ask for. Today: the Check's console line and your read-back.
So what: numbers, not the agent's word. An agent can say "fixed" without checking. A check that measures cannot.
In web-factory: every Review check measures your served site by its address, so the checks work whatever stack you chose, and each failing check gives one reason in plain words and where it failed.
- Performance
- Lighthouse, the median of several runs against fixed budgets, so one noisy run neither passes nor fails your site. Layout shift is held to Google's line, a CLS of 0.1, not to zero.
- Accessibility
- axe on every page, and the phone menu really tapped at phone width, so a menu that only looks right is caught.
- Privacy & legal
- Your Locale pack's checks: with the German pack, the Impressum, the privacy policy, prices and no fonts or third-party requests before consent.
- Visibility
- A sitemap, canonical addresses, tags for link previews, structured data, and one address per page.
- Code & security
- Security headers, known holes in the code your site depends on, a console without errors, and your site's own tests. On top, a code review by your agent, which cites Modern Web Guidance by name and can send a ticket back to Build, but can never pass a ticket whose checks failed.
You can run the same checks on any site yourself: factory review followed by its address.
Check yourself: what happens when the Performance check fails, and who is told?
The ticket goes back to Build with the finding, for example "LCP 3.4 seconds". Build fixes it and the whole review runs again, as often as it takes. Nobody is told unless the ticket stays stuck: after its attempt limit, it is parked for you.
Approve and publish
Tickets that passed arrive as a pull request, one per spec: the change, the review's results and the evidence. You read it and ask what QA taught you: show me how you know. Then you merge it, and the pipeline does the rest. Today, GitHub Actions built your site and published it for Ops.
In web-factory: each pull request comes with a Preview, an address where you see the change live before it goes live. The approve skill tells you what to look for first. You approve by merging the pull request, and the merge publishes your site to the host you chose, without GitHub Actions in between: with Firebase, Kickoff connects Firebase to your GitHub repo one step at a time.
So what: passing the review does not mean it is what you wanted. The checks measure what they measure; only you know whether the new section says the right thing about you. That is why Approve stays blue.
Measure: the loop closes
Live is not the end. Real visitors use other phones and other networks than QA's emulated one. Their numbers are field data, and one number comes only from them: INP, how fast the page answers a tap. PageSpeed Insights shows them for sites with enough visitors, over the last 28 days. Errors that visitors run into and emails they send count too.
In web-factory: Lighthouse runs on your live site after every publish, so you have numbers before the site has visitors. Once it has enough traffic, factory measure reads your real visitors' numbers from the Chrome UX Report, Google's public data from real Chrome users; if you like, a small script called web-vitals also sends them to Google Analytics, only after consent with the German pack. Each measurement is compared with the one before, and a measurement that got worse opens a new request by itself, with the evidence attached. It waits there for you: Request stays blue, you decide whether to build it.
So what: what you measure becomes the next request. A page that got slower, a bug, a wish: each becomes a new issue, and the factory takes it from there.
The factory measures itself
Measure watches the factory too, not only the site. A factory that keeps getting stuck on the same thing should change, the way you would change a habit.
Today: nobody measured the factory for you. If a Role needed several tries for the same fix, you noticed it yourself, and asked differently in the next chat.
In web-factory: after every run, the ticket loop writes a run report in plain words, on the board and as a comment on the spec: which tickets went straight through, which needed fix rounds and why, what is parked and why, flaky tests, the time spent waiting on rate limits, and which Review checks failed. Measure reads these reports across runs. When a pattern shows, the same check failing on several tickets, tickets too big to finish, or parks for the same reason, it proposes a factory request: a change to the factory's setup in your site, such as a rule, a skill or how tickets are cut, with the cases as evidence. Every request is labelled as about the site or about the factory, and a factory request travels the same stations as any other: you decide on it, the spec, the ticket loop, Review and your approval. After a run with several parked tickets, Measure also offers factory feedback: a short anonymous summary for the web-factory project, filed as an issue on its repo. You see all of it before you send it, it holds no code and nothing from your site, and nothing is ever sent without you.
So what: the factory improves from its own runs, and you stay in charge of every change: it proposes, you decide. Your feedback improves web-factory for everyone, and the next workshop teaches what people got stuck on.
The board, and what to do next
npm run board, typed in the terminal inside your site's folder, opens a page drawn like the closing slide: one column per station, blue where you decide, green where it runs by itself. Each card shows its attempts, the check that failed, the reason in plain words, and the one thing to do next; a request shows whether it is about the site or the factory, and each spec shows its latest run report. When you do not know where you are, ask your coding agent for next: it reads the board and tells you what to do now and which skill to use. Every skill explains why its step exists before it asks you anything, asks one thing at a time, and gives every terminal step as one explained command, with what you should see.
Common misconceptions
- "A factory means no people." No: it means no people in the middle. You decide what is built and what goes live; the agents do the work between.
- "More agents, better results." Each agent costs tokens and time, and each one adds a place where things go wrong. Start with one check that runs by itself, then add one reviewer at a time, where you see it catching mistakes.
- "If the review passes, it is right." The review proves it is fast, accessible, legal, visible and clean. Whether it is what you meant, only you can say.
- "Done when it is live." Real visitors decide whether it works. Measure is a station, not an afterthought.
- "A factory is a black box." It should not be. Whether you use web-factory or build your own, its rules and skills are plain text in your repo: read them, so you know why it does what it does, and change what does not fit.
One ticket through the factory
A visitor writes: "Your site is slow on my phone." Follow it through every station.
- Request You open an issue: "The home page is slow on phones." One goal, one fresh chat.
- Spec The goal in numbers: LCP under 2.5 seconds on QA's slow phone, the page otherwise unchanged. You agree.
- Design Nothing to design: the look stays. The ticket skips this station.
- Build The agent reads the Modern Web Guidance guide on image priority, then loads your photo first and the images further down later.
- Review, round one Performance: LCP 2.9 seconds. Does not pass. Back to Build with the number.
- Build, round two The photo was still too big for a phone. The agent makes a smaller copy for small screens.
- Review, round two LCP 1.4 seconds. The other four checks pass too. It leaves the loop.
- Approve You read the change and the two numbers. You merge.
- Publish GitHub Actions builds and publishes. A minute later it is live.
- Measure Over the next weeks, PageSpeed Insights shows your visitors' numbers. If they still wait, that is the next request.
Where to start at home
Start your next site with web-factory: open the template in a Codespace, or install it on your computer with the command in the repo's README and run npm create web-factory@latest my-site in a terminal. Open the new my-site folder in your coding agent and ask it for next: Kickoff takes you to your first decisions, and next tells you every step after that, and which skill to ask for. A skill starts when you ask for its job, as today: "Start the request step." In the same folder, npm run board in the terminal shows where every ticket is. Found a bug, or missing something? Report it as an issue on the web-factory repo.
Want to keep your workshop repo instead? Start with the first step in Do it professionally: let GitHub Actions run the Check on every pull request, so a change that fails cannot be merged. That is Review, without an agent. Agentic frameworks shows three ways to cut a spec into tickets.
The web factory on one card
Keep this
The idea: today's Roles as a loop. You decide at both ends, agents run the middle.
You decide: Kickoff (once per site), Request, Spec, Design, Approve.
Runs by itself: the ticket loop (Build, Review, Pass?), Publish, Measure.
Measure: the site (real visitors) and the factory (a run report after every run); both become requests, labelled site or factory.
Runs the loop: an orchestrator, an agent or a program; in web-factory, the factory loop command.
Every station: the right model, the context (AGENTS.md, a skill, Modern Web Guidance), the tools (DevTools).
Review: Performance (LCP under 2.5 s, CLS under 0.1), Accessibility, Privacy & legal, Visibility, Code & security. All five pass, or back to Build; a ticket that stays stuck is parked for you.
Run one: web-factory. npm create web-factory@latest my-site, then factory loop, factory review, factory measure and npm run board in the terminal; next to your agent.
Check yourself
Four questions on this page. Pick one answer each, then check them.