prose-slop-guard
The problem this solves
AI-slop prose passes every existing gate. Tests don't read English. Render
review checks pixels. Code review checks logic. A doc full of em dashes,
"delve", and "it's not X, it's Y" ships green — and readers clock it as
machine-written in two sentences, which costs the text its credibility no
matter how correct it is.
Worse, the author-model can't self-grade: the same distribution that produced
the tells rates them as fine. Community consensus (HN, on anti-slop skills)
is blunt about this — filtered output can still read as slop when the filter
and the writer are the same pass.
So the fix is the same shape as design-token-guard and
class-extraction-guard: a source-level gate with a deterministic layer a
hook can run, plus a fresh-eyes judgment pass for the tells no regex can
catch. The canonical spec is Wikipedia's
Signs of AI writing
page — the same ground truth the Humanizer skill family builds on — mirrored
and condensed in references/slop-patterns.md.
Layer 1 — Lexical audit (deterministic)
Run the bundled checker from the project root:
python3 SKILL_DIR/scripts/check_prose_slop.py --root .
What it catches (the mechanical tells):
| rule |
catches |
default |
no-em-dash |
em/en dashes in prose (use -, comma, or parentheses) |
error |
banned-phrases |
throat-clearing openers, "not only... but also", "it is worth noting", "a testament to", "let's dive in", "ever-evolving" |
error |
sycophancy |
"great question", "you're absolutely right", "I hope this helps" |
error |
banned-vocabulary |
delve, leverage, seamless, tapestry, unleash, harness, elevate, landscape, game-changer, paradigm, synergy, empower, streamline, unprecedented, testament, boasts, furthermore... (each with a plain replacement hint) |
warn |
negative-parallelism |
"it's not just...", "isn't merely..." setups |
warn |
Scoping is prose-aware: fenced code blocks, inline code, link targets, URLs,
YAML frontmatter, and blockquotes are skipped (quoted content keeps its
author's punctuation — an em dash inside a quote is not your finding).
Useful flags:
--staged — only git-staged files (what the pre-commit hook runs).
--stdin — pipe a commit message or PR body through the same rules:
git log -1 --format=%B | python3 .../check_prose_slop.py --stdin
--json — { ok, summary, findings } for a gate/CI to parse.
PATHS… — limit to the files you just wrote.
Exit code 1 on error-severity findings, 0 when clean — drops straight into
a hook or CI step.
Layer 2 — Judgment pass (model, fresh eyes)
The regex layer cannot see rhythm, structure, or voice. After (or instead of,
for a quick review) the lexical audit, read references/slop-patterns.md and
sweep the text for the judgment-level categories:
- Rule-of-three rhythm — triplet lists as a crutch ("fast, simple, and
powerful"). One is fine; a pattern is a tell.
- Bullet soup / formatting inflation — bullets, bold, and headers where a
paragraph would carry the argument. Structure should encode information, not
decorate it.
- Vague attribution — "experts say", "studies show", "many users report"
with no named source. Name it or cut it.
- Inflated symbolism — "stands as", "serves as a reminder", "underscores
the importance of". State the fact; skip the ceremony.
- Superficial -ing analysis — trailing participial glosses ("...,
highlighting the need for...", "..., showcasing its versatility").
- Hedged everything-ness — "whether you're a beginner or an expert".
- Sycophantic or performative openers/closers in context the regexes miss.
Rewrite toward the require-list: short active sentences with varied length,
direct "you/your" address, concrete examples over abstractions, specific
attribution, direct claims with evidence, prose paragraphs by default.
Who runs the judgment pass matters. If you wrote the text this session, do
not grade it yourself — dispatch a fresh-context reviewer (a subagent that gets
only the text plus references/slop-patterns.md, not your drafting context) or
run the lexical checker plus a self-sweep and say plainly that the judgment
pass was self-graded. This is the same evaluator principle as
loop-controller's fresh-context evaluator: the writer's context contaminates
the grade.
Interpret and fix
Two kinds of findings, different fixes — don't blur them:
- Mechanical (em dash, banned word, banned phrase): replace per the hint.
Safe, fast, no meaning change.
- Judgment (rule of three, vague attribution, bullet soup): these usually
signal a missing fact, not a wrong word. "Studies show" means go find the
study or delete the claim. A triplet means pick the one that matters.
Rewriting the surface while keeping the emptiness is how "humanized" text
still reads as slop.
Legitimate exceptions — leave these alone and, where recurring, encode them
in config: quoted material (already exempt), upstream changelog text,
vocabulary that is a real term of art in the domain ("harness" in an agent
harness, "landscape" in ecology), and any project whose house style explicitly
allows em dashes ("rules": {"no-em-dash": "warn"}). The guard serves the
project's voice; it does not impose one.
Enforce (scaffold into the repo)
A one-time cleanup doesn't stop regression — the next generated README
reintroduces everything. Wire the gate in:
- Write
.prose-guard.json at the repo root (template:
assets/prose-guard.config.json). Tune rules, add domain terms to
allowVocabulary, scope extensions/skipPaths.
- Add a script entry —
"lint:prose": "python3 scripts/check_prose_slop.py --root ."
(copy the checker into the project's scripts/ or reference the skill path).
- Pre-commit hook — run
check_prose_slop.py --staged; block on errors.
Wire via Husky/lefthook if present, else a plain .git/hooks/pre-commit.
- CI step — run
lint:prose next to lint/typecheck.
- Standing style rules — for repos where agents generate prose routinely,
add the CLAUDE.md block from
references/wiring-into-agents.md so the rules
apply at generation time, not just at gate time. Prevention beats filtering.
Tell the user which layers you installed and which you skipped.
Using it as an agent / orchestrator gate
- A docs-agent (or any agent producing READMEs, docs, PR bodies) runs the
checker with
--json against the files it changed before reporting done;
error findings mean the task isn't done.
- The orchestrator runs it at the wave gate alongside typecheck/test;
non-zero
summary.errors blocks the wave and routes back to the owning
agent.
- git-pr pipes the drafted PR body through
--stdin before gh pr create.
- The judgment pass at gate time goes to a fresh-context reviewer, never
the agent that wrote the text.
Exact snippets: references/wiring-into-agents.md.
Reference files
references/slop-patterns.md — the full pattern catalog by category
(Vocabulary, Phrases, Structure, Voice, Sycophancy, Rhythm, Specificity,
Formatting), each with why it reads as slop and what to write instead;
provenance and upstream sources.
references/wiring-into-agents.md — agent self-check, orchestrator
wave-gate, git-pr --stdin check, the fresh-context evaluator dispatch, and
the copy-paste CLAUDE.md standing-rules block.
1---2name: prose-slop-guard3description: Source-level gate that catches AI-slop prose ("claudenese") before it ships: em dashes, AI vocabulary (delve, leverage, seamless, tapestry), negative parallelism ("it's not X, it's Y"), throat-clearing openers, sycophantic filler, rule-of-three rhythm, bullet soup. Use whenever writing or reviewing outward prose — READMEs, docs, announcements, blog posts, PR descriptions, wiki pages, marketing copy — before committing or declaring a writing task done, when setting up enforcement so slop can't ship again, or as an orchestrator/docs-agent wave-gate. Trigger on: "sounds like AI", "reads as AI-written", "humanize this", "make it sound human", "de-AI this", "too ChatGPT", "AI tells", "AI slop", "claudenese", "stop writing like a bot". A deterministic lexical checker plus a judgment rewrite pass; don't skip it because it "looks fine" — self-grading is the failure mode this guards.4---56# prose-slop-guard78## The problem this solves910AI-slop prose passes every existing gate. Tests don't read English. Render11review checks pixels. Code review checks logic. A doc full of em dashes,12"delve", and "it's not X, it's Y" ships green — and readers clock it as13machine-written in two sentences, which costs the text its credibility no14matter how correct it is.1516Worse, the author-model can't self-grade: the same distribution that produced17the tells rates them as fine. Community consensus (HN, on anti-slop skills)18is blunt about this — filtered output can still read as slop when the filter19and the writer are the same pass.2021So the fix is the same shape as `design-token-guard` and22`class-extraction-guard`: a **source-level gate** with a deterministic layer a23hook can run, plus a **fresh-eyes judgment pass** for the tells no regex can24catch. The canonical spec is Wikipedia's25[Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing)26page — the same ground truth the Humanizer skill family builds on — mirrored27and condensed in `references/slop-patterns.md`.2829## Layer 1 — Lexical audit (deterministic)3031Run the bundled checker from the project root:3233```bash34python3 SKILL_DIR/scripts/check_prose_slop.py --root .35```3637What it catches (the mechanical tells):3839| rule | catches | default |40|---|---|---|41| `no-em-dash` | em/en dashes in prose (use ` - `, comma, or parentheses) | error |42| `banned-phrases` | throat-clearing openers, "not only... but also", "it is worth noting", "a testament to", "let's dive in", "ever-evolving" | error |43| `sycophancy` | "great question", "you're absolutely right", "I hope this helps" | error |44| `banned-vocabulary` | delve, leverage, seamless, tapestry, unleash, harness, elevate, landscape, game-changer, paradigm, synergy, empower, streamline, unprecedented, testament, boasts, furthermore... (each with a plain replacement hint) | warn |45| `negative-parallelism` | "it's not just...", "isn't merely..." setups | warn |4647Scoping is prose-aware: fenced code blocks, inline code, link targets, URLs,48YAML frontmatter, and blockquotes are skipped (quoted content keeps its49author's punctuation — an em dash inside a quote is not your finding).5051Useful flags:52- `--staged` — only git-staged files (what the pre-commit hook runs).53- `--stdin` — pipe a commit message or PR body through the same rules:54 `git log -1 --format=%B | python3 .../check_prose_slop.py --stdin`55- `--json` — `{ ok, summary, findings }` for a gate/CI to parse.56- `PATHS…` — limit to the files you just wrote.5758Exit code `1` on error-severity findings, `0` when clean — drops straight into59a hook or CI step.6061## Layer 2 — Judgment pass (model, fresh eyes)6263The regex layer cannot see rhythm, structure, or voice. After (or instead of,64for a quick review) the lexical audit, read `references/slop-patterns.md` and65sweep the text for the judgment-level categories:6667- **Rule-of-three rhythm** — triplet lists as a crutch ("fast, simple, and68 powerful"). One is fine; a pattern is a tell.69- **Bullet soup / formatting inflation** — bullets, bold, and headers where a70 paragraph would carry the argument. Structure should encode information, not71 decorate it.72- **Vague attribution** — "experts say", "studies show", "many users report"73 with no named source. Name it or cut it.74- **Inflated symbolism** — "stands as", "serves as a reminder", "underscores75 the importance of". State the fact; skip the ceremony.76- **Superficial -ing analysis** — trailing participial glosses ("...,77 highlighting the need for...", "..., showcasing its versatility").78- **Hedged everything-ness** — "whether you're a beginner or an expert".79- **Sycophantic or performative openers/closers** in context the regexes miss.8081Rewrite toward the require-list: short active sentences with varied length,82direct "you/your" address, concrete examples over abstractions, specific83attribution, direct claims with evidence, prose paragraphs by default.8485**Who runs the judgment pass matters.** If you wrote the text this session, do86not grade it yourself — dispatch a fresh-context reviewer (a subagent that gets87only the text plus `references/slop-patterns.md`, not your drafting context) or88run the lexical checker plus a self-sweep and say plainly that the judgment89pass was self-graded. This is the same evaluator principle as90`loop-controller`'s fresh-context evaluator: the writer's context contaminates91the grade.9293## Interpret and fix9495Two kinds of findings, different fixes — don't blur them:9697- **Mechanical** (em dash, banned word, banned phrase): replace per the hint.98 Safe, fast, no meaning change.99- **Judgment** (rule of three, vague attribution, bullet soup): these usually100 signal a missing fact, not a wrong word. "Studies show" means *go find the101 study or delete the claim*. A triplet means *pick the one that matters*.102 Rewriting the surface while keeping the emptiness is how "humanized" text103 still reads as slop.104105**Legitimate exceptions** — leave these alone and, where recurring, encode them106in config: quoted material (already exempt), upstream changelog text,107vocabulary that is a real term of art in the domain ("harness" in an agent108harness, "landscape" in ecology), and any project whose house style explicitly109allows em dashes (`"rules": {"no-em-dash": "warn"}`). The guard serves the110project's voice; it does not impose one.111112## Enforce (scaffold into the repo)113114A one-time cleanup doesn't stop regression — the next generated README115reintroduces everything. Wire the gate in:1161171. **Write `.prose-guard.json`** at the repo root (template:118 `assets/prose-guard.config.json`). Tune rules, add domain terms to119 `allowVocabulary`, scope `extensions`/`skipPaths`.1202. **Add a script entry** — `"lint:prose": "python3 scripts/check_prose_slop.py --root ."`121 (copy the checker into the project's `scripts/` or reference the skill path).1223. **Pre-commit hook** — run `check_prose_slop.py --staged`; block on errors.123 Wire via Husky/lefthook if present, else a plain `.git/hooks/pre-commit`.1244. **CI step** — run `lint:prose` next to lint/typecheck.1255. **Standing style rules** — for repos where agents generate prose routinely,126 add the CLAUDE.md block from `references/wiring-into-agents.md` so the rules127 apply at generation time, not just at gate time. Prevention beats filtering.128129Tell the user which layers you installed and which you skipped.130131## Using it as an agent / orchestrator gate132133- A **docs-agent** (or any agent producing READMEs, docs, PR bodies) runs the134 checker with `--json` against the files it changed *before* reporting done;135 error findings mean the task isn't done.136- The **orchestrator** runs it at the wave gate alongside typecheck/test;137 non-zero `summary.errors` blocks the wave and routes back to the owning138 agent.139- **git-pr** pipes the drafted PR body through `--stdin` before `gh pr create`.140- The **judgment pass** at gate time goes to a fresh-context reviewer, never141 the agent that wrote the text.142143Exact snippets: `references/wiring-into-agents.md`.144145## Reference files146147- `references/slop-patterns.md` — the full pattern catalog by category148 (Vocabulary, Phrases, Structure, Voice, Sycophancy, Rhythm, Specificity,149 Formatting), each with why it reads as slop and what to write instead;150 provenance and upstream sources.151- `references/wiring-into-agents.md` — agent self-check, orchestrator152 wave-gate, git-pr `--stdin` check, the fresh-context evaluator dispatch, and153 the copy-paste CLAUDE.md standing-rules block.