As built · 2026-08-04 · running now

01

The Operator

You make decisions. Everything else drives itself. One picture:

YOU answer questions · steer ARCHITECT builds a world per task Operator on the line Worker does the task Operator on the line Worker does the task Operator on the line Worker does the task 15 TASKS AT ONCE, ONE PAIR EACH Supervisor mints Operators · wakes on mail

Matrix names, one sentence: the Architect builds the world, the Worker goes in, the Operator stays on the line — and you answer the questions. Dashed means a dumb process — no model, cannot rot, survives a reboot.

Who does what

02

Four roles. Only one is you.

gera

You

Live in AgentFlow — now a section of PropFlow at /agents. Answer decisions, skim each task's timeline, say "I don't like what X is doing." Nothing else is your job.

was: god

Architect

Builds the world for each task — pulls transcripts, Slack, code. Writes the criterion into task.json. Spawns the pair, arbitrates. Event-woken, never looping.

shipped

Operator

Holds the criterion. The only role that loops — and it ends the loop when the task closes. Raises the questions only you can answer, then executes your answer.

was: child

Worker

Does the task. No self-nudge — it keeps only a Stop gate, so it cannot finish on a lie. Doesn't manage itself; its transcript is the report.

A fifth piece has no card because it is not a role: the Supervisor, a launchd daemon that mints the first Operator for any unwatched task, revives dead ones, and pings a session that is idle with unread mail. Your rounds now route through the Operator rather than straight at the Worker — the criterion only moves with the Operator's knowledge.

Left to right, one task

03

A task's life

"Go look at Sean's collections asks" Architect context + "done" = written before work Worker builds code · tests · PR Preview to you "ship it" / "change:" Merged verified done the Operator is on the line for this whole span nudges past stalls · answers what Fable can settle · escalates what only you can · checks "done" against the written goal EVENTS.JSONL minted verified · steered blocked · answered closed

Every task now writes its own timeline: one append-only line per checkpoint, in your language, at ~/.claude/jobs/tasks/<task>/events.jsonl. You asked, opening a task: "I was expecting the operator to fill me in… I see nothing." A layer that judges and records nothing is indistinguishable from one that isn't there. The reviewer at the end still exists — the Operator isn't a second reviewer, it's the one that makes sure the task gets to the reviewer.

Both daemons are live · launchd

04

Two dumb processes under two smart ones

Worker just works Operator reads STATE.md only "keep going" · "don't do that" — one-way its transcript EVERY STEP, FREE Digester tails it every 30–60s INSIDE AGENTFLOW-RELAY STATE.md · events.jsonl criterion · last step · PR · ctx% + THE TASK'S OWN TIMELINE Supervisor MINTS the first Operator · revives · MAIL-WAKE · ADOPTS BLOCKS /agents — the feed DERIVED — CANNOT GO STALE

Solid boxes are smart sessions; dashed are dumb processes — cannot rot, survive reboots. Both daemons are real launchd services now: the Digester rides inside agentflow-relay, the Supervisor is its own. It mints as well as revives — it used to only revive, so a task that never had an Operator never got one. The Worker stops every 2–4 minutes; a dumb tail keeps STATE.md fresh for pennies. The feed you skim is derived from files, never narrated — the app can't become the source of truth.

Owner-first routing · shipped, controlled both directions

05

Your click executes

Worker blocked can't decide X Operator digs transcripts · Slack · Fable ×N solved — most blocks end here, you never see them truly yours Decision Dock options + a recommendation YOU one click the answer flies STRAIGHT to the Operator that raised it — which EXECUTES it, then verifies every question now carries its block id and owner sid as page metadata, so the hook can resolve the raiser the Architect is the fallback ONLY when that owner is dead or unreachable

Why this slide exists: you approved the Camellia Saturday rule at noon and it sat unexecuted for hours — delivered, but executing it was nobody's job. Then the first fix leaked: the dock rendered questions by text only, so the hook could not tell who had asked and fell back to the Architect every time. Both are closed. If the owner dies, its successor adopts the block — the id is preserved, so the answer it was adopted to receive still lands. An answered question always has a live executor.

Your words: "the only thing really looping is the operator, right?"

06

Only one thing loops.

Three sessions run a task. One of them holds a loop — and it lets go of it when the task closes.

