ran in production · 35 posts, no human in the loop
Daily LinkedIn auto-poster
- Built for:
- One person who wanted to stay visibly current on AI without spending the first hour of every day reading and writing — and who trusted a machine to do it only if it checked its own work first.
- Not built for:
- A content farm. Wire writes two careful posts a day from real sources, not a firehose; the scoring exists to throw most of the day away.
Every morning Wire pulled the day’s AI news from seventeen feeds, scored every item across eight signals, drafted two posts on a local model, ran a second model over them to fact-check and trim, and published — no approval screen, no human in the loop. It ran this loop in production for three weeks and shipped thirty-five posts before I parked it. This is how it worked.
The problem
Staying current on AI as a solo engineer is a daily tax: read the feeds, find the one thing worth saying, write it without sounding like everyone else, post it before the day eats the time. Done by hand it’s an hour every morning. Skipped for a week and you’ve gone quiet.
The interesting part isn’t the writing — a local model can draft. The interesting part is trust. An agent that posts to a real account under your name with no one watching has to be wrong about facts roughly never. So Wire spends most of its effort not on drafting but on choosing what’s worth saying and checking that the draft is true before it goes out.
Decisions
local
Every model runs on the machine — Ollama with a 26B Gemma for drafting and review, a small embedding model for semantic novelty. No tokens leave the box, no per-post API bill, and the whole pipeline keeps working when a provider has a bad day. The cost of a post is electricity.
second pass
Drafting and reviewing are two separate model calls with two different jobs. The first writes; the second is handed the draft and the source it came from and told to fact-check it against that source, enforce the style rules, and rewrite anything that drifts. A draft never reaches the publish step without a second model having tried to break it. This is the only reason an unattended loop is defensible.
no gate
No approval screen. The first design had a phone notification asking me to bless each post; I deleted it. A loop that needs a human at 6 a.m. isn’t autonomous, it’s a chore with extra steps. The safety has to live inside the loop — in the scoring and the review pass — not in a person tapping approve. It’s currently parked, not because it broke, but because I’d proven the point.
System
Six stages, one direction. Findings are scored and deduped against a seven-day topic memory so the same story never posts twice; the draft and review passes are separate models; publishing is the last thing that happens, after the work is already trustworthy.
| Layer | Implementation | Purpose |
|---|---|---|
| Schedule | Windows Task · 6 a.m. | Unattended daily trigger |
| Sources | 17 RSS feeds + Firecrawl web search | Raw findings, deduped |
| Scoring | 8-signal composite + nomic-embed | Throw most of the day away |
| Draft + Review | Ollama · gemma4:26b (×2 roles) | Write, then fact-check vs source |
| Publish | Playwright (headless Chromium) | Posts to the feed · no human gate |
| State | Peewee + SQLite | Briefs · topic cooldown · audit |
# Eight independent signals, each a float in [0, 1], combined
# by config weights. The point of the composite isn't to rank
# the winners — it's to discard the ~95% not worth a post.
def compute_composite(scores: dict[str, float]) -> float:
weights = {
"recency": cfg.scoring.recency,
"novelty": cfg.scoring.novelty,
"relevance": cfg.scoring.relevance,
"semantic": cfg.scoring.semantic, # embedding sim
"velocity": cfg.scoring.velocity,
"actionability": cfg.scoring.actionability,
"authority": cfg.scoring.authority,
"signal": cfg.scoring.signal, # source count
}
total = 0.0
for dimension, weight in weights.items():
total += scores.get(dimension, 0.0) * weight
return round(total, 4)
{
"title": "...",
"scores": {
"recency": 0.95,
"novelty": 0.88,
"relevance": 0.91,
"semantic": 0.73,
"velocity": 0.40,
"actionability": 0.66,
"authority": 1.00,
"signal": 0.80
},
"composite": 0.842,
"decision": "draft",
"cooldown": "topic unseen in 7d"
}
What it did
Over about three weeks it ran fifty-five times and published thirty-five posts to a real account, twice a day, with no one approving them — the database still has every run logged with its timestamps. Then I disabled the schedule. The point I’d wanted to prove — that an unattended local agent could research, write, check itself, and act in the world without embarrassing me — was proven, and I’d rather show the working pipeline than leave a bot posting forever. It’s parked, not broken; the loop is intact and the history is real.
Acknowledgments
Wire stands on Ollama and the Gemma models that run on one GPU, nomic-embed-text for semantic novelty, Playwright for the publish step, Firecrawl for search, and the rule that an agent allowed to act unattended must be made to check its own work first.
← Index