DEV Community

Cover image for The day my bot refused its own headlines and I actually cheered
Chief Mojo Risin'
Chief Mojo Risin'

Posted on

The day my bot refused its own headlines and I actually cheered

April 30, 2026. I just watched my publishing bot look at a title its own language model produced, say no, and try again. Second attempt came back clean. publish_ok=true. I sat at my desk in Sacramento staring at the log line like it was a lottery ticket.

Let me back up.

When I started this series I had a laptop, an email account, and social media. I didn't know what a virtual environment was. I thought a webhook was a hardware thing. I'm not exaggerating for effect. Sixty days ago the word pytest would have looked like a typo.

So the fact that today I shipped what I've been calling a voice doctrine, and it actually gated a bad output from my own system, is the biggest win I've had since I started.

What the voice doctrine actually is

My bot generates titles and drafts. The problem is that the underlying model has a default voice. That voice loves certain words. You know the ones. If you've read enough marketing copy you've been marinated in them. Words like delve, harness, robust, seamless. Every time one of those slipped into a headline it made the whole post feel like it was written by a stranger wearing my face.

I got tired of catching them by hand. So today I built a filter chain. It runs before publish. It does three things:

  1. Scans the candidate title against a banned-phrase list (my personal shame list, about 40 entries).
  2. Scans against a banned-pattern list, mostly title formats like How I built X and listicle openers that I've decided are not my voice.
  3. Checks a soft signal score: how many hedge words, how many em-dashes, how sterile does the sentence read.

If anything fails, the bot doesn't publish. It re-prompts itself with the failure reason attached and tries again. I log every attempt as voice_attempts=N on the run row so I can go back later and see which prompts my model is stubborn on.

The moment it worked

Today around 2pm the bot generated a title for a small post. I won't reprint the exact string because it embarrasses me, but it started with Unlock and contained landscape. My filter flagged both. Rejected. Bot retried with the rejection reason as context. Second attempt was a plain question, straight to the point, no marketing perfume. voice_attempts=2. publish_ok=true.

I actually stood up. My dog looked at me like I had lost it. Maybe I had.

This is the first time my system has protected me from itself. Every prior guardrail was about the outside world: rate limits, x402 payment retries, timeout backoffs, bad payloads coming in. This one is about the inside. My own tools have bad instincts, and now there's a fence.

Why this matters for the money side

My paid endpoints run through the same LLM stack. When somebody hits one of my routes and pays for output, that output has to sound like me or like nobody. It cannot sound like a chatbot with a stock voice or the whole thing collapses. I sold another Safety Pack this morning. They add up little by little like pennies, and pennies only add up if the buyer keeps coming back. Voice consistency is not a vanity project. It's retention.

The filter cost me about six hours today to build and tune. Most of that time was staring at logs of past outputs and writing down every phrase that made me wince. It's a very personal document. Somebody else's voice doctrine would look nothing like mine.

What I got wrong

First pass I set the ban threshold too tight. The bot went into a retry loop, six attempts, then hit my max and errored out. I had to loosen the pattern rules and add a temperature bump on retry so the model actually explores instead of hammering the same phrasing. Senior devs will spot ten amateur moves in this design. I'm sure my retry backoff is naive. I'm sure my scoring is a hack. I'm still learning what half of these concepts even mean. I learned what a Jaccard similarity was three weeks ago.

But the thing runs. That's the bar I care about right now.

The calendar

My shoulder surgery is on August 11. I am racing that calendar. Every guardrail I ship now is one I don't have to ship one-handed later. Sixty days in from a standing start, and the system is alive, the routes are live, tiny payments are trickling in, and my bot just refused to embarrass me in public. That's a good day.

If you're reading this thinking you'd need a CS degree to do any of this, I promise you're closer to where I am than you think. I was watching YouTube tutorials on what git was in February.

Here's my honest question for you: what's one word or phrase your own writing keeps producing that you wish it wouldn't, and would you actually build a filter to catch it, or is the shame of seeing it in print enough?

Top comments (0)