WORKER NO LOOP It keeps a gate, not a loop. A Stop hook can refuse to stop. It cannot restart a stopped session — so it is a guard, not a heartbeat. Reached by: its Operator's steer, mail-wake, the decision hook. COSTS NOTHING TO ARM OPERATOR THE ONLY LOOP holds it until done It claims the loop explicitly, and ENDS it beside the terminal state. NEVER ARMS ONE ON ITS WORKER ARCHITECT NO LOOP Event-woken. Four sources, none of them a timer: mail-wake — a session wrote to it the decision hook — you answered an Operator escalated a CI / PR verdict landed NO TIMER · NO POLL · NO BILL

The rule that makes this safe to say out loud: the gate is not the loop. Arming a gate costs nothing and stops a session declaring itself finished on a lie; arming a watcher is a poll that bills a model every few minutes. Three loops per task was three times the cost for one job — and the two extra ones outlived their tasks, which is how a machine ends up full of watchers nudging work that closed hours ago.

Your question: "always on? every 10 seconds?"

07

~30 wakes per 16-hour task, not 400

DUMB LAYER — EVERY 30–60s, NO MODEL, PENNIES OPERATOR — WAKES ONLY WHEN JUDGMENT IS NEEDED quiet stop(5 min silent) your answerlands CI / PRverdict 60–90 minheartbeat "done?" —judged, not assumed

Measured: a real Worker produced 1,064 messages in 17.7 hours — a stop every 2–4 minutes. Any per-stop smart cadence turns the Operator into a second full-price model that compacts away its own criterion by early afternoon. Cost as amended: ~10–15% of its Worker.

propflowai.co/agents · PR #5376, draft · read-only end to end

08

AgentFlow is not a second app. It's a section of this one.

SessionsDecisions
Running — one line each, derived from the files
tour-saturday-rule ▾14:32 — tests green locally, opening the PRPR #5344ctx 41%
the row opens the house dock-drawer — the same one a work order or a tenant opens  LIVE TERMINAL — real xterm, colours and cursor, streamed read-only  THE TASK TIMELINE — criterion at the top, then every checkpoint from events.jsonl
docs-surface-adr14:29 — rebasing onto main, resolving the index conflictPR #5341ctx 22%
camellia-saturdayApply the rule you approved — one prod write. Who presses the key?Answer
email-capture-driftmerged 19:59 — your call, executed 17s after the greendone

You asked for "ideally a one-for-one copy, so if we update PropFlow we could just pull it in." A literal clone is the version of that which decays — two shells drift and the sync nobody runs is what keeps them together. A route has nothing to pull in, because the shell IS the shell. Side nav, top bar, house auth, the same drawer. One genuinely new component: the terminal. Read-only is structural — the relay has no code that can type, kill or resize a pane, and the app proxies three GETs and one stream. The old standalone viewer at /a/agentflow is retired; it is a tombstone pointing here.

Five skeptics before a line was written — and the rulings held

09

What the attack changed

SURVIVEDTranscript as the Worker's feed · one-way steering · Operator executes answers · the ~30-min threshold
AMENDEDOperator per task — kept, but lean: ~30 wakes, reads a 2 KB state file, never raw transcripts
AMENDED"Fable-then-escalate" — kept, but named honestly: a convention, not a mechanical gate
KILLEDWake the Operator on every stop + 10-min heartbeat → Workers stop every 2–4 min; replaced by the Digester + event wakes
KILLEDOperator narrates the checkpoint feed → a new store with a new writer, the exact drift you forbade; feed is now derived from files
KILLED"The dumb daemon already exists" → today's nudge dies with its target and on reboot; replaced by a real launchd Supervisor

Both preconditions the attack found are closed. Loop state was one global slot — a second Operator stole the first's on its first command; it is per-session now, and two loops armed at once leave each other alone. And a raise published before saving its ledger row, so a failed publish silently erased the question; it writes ahead now.

Every step landed with an executed control

10

Built — and the one thing that isn't

1

Loop state, namespaced

Per-session gate + registry; raises save before they publish. Proved: two loops armed at once, neither steals the other's. Then narrowed again — only the Operator arms one at all.

2

The Digester

Lives inside agentflow-relay, tailing every paired Worker into STATE.md. Proved: a wedged session flips to STALLED within a minute, zero model tokens.

