Two years ago you could spot AI writing by the em dash. Three per paragraph, one in every heading, a rhythm crutch no human editor would let through. I put "no em dashes" in my rules file, the dashes disappeared, and for a while the text read like a person wrote it.

The colon came next. "The detail that makes it work: a separate agent grades it." Same job the dash was doing, one character over. After that came "It's not X. It's Y.", then headings called "Why this matters" over two paragraphs, then three-pillar frameworks nobody asked for. Each time I banned a form, the form vanished and something with the same function appeared one level up.

Last week it reached a place I had not thought to look. I asked my agent to build a slide deck about an AI test runner from my notes. The paragraphs passed every rule I had. The slide titles were "One harness, four swappable sides", "Four walls, seven accounts", "Five calls to make this month", headlines in the slot where a title belongs. Every rule I had was written for prose, and the agent had, reasonably, treated a slide title as layout.

Training corpus and preference tuning

For a year I treated slop as a list of habits. The model uses "delve", you ban "delve", it stops using "delve", and the sentence that needed a filler word finds another one, because the model is producing text that resembles a finished, thorough, confident answer and that is what it was rewarded for.

Most text about work on the internet is marketing copy, consulting decks, listicles, LinkedIn posts, and documentation written to a template. In that corpus a title is a headline, a section has a "Why this matters", an argument has three parts, and a conclusion restates the opening. When a model reaches for the most probable shape of a slide title, it reaches for "Four walls, seven accounts".

Preference tuning then rewards the look of completeness. When people rate two answers, the one with headers, bullets, a summary and a confident closing line tends to win, because it looks like more work was done. So the model learns structure as a proxy for quality. A paragraph of reasoning scores lower than the same reasoning wrapped as "the three-layer model", so the wrapper appears even when the source material contains no layers, and answers end with "the bottom line" because answers that end that way rate as more finished than answers that stop when the information stops.

And words cost the model nothing. A human writer runs out of energy; the sentences that survive are the ones you cared enough to type. A model fills every slot the template offers, so two paragraphs get a header, a single point gets a bulleted list, and a table gets a "So what" column.

Under those conditions a ban moves the behaviour to the nearest unbanned surface: from a word to a sentence shape, from a sentence shape to section structure, from prose structure to whatever medium has no rule yet.

There is data on this now. A Reddit user pulled 89,239 posts from 47 subreddits about spotting AI writing, filtered to 7,984 on topic, and hand-audited 600 of them for what readers actually cite as the tell. The em dash leads at 7.1 percent of audited posts. Second is uniform sentence rhythm at 4.0 percent, third "not just X, it's Y" at 2.8, then the five-paragraph essay shape and sycophancy, both at 2.5. The words everyone bans, "delve" and its cousins, sit at 1.3 percent. The keyword scanner in the same study ranked "however", "thus" and "hence" as the top match, at 6.3 percent of posts, and readers cited them as a tell zero times. Rhythm and emptiness, the tells readers cite most after the dash, cannot be keyword-matched at all.

One commenter on that thread had run the same experiment I had, with Claude. He banned constructions one at a time and watched the model comply with each ban while keeping the underlying behaviour: ban the em dash and it switched to ", and" and semicolons. After two dozen bans he found the driver was over-explanation and restatement, and a single instruction, "be airy, don't over-explain", did more than the whole list. The study's repository is public and MIT licensed.

Slide titles

My rules file says headings identify subject matter, never rhetorical function. It lists banned heading shapes. The agent had it in context and still wrote "One harness, four swappable sides" into the slide-title slot. When I asked why, the answer was that the paragraphs had been written in a writing step, where the rules fired, and the slide titles had been written in a layout step, as strings that look like slide titles look, where the rules did not fire because that step was not, in the agent's own accounting, writing.

Every medium I had not explicitly named was sitting on that unchecked side: diagram node labels, table headers, chart legends, Slack drafts, commit messages, the one-line descriptions on memory files.

Information gain as the test

The test that has survived every surface change so far is marginal information gain: after each unit of output, ask what the reader now knows that they did not know before it, and delete the unit if the answer is nothing.

