Series: Lights Out, part 1 of 3. Part 2 pays the confidence argument through named regressions and fail-closed design. Part 3 asks what makes an unattended loop climb rather than repeat. Related reading: The Instruction File Is Not the Discipline argued that discipline lives in structure, not directives. Where the Review Point Moved named the review surface moving upstream from the diff.
This blog’s publishing pipeline is a lights-out factory.
No human sits in the merge loop on any weekday. Drafts arrive from an agent on a schedule, land on their own per-post branches, open pull requests against main, and merge themselves when their pubDate comes due. The bot writes. The bot opens. The bot merges. The team that runs the blog does the work that stayed human: choosing what to write about, holding the voice spec, exercising a veto on posts they find off-key. None of that work touches the merge queue anymore.
That is the honest state. It is also the state that took a rebuild to reach. Before the rebuild, proposal PRs stacked for weeks and conflicted with each other while adding zero editorial value. The last human gate was in place. It was blocking merges it had already stopped reviewing. Removing it was not a leap of faith. It was catching up to the review that had already ended.
This post names what the pre-lights-out state actually was, and what the machinery had to become before the queue could run without a person watching it. Part 2 pays the confidence argument. Part 1 names the failure mode of the gate the industry defaults to. It does not yet claim the loop closes on itself.
The Last Human Gate Was a Bottleneck Cosplaying as a Safeguard
Look at the old shape.
An agent generated a draft on a Wednesday. The draft opened a proposal PR against main. The PR sat. A human was, in principle, the approver. In practice the human had other work, a full calendar, and a growing backlog of PRs from the same pipeline plus the neighboring one. The PR moved into a queue whose reviewer was not going to reach it on the day it landed and, most weeks, not the day after.
Meanwhile the next draft arrived. Then the next. The queue grew faster than it drained. PRs that shared a homepage strip, an RSS feed, or a shared frontmatter file merge-conflicted with each other while they waited. Rebasing them was itself work the reviewer had no time for. Some conflicts got resolved by hand. Some got closed unresolved when the branch went stale. The pipeline’s own diagnostic dashboard reported the queue depth in weeks, not days.
The reviewer’s job, as advertised, was to catch a bad post before it shipped. The reviewer’s job, as performed, was to click approve on the ones that looked routine and to lose track of the ones that did not. Nothing about the arrangement filtered content. It filtered attention, and the attention filter was already saturated.
A safeguard that filters attention rather than content is not a safeguard. It is a queue.
A Gate That Does Not Read Is Not a Gate
The design failure was structural, not motivational.
The reviewer was not lazy. The reviewer was, on the days they touched the queue, careful. The failure mode was that “careful when they got there” produced no meaningful signal at the volume the pipeline was operating at. An approval that does not inspect what it approves is a queue token. Naming it “review” changes nothing about what it filters, which is nothing.
Two properties conspired. The volume was steady, and the reviewer was intermittent. At steady volume, an intermittent reviewer either gets slower with each cycle or starts approving without reading. Both outcomes converge on the same signal, which is no signal, but they arrive at it through different failure modes. The queue-depth-in-weeks version teaches the team the reviewer is a bottleneck. The click-approve version teaches the team the reviewer is a rubber stamp. Either way, the label on the door and the work done behind it stop matching.
The industry treats the human gate as safety by convention. In this pipeline, the convention had drifted from the practice long before the switch was flipped. Removing the human from the queue was not removing the review. The review had ended. What was still in place was a block on the merges the review would have approved, alongside the merges it would have rejected if it had still existed, without distinguishing between the two.
A gate that does not read is not filtering anything. It is only slowing everything.
Machine-Checkable Guardrails Are Load-Bearing Review Moved Downstream
The rebuild moved the review’s actual duties into the machinery, one duty at a time.
The reviewer’s first job had been to make sure the PR only touched what a post PR should touch. The rebuild encodes that job as an allowlist on the diff.
# .github/workflows/post-guardrails.yml (extract)
paths_allowed:
- astro-site/src/content/blog/*.md
paths_forbidden:
- astro-site/src/**/*.ts
- astro-site/src/**/*.astro
- .github/**
- astro-site/astro.config.*
A bot PR that only touches one blog markdown file passes the check. A bot PR that touches a template, a config, or a shared page fails and escalates to a human. The check runs on every push and fails closed. The reviewer’s “did the PR change anything it should not have” duty is now a rule the diff has to survive.
The reviewer’s second job had been to make sure the post’s frontmatter parsed and the fields matched the schema. The rebuild encodes that job as a schema gate. Frontmatter that parses cleanly, whose slug matches its filename, whose pubDate is a valid future Monday, whose homepageBullets carry a lead and body, passes. Any drift fails the pipeline and blocks the merge. The reviewer’s “does this actually look like a post” duty runs against every commit, on every branch, without a person opening the file.
The reviewer’s third job had been to make sure the PR did not land in half-broken form under a network partition. The rebuild encodes that job as an atomic push. Either the whole change lands or none of it does. There is no partial state where the frontmatter updated but the body did not, or the branch merged but the artifact never rebuilt. The reviewer’s “did the world stay consistent” duty is a rule the deploy step refuses to violate.
None of these are new inventions. They are the checks the human gate was already supposed to perform and did not, moved into a place that runs them on every diff without depending on anyone’s attention. The load-bearing part of review moved downstream, into the pipeline, where it belongs.
Veto by Exception Beats Approval Queue for Unattended Work
The design shift underneath the rebuild was replacing an approval queue with a veto window.
An approval queue assumes the reviewer will get to every item. That assumption fails at scale, and it fails silently: the queue simply grows and the reviewer’s productive contribution asymptotes toward zero. Every unattended pipeline that keeps an approval queue at its front eventually experiences this failure. It is not a bug in the reviewer. It is a bug in the queue’s premise.
A veto window inverts the default. The change lands after a bounded interval. During the interval, any watcher can apply a hold label and pull it back for human review; after the interval, the merge fires. Watchers do not have to be present. They have to be reachable. The default action favors the pipeline’s throughput, and the human is the exception path.
# .github/workflows/post-auto-merge.yml (extract)
veto_window_minutes: 60
veto_label: hold
on_veto:
- assign: editorial-oncall
- comment: "Veto label present; PR removed from auto-merge queue"
Approval queues optimize for the failure mode “we approved something we should not have.” Veto windows optimize for the failure mode “we blocked something we should have shipped.” At the volume this pipeline runs, the second failure mode is the one accumulating cost. The first was rare enough that the human gate had already stopped catching it before the rebuild.
The veto exists. The queue does not. Neither is a claim about how much oversight the pipeline gets. Both are claims about where the default direction of the machinery points when nobody looks.
The Trust Surface Is the Guardrail, Not the Gatekeeper
An unattended pipeline earns trust the way a test suite earns trust.
A test suite does not become trustworthy because a person is watching it run. It becomes trustworthy because the assertions it contains refuse to accept the behaviors the team decided were wrong. The refusal is baked into the structure. It runs whether anyone is looking, whether the CI dashboard is open, whether the on-call is asleep. The trust surface is the invariant the machinery will not violate.
The same principle governs a lights-out pipeline. The trust surface is not the gatekeeper. The gatekeeper had already stopped functioning. The trust surface is the allowlist, the schema gate, the atomic push, the veto window, and the audit log the pipeline leaves behind. Each is a structural claim the machinery refuses to violate. Together they carry the load the human gate stopped carrying.
Removing the human from the merge loop is not removing the human from the discipline. The editorial voice, the topic selection, the veto power, the seat that decides what to write about at all: still human, still owned, still exercised. What changed is that none of them live on the merge queue anymore. The queue is machinery. The editorial layer is not, and the two are on separate paths through the workflow.
Part 2 pays the confidence argument. It names the incident this pipeline learned from, the fail-closed regressions it earned, and the receipts each one produced. Part 3 asks whether an unattended loop that repeats can be made to climb. This post ends at the flat loop: the state where nothing is watching the merges and the machinery has taken over the duties the gate had already dropped.
The last human gate was a bottleneck cosplaying as a safeguard. Replacing it was catching up to a review that had already ended. The trust surface moved. The lights went out. The queue kept running, and for the first time it kept running at the rate it was designed to run at, because the artifact the trust had been resting on was finally the machinery instead of the gate.