← All Projects
Built · its IP lives on in the Content Engine

Ghost Publishing Engine

A production-grade, multi-brand AI publishing system: topic extractors feeding a queue, three genuinely distinct AI personas writing in enforced voices, self-scoring quality gates, and scheduled auto-publishing to Ghost CMS. Designed to run what a publication's staff does, operated by one person.

The Problem

A real publication needs writers, editors, an SEO person, and a publishing schedule. A solo operator gets none of that, and the obvious shortcut (one AI prompt writing generic articles) produces exactly the sameness readers smell instantly. The goal was the hard version: multiple distinct voices, automated topic discovery, quality control that rejects slop, and a posting cadence that runs whether or not anyone is at the desk.

What I Built

A multi-brand Python engine where every component takes a brand as its first parameter and brands live entirely as data: voice, personas, series frameworks, and affiliate products in JSON, never hardcoded. Extractors (Polymarket prediction markets, Google News RSS, a transcript watch-folder) feed a SQLite topics queue. A queue processor routes topics to Claude with persona-specific prompts, weighted by each persona's affinity keywords. Output passes a quality gate: Claude scores its own work 1 to 5, a refusal detector catches voice violations, and fuzzy matching kills duplicate topics. Articles then publish to Ghost CMS through the Admin API with JWT auth, persona-based author assignment, SEO metadata, affiliate links, and a 5-per-day limiter across optimal posting windows. A Flask dashboard ran the whole thing without touching a terminal. Seven launchd jobs kept it on schedule, from every five minutes to monthly.

The Pipeline · extraction to publication

01ExtractPolymarket · Google News RSS · transcript watch-folder → SQLite topics queue with source metadata
02RouteAffinity-keyword weighting assigns each topic to the right persona · fuzzy dedup kills repeats
03WriteClaude with persona-specific prompts · listicles, deep-dives, opinion pieces · video prompts for the AI-video lane
04GateSelf-scoring 1–5 · refusal detector for voice violations · banned-AI-ism lists per persona
05PublishGhost Admin API (JWT) · persona author assignment · SEO + affiliates · 5/day limiter, optimal windows

Tools & Stack

PythonClaude APIFlask DashboardSQLiteGhost Admin API (JWT)launchd × 7Polymarket / RSSYouTube APIOOP Multi-Brand

Key Results

  • Three engineered personas with 20+ explicit constraints each: a dry observational skeptic, a Gen-Z philosophy-literate conspiracy-questioner, and a gentle naturalist who finds dark nature facts delightful. Measurably different output, not prompt flavor
  • Anti-slop as code: a refusal detector, per-persona banned-phrase lists ("here's the thing" is contraband), and self-scoring before anything ships
  • Brand-as-data architecture: a second brand onboards as a JSON folder, zero code changes
  • Full test suite across core, agents, extractors, destinations, and the dashboard, plus dry-run modes on every automation script
  • The IP survived the system: its persona engineering, scoring discipline, and brand-as-data pattern were mined into the current recipe-based Content Engine

Challenges & Lessons

The hard problem was never generation, it was sameness. Distinct voices required structure, not adjectives: affinity keywords that bias which persona gets which topic, ban-lists of AI tells enforced by a detector, and worked example openers per series so the model imitates specifics instead of vibes. The second lesson was about leverage: building this taught me that the durable asset isn't the running system, it's the captured method. When I later rebuilt content production as a recipe engine, this system's persona definitions, scoring rubric, and brand-as-data philosophy transferred almost intact, which is the whole argument for writing your machinery down.

← RelatedContent Engine (successor)