Chapter 3 of 7. Prerequisite: Chapter 2. You have a devlog that parses and a triage rubric that turns entry clusters into a four-way decision. This chapter is the machine those decisions feed: the full state machine from raw devlog entry to merged PR, and what each gate refuses to let through.
Every personal writing system dies the same death. It works during the quiet fortnight in which you built it, then a deadline lands and the ritual slips for a week. Restarting costs more than starting did, so the notes pile up and the publishing stops. The usual diagnosis is moral: not disciplined enough. That diagnosis is wrong. A system that needs discipline to survive a busy week is a system with too much friction, and friction is an engineering problem. The fix on this site is a pipeline in which every article candidate is a database row with an explicit state, and every transition passes a gate that holds its position whether or not you showed up that day.
A developer editorial pipeline: devlog to article in six steps
The pipeline is a six-step gate. Steps 0 through 2 decide what to argue. Steps 3 through 5 produce and ship the prose. The order is the point: no prose exists until the argument has survived two checkpoints.
- Step 0: Triage
Does the devlog have enough source to back an article at all? Chapter 2 covered the rubric. The decision is one of four outcomes:
upgrade,leave,skip,defer. Onlyupgradeproceeds. - Step 1: Propose
One thesis when the angle is unambiguous. Two or three variants when real angles differ. Never four or more; past three the choice becomes decision fatigue, not deliberation. Each variant is three sentences: the angle, the claim, what the article argues. No prose.
- Step 2: Clarify
The human corrects the framing, kills wrong angles, adds nuance the log lacked. The output is one locked thesis.
- Step 3: Draft
From the agreed thesis only. Devlog entries are the evidence; the draft quotes its source rather than gesturing at it.
- Step 4: Edit
A prose-level pass for voice and truth. Human-driven, tool-assisted.
- Step 5: Ship
Commit, PR, merge. Then the tooling records which devlog entries the article cites, so future triage knows this ground is covered.
The rationale for thesis-first is recorded in the system's own docs: "LLMs draft confidently. A finished draft feels defensible even when its thesis is wrong. Thesis-first discipline strips the prose so the argument is what's being evaluated." Skip step 0 and you get articles whose source cannot back the thesis. Skip step 1 and you get fluent prose against the wrong premise. Both failure modes produce something that reads finished, and that is exactly why the gate exists.
The gate was not designed complete. The documented version said five steps while actual practice had already drifted ahead of the docs, running an informal source-check before any thesis was proposed. An honest audit caught the drift and codified triage as step 0, the same day one candidate was caught with zero backing entries, and the devlog records the codification the day it happened. Treat your own gate the same way: a step earns its place by the failures it stops.
The steps are rows, not intentions
A checklist holds intentions. This pipeline holds state in two SQLite databases, and the difference is what survives a busy week.
The editorial index keeps one proposals row per topic. Its state column moves proposed → drafting → shipped, and the triage outcomes from step 0 land as their own states: deferred, skipped, left. A topic you deferred in March is still sitting there in June, with its reason, when the next triage digest runs. Nothing depends on you remembering.
The automation queue keeps one row per accepted work item. Its state machine is CHECK-constrained in the schema:
queued → drafting → drafted → (edited) → shipped
↘ cancelled
An illegal state is a write error at the database layer, not a silent drift discovered weeks later. The set of states from which a row can still be cancelled is ('queued', 'drafting', 'drafted', 'edited'), defined as one constant on each side of the system and kept in sync by contract, mirrored wherever cancellation logic runs. Terminal states stay terminal.
In the scheduling layer, every slot mutation writes a paired append-only audit event in the same transaction as the state change, carrying the from-state, the to-state and the actor. SQLite's atomic-commit guarantee is what makes the pairing trustworthy: "Atomic commit means that either all database changes within a single transaction occur or none of them occur." A state change without its event cannot be persisted. The editorial index keeps a separate append-only audit of every CLI command it runs, and the system's docs justify that table in one line: "Without it, decisions evaporate at session close." The full scheduling vocabulary runs to 18 event kinds, and Chapter 6 walks the schema that holds all of this. What matters here is the design stance: state lives in tables, history lives in events, and the markdown devlog stays authoritative with every database rebuildable from it.
What each gate enforces
Between the steps sit automated gates. Each one encodes a rule that a tired human skips.
The duplicate gate. Every shipped article gets a fingerprint: a topic key derived from slug and tags, a normalised thesis key, and the exact set of devlog entry ids it cites. Two articles are duplicates when:
same primary_topic_key
AND intersection ≥ 5 shared source entries
AND Jaccard similarity > 0.5
OR same thesis_key
The check runs at three points: propose time (advisory, with a hard block if the topic already shipped with an article attached), ship time (blocking, with any override recorded in the audit trail), and pre-publish in the automated path. Three checkpoints because each catches a different escape route, and Chapter 5 tells the story of the article that would have republished verbatim without the last one.
The citation policy. The codified rule, verbatim from the system:
We can only cite previous sources if we've improved on, diverted from,
or regressed against them — otherwise no linking back.
Enforcement has two layers. The drafter prompt carries the list of already-published overlapping articles and the drafter is allowed to refuse the assignment as a rehash of a sibling; a refusal cancels the queue row and deliberately does not requeue it. At substitution time, any candidate sharing more than half its source entries with the shipped pool is rejected unless its frontmatter explicitly declares a sequel relationship. The policy stops the laziest possible article: the one that cites your own back catalogue because the entries were adjacent in the log.
The voice gate. A deterministic tells scrub runs before publish, with an article-scope budget of at most 3 em-dashes per 1,000 words. Article scope, never per paragraph. Chapter 2 documented the incident that forced that wording: a per-paragraph proxy let three over-budget articles ship with clean reports. The whole voice problem has its own course; start with the pre-publish style audit if the drafting model keeps sanding your prose into everyone else's.
The kill-switch. In the automated path, a preview posts to chat before each PR opens: title, slug, the first prose lines and a cancel button, then a 10-second window. A tap rolls everything back. Silence is consent. It is a human veto with a deadline, and Chapter 5 examines why that shape was chosen over an approval requirement.
One design decision spans all of these: in the automated path, gates fail open. An unreachable database or a parse error allows the publish, on the reasoning that a broken check should not silently halt the editorial SLA. The two exceptions fail closed: a confirmed duplicate hit blocks, and a kill-switch tap blocks. Auxiliary steps get the same treatment; the hook that generates an article's hero image warns on failure but never blocks, because a missing illustration should not stop a publish.
What the gates caught in one 72-hour window
The gate design transfers. The clearest evidence is a second project running the same discipline, a UK personal-finance calculator site with a grounded article pipeline. Inside one 72-hour window, 11 to 14 June, three separate tracks converged, and every save was a gate doing its job.
First, the happy path proved out under pressure. PR #53, the first article through the full grounded pipeline, merged on 12 June on its second attempt: attempt one was blocked by the gates, two operator tweaks followed, attempt two opened and merged. A pipeline whose first real run needs zero interventions is a pipeline that is not checking anything.
Second, a routine morning sweep on 13 June caught PR #36, open since 11 June against a stale base. Merging it against current main would have deleted more than 4,000 lines, including the Bank of England rates fetcher that the pipeline classifies as mandatory with no bypass. The fix closed a hole that had been open since before the article pipeline existed, a gap older than the system now guarding it.
Third, the composer needed restraint taught to it. On 12 June a trial cohort ran 8 trials against the pipeline, and the dominant block reason, at roughly 20 instances, was unsupported interpretation. Not fabricated numbers. Phrasing. The shape that kept getting blocked looks like this (reconstructed, not a verbatim trial output):
The March data suggests lenders are already pricing in a cut,
indicating that borrowers should expect further falls.
The source said rates moved. The draft said what the movement means, and no source said that. The fix shipped on 13 June in two parts: a composer constraint (report what the source says, not what it implies) and a matching calibration on the verifier side so genuinely grounded text stopped being over-flagged. Tightening a gate and calibrating it are one change, done together, or the gate just moves the failure from false negatives to false positives.
Three tracks, one window, and in each case the interesting part is what did not happen: no ungrounded article shipped, no fetcher died, no interpretive drift reached readers. Gate systems earn their keep invisibly, in the counterfactual.
One article, one PR
Step 5 has a shape worth copying exactly: every article ships as its own PR on its own dated branch, writing/YYYY-MM-DD-<slug>, squash-merged. The automated path mirrors the manual one, branch to hook to commit to PR.
The rationale is that the review unit should equal the editorial unit. A bad article gets reverted or cancelled without entangling its siblings. The duplicate gate and the sibling-cancellation logic key off the article's slug and PR identity, and that identity only stays clean when one PR carries one article. The discipline was chosen deliberately on this site: offered a faster bulk PR across six rewritten articles, the owner took six PRs instead, one per article, and that precedent has held since.
The same 72-hour window above makes the volume argument. The calculator site's design sprint landed 25 PRs in a single day, each scoped to one concern, and the write-up frames it explicitly: 25 PRs is discipline, not churn, because each one was trivially reviewable. Small units are not slower. They are the reason review stays honest at speed.
Friction is the failure, so the machine holds the state
Return to the opening problem. The busy week arrives regardless. In a discipline-based system, the week erases your position: half-formed drafts in six files, no record of why you rejected an angle, a restart tax high enough that you never pay it. In this pipeline the week erases nothing. Deferred topics keep their deferred row and their reason. Queued work keeps its state. The triage digest keeps arriving, and the audit trail still knows what you decided in March and why. The system's cost when you are absent is zero, and its restart cost is reading a table.
That only holds while the drafting layer stays trustworthy, and trust is where LLM drafting strains hardest. Chapter 4 stays inside step 4's territory: keeping a model's output in one recognisable voice across months of articles, and what a blind test did to two confident iterations of a voice spec. Chapter 5 then opens the gate that no automation replaces: the human one.
Put your pipeline's state in rows
Define your own gate as data rather than intentions. Write down the steps of your pipeline, enumerate the explicit states a piece can occupy including the parked triage outcomes, then move the state into a table: one row per candidate with a state column, a reason column and a date. Implement one automated gate from the chapter, such as a duplicate check keyed on topic and shared sources, and decide in writing whether it fails open or closed and why.
Expected behaviour
- Your pipeline's states are enumerated in one place, including at least one terminal state and the parked outcomes
- Every current candidate in your system occupies exactly one named state in the table, with a reason recorded
- One gate runs mechanically against the table and you can show it rejecting a real or synthetic duplicate
- The fail-open or fail-closed behaviour of that gate is written down next to the reasoning, before its first incident
PROVE IT Simulate the busy week: leave the system untouched for several days, then answer from the table alone which candidates are in play, which are deferred and why. If any answer requires memory, the state is still living in your head.
In the automated path, which two events fail closed while everything else fails open?
The duplicate rule combines a shared-entry count with a similarity measure. Above what Jaccard similarity value are two articles with the same topic key flagged as duplicates?
Why does the pipeline refuse to let any prose exist until the argument has survived two checkpoints?
Show answer
LLMs draft confidently, so a finished draft feels defensible even when its thesis is wrong; thesis-first discipline strips the prose so the argument itself is what gets evaluated. Skipping triage yields articles whose source cannot back the thesis, and skipping the proposal step yields fluent prose against the wrong premise. Both failures read as finished, which is exactly why the gate sits before drafting.
↺ re-read: “A developer editorial pipeline: devlog to article in six steps”