How to Wire an Anti-Slop Pass Into Your Blogging or Artifact Pipeline
If you draft anything with a model in the loop, the output carries tells. The same warm-up opener, the same overused verbs, the same little contrast that argues with a point nobody made. Readers learn to smell it, and they bail.
The fix is one step you add at the end of your pipeline: an anti-slop pass. After the draft is written and correct, you read it against a list of known AI tells and rewrite every hit. That's the whole trick, and it's the cheapest quality win I've added to my writing pipeline.
The list I use is stop-slop, an open-source catalog of the phrases and structures that mark AI-generated prose. I wired it into Blog Bot, the Slack agent that drafts my posts, as its own pass. This post is the test case: the same pipeline drafted it, then ran the pass on it. The first run flagged eleven hits and a HIGH long-dash count; the run that opened its PR returned zero. Here's how to build the step.
Step 0: know the tells, and why they read as fake
You can't strip a tell until you can name it. These phrases stand out because they announce the move instead of making it.
Start with the throat-clearing opener. The draft warms up with a phrase that promises a hard truth, or one that frames the next sentence as a surprise discovery. Each one gives the reader runway. The writer clears their throat in place of leading with the point. A person who knows what they think says the thing. The model predicts the safest next sentence, and the safest next sentence is a warm-up.
The negate-then-assert contrast is the loudest one, and the tell I catch most often in my own drafts. It brushes aside a placeholder claim about speed, then promotes trust as the wiser frame, knocking down a position no reader needed knocked down. The sentence feels sharp until you ask who argued that speed was the whole story. State the claim and the negation falls away with nothing lost.
Then come the small tells that pile up: adverbs that intensify nothing, business verbs standing in for plain ones, lists padded for rhythm, long dashes scattered through parenthetical asides. One long dash won't ruin a post. A dozen in a thousand words make the draft look machine-set.
The common failure under all of them is refusing to choose. The model hides the actor, gives three safe examples, and rounds off the claim. My edit is the opposite: name who did the thing, pick the one example, and write the claim a reader could object to.
Step 1: put the pass at the end, not in the loop
The pass works on any artifact a model helped produce. The last non-blog place I ran it was a generated changelog. The model wrote that a release "improved reliability across workflows," which is true of nothing. The pass forced the edit: "fixed the retry loop that dropped queued webhooks after a 429." Same artifact, now with a fact in it. Put the pass anywhere model output becomes text under your name.
I use this order, with position as the rule that matters:
Anti-slop runs after the draft is written, structured, fact-checked, and in voice, not during any of that. If you strip tells while you draft, you fight the list instead of making the argument, and the prose comes out stiff in a new way. So the substance goes in first. Then the pass reads the whole piece against the list, and the gate bounces it back until it's clean.
Step 2: pull the tells list live, pin a copy for CI
Don't hardcode the list from memory. It changes. Maintainers add tells as models pick up new habits, and a copy I memorized six months ago would miss whatever the current models overuse this week.
Two policies keep the gate steady. My local pass reads the latest stop-slop list as advisory input during the final local run, so it warns me about fresh tells. CI enforces a pinned copy I vendor into the repo and update on purpose:
# local: read upstream live (freshness)
curl -fsSL https://raw.githubusercontent.com/hardikpandya/stop-slop/main/references/phrases.md
# CI: enforce a vendored copy pinned to a commit (stability)
STOP_SLOP_SHA=2f1c9ab0c4e1d7a9b3f08e51d2c6a47b9e0f1a3c # replace with the real commit; bump in its own PR
mkdir -p tools/voice/vendor
curl -fsSL "https://raw.githubusercontent.com/hardikpandya/stop-slop/${STOP_SLOP_SHA}/references/phrases.md" \
-o tools/voice/vendor/phrases.md # committed; CI reads this, not upstream
If upstream adds a bad rule, I see the local warning, but CI keeps reading the vendored file and won't block a publish until I update the pin. Live where you want freshness, pinned where you need a stable gate.
Step 3: rewrite the sentence, never the token
The naive pass uses find-and-replace. It spots a banned word, deletes it, and moves on. That removes evidence while leaving the broken sentence intact.
A tell often covers more than one word. The sentence carries the move. Take a line that opens with a clarity announcement and ends by saying the implications are significant. It has two problems: the throat-clearing opener and a claim that never names the implication. Cut the opener and the empty claim still stands. The repair is to name the actual implication, which means rewriting the clause, sometimes the paragraph.
The pass works in this order. I read the full draft against the loaded list. I mark phrase tells and structural tells. For each one, I rewrite the sentence so it states the thing in plain language, names the actor, and picks one example. Then I re-read, because one fix can expose the weak sentence next to it.
anti-slop pass
1. fetch the live stop-slop list (phrases + structures)
2. read the full draft, mark every hit
- phrase tells: throat-clearing, jargon, dead adverbs (regex-catchable)
- structure tells: negate-then-assert, negative listing,
false agency, passive voice, list-of-three (human/judge)
3. for each hit: rewrite the clause to state the thing plainly
(don't just delete the token; the sentence is the unit)
4. run the scanner (mirrors the CI banned-phrase gate)
5. repeat until the scanner is clean AND the human read is clean
The split in step 2 trips people up. A regex catches the look-into verb on sight, and a couple of structural shapes are cheap enough to match. The subtler structure, a paragraph that dodges the actor while keeping every word clean, needs judgment from a model or a human. I also separate writer from editor. The model that drafted the piece is the worst judge of its own tells. I hand the draft to a different model family for the rewrite, so the writer and editor bring different habits.
Step 4: make the scanner a gate, not the finish line
Step 4 in that list is a hard gate. My scanner reads the prose, the JSX props, the image alt text, and the code fences alike, with no idea which is which. It lowercases the file, matches the banned-phrase list plus two cheap structural heuristics, and exits non-zero on any hard hit. Long-dash density above 8 per 1,000 words flags HIGH, which warns without failing the build. Scanning everything, including alt text and code, creates false positives in examples like this post, and I take that over a parser that lets dirty alt text ship because it decided a prop didn't count.
It mirrors my CI gate, and the script that opens my pull requests exits before it calls GitHub if the scanner fails, so I can't publish a dirty draft from muscle memory. Making the gate a plain shell command is the trick that lets a bot run it at all: the same way my publishing pipeline encodes the voice rules as a contract every agent reads, a quality bar an agent can run is one that exists as a command, not a vibe. scan:voice is a thin wrapper that maps each stop-slop phrase to a local rule ID, so it can print phrase.jargon.look-into instead of re-emitting the banned token. Wire it in package.json and run the same check in CI on every push:
"scripts": {
"scan:voice": "tsx tools/voice/scan-voice.ts"
}
- name: scan voice
run: pnpm scan:voice src/content/blog/**/*.mdx
The scanner sets the floor, the judgment read sets the ceiling. The regex catches the worst phrase offenders and runs two cheap structural heuristics I trust in CI: a negate-then-assert shape and a bare list of three. Everything subtler, a paragraph that dodges the actor while keeping every word clean, still needs the human or cross-model read that happens outside the scanner. Wire both: an automated gate so a tell can't slip through by accident, and a structural read so clean-worded slop still gets caught.
A clean scan can still hide an empty draft
Don't let a green scanner flatter you, because the pass can lie. Strip each tell from a hollow draft and you get hollow prose with no tells. It reads clean and says nothing. A draft can pass each regex and still say "this workflow changes how teams think about content," which is grammatical, on-brand, and empty. The pass pulls the surface markers of machine writing without adding the substance that makes writing worth reading: the specific number, the production bug, the opinion someone would argue with. Those come from the spark, the lived experience and the point of view you have to supply. No pass generates them.
The pass earns its place because of where it sits. It runs after the draft has truth, utility, structure, and substance. Its job stays narrow: keep the surface from betraying the substance, keep a reader from flinching and bailing on something that was worth their time. Run it on a hollow draft and you hide the hollowness, which makes the problem harder to catch.
The list defends prose you already made worth reading; it won't make the draft worth reading for you.
Wire it into your own pipeline
Add the pass wherever your drafts leave the model, whether the artifact is a blog post, a changelog, or a release note. The recipe:
- Pull a current tells list. stop-slop is free and a good start. Read it live; pin a copy for CI.
- Run it last, after structure, facts, and voice. Keep it out of drafting.
- Rewrite the sentence around each hit instead of deleting the token.
- Hand the rewrite to a different model than the one that drafted it.
- Gate on an automated scanner, then do the structural read the scanner can't.
The scanner is the floor; the structural read is the ceiling. It's the same division of labor I care about across the interface around the model: the machine runs the repeatable check, and I keep the first idea and the final judgment. An anti-slop pass can follow a list and run in automation, and it still can't hand a post a reason to exist. That's the line I want every pipeline to hold.

Discussion
Giscus