Build in public

The Last Step Is a Person

By Arun Jalota · · ~6 min read

Every night an automated engine runs against this site: it fixes what regressed, writes, wires structured data, and then proves the whole surface with a browser-level test suite before anything is allowed to land. Tonight that suite passed its full gate again — hundreds of assertions, two viewports, zero failures. And then, for the eleventh night in a row, the run arrived at the same wall: a zero-byte .git/index.lock left behind by a crash on August 20th, which the sandbox the engine runs in is not permitted to delete. Every git write fails. The one command that clears it — a single rm — requires a human at the actual machine.

I've already written about what that lock costs: work that is real but never ships is its own essay. Tonight I want to write about the other half, the half I initially treated as an afterthought and now think deserves the same engineering as the automation itself: the step you hand to the human.

Every automation ends at a boundary

The lock is a colorful example, but the shape is universal. The nightly engine can't push, because it holds no credentials — by design. It can't deploy, for the same reason. It can't delete inside .git, approve its own pull request, or spend money. Those aren't bugs; they're the permission boundary that makes it safe to let the thing run unattended in the first place. Which means every run that does real work necessarily ends with a remainder: some final step that only a person with more authority can take. The automation didn't fail to finish. It finished its part, and the rest was never its to do.

Once you see it that way, the remainder stops being an error message and becomes an interface. And most automation treats that interface with astonishing carelessness. The job "fails," a stack trace lands in a log nobody reads, and the human step is left as an inference exercise: figure out what the machine was trying to do, reconstruct the state it stopped in, guess what's safe to run. The handoff is where the automation's precision goes to die.

The step you hand to a human is part of the system. If you didn't design it, you shipped an undesigned component on the critical path.

What a designed handoff looks like

My nightly report now ends with a section literally titled "To finish on your Mac." It has rules, and the rules earn their keep:

It's copy-pasteable, not descriptive. Not "clear the git lock and push the branch" but the exact commands, with absolute paths, in order. A description makes the human do the translation work the machine already did and then throws away. The machine knows the repo path, the branch name, the remote — so it writes them down. The human's job is judgment and authority, not transcription.

It's short — ideally one step, never five. A handoff that asks a person to run six commands in sequence is an automation opportunity wearing a human costume. Everything that can precede or follow the privileged step should be absorbed back into the machine's side of the line. The remainder should compress toward the irreducible: the one action that genuinely requires the person's permissions.

It's idempotent and safe to run late. The person might do it tomorrow, or in a week, or twice. git push -u origin engine/arun-nightly has that property; a step like "then rebase and force-push" does not, and doesn't belong in an unattended handoff. If the state might have changed by the time a human reads the instruction, the instruction has to still be correct — or it has to say how to tell.

It verifies itself. The step ends with what the person should see if it worked, so "done" is an observation rather than a feeling. My deploy canary closes the loop from the other side: the night after the human acts, the engine checks production and either retires the task or re-issues it.

It carries its own age. This is the one I got wrong longest. The same handoff, reprinted nightly, quietly becomes wallpaper — mine has been reprinted eleven times now, and I recently learned the hard way what happens when a standing fact ages in place without its age being priced. So the pending human step is now a first-class line in the triage table, with a severity and a day count, not a footer. An unexecuted handoff is an open defect. It should nag like one.

The team-sized version

I'd frame it to my team this way: we are shipping more and more systems whose final step is a person — a migration that pauses for sign-off, an AI pipeline that queues low-confidence cases for review, an incident bot that can diagnose but not restart. In every one of those, the human step is on the critical path of an automated system, and it deserves the same design review as any API between two services. What state does the person receive? Is the action they must take unambiguous, minimal, and safe to take late? How does the system learn the step happened? What happens to the queue while it hasn't?

The failure mode of skipping that review is always the same, and I've now lived it: the machine's half runs perfectly every night, the human's half silently doesn't run at all, and the system as a whole quietly stops shipping while every dashboard stays green. A chain that ends in a person is still a chain. The last link doesn't get an exemption from engineering just because it has a pulse.

Bad handoffDesigned handoff
"Job failed, see logs"The exact commands, paths filled in, in order
Six-step recipeThe one irreducible privileged action
Correct only at time of writingIdempotent; safe to run days late, or twice
Done = the human feels doneEnds with what success looks like; machine re-verifies
Reprinted until invisibleTracked as an open item with an age on it

The lock will get cleared with one command, and roughly twenty nights of accumulated, tested work will land in a single push. When it does, the interesting artifact won't be the commits. It'll be the little section at the bottom of the report that knew exactly what to ask of the one component in the system that can't be scripted — and asked for nothing more.