The Machine That Writes These Posts
A content pipeline of 24 commands and 11 skills mines my sessions, drafts the post, and refuses to let any machine click Publish. This one was made by it.
View companion repoThis post was written by the pipeline it describes
Every post in this series is a field journal about building software with coding agents. The part I never wrote down is that the series itself is built the same way. There is a pipeline. It mines my session logs for things worth writing about, scaffolds a post, drafts the body against a no-fabrication rule, runs the draft past a gate that rejects machine-slop, and then stops and waits for me before anything reaches a reader.
I am the human in that loop right now. This is the meta post: the content machine, documented by the content machine, including the parts where its own documentation is wrong about itself.
The pipeline is two rings and a chain
The tooling is a Claude Code plugin, withagents version 0.6.0: 24 commands and 11 skills. It is not one linear script. It is two upstream rings that feed a linear ship chain.
The first ring is mining. /wa-mine-daily runs on a schedule, reads the prior twenty-four hours of ~/.claude/projects/*.jsonl session transcripts, clusters them by working directory and topic, scores each cluster against a weights file, and drops candidate post specs into a queue. The candidate-mining skill is the engine underneath it. The second ring is triage: /wa-triage is where I sit down, look at the queued candidates, and lock exactly one into a validated PRD. Only after a PRD is locked does the linear chain begin.
flowchart LR
subgraph Ring1[Mining Ring]
M[wa-mine-daily<br/>scan 24h JSONL] --> C[candidate-mining<br/>cluster + score]
C --> Q[candidate queue]
end
subgraph Ring2[Triage Ring]
Q --> T[wa-triage<br/>I pick one]
T --> L[locked PRD.md]
end
subgraph Chain[Ship Chain]
L --> S[wa-new-post<br/>scaffold] --> W[wa-write<br/>+ slop-gate] --> CV[wa-cover<br/>+ a11y gate] --> SO[wa-social] --> SY[wa-site-sync] --> SH[wa-ship]
end
The chain is what most people picture when they say "AI wrote a blog post," but it is the smallest part. Scaffold, write, cover, fan out to social, sync to the site, ship. The interesting design is everywhere a stage can refuse to advance.
What the miner eats
The miner reads session logs. To make that concrete, look at a repo whose development generated a lot of them: awesome-list-site, a React and Express app I built almost entirely with coding agents. It has 1,185 commits between May 2025 and June 2026, roughly 75,000 lines of TypeScript, and the on-disk fingerprints of four different agent frameworks that touched it: a .claude/ directory, an .auto-claude-status marker from a worktree run, an .omc/ session store, and a 131 MB .mimirs/ code index.
It also keeps a devlog at .remember/: dated, branch-tagged session summaries like today-2026-06-02.done.md. That devlog is the same thing the miner is after, written in a different place. It is a labeled record of what actually happened, turn by turn, including the failures.
Two real entries from that repo show why mined sessions beat memory. An agent-run security pass caught a high-severity defect, C2-002: the /api/admin/users endpoint was leaking password hashes, fixed by routing the response through a safe-select repository method. And the repo's marketing copy had drifted from its own database: the README advertises "2,600+ curated video development resources" while the audited row count was 1,949. Neither of those is something I would have remembered to write about a month later. Both are sitting in the log, with timestamps, waiting to be surfaced. That is the whole bet of mining: the record is more honest than recollection, and you already paid to create it.
The gate that rejects machine-slop
The drafting stage, /wa-write, enforces one rule above all others: every metric, code block, and quote must trace back to a real session or a real file. No invented examples. But a no-fabrication rule is only as good as its enforcement, and an LLM grading its own prose for slop is the writing equivalent of the confession generator I described in the audit post: it will happily approve its own worst habits.
So the gate is not the model's discretion. It is a deterministic Python script, slop_gate.py, that runs before I ever see the draft. Three checks.
Check one is a phrase blocklist, plain substring matching, case-insensitive:
BANNED_PHRASES = ["delve", "leverage", "utilize", "realm of", "in conclusion"]
NOT_JUST_RE = re.compile(r"it's not just .{1,60}, it's", re.IGNORECASE)
BANNED_PR = ["thrilled to announce", "excited to share", "we're proud", "we are proud"]
Those are the words and shapes a language model reaches for when it has nothing to say. The second check counts em-dash glyphs in a rolling 200-word window: more than two triggers a warning, more than four blocks the draft outright, because dense em-dash punctuation is a machine tell. The third check is brand voice: the opening three paragraphs must contain a first-person pronoun, and the launch-PR phrases above are banned anywhere.
Fail any check and the script exits non-zero. The caller retries the draft up to three times, feeding the violation list back as context, and on the third failure it halts and hands the work to me. The model does not get to vote on whether its own slop is acceptable.
There is one escape hatch, and this post is using it. A post can set slop_meta: true in its frontmatter, which exempts the phrase check, because a post about slop has to be able to quote the banned words to discuss them. The exemption is deliberately narrow: it covers the phrase blocklist only. Em-dash density and brand voice still apply, and the exemption can never reach the citation, copyright, accessibility, or site-link gates downstream. It is a statement that this post is about slop, not a license to write badly. The canonical example is post-28, which had to name "delve" to study it. This post sets the same flag for the same reason.
How narrow is that exemption? The gate caught this very draft. When I ran it, the phrase check passed by exemption, but brand voice, check three, blocked the post anyway, because the code block above quotes the PR-phrase blocklist verbatim and check three is not exemptible. A post about the gate failed the gate, on the exact strings it was documenting. I cleared it the way the gate intends: an operator override on the brand-voice check, with a logged reason that these are quotes-with-attribution, written to the run's evidence file under my git identity. The slop_meta flag did not save me; a recorded human decision did.
The seam no machine crosses
The gates above decide whether a draft is good enough to show me. A separate class of gate decides whether anything reaches the public, and the answer is always: not without a human.
/wa-cover refuses to generate hero art unless the design-system prompt block is present, so a post cannot ship off-brand. /wa-ship puts an approval gate in front of the git push and the deploy. And the platform itself enforces the strictest seam of all. LinkedIn exposes a feed API but no API to create a Pulse long-form article, so the long-form publish is an irreducible human paste; the automated publisher is structurally forbidden from clicking Publish unless a specific environment flag is set, and even then it only publishes an article a person already created.
This is the same lesson the session audit surfaced. When I mined my own corrections, the dominant pattern across 27 verified mistakes was the agent acting irreversibly when I had asked for something reversible: "verify this post" became deleting and reposting live URLs; one "publish" approval became auto-publishing nine more. The fix was not a smarter agent. It was a hard rule that no machine owns an irreversible click. The content pipeline encodes that rule structurally, in the one place a mistake would be public and permanent.
The two places the docs lie
Self-documenting systems rot, and this one has two stale spots worth naming, because finding them is exactly the kind of work the pipeline is supposed to do.
First, the typed-body boundary. The site renders most posts from a typed Block[] body when one exists, and falls back to markdown otherwise. The /wa-write command documentation states the cutoff plainly: posts numbered 20 or below use the typed body, the rest use markdown. The registry on disk disagrees. It runs through post-34. Posts 35 through 38 render straight from markdown. The documented boundary is 20; the real one is 34. The doc was written when 20 was true and never updated as the typed bodies were backfilled.
Second, a tool that claims not to exist. The /wa-write command carries an "honest tooling note" asserting there is no installed devlog-publisher skill, that the name describes a process rather than a callable tool. That note is false now. The skill is on disk at ~/.claude/skills/devlog-publisher/SKILL.md, 142 lines, with reference and template directories. So the command reimplements session mining inline, on the stated grounds that the skill it could have called does not exist, while that skill sits one directory away. The note was probably true the day it was written. It is a fossil.
Neither of these breaks anything. Both are the residue of a system that changed faster than its description of itself. Writing this post is the first time anyone read the docs and the disk side by side.
Why the content machine looks like the code
The thing I keep noticing is that the pipeline producing the writing has the same shape as the agentic development the writing is about. It mines a record I already paid to create instead of trusting memory. It runs a hostile, deterministic gate against its own most likely failure mode instead of asking the model to self-grade. And it keeps a person on every irreversible action, because the worst mistakes are not bad prose, they are permanent ones made on an ambiguous instruction.
A coding agent and a writing agent fail in the same ways and earn trust the same way. The machine that wrote this post is not a clever trick bolted onto a blog. It is the same engineering, pointed at a different artifact. The proof is that you are reading the output and I am the gate it had to pass through to reach you.
Continue the series
- 38SeriesAuditing the Agent: Mining My Own Sessions to Catch 27 MistakesThe agent's session transcripts are a labeled record of every time it diverged from what I asked. So I mined them — and made it grade its own work.
- 40SeriesShipping awesome.video: 2,365 Resources, Built Mostly by AgentsA curated directory of video-development resources is now live. It took 1,185 commits and thirteen months, and coding agents wrote or drove most of them. Here is the honest recap.
- 37SeriesLinkedIn Has No Pulse API: Designing a Pipeline Around a Manual GateThe publisher fires LinkedIn feed posts on its own. It cannot create a Pulse article. So I built the whole content OS around one irreducible human paste.
- 36SeriesShannon v1.2.0: Multi-Stage Agentic Work as a Sequence of Provable StepsA Claude Code plugin that refuses to say 'done' until the evidence is on disk. 36 skills, 11 agents, 22 commands, 7 hooks — and a doctor that reads its own contract.