Most client sites I've seen go wrong at launch didn't break because of the code. They broke because nobody checked the boring stuff. A contact form posting to nowhere, a staging noindex left on, the favicon still the Astro rocket, a phone number with one digit wrong.
Since I started building client sites with a coding agent (mostly Claude Code, sometimes Cursor), I stopped doing that check by hand. The agent runs it every time, before anything goes near the live domain. Here's the list and how I set it up.
The rule I put in the repo
Every client project has an AGENTS.md (or CLAUDE.md) with one section that matters more than the rest:
## Before any deploy
1. Run `npm run build`. If it fails, stop and fix it.
2. Run `npm run check:launch` and paste the output.
3. Do not deploy to production if any check says FAIL.
4. Deploy to a preview first and give me the preview URL.
That's it. The agent is happy to follow a checklist, it just needs one that exists. Without this it will cheerfully ship whatever builds.
What check:launch actually checks
It's a small Node script that crawls dist/ after the build. Nothing fancy, no headless browser for most of it.
SEO basics, per page
- Has a
<title>and it's not the same as every other page - Has a meta description under ~160 chars
- No
noindexanywhere unless the page is on an allow list (thank you pages, etc) - Canonical points at the real domain, not the preview or localhost
Links
- Every internal
hrefresolves to a file indist/ - No links to the old WordPress domain or
?p=123style URLs if it's a migration -
tel:andmailto:links match the phone and email in the client's data file. This one has saved me twice.
Assets
- No image over ~400KB
- Every
<img>has analt(empty is fine for decorative, missing is not) - Favicon and og image aren't the starter template ones. I literally hash the default files and fail if they match.
Forms
- Every
<form>has an action and a honeypot field - Required fields actually have
required
Redirects (migrations only)
- Parse
/_redirects, check no rule points at a page that doesn't exist indist/
Output is just lines of PASS / WARN / FAIL with the file name. Easy for the agent to read, easy for me to skim.
Preview first, then the real test
Once the build passes I have the agent deploy to a preview. I host client sites on Novence, so that's npx novence deploy and it gives back a URL. If I'm working in Claude Code with the MCP server connected it can do the deploy itself and hand me the link.
Then a second round runs against the live preview, because some things only show up over HTTP:
- curl every URL in the old sitemap and confirm 301 to a 200
- submit the contact form once with a test message and confirm it lands
- check response headers look sane
I look at the preview on my phone myself. That part I don't automate, a five minute thumb test catches layout stuff no script will.
Why bother when the agent "already knows" this stuff
Because it doesn't remember between sessions and it won't check what you didn't ask for. The agent wrote the page, so of course it thinks the page is fine. A dumb script that doesn't care is a better reviewer than the thing that wrote the code.
The other win is for the client. When I hand over a site I can send the check output with it. Every page titled, every link resolves, forms tested on this date. It's a small thing but it reads as way more profesional than "it's live!".
Start small
You don't need my whole list. Start with three checks: no noindex, no broken internal links, form has an action. Put the rule in AGENTS.md. Add a check every time something slips through on a real project. Mine is about 150 lines now and every single line is there becuase something went wrong once.
Top comments (0)