Skip to content

fix(buzz-acp): clear steer_rx on all run_prompt_task exit paths#1391

Merged
wpfleger96 merged 2 commits into
mainfrom
duncan/steer-rx-guard
Jun 30, 2026
Merged

fix(buzz-acp): clear steer_rx on all run_prompt_task exit paths#1391
wpfleger96 merged 2 commits into
mainfrom
duncan/steer-rx-guard

Conversation

@wpfleger96

Copy link
Copy Markdown
Collaborator

Problem

Any first-turn error in run_prompt_task — session-create failure, initial_message error, etc. — returns the agent via PromptResult before the read loop's steer_rx.take() runs. This leaves steer_rx = Some on the returned agent. The next dispatch for the same channel reclaims the same AcpClient, install_steer_rx hits its is_none() assert, and the main thread panics — far from the original error.

Root cause traced to pool.rs: there are ~22 early-return paths in run_prompt_task, all of which send PromptResult { agent, .. } back to the pool. The only steer_rx.take() is at acp.rs:890 inside the read loop, which none of the error paths reach.

Fix

Add AcpClient::clear_steer_rx() (idempotent, sets steer_rx = None) in acp.rs.

Add a send_prompt_result() helper in pool.rs that calls agent.acp.clear_steer_rx() immediately before moving agent into PromptResult. Every exit path in run_prompt_task is routed through this helper — making the install_steer_rx invariant structurally unviolatable rather than a reachable crash. The assert! in install_steer_rx is preserved as a true never-happens guard.

Tests

Two new unit tests in pool::tests:

  • test_send_prompt_result_clears_steer_rx_on_early_return: installs a receiver, calls send_prompt_result without the read loop running (early-return simulation), asserts steer_rx is None and a subsequent install_steer_rx does not panic.
  • test_send_prompt_result_is_noop_when_steer_rx_already_consumed: receiver already None (happy path), asserts clear_steer_rx is idempotent and install_steer_rx still does not panic.

Files changed

  • crates/buzz-acp/src/acp.rs: clear_steer_rx() method + steer_rx_is_none() test helper
  • crates/buzz-acp/src/pool.rs: send_prompt_result() helper + 22 call-site replacements + 2 new tests

npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 2 commits June 30, 2026 10:30
Any first-turn error (session-create failure, initial_message error,
etc.) returns the agent via PromptResult before the read loop's take()
runs, leaving steer_rx = Some. The next dispatch for the same channel
reclaims the same AcpClient and install_steer_rx's is_none() assert
fires, crashing the main thread far from the original error.

Add AcpClient::clear_steer_rx() and a send_prompt_result() helper that
clears steer_rx immediately before moving agent into PromptResult. Every
exit path in run_prompt_task now goes through this helper, making the
install_steer_rx invariant structurally unviolatable rather than a
reachable crash. The assert is preserved as a true never-happens guard.

Two unit tests pin the seam: one simulates an early-return (receiver
installed, read loop never ran take()) and one simulates the happy path
(receiver already consumed). Both assert steer_rx is None on the
returned agent and that a subsequent install_steer_rx does not panic.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…oundary

The previous wording said 'every exit path in run_prompt_task goes
through this function', which overstated the guarantee and risked
reintroducing the panic-unwind confusion Thufir flagged in review.
Tighten to the accurate boundary: only paths that return OwnedAgent
via PromptResult go through this helper; panic/abort paths do not and
do not need to.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 merged commit 10aaa72 into main Jun 30, 2026
29 checks passed
@wpfleger96 wpfleger96 deleted the duncan/steer-rx-guard branch June 30, 2026 15:41
wpfleger96 pushed a commit that referenced this pull request Jun 30, 2026
…work

* origin/main: (25 commits)
  fix(thread): stop mid-scroll content jump in live threads (#1397)
  fix(ci): restore main to green — tauri fmt, personas.rs file-size split, Windows path test (#1399)
  fix(desktop): enable buzz-dev-mcp MCP server for Codex agents (#1394)
  fix(ci): restore E2E flakiness fixes for pgschema, docker-pull, and spec timing (#1396)
  fix(personas): persist pack-backed persona UI edits across reboot (#1392)
  fix(buzz-acp): clear steer_rx on all run_prompt_task exit paths (#1391)
  Restore channel date divider rule (#1395)
  Speed up profile wave action (#1379)
  Restore visible links for rich previews (#1378)
  Mobile channel list polish (#1367)
  style(desktop): unify corner radii to rounded-2xl (16px) (#1393)
  fix(desktop): skip keychain write when blob contents are unchanged (#1377)
  fix(desktop): stop clipping the agent-activity row under the composer (#1371)
  Constrain macOS overscroll to conversations (#1317)
  Mobile appearance foundation (#1366)
  chore(release): release Buzz Desktop version 0.3.38 (#1375)
  feat(desktop): provider-agnostic model selection + databricks discovery (#1307)
  release(helm): buzz chart 0.1.1 (#1374)
  Harden relay attack surfaces (#1369)
  ci(helm): publish chart to GHCR on chart-v* tags (#1372)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant