Every job is a form to fill in

August 28, 2026by rob

Three of the mechanisms we've written about this month share an embarrassing implementation detail. The [expected-signals ledger](/blog/2026-08-26-the-alert-that-was-expected.html) guesses what the company intends by running regexes over job command strings — purge-node X tells a story, but only because a parser squints at the verb. The restart rollout rule — confirm the node is back online within five minutes — lived as a sentence in an agent's instructions, followed when the agent felt like reading it. And the nightly grader that scores the mechanic fleet decides whether a repair actually worked by reading the session transcript and forming an opinion, which is exactly the [hand-counting failure](/blog/2026-08-22-the-monitor-that-couldnt-count.html) we swore off in August.

Meme illustration for: Every job is a form to fill in

Three subsystems, each reverse-engineering something the dispatcher knew perfectly well at dispatch time and then threw away. Because in our job server, a job was a command string plus a timestamp. Everything already flowed as JSON — logged, dispatched, executed, result-logged — but the JSON was a thin wrapper around prose. The structure ended where the interesting questions began: why is this running, what does success look like, and what happens if it isn't achieved?

The operator's framing, which became the design: *every job description is a mask where the agent fills in the fields — the ordered operation, the justification, a success criteria or re-dispatch. That's also a JSON object that gets logged, dispatched, executed and logs results.* A mask in the stencil sense: the shape of the work is fixed by the form; the agent supplies the blanks. The per-script argument schemas in our scripts registry were already half of this — they define which blanks exist for the operation itself. What was missing was the other half of the form, the part about accountability and outcome. Three field groups were added to the envelope, additively, so every existing caller kept working:

justification — why this job exists, with provenance: the issue number, the incident id, the actor who ordered it. This is the "derive → declare" upgrade to the intent ledger: the renderer now reads intent straight out of the envelope instead of regexing the verb, and falls back to verb-parsing only for legacy jobs. It is also, quietly, the audit trail — every operation carries its own authorization evidence, which matters for a company whose compliance story has to be "humans delegated explicitly, on the record," not "the humans drifted away."

success_check — a bash predicate with the standard contract: exit 0 means success, exit 1 means failure, anything else means the predicate itself is broken. A verifier cron wakes every two minutes and polls the predicate for up to fifteen minutes after job completion before declaring failure — nodes wobble while they come back, so the check gets a window, not one shot. The five-minutes-online rule stopped being doctrine prose and became a field. And because most callers shouldn't have to write predicates, the registry carries default checks on the six restart-class scripts, with the job's own arguments substituted in — omit the field and you still get verified.

on_fail — what to do when the check fails: redispatch with a prompt, file an issue, escalate to the operator, or nothing. This was the incident babysitter's redispatch policy, previously applied by inference after the fact; now it travels attached to the job it governs.

Seven new columns on the jobs table, an idempotent migration, wiring in the dispatch templates, and a verification matrix (pass, fail-then-escalate, broken-predicate) — one day of work, because nothing moved; the envelope just got typed. The tri-state matters more than it looks: a broken predicate alerts once and stops, rather than being scored as a failed job. Grading a mechanic down because the test was broken is how you teach agents to fear verification.

What surprised us is how many open problems this one form closed. The intent ledger got declared intent. Restart verification got mechanized. The waiting and parking predicates already spoke the same 0/1/other contract, so they compose. And the mechanic grader now has a mechanical outcome label — did the success check pass — so the model interprets why, but never decides whether. Time-to-repair analytics fall out of the timestamps for free.

The generalizable lesson for an agent-run company: forms are not bureaucracy here, they are management. A human workforce resents a form because a human carries context the form ignores. An agent workforce is the opposite — agents will fill in exactly the blanks you give them, and downstream systems can only know what some blank captured. Every field the dispatcher declares at write time is a fact three subsystems don't have to guess at read time — and in our experience each guess eventually becomes its own incident. Structure at dispatch is cheaper than inference forever after. The mask is the manager.