A message to a builder holds with reason busy and never delivers, because the agent left text in its own composer. The gate is correct. Nothing clears the text, so the wait never ends.
Hit five times on 2026-08-21, each needing manual intervention. mailbox currently records 16 dismissed/busy rows, nearly all from clearing these by hand.
Mechanism
After a turn ends, Claude Code leaves a suggested next action sitting in the composer, e.g.
spawn a real opencode builder and send it a message
keep it, re-request the gate
Retry the deactivate call
Verified by reading the terminal buffer directly via GET /api/terminals/<id>/output on five separate occasions.
classifyBuffer sees a non-empty composer and returns not-clean. Correct: injecting there would corrupt the agent input. But:
- the agent will not clear its own composer, it is idle and waiting
- the gate will not deliver until it is clear
- nothing else touches it
Deadlock. The message sits until a human intervenes.
The alert names the wrong remedy
The stuck-mailbox notification says:
Remedy: run afx inbox to inspect; afx interrupt builder-x clears a stuck composer.
afx interrupt sends ESC. ESC does not clear typed text in the composer. Running it changes nothing and the alert fires again three minutes later.
What actually works is afx send <id> --interrupt, which sends Ctrl+C first and clears the line. That is documented as a way to send a message, not as the remedy for this state, so nobody finds it.
Worse, afx interrupt defaults to ESC followed by Enter. When the composer holds a selection dialog rather than free text, that Enter selects the highlighted option. On 2026-08-21 the highlighted option was Retry the deactivate call, which would have deactivated the whole workspace and killed the architect session. ESC dismissed the dialog first, so the Enter landed harmlessly, but that was ordering luck.
Fix
- Correct the alert. Name
afx send --interrupt, not afx interrupt. This alone removes most of the pain.
- Make the hold recoverable. A message held on
busy for N minutes against an otherwise idle agent should either clear the composer itself, or escalate with the working command. A hold that can never resolve on its own is not a hold, it is a drop.
- Distinguish idle-with-prefill from genuinely busy. They are the same
busy reason today. The first is safe to clear, the second is not. The render gate already knows the screen; it can tell an idle composer holding text from an agent mid-turn.
afx interrupt should not send Enter by default, or should refuse when the screen shows a selection dialog. Sending Enter at an unknown dialog is a destructive act chosen by whatever happens to be highlighted.
Note
This is not the render gate being wrong. It is right every time. The gap is that nothing owns clearing a prompt the agent abandoned, and the documented escape does not work.
A message to a builder holds with reason
busyand never delivers, because the agent left text in its own composer. The gate is correct. Nothing clears the text, so the wait never ends.Hit five times on 2026-08-21, each needing manual intervention.
mailboxcurrently records 16dismissed/busyrows, nearly all from clearing these by hand.Mechanism
After a turn ends, Claude Code leaves a suggested next action sitting in the composer, e.g.
Verified by reading the terminal buffer directly via
GET /api/terminals/<id>/outputon five separate occasions.classifyBuffersees a non-empty composer and returns not-clean. Correct: injecting there would corrupt the agent input. But:Deadlock. The message sits until a human intervenes.
The alert names the wrong remedy
The stuck-mailbox notification says:
afx interruptsends ESC. ESC does not clear typed text in the composer. Running it changes nothing and the alert fires again three minutes later.What actually works is
afx send <id> --interrupt, which sends Ctrl+C first and clears the line. That is documented as a way to send a message, not as the remedy for this state, so nobody finds it.Worse,
afx interruptdefaults to ESC followed by Enter. When the composer holds a selection dialog rather than free text, that Enter selects the highlighted option. On 2026-08-21 the highlighted option wasRetry the deactivate call, which would have deactivated the whole workspace and killed the architect session. ESC dismissed the dialog first, so the Enter landed harmlessly, but that was ordering luck.Fix
afx send --interrupt, notafx interrupt. This alone removes most of the pain.busyfor N minutes against an otherwise idle agent should either clear the composer itself, or escalate with the working command. A hold that can never resolve on its own is not a hold, it is a drop.busyreason today. The first is safe to clear, the second is not. The render gate already knows the screen; it can tell an idle composer holding text from an agent mid-turn.afx interruptshould not send Enter by default, or should refuse when the screen shows a selection dialog. Sending Enter at an unknown dialog is a destructive act chosen by whatever happens to be highlighted.Note
This is not the render gate being wrong. It is right every time. The gap is that nothing owns clearing a prompt the agent abandoned, and the documented escape does not work.