AI-written news drafts, published by a human
Twice a week the pipeline collects facts, lets a model write from those facts only, checks the result and leaves a draft in WordPress. A person presses publish.
- Problem
- A local sports site running since 2004 needed a weekend preview and a weekend summary every week, work that used to take an editorial team.
- What runs
- Monday and Thursday at 18:00: collect, write, validate, draft, notify.
- How it is watched
- Every run, successful or not, is logged. Failures send an alert to Telegram. Known blind spots are written down.
- Stack
- Node.js, one model call through OpenRouter with fallbacks, WordPress REST, Telegram bot, systemd.
- Code
- Private repository. A walkthrough is available on request.
The problem
sport.czest.pl covers volleyball, football and youth sport in Częstochowa. The twice-weekly round-ups are valuable to readers and tedious to write: gather fixtures and results from several sources, check them, turn them into readable text.
What it does
- Collects facts from chosen sources with ordinary code and saves them as JSON. No model is involved in this step, so the facts are the same every time.
- Makes exactly one model call: the prompt and the facts in, an article out. The model runs no commands and publishes nothing.
- A validation gate checks the output against a contract: structure, tags, and that every link actually resolves.
- Files the article as a WordPress draft and sends the link to Telegram. A person reads it and publishes.
- The editorial rules live as Markdown in the repository, so changing the house style is an edit, not a code change.
How it stays under control
- Every run writes a line to a run log: status, model used, number of requests.
- Failures in collection, the model, validation or WordPress try to send an alert to Telegram.
- A lock that checks the owning process prevents two runs at once; drafts are idempotent, so a retry never creates a duplicate.
- Time limits are coupled: call timeout × attempts × fallback models sets the run budget, which sets the lock expiry, which must stay under the service timeout. The code derives the first two so a change cannot silently break the third.
The documentation also lists what no alert can catch, such as the timer never starting at all. Knowing where monitoring ends is part of monitoring.
What a business could reuse from this
Weekly client reports, internal digests, product descriptions, any recurring text built from data. The model writes; code collects the facts, checks the result and keeps a human on the publish button.