Add manual ACP session rotation command#932
Conversation
e16ab22 to
63978f4
Compare
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
63978f4 to
3b38579
Compare
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
52dfc4d to
3281f19
Compare
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
tlongwell-block
left a comment
There was a problem hiding this comment.
Reviewed at merge head f4ea8fc. Verified locally: cargo fmt --check, clippy -p buzz-acp --all-targets -D warnings, cargo test -p buzz-acp (288 pass) — all green in a clean worktree.
The design is right: generalizing CancelMode → ControlSignal and reusing the cancel path for rotation is the minimal way to do this. The is_owner_control_command extraction de-triplicates the shutdown/cancel/rotate checks nicely. README distinction between !cancel and !rotate is accurate.
One correctness gap — rotate intent can be silently dropped in the Race 1 window (pool.rs ~L1122):
When !rotate arrives while a turn is in-flight but the turn completes naturally just before the signal lands (has_in_flight_prompt() is false), the else-branch sends PromptOutcome::Ok without invalidating the session. The main loop's fired=true path also skips invalidate_channel_sessions. Net: the owner explicitly asked for a fresh session, the harness logged "rotating", and the next turn reuses the old session anyway.
For !cancel this race resolution is correct — nothing left to cancel. For !rotate the intent isn't "stop the turn", it's "next turn gets a fresh session", and that intent survives the turn finishing. Suggested ~3-line fix in the else branch:
if control_signal == ControlSignal::Rotate {
agent.state.invalidate(&source);
}This is also the one place ControlSignal::Rotate must differ from Cancel — right now every match arm treats them identically (Cancel | Rotate => None), so the variant is purely informational. The fix makes it load-bearing.
Same-shaped (rarer) edge: if control_tx was already consumed by a prior signal, fired=false → the idle-invalidation fallback runs while the agent is checked out, so invalidate_channel_sessions misses it. Could be handled by tracking a pending-rotate per channel, but that's heavier machinery; probably fine to note and skip.
Narrow windows, non-corrupting (worst case: one stale-context turn after rotate), so not blocking — but the 3-line fix is cheap and closes the main one.
Nit: is_owner_control_command doesn't actually check ownership — that stays at the call site (consistent with prior code, but the name overpromises slightly).
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
…session-new * origin/main: fix(huddle): Pocket TTS quality overhaul — reference parity + cross-message pipelining (#997) Add manual ACP session rotation command (#932) fix(desktop): heal stale persona_team_dir paths in release builds (#1003) ci(docker): publish public ghcr.io/block/buzz image (native multi-arch) (#986) fix(buzz-agent): cap tool-result text at 50 KiB with middle elision (#952) feat(huddle): sentence-at-a-time voice-mode guidelines for lower TTS latency (#996) Shard desktop Playwright CI jobs (#992) chore(release): release version 0.3.18 (#995) Video Player Improvements (#993) Improve first-run welcome setup (#970) fix(release): use legacy updater key secret (#991) Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # crates/buzz-acp/src/lib.rs # crates/buzz-agent/src/config.rs
…tate * origin/main: Add relay disconnect UX: friendly errors, reconnect, cached identity (#1004) feat(agents): add active turn indicators to Agents Menu (#1005) ci: add fork guards to docker, release, and auto-tag workflows (#1007) docs(nip-rs): add optional thread read context scheme (#1006) fix(huddle): Pocket TTS quality overhaul — reference parity + cross-message pipelining (#997) Add manual ACP session rotation command (#932) fix(desktop): heal stale persona_team_dir paths in release builds (#1003) ci(docker): publish public ghcr.io/block/buzz image (native multi-arch) (#986) fix(buzz-agent): cap tool-result text at 50 KiB with middle elision (#952) feat(huddle): sentence-at-a-time voice-mode guidelines for lower TTS latency (#996) Shard desktop Playwright CI jobs (#992) chore(release): release version 0.3.18 (#995) Video Player Improvements (#993) Improve first-run welcome setup (#970) fix(release): use legacy updater key secret (#991) Replace built-in personas with Fizz (#987)
Summary
!rotatecontrol command to the sprout-acp harnessVerification
cargo test -p sprout-acpgit show --stat --check HEADNotes
git pushran repo pre-push hooks and hit an existing/unrelated desktop Rust compatibility error indesktop/src-tauri/src/commands/agent_discovery.rsfor unstablestr::floor_char_boundary. This change only touchescrates/sprout-acpand the focused sprout-acp tests pass.