Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

I told the model to vary sentence length. That was the bug.

For a year I gave the same instruction inside every drafting prompt. Vary the sentence length. It made the output worse. Not at first. At first it looked like range. Then I put a hundred outputs side by side. A four-w

For a year I gave the same instruction inside every drafting prompt. Vary the sentence length.

It made the output worse.

Not at first. At first it looked like range.

Then I put a hundred outputs side by side. A four-word sentence sat next to a thirty-word one, and then another four-word sentence followed.

The model had learned to perform burstiness. It had not learned to write.

Worse, the tells survived. The colon, the triad, the forced opposition, all still there, now wrapped in a rhythm that looked deliberate.

The instruction was the bug

The repair prompt carried a numeric target: keep the coefficient of variation near 0.45. The model optimized for the number.

Anything a model can optimize, it can fake.

Version 0.2.0 removed both things. The instruction to vary sentence length is gone, and the repair prompt no longer carries a CV number.

HUMANIZE now fixes only the flagged spans. Structure and information stay where they are.

So I measured the thing instead of asking for it

We measured 134,522 organic posts from 1,499 founder profiles. The window runs from January 2023 to September 2026.

Signal Q1 2023 Q3 2026
Opening formula ("Here is why", "The truth is") 1.3% 9.6%
Forced opposition ("Not X, but Y") 1.2% 5.9%
Arrow and emoji bullet cascades 0.6% 12.3%
Mocked cliche vocabulary 4.2% 0.5%
Mean sentence burstiness (CV) 0.48 0.23

Writers deleted the words everyone mocked. Then they kept the sentence rhythm that gives a model away.

That is why a word blocklist has a short shelf life. The vocabulary is a symptom. The cadence is the disease.

The measurement is one number

Burstiness is the coefficient of variation of sentence length.

CV = standard deviation / mean

Short sentences next to long sentences produce a high CV. A wall of 18-word sentences produces a low one.

Our thresholds come from the same corpus:

  • 0.40 and above: human range
  • 0.35 to 0.40: warning band
  • below 0.32: flat, model range
  • 0.45: repair target

Two guards make it usable. The sample needs four sentences.

Mean sentence length must reach four words. Below that, the result is noise, and we say nothing.

Eight rules, not a vibe

The linter checks eight structural patterns. Each carries its own weight.

  1. CONTRASTIVE_NEGATION: "It is not X, it is Y". It also catches the comma splice variants.
  2. LESS_MORE_SYMMETRY: "Less X, more Y" and "Stop X, start Y"
  3. EPIPHANY_OPENER: "Here is why", "The harsh truth is", "Let that sink in"
  4. ARROW_BULLET_SLOP: arrow and emoji cascades used as structure
  5. TRIADIC_PADDING: "No X. No Y. Just Z."
  6. LEXICAL_SLOP: the surviving synthetic vocabulary, with a domain noun allowlist
  7. VERDICT_COLON: "The reality is:", "The takeaway:"
  8. PROSE_TRIAD: three rhetorical questions in a row, or a heavy comma enumeration with repeated determiners

Two metrics are reported and never scored. They are context, not verdict.

Deterministic by construction

The engine imports four modules from the Python standard library. They are re, sys, json and math.

It never calls the network and it never writes a file. It never asks a model for an opinion.

The same text produces the same score on every machine, forever.

That matters more than accuracy for one job: a gate inside an agent loop. A gate that changes its mind between runs cannot be trusted to block a commit.

The rule that keeps it honest

The free endpoint used to certify a draft with a score above 88 even when it still carried tells. A high score is not a pass.

The verdict now requires zero flags. A draft can score 92 and still be refused because one VERDICT_COLON survived.

That is correct behavior for a gate. It is also why the tool reports a verdict separately from a score.

Using it

pip install grai-gate
python3 gate.py README.md
python3 gate.py docs/*.md -j

Clean files stay silent. A linter that prints a banner on success gets removed from the pipeline within a week.

For agents, the same file runs as a PostToolUse hook. It reads the tool payload on stdin, writes the required JSON on stdout, and always exits 0.

It never denies a tool call. It reports. The harness decides.

What it costs in time

We measured it, because fast is not a number. On a 4.1 KB markdown file, five runs, the median was 26.1 ms end to end.

That figure includes Python interpreter startup. The analysis itself is under a millisecond.

On a very large file, a 6000 sentence cap applies.

What it does not do

It does not detect authorship. It does not prove a text is human.

It does not catch a confident lie. It is an advisory style gate for prose that a human will read.

And it is wrong on purpose in one direction: it says nothing when the sample is too small to judge.

The CLI is open source and on PyPI. The workbench builds a persistent voice profile from 5 to 20 of your own posts and checks every draft against it.

https://grai.page?utm_source=dailydev&utm_medium=article&utm_campaign=cadence_gate_2026_11

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.