The same question removes "delve" (a shorter word carries the same information), "It's not X. It's Y." (X was never on the table), "Why this matters" as a heading (it names the prose's job and says nothing about the subject), and a three-pillar framework whose pillars were not in the source. It works at the medium level too. A reader glancing at the slide index learns nothing from "Four walls, seven accounts" and learns exactly when to jump from "Common challenges across customers". Here is the full set from that deck, before and after, with the check applied to each title:

WasNow
What runs todayCurrent AI Test Runner workflow
Who uses the runner, and the wall each one hitsCustomers using the AI Test Runner and their use cases
Four walls, seven accountsCommon challenges across customers
What stands between the runner and production useCurrent challenges
One harness, four swappable sidesNew runner architecture
Why hard apps are the nicheAUT knowledge base: how it is built with a customer
Where the same engine can liveTwo paths: local file-based vs online integrated
One harness, two shells, and a bridgeRecommendation: ship the local path first, keep True as the system of record
Five calls to make this monthDecisions needed
Where the facts come fromSources

The right column is duller, and "Sources" tells a reader scanning the index what is on the slide without opening it.

The same check catches what the bans miss inside the content. The deck had a slide body that said "the walls are the pattern" and a tile that said "hard apps are hard for everyone; we are the ones with a slot for the answer". Neither adds a fact the surrounding text lacks, and both sit where a conclusion would go. A ban list names repeated patterns and cannot see a one-off empty sentence.

The test also settles the argument about single words. My first draft of the new rule tried to protect words like "robust" by giving an example where the word was needed: "a robust retry on OTP fetch". My reviewer asked why the retry needed "robust". It did not, and "a retry that survives a mail-slot timeout" tells the reader what it does, where "robust" tells them the author wanted the sentence to sound engineered. The same applies to "clean", "plain", "simple", "elegant". Remove the adjective, and if the reader would do nothing differently, it was decoration. The exception is a term of art, "robust statistics", where the word is the name of the thing.

Word counts across my own archive

Once I stopped trusting the ban list I counted. The twenty-five posts published here before this one, all of them drafted with an agent and edited by me, about 2,158 sentences.

My first pass used grep -o and reported "ship or shipped: 124 uses". That was wrong, and wrong in a way worth keeping: grep -o counts substrings, so it had been scoring "relationship", "shipping" and "ships" as instances of my tic. The tool I ended up writing counts on word boundaries and reports a per-file rate, which is the number that matters. The real figure is 51 across 25 posts, about two a post.

The shapes were the louder finding.

Behind "The" in the heading census sit "What" at 22 and "Why" at 7. The narrative heading I borrowed from a writer I admire, "Then the build lied to me", appears seven times across two posts, and the agent has since treated it as the house pattern.

This is specific to working with one agent over time. Besides the median of the internet, the model samples the median of me, from the posts and rules and memory files it has in context, and it regresses toward my most frequent choices because those are the highest-probability tokens in my corpus. "Ship" was a word I used for software releases. In the agent's hands it became the verb for publishing a post, saving a file, sending a Slack message and deploying a service. "Honest" was a word I used once when I meant it, and it came back as "the honest part:" at the top of paragraphs where nothing dishonest was on offer.

The census is now part of my pre-publish scan. It prints only what sits above the per-file rate, and I decide whether each one is my voice or my mode.

Specimens

The patterns are easier to refuse next to their repair. Every line on the left came out of a draft of mine, most of them out of drafts of this post.

Number pairing
One harness, four swappable sides
Two small integers joined by a comma read as structure and carry none.
Names its subject
New runner architecture
A reader scanning the index knows which slide to open.
Question as title
Why hard apps are the niche
Withholds the answer so the slide can deliver it. Fine in a magazine, a beat wasted on slide eight.
Names its subject
How the knowledge base is built with a customer
Same slide, findable from the index.
"X, not Y"
The rule now reads as a test, not a permission.
Nobody proposed "permission". The contrast is invented to give the sentence a shape.
States the thing
Remove the adjective. If the reader would do nothing differently, it was decoration.
The rule itself, with no straw man standing in front of it.
Verdict kicker
The right column is duller. That is the point.
A closing line placed for cadence, adding no fact to the paragraph.
Carries the fact
"Sources" tells a reader scanning the index what is on the slide without opening it.
The same claim with its reason attached.
Filler adjective
a robust retry on OTP fetch
A retry is a retry. "Robust" says the author wanted the sentence to sound engineered.
Names the behaviour
a retry that survives a mail-slot timeout
Now the reader knows what it does. Keep "robust" only where it is the term of art.
Empty content under a fine heading
The walls are the pattern.
No ban list names this. It is not a repeated shape, only a sentence with nothing in it.
Adds a fact
Three of seven accounts cannot reach a cloud-only runner at all.
Same slot in the paragraph, now carrying a count the reader can act on.
Declared count
Three causes sit under this. [...] The deck showed a fourth cause.
The taxonomy is invented, then has to be patched when a fourth thing arrives.
Just the causes
Most text about work on the internet is business prose. Preference tuning then rewards the look of completeness. And words cost the model nothing.
Each paragraph carries a mechanism. Nobody has to count them.

Current setup

My rules file now opens with the target: optimize for information gain over apparent completeness, and compose as finding, evidence, reasoning, decision rather than principle, framework, exposition, takeaway. The ban list is still there, as examples of the target being missed.

After the deck the agent saved a rule that lists the places the quality bar applies: slide titles, kickers, tile and card labels, table headers and row keys, diagram node and edge labels, chart titles and legends, button copy, subtitles, Slack, email, Jira, PR bodies, commit messages, code comments, memory files, and the content under all of them.

The banned shapes carry verbatim examples from my own outputs. "One harness, four swappable sides" is in the memory file as the example of number pairing, "The wedge" as metaphor in place of the noun, "Why hard apps are the niche" as question-as-title, "The fix was one line." as the verdict sentence. A rule that quotes the exact string the agent produced last Tuesday is harder to route around than an abstract one.

Rules in a file are advisory, and the deck showed what advisory gets you, so the last step was a scanner that runs whether or not anyone remembers it. Before the agent writes a file, publishes a page, or sends a Slack or Confluence message, the script reads the outgoing text and refuses the call if it contains a hard tell, returning the hit list so the rewrite is targeted. The same script runs on the agent's chat replies. Hard tells refuse on one hit: the em dash, honesty framing, "not X, it's Y" and its trailing cousin "X, not Y", headings that name their own rhetorical job, "That is the point" closers, sycophantic openers like "great question". Filler adjectives are counted and refused at three in one piece, because three is where use turns into decoration. Text inside quotes or code formatting is skipped, so a post like this one, which quotes the tells as exhibits, can still be written.

Both halves are now skills in COG, the public version of my agent setup, so they work without my vault:

  • slop-gate is the scanner. python3 scan.py DRAFT.md prints the tells and exits non-zero, so it runs as a CLI, a CI step, or a Claude Code hook with --hook. The pattern list, the quoted-span exemption and the slop-ok escape hatch are in the skill.
  • voice-baseline is the census. Point it at a directory of your own writing and it reports the per-file rate of every word you over-use, plus the shapes: verdict endings, kicker closers, heading first words. --check DRAFT.md prints only what sits above your own baseline. Every number in the section above came out of it, including the correction to my grep.

The no-ai-slop skill they sit beside picked up the reader-cited ranking, the list of media, and the voice-mode caps in the same release.

The hook fired on its own author within the hour. My agent's reply describing the hook listed the banned words in a sentence, and the gate refused the reply until the list was moved into quotes. A few messages later I caught it writing "reads as a test, not a permission", the trailing contrast form, which the first version of the script did not cover, and the pattern went in.

A model from a different family reviews what the lead agent writes. Same-family reviewers share the author's training distribution and therefore the author's sense of what a finished answer looks like. On this post, the reviewer found 57 problems in a draft I thought was finished, 6 of them the exact verdict sentence the post complains about.

Each catch becomes a memory file the agent reads at the start of the next session. The deck produced three of them: titles name the subject, the bar applies to every medium, the bar applies to content as well as labels.

Alt text, issue descriptions, memory summaries

The drive to resemble a finished answer is what preference tuning selects for, and every lab is doing preference tuning, so the forms will keep moving to whatever surface I have not written a rule for yet. My guess for the next one is somewhere I do not read carefully: alt text on images, the descriptions on GitHub issues, the one-line summaries my agents write for their own memory files. The hook will not catch those until someone names the shape, so the first one through will be a human reader, probably my tech lead, who reads the deck I send him and tells me which slide he could not find.