A release gate for static sites. Not a warning. A refusal.
Banned words, dead links, missing disclaimers, claims nobody can source. A linter grumbles about these and ships them anyway. A gate fails the build, and nothing goes out.
Bad content ships on a bad day.
Every studio has a list of things that must never go live: the phrase the client’s lawyer struck, the price that changed last quarter, the link to a page that was deleted, the claim someone typed from memory. The list lives in a document, or in one person’s head, and it is consulted when there is time.
There is not always time. The deploy that goes wrong is the one pushed at six on a Friday by the person who did not write the list. Nothing about their care was lacking. The check depended on a person remembering, and a person is the least reliable component in any pipeline.
A warning does not fix this, because a warning can be read and overridden. The fix is to make the check a condition of the build: if the output fails, there is no output.
A person forgets. A build step does not.
Six checks on the output. Any failure stops the deploy.
The gate runs after the build, on the finished HTML, CSS and JavaScript — the thing the visitor will actually receive, not the source someone intended to ship. Each check reads every page and reports what it found. If the sum of the findings is anything but zero, the build exits with an error and the deploy never starts.
- Banned words and phrases
- 0 foundPASS
- Dead internal links
- 0 foundPASS
- Duplicate search queries
- 0 foundPASS
- Unsourced claims
- 0 foundPASS
- Legal disclaimers
- presentPASS
- Third-party runtime requests
- 0PASS
A violation does not produce a warning. It fails the build.
Banned words are a list in a text file, matched on whole words in visible copy, alt text, labels and comments alike: the vocabulary marketing reaches for when it has nothing to say, along with every exclamation mark and every emoji. Dead links are any internal address that does not resolve to a built page. Duplicate queries are two pages declaring the same search intent.
Unsourced claims are money, percentages and large integers in the copy that nobody has registered with a source. Legal pages must exist and be linked from every footer. Third-party requests are any URL in the output that leaves the origin.
The table above is not an illustration. This site runs exactly that gate at the end of every build, and the page you are reading passed it before it was deployed. The checks live beside the site’s source, and the rules they enforce are the rules the playbook teaches.
What changes when the build says no.
The first thing that changes is the conversation. A failed build is not an opinion, so nobody argues with it. The copywriter who reached for a banned adjective gets the page, the phrase and the sentence it sits in. The developer who linked to a page that no longer exists finds out at build time rather than from the client.
The second is that the checks compound. Once the build owns the rule, adding a rule costs a few lines: the client’s struck phrase goes into the list, the changed price into the claims register, and from then on every deploy checks both without anyone remembering to.
The third is what you can claim. A footer that says the site makes zero third-party requests is a boast on most sites. Behind a gate it is a test result, and the audience that reads footers will open the network panel and find it true.
Bad content cannot ship. Even on a bad day.
The gate checks pages. Silos decide where they live.
The gate runs on the owned stack, and the free chapter contains it in full. The next system, content silos, gives it a structure to enforce. It is also the shortest answer to the first CODES letter, Control: a build you can add a rule to is a system you can change.
Read the sharpest chapter first. It costs an email.
We’ll send the build-gate chapter in full: the checks, the code, and the reasoning. Read it. If it doesn’t change how you think about shipping client sites, you’ve lost ten minutes and kept a working release gate.
That did not send. Check the address and try again.