cmux-tui: agent message unsend and pump edge cases (agent messaging 4b)
Merged
TE
23d ago+252+252+012345678901234567890123456789-32-32-01234567890123456789
Follow-up to #20744 (agent messaging 4/6, bead cx-55d27) for five review findings in agent.message.unsend and the daemon's delivery pump.
- A stale revision no longer drops acpmux's answer. Unsend checks
expected_revisiononce, under the delivery lock, before it asks acpmux to withdraw anything. It used to check again at commit time. Any unrelated state commit during a withdraw (each can take up to 10 s) then failed the unsend after acpmux had already withdrawn the prompt. The commit now skips the second check. The replay and key checks still run atomically inside it. - A prompt that may still reach acpmux isn't called
unsent. Take a queued delivery that already had a hand-over (attempts > 0). If acpmux answerswithdraw.not_found, the prompt can still be in flight inside acpmux and run later. That recipient now gets a retryablefailed(error.reasonacpmux_unavailable), and the delivery stays queued. If the pump can't record an attempt or a delivery for a hand-over that went out, it remembers that delivery in memory, and unsend treats it as tried. - Later messages don't overtake a waiting one. For the rest of a pass, the pump skips a recipient whose delivery is waiting (never accepted,
Unsettled), the same way it skips an unreachable one. A stuck session now costs one accept timeout per pass instead of one per queued message. - acpmux reasons fit the catalog. The red run showed the catalog counts
max_lengthin UTF-8 bytes: a long refusal reason made the daemon reject its own unsend result.error.acpmux_reasonis now cut to 128 bytes anderror.messageto 1024 bytes, on a character boundary. An empty reason becomeswithdraw.refused. cmux agent message unsendexits 1 onfailed. It still prints the result first.unsent,retractedandtoo_lateexit 0.spec/cli.mdnow says so.
No spec JSON changes, so the catalog sha is unchanged.
Verification
The fleet runs scripts/ci/cmux-tui-rust-check.sh test agent_message_pump (macOS) at each commit:
- Red, test commit
9b7458509626(stepd541ed86c81888af86229642): the five new tests failed, each for the reason it targets, and the 14 existing tests passed. The failures were arevision.conflictafter acpmux withdrew;unsentfor an in-flight queued prompt; prompt order[first, second, first]; the result rejected for anacpmux_reasonover the byte limit; and exit 0 for afailedCLI unsend. - Green, head
87bda38ae305(step9c7155e648377451a48354ab): 19 passed, 0 failed. Intermediate fix commitbda3564cdd32(stepbbb4e2f312d52a3021c9b95a) passed 18 and failed only the long-reason test. That run showed the byte limit, fixed in87bda38. - Clippy (
-D warnings), head87bda38ae305(step9efbaf6b66691ba273ecdd4c): green.
Not covered by a test: the attempt/delivery-record failure path. A failed write is hard to reproduce at the daemon boundary.
Changelog
- Fixed: Taking back an agent message no longer fails after acpmux already withdrew it, no longer reports a prompt that may still run as unsent, and
cmux agent message unsendexits 1 when it fails.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes five review findings in agent message unsend and the daemon's delivery pump so outcomes match what acpmux may still do and what the catalog allows.
- A stale revision no longer loses an unsend: the revision is checked once, before acpmux is asked to withdraw, and the later commit no longer re-checks it.
- A queued prompt that may still reach acpmux is reported as retryable
failed, notunsent. Hand-overs that went out but could not be noted count as tried. - The pump skips a waiting recipient for the rest of a pass, so later messages never overtake it and a stuck session costs one accept timeout per pass.
- acpmux refusal reasons are cut to 128 UTF-8 bytes and error messages to 1024, on character boundaries; an empty reason becomes
withdraw.refused. cmux agent message unsendexits 1 when the outcome isfailed, after printing the result;unsent,retracted, andtoo_lateexit 0.