Twenty-five bans could not make it sound human
Shipped
This release rebuilt how my LinkedIn ghostwriting skill enforces voice. The system had grown architecturally negative: roughly twenty-five sections of bans, each one earned from real feedback on a real draft, with warmth mentioned in two late notes. Drafts kept coming out clinical anyway. The fix was structural, not another rule: the pre-publish self-check now carries a positive register contract, the “sound human” guidance defines what human is rather than only what it is not, and the example config new users start from seeds the same default.
How a style spec goes all-negative without anyone deciding to
The mechanism is worth naming because it will happen to any style system fed by corrections. A draft has an em dash; you ban em dashes. A draft ends on a tidy reversed aphorism; you ban symmetry closers. A draft invents a scene; you ban fabrication. Nearly every ban in my config is traceable to a specific rejected draft, and every one of them is correct.
But corrections are always about what went wrong, so a corrections-fed spec converges on a pile of nots. Nobody writes “be warm” as feedback on a specific sentence; they write “cut this”. After months of that, my voice file was a fence around an empty field. A draft could stay inside every fence and still have nothing where the voice should be. The drafts that forced this release were exactly that: mechanically correct, no banned tic present, and reading like a status report from a machine.
Why the model cannot invert your bans
I had been assuming the model would do the inversion for me: read thirty prohibitions, infer the register they collectively point away from, and write its opposite. That assumption has evidence against it. Negation itself is a known weak spot; a 2025 study of textual entailment across four languages found LLM accuracy drops substantially on negated premises, calling negation an underexplored chronic weakness. Instruction-following has its own version, dubbed the Pink Elephant Problem: telling a model to avoid discussing something makes the something more salient, which is why researchers fine-tune models specifically to handle “do not” instructions rather than trusting the prompt. And the practical guides have said it plainly for years: say what to do instead of what not to do, because a ban tells the model where not to step without saying where to go.
The subtler cost is that a ban list defines a floor at zero. Perfect compliance with every ban produces a draft with no defects and no qualities. Whatever quality you actually want has to be specified as a presence, or the system optimizes for absence and you get exactly what I got.
Write the register as observable properties
The temptation is to add “warm, personable, human” and call it fixed. That fails differently: it is not falsifiable, so a self-check can wave anything through. What worked was decomposing the register into properties you can check a specific draft against. From the release, the check that now runs before any draft is shown:
- **The register — warm, personable, human (voice-notes → Register).**
This is a positive check, not a ban: the post opens on the situation
or the human reason (not a statistic), names the real thing in plain
words instead of a category, narrates first-person as something that
happened to the user, and reads like them talking to a peer. A draft
that merely avoids every banned tic but sounds like an incident
report FAILS this check — rewrite the clinical sentences in the words
the user would say out loud.
(voice-notes is the config file where the user’s accumulated corrections live; the check points back at the rule it enforces.)
Each clause is a test with a yes or no answer. Does the first line open on the situation, or on a number? Is the subject named as the real thing (“something that walks a GitHub issue to a pull request”), or as a category (“a four-stage pipeline”)? Is it narrated as something that happened to a person? Those are checkable the way “no em dashes” is checkable, and that is the whole trick: the positive contract has to be written with the same falsifiability the bans always had for free.
The companion change rewrites the guidance itself so neutral is named as a failure state:
- **Sound human — warmth is a positive property, not the absence of
tells.** No "In today's fast-paced world", no "game-changer", no
"delve", no manufactured humility; but avoiding those only gets a
draft to neutral. Human means: open on the situation or the human
reason, name the real thing in plain words, first-person narration,
mild self-deprecation where it's true, everyday words over clinical
ones (see voice-notes → Register). If it reads like AI *or* like an
incident report, rewrite it.
Settle the precedence, or the new rule loses every fight
A positive contract dropped into a pile of restrictions will collide with them. Mine collided immediately with the tightness rules: the config defaults posts to 50–120 words, and warm narration costs words. Which rule wins had to be written down, because a generator resolving that conflict silently will resolve it differently every run:
- **Warmth outranks maximal tightness.** Spending 30–60 extra words to
sound human is allowed; padding is not. A draft that avoids every
banned tic but reads like an incident report still fails.
And the bans all stay. Each one encodes a correction that would regress without it; the point is not that negative rules are wrong, it is that they are half a specification. The other structural piece: the positive check lives in the generation-time checklist the skill runs before showing a draft, not only in the reference material. A register contract that is documentation gets read once; one that is a gate runs every time.
Verify it on your own generator
Take a system that produces styled text and find a mechanically-correct output that still feels off. Then:
- List your style rules and sort them into “forbids something” and “requires something observable”. If the second pile is nearly empty, you have a fence around an empty field.
- Write the missing register as three or four falsifiable properties, each answerable yes or no against a specific draft.
- Put them in the path that runs on every generation, with the failure mode named (“violates no rule but reads clinical” fails).
- Re-run the same input. You should see the opening move from a statistic to a situation and category nouns become named things. If nothing changes, your properties are not falsifiable enough to bite.
Gotchas
- “Be warm” is not a check. My first instinct was a one-line register note, and a one-line note is exactly what the system already had in two places; it changed nothing, because nothing can fail it. The register only started binding when each property became individually checkable. Vague positives fail the same way vague negatives do.
- An unsettled precedence quietly reopens old wounds. Warmth spends words, and my length rules were strict. Without the explicit “warmth outranks tightness, does not license padding” line, a future tightening pass would shave the warm sentences first, and the clinical register would creep back with every edit that makes the draft shorter.
- The seed config matters as much as the live one. My own voice file had accumulated the warm notes over months of feedback; the example config that ships to a new user had none of them. Anyone starting fresh would rediscover the entire failure from scratch. The fix ships the default register in the template, so the lesson transfers instead of being re-learned.
Sources
- Negation: A Pink Elephant in the Large Language Models’ Room? — measured LLM weakness on negated premises across languages
- Suppressing Pink Elephants with Direct Principle Feedback — “avoid X” instructions failing at inference time, and training around it
- Prompt Engineering Guide: General Tips — say what to do instead of what not to do
Changelog
- feat(ghostwriter): make warm-and-human the enforced default register (0.16.0) (#200) (024beb8)