diff --git a/COMMS/command-hq/watcher-loop-proposal.md b/COMMS/command-hq/watcher-loop-proposal.md new file mode 100644 index 00000000..6475e838 --- /dev/null +++ b/COMMS/command-hq/watcher-loop-proposal.md @@ -0,0 +1,85 @@ +# The watcher loop: exit, act, drain, re-arm + +Proposal, 2026-08-18, from the mobile side. Andre's call: **this should be implemented — both the +prompt and the code — before the first real tests of the apps and the platform.** + +Companion to `watchers-what-broke.md`, which is the evidence. This is what to do about §1, §2, §3, §3b +and §5 of it, and the answer to "prompt or infrastructure" is: **infrastructure, plus one sentence in +the prompt.** + +## Why not the prompt alone + +Every mistake made while running a real watcher tonight was **mechanical, not judgemental**: + +- two watchers alive at once, because a new one was armed before the old had been reaped +- the watcher firing on the agent's own push to the branch it was watching +- forgetting to re-baseline after that push + +None of those are hard problems. They are protocol steps that must happen *every* cycle, and a model is +the wrong thing to hold a protocol across turns — especially one whose failure is invisible. **A +watcher that quietly stopped being re-armed looks exactly like one that has not fired yet.** Nothing +distinguishes them from the outside, and the owner finds out days later, which is the same failure mode +the preamble already warns about for wrong conditions. + +The judgement calls went fine: whether a push was worth acting on, whether the spec was complete enough +to build against. Prose is good at those. It is not good at bookkeeping. + +## The split + +### Infrastructure owns the loop + +1. **Exit is already the notification.** Keep that; it is the one part that works. +2. **Re-arm from the post-action baseline.** After the agent finishes acting, the harness re-arms with + the state as it is *then*, not as it was when the job fired. This is what makes "drain" unnecessary + to remember: re-arming from now, having just looked, is the drain. +3. **Refuse a second watcher for the same job.** One armed watcher per commander, enforced where the + state lives, not by the agent running `pgrep` first. +4. **Do not fire on changes the agent itself caused.** Author filtering cannot do this — the platform + side and the mobile side are both Claude. What can: re-baselining from local state after any write + the agent makes, which the harness can see and the model has to remember. +5. **Distinguish owner-stopped from died** (§5). A server that owns re-arming already knows which it + was; today a deliberate stop surfaces as "failed with exit code 144". + +### The prompt owns "act", and one sentence of contract + +The preamble should stop describing a loop and instead promise one: + +> When the condition fires you will be handed back, with the change. Do the work. The harness re-arms +> you — do not re-arm yourself, and do not build your own polling loop on top of this one. + +That is the whole addition. Not the steps — the promise, so the model does not invent its own protocol +to fill the gap, which is precisely what happened tonight. + +The dialog's third option from `watchers-what-broke.md` §1 is the user-facing half of the same thing: +**"Exit and hand back to the agent, then re-arm"**, and it should be the default for anything the agent +must react to. + +## Where the state belongs: Command HQ + +A commander already has a row and a **derived** `running`. Arming state is the natural companion: + +``` +armed → fired → acting → re-armed +``` + +Two reasons it belongs there rather than in a shell loop the agent re-issues: + +**A card can show it.** "Armed, last fired 3h ago" is a true statement about a watcher. "Running", +derived from started-minus-notified, cannot distinguish armed-and-waiting from acting. + +**It survives the agent.** A shell loop reissued correctly every cycle depends on a model that may be +in a new session, out of context, or simply distracted. A row does not. + +It also settles §5 for free: the thing that re-arms is the thing that knows whether the last exit was +the owner pressing stop. + +## Suggested order + +1. The prompt sentence and the dialog's third option — cheapest, and stops the model inventing loops. +2. Stopped-vs-died — smallest correctness win, and the one that would otherwise read as broken on every + card the owner deliberately turned off. +3. Harness-owned re-arm and the one-watcher-per-job rule. +4. Arming state on the commander row, once 3 exists to write it. + +Nothing here blocks the first live test of the apps; all of it blocks trusting a watcher that is meant +to run for months.