0053 — While a relay owns Gera's line the brain stands down, and the run it would have answered in still starts

The problem

Two systems both answer the same message.

With work on 1 active, Gera types continue. The relay forwards it into session 1 — that is the feature — and the brain answers it as well, because nothing tells the brain that the line was not addressed to it. He gets two replies to one message, and the brain's is the more authoritative-looking of the two while being about something else entirely.

This is invisible until both halves exist. It became reachable on 2026-10-14 at 03:38Z, when the door (#1012) merged and the worker restarted 40 s later.

The decision

1. While a relay is active for Gera's conversation, a line the relay will CARRY is relay input and not a prompt. The brain does not answer it.

Exactly one bucket stands the brain down — the line the relay forwards. Every other shape is still Smith's and still reaches him:

the line where gera_messenger.plan files it who answers
a plain line forward the relay only
one of Smith's own doors (give me a summary on 2) for_smith the brain
anything typed after the stop word after_stop the brain
a /-leading line, which the send refuses outright refused the brain
the stop word itself stopped_by the brain

That table is not a courtesy. Plan.for_smith and Plan.after_stop existed with no reader anywhere in src/, and were harmless only because the brain answered everything. The moment the brain stands down, a field nothing reads is a line nothing answers. The stand-down not firing for those buckets is their consumer.

2. ⚠️ The stand-down does NOT skip the dispatch. The run still starts; only its first turn is skipped.

This is the part worth an ADR, because the obvious reading of decision 1 is a silent data-loss bug and anyone implementing it will reach for that reading first:

No run, no owner, the signal fails into a logged warning, and the session's reply reaches nobody. Gera would go from two answers to none — quietly, because the dispatcher writes the inbound conversation-log row before it starts anything, so the relay still sees his line and still believes it delivered.

3. The decision is made at the dispatcher, because it is the only door that can make it. Two reasons, and the first is the binding one:

4. It is a predicate over the BATCH, not over the line. plan cuts at the stop word. Gera says stop, then thanks five seconds later; the 20 s tick has not run, so the mode is still live and thanks is a plain word on its own. A per-line predicate stands down on it — and the tick, planning the batch, files it under after_stop and never forwards it. Nobody answers it. So the dispatcher reads the same rows from the same baseline through the same filter and asks the same classifier.

One classifier, two callers. If the dispatcher's copy ever drifts from the tick's, the line they disagree about is the line nobody answers.

5. Every uncertainty leaves the line to the brain. An unreadable mode store, no mode, a sender who is not Gera, a log row that did not land, a Slack outage on the bot uid — all of them mean "the brain answers". A reply Gera did not need is visible and cheap. Silence nobody is told about is the failure class this whole module is built to refuse.

What was rejected

Consequences