They were written by a model, posted through our own API, and made public on arrival. One of them had no text in it. Here is the count, and what we changed.
This blog published 108 articles that nobody at this company read before they went out, and one of them contained no text at all.
They were written by a model, posted through our own API, and made public the moment they arrived. That went on for about a month. Here is the count, taken from the database rather than from memory.
What actually got through
Of the 108:
108 arrived with no summary.
108 arrived with no tags.
108 arrived with no meta description, which is why every one of them shipped a page with nothing for a search result to quote and an empty entry in the feed.
104 had no author.
107 were under 600 words. The median was 303. The shortest was zero.
4 were the same article posted twice, which we know because the slug generator had to add a number to the end.
80 distinct openings across 108 titles, so twenty-eight of them begin with the same eighteen characters as something else on the same site.
None of that is a model behaving badly. A model sends what it is asked for. Every one of those fields was documented as optional.
The four fields every article was missing, and how long they were against the floor that now applies.
The gate was open in the documentation
The contract document for the API had a copy-paste example, the one you would naturally lift into a prompt. That example set the status to published.
So the path from a model's output to a public page on our domain contained no person. Not as an oversight in the code. As the first thing the documentation told you to do.
The fields tell the same story from the other side. The example listed summary, tags, author and description, and the generator sent none of them, because the API required only a title. What is not enforced is not sent. Documentation is a suggestion; a validator is a rule; and we had written the rule to ask for almost nothing.
What we changed
Four fields are now required on any article arriving through the API: a summary, at least one tag, an author, and a meta description. The body has to clear 600 words.
Words are counted the way the editor counts them, which matters more than it sounds: Thai does not put spaces between its words, so a naive word count reports a Thai article as one word and would have rejected every one we ever write in our own language.
What survived is all build log: posts about a specific thing that broke, written by whoever fixed it.
The gate runs before anything is written, including before inline images are uploaded, so a rejected article does not leave a set of orphaned files behind every time a sender retries.
And publishing is no longer something the API can do. A status of published in a payload is refused, and the separate publish route is closed, because it was the same decision behind a second URL with the same key. An article arrives as a draft. A person reads it and presses the button.
What we deliberately did not build
No detector. No regex hunting for a model's refusals, no similarity check against existing posts, no heuristic for "this reads like filler".
Every one of those is a machine guessing at something a person settles in ten seconds by reading. Once a human being has to press publish, the problem those detectors solve stops existing, and the detectors themselves become a thing that can be wrong in both directions for ever.
The rule that replaced them is shorter: an article a person has not read does not go out.
What happened to them
They are off the blog now, and we used the gate itself to choose: any published post that could not be published today came down. Not a date range, not a guess at who wrote it. The rule that keeps the next one out is the rule that decided what already got in.
That took 125 posts off the site and left thirteen, every one of which a person read before it went out. Nothing was deleted. The rows are archived and still queryable, each keeping the date it originally went out with, because that archive is the only honest record of what the generator actually produced.
If you arrived here from one of them, that is what happened.