3

Supervisor, and the answer's owner

It mints first Operators, not just revivals. Answers go owner-first. Proved: controls in both directions — live owner executes, dead owner is adopted.

Next: you type a task into AgentFlow and a session exists

The + button writes an intake file through the relay — a file on disk, never a keystroke into a terminal, the same shape as the ask path that already works — and the Supervisor picks it up and mints the pair. The new row arrives through the normal roster. Deliberately not in #5376: that PR's criterion says "read-only end to end", and a POST route would contradict it. Intake is its own PR, with its own criterion, and the write-surface question gets raised then rather than assumed now.

Three honest risks — all still open

The Fable line is a convention. Nothing mechanically stops an Operator settling a question that was really yours. Every self-resolve is logged and visible — reviewable, not prevented.

Hang detection is a heuristic. A 20-minute build, a permission prompt, and a true hang look identical in a stale transcript. The Digester will sometimes cry wolf.

Steering can land late. A parked "don't do that" is read at the next inbox drain — possibly after the thing it forbade. "Ignored" and "hasn't read it yet" look the same.

Proposed · 2026-08-05 · NOT as-built — full design at /a/operators-on-temporal.html

11

Proposed: the Operator becomes a workflow. Nothing else moves.

Your question: "perhaps operators and the architect should be on Temporal?" The answer argued in the companion deck: Operator yes, Worker no, Architect not yet.

MOVESThe Operator's loop — its whole job is durable waiting, and today's durability is faked: pane death, the Supervisor's revive-×5 machinery, block adoption from dead owners, orphan nudge watchers. As TaskOperationWorkflow, the loop is a durable timer + signals; the judging stays a claude -p run inside an activity — Smith's exact brain pattern, replay-safe because replay reads recorded results.
STAYSThe Worker — it IS the interactive session whose terminal this page streams; nothing durable to hold. The Architect — already event-woken, no loop to move, and its value is working context, which Temporal cannot persist.
THE RULESkeleton on Temporal, judgment in the skill. The workflow stays dumb (wait → judge → route on an enum) so behavior changes keep landing in SKILL.md prose, never in patch-gated workflow code. If the pilot finds us patching the loop, the design failed and we stop — that exit test is written down in advance.

What dies: operator liveness/respawn (alive.ts, spawn.ts), block adoption, operator mail-rescue and mail-wake, operator auto-compact. What stays: the Digester, worker spawn/compaction, intake validation, and — deliberately — task.json + events.jsonl as the contract and the human timeline. This page's feed does not change shape.

Proposed · 2026-08-05 · NOT as-built — ships first, independent of the operator move

12

Proposed: Smith's sessions in AgentFlow, source agent-smith

A ROW= a workflow execution, not a pane — Smith's live drives (smith-reply-<channel>-<thread>, approvals, fan-outs), merged into sessions[] through the per-session source seam #5409 built, plus a sessionKind: tmux | workflow discriminator so kind is never inferred from a label.
ITS PAGEshows the conversation timeline (Smith's per-channel log already stamps every row with its workflow id) where a tmux session shows its terminal. No pane ⇒ no terminal panel — the panel swaps, it doesn't apologize.
TALK TO IT= signal-with-start on SmithReplyWorkflow — the same door its Slack messages walk through; replies land in Slack too. Never a keystroke: there is no pane, and untrusted text through a keyboard is the RCE the intake review already outlawed.
PREREQThe relay half of source (agentflow-relay#11) is written but unmerged and undeployed — the live roster serves no source key. That lands first; everything above stacks on it.

Read-only stays the default: the one write path is a Temporal signal through a named, tested route — and the read-only invariant test gets amended by name, not worked around. Decision page: /a/operators-on-temporal.html, last slide.

Answered 2026-08-03, 22:57 — kept here, not restated

13

You said build it. It's built.

This was the question, and it is still live — change the answer and the machinery reads the change. Nothing here is a re-ask.

All three steps landed inside a day, and the design changed twice more after you approved it: rounds now route through the Operator, and the loop narrowed from three per task to one. The next call is not on this page — it arrives when the intake PR opens and asks how much write surface AgentFlow should have. The full attack dossier (five skeptics, per-claim rulings, file:line citations) is preserved if you want the long version.

All slides

PropFlow Docs