Spec: mngr message delivery signals#2411
Draft
weishi-imbue wants to merge 2 commits into
Draft
Conversation
mngr drives Claude through a tmux pane, so `mngr message` has no ACK and must reconstruct "did it land" from side effects. Almost every side effect Claude Code emits is semantic -- it records what the input meant, not that input arrived -- and the two signals mngr races (the UserPromptSubmit hook and the enqueue transcript event) are both model-bound. Input that never reaches the model satisfies neither, which is why a typo, /login, and /cost each hang for the full 90s despite being delivered, and why /clear and /compact needed their own bespoke hook. The spec categorizes the seven input classes, inventories every signal with its producer, maps signals to cases in a matrix measured against a real agent, and records twelve issues. Two are silent GNU-coreutils dependencies that break `mngr message` on stock macOS: `timeout` in the tmux wait-for waiter (so the hook signal is fired into the void) and `tac` in transcript offset reconciliation (whose stderr is painted into Claude's input box). `~/.claude/history.jsonl` is the only artifact written before interpretation, and so the only one that fires for every input class. No production code changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
weishi-imbue
commented
Jul 9, 2026
|
|
||
| 1. Reject the agent if its lifecycle state is `STOPPED`. | ||
| 2. Acquire an exclusive per-agent file lock. | ||
| 3. `_preflight_send_message` -- check the `permissions_waiting` marker and match the pane against `_DIALOG_INDICATORS`. Fail fast on a hit. |
Contributor
Author
There was a problem hiding this comment.
why can't we extend this to capture those modal commands like /cost and /login, so they can fail fast?
…nals
Three defects, all measured against a real Claude agent on macOS, where
`mngr message` previously failed after ~93s for EVERY message -- a plain
prompt included, not just slash commands.
1. `timeout` is GNU-only. The tmux submission waiter ran under it, got exit
127, and was never registered, so the UserPromptSubmit hook fired
`tmux wait-for -S` into the void. No portable `timeout` binary exists
(`gtimeout` needs `brew install coreutils`), so the waiter is now bounded
with plain bash, which these commands already run under.
2. tmux LATCHES `wait-for` signals: a `-S` sent with no waiter registered is
remembered, and the next `wait-for` returns immediately. The code asserted
the opposite ("a signal with none registered is lost"). Fixing (1) alone
would expose this as a false success -- a stale signal confirming the next
send before its Enter is processed. The channel is now drained first.
Two traps found while fixing it: `tmux wait-for` handles SIGTERM and exits
0, indistinguishable from being woken, so the deadline must SIGKILL; and
wrapping the waiter in a subshell orphans the `tmux wait-for` client on
kill, leaving it registered to steal a later submission's signal.
3. `tac` is GNU-only. Transcript offset reconciliation used it, and on macOS
the resulting "command not found" was painted into Claude's input row,
which then broke the next send's paste-visibility check (16.7s failure).
Measured on the affected host: normal prompt 93.6s/exit 1 -> 2.1s/exit 0;
`/clear` 93.7s/exit 1 -> 2.1s/exit 0; `/clear` followed by a prompt now
succeeds without concatenating.
Also records the full signal inventory and remaining issues in
specs/mngr-message-delivery-signals.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
mngr messagedrives Claude through a tmux pane, so it has no ACK and must reconstruct "did it land" from side effects. Almost every side effect Claude Code emits is semantic -- it records what the input meant, not that input arrived -- and the two signals mngr races (theUserPromptSubmithook, theenqueuetranscript event) are both model-bound. Input that never reaches the model satisfies neither.That is why a typo,
/login, and/costeach hang for the full 90s despite being delivered, and why/clearand/compacteach needed a bespoke hook.This PR adds a problem-statement spec. No production code changes.
What's in the spec
Headline findings
Two silent GNU-coreutils dependencies break
mngr messageon stock macOS, independent of any slash-command behavior:timeoutis absent, sotimeout <n> tmux wait-forreturns 127 and the waiter is never registered. When theUserPromptSubmithook firestmux wait-for -S, the signal wakes nobody. A plain prompt that Claude demonstrably answered was reported asFailed to send messageafter 93.6s.tacis absent (mngr_transcript_lib.sh:105), so transcript offset reconciliation fails and its stderr is painted into Claude's input box.Beyond those:
mngr messagecan return before Claude's input buffer is clear, so the next message is concatenated onto residue (/clear /zzzrace1, reproduced 1-in-3)./cost,/model,/login) leave no input row; every later send fails after a 30s generic timeout, and_preflight_send_messagedoes not detect it.exit 0in 2.6s (it enqueued); sent to an idle agent it fails after 93.7s.type=system, level=warning), already tailed intoevents.jsonl.~/.claude/history.jsonlis the only artifact written before interpretation, and the only one that fires for every input class. Its one deviation is a consecutive-duplicate dedup.Also adds a top-level
uncertainties.mdrecording two comments/docstrings that assert behavior which does not hold.Review focus
The design section is deliberately a proposal. The open questions -- what a sound "settled" predicate is, which dedup guard to use, and whether modal commands should fail or warn -- are the parts most worth arguing about.
🤖 Generated with Claude Code