Fix local Oz run_agents child rendering as duplicate local + remote pills - #15583
Merged
Conversation
…ills One local run_agents dispatch could produce two orchestration pills for a single agent run: a local one and a remote-flagged one, both pointing at the same server run_id. Root causes, all client-side: 1. launch_local_no_harness_child only stamped task_id on the local conversation via set_task_id, which never updates the agent_id_to_conversation_id index used as the SSE placeholder's idempotency key. Now it calls assign_run_id_for_conversation, mirroring launch_local_harness_child. 2. finish_remote_child_placeholder treated every child_agent_started event as an out-of-band remote child, with no exemption for children the owning (Primary) client will register in-band. It now skips creating a placeholder when the fetched task reports execution_location = LOCAL under Primary drain mode; Observer (shared-session viewer) placeholders are unaffected since they have no in-band counterpart. 3. Both local launch paths now remove any existing conversation already indexed under the run id before creating their own, closing the reverse race where the SSE placeholder's fetch resolves before the local pane exists. Fixes QUALITY-1897.
Contributor
Author
|
This PR was generated with Warp. Comment |
…-index test hold the fix, trim comments - finish_remote_child_placeholder: remove the execution_location == LOCAL skip. It incorrectly suppressed out-of-band LOCAL children (dispatched via CLI/API, executing on another device) that this process never launches in-band. The pre-existing conversation_id_for_agent_id re-check plus the new remove_existing_conversation_for_run_id call at both local launch sites already converge both race orderings to one conversation without needing to special-case execution_location. - Extracted finish_local_oz_child_conversation (child_agent_launch.rs) so launch_local_no_harness_child's run-id indexing is a named, directly testable call. local_oz_launch_indexes_run_id_before_child_agent_started_sse now drives that exact helper instead of hand-rolling the equivalent history-model calls, so a regression in the helper fails the test (verified locally by reverting it to set_task_id and confirming the test fails). - Added primary_placeholder_still_created_for_out_of_band_local_children covering the Primary + out-of-band-LOCAL case; removed the two tests that only exercised the now-removed execution_location branch; updated sse_placeholder_then_local_launch_converges_on_one_conversation for the new (no-skip) behavior. - Trimmed transformation/cross-reference comments per review.
…rt (QUALITY-1902) - Move the raced-placeholder discard from per-call-site remove_existing_conversation_for_run_id into a centralized guard, discard_stale_placeholder_for_run_id, invoked from inside assign_run_id_for_conversation itself (credit: PR #15584 by the requester). This guard only discards an existing mapping when it is a remote-child placeholder (is_remote_child); a non-placeholder mapping is left in place and logged, since that would indicate an unrelated bug rather than a race to resolve. Because both local launch paths already call assign_run_id_for_conversation (the no-harness path via finish_local_oz_child_conversation, added earlier in this PR), no launch site needs its own explicit cleanup call anymore; removed remove_existing_conversation_for_run_id and its call sites. - Adopted PR #15584's regression test verifying the centralized guard directly, and updated the local-launch composition test to rely on it transitively through finish_local_oz_child_conversation instead of calling the (now removed) explicit helper. - Fixed the TUI counterpart of the same bug (QUALITY-1902): register_local_oz_child_session in crates/warp_tui/src/orchestration_model.rs had the identical set_task_id-only omission. It now calls finish_local_oz_child_conversation, the same shared helper the GUI path uses (re-exported via tui_export.rs), and gets the centralized discard guard for free. Added a mirroring regression test, local_oz_child_session_indexes_run_id_immediately.
…test The previous version pre-created the conversation via the add_child_session test helper and called finish_local_oz_child_conversation directly, bypassing register_local_oz_child_session entirely -- the same mistake already caught and fixed once on the GUI side (finding 2 in the first review pass). Reverting the production call site inside register_local_oz_child_session back to set_task_id left the test green. Now constructs a real MaterializedLocalOzChildSession (with a lightweight test terminal session/view, no real PTY) and calls register_local_oz_child_session itself, then asserts the run id resolves via the parent's single child conversation. Verified by reverting the call site, confirming the test failed, then restoring it.
cephalonaut
marked this pull request as ready for review
August 27, 2026 01:28
seemeroland
approved these changes
Aug 27, 2026
|
An unexpected error has occurred: managed MCP server 01a01a21-3ec9-71a1-99f2-f13b1773c920 is not active |
The server emits child_agent_started on the parent for every child, local ones included, so the SSE family drain cannot tell an in-band child apart from one spawned out-of-band (CLI/API) purely from the event -- that's why a locally spawned child can still get an is_remote_child placeholder materialized for it. Add that clause so the comment doesn't just name the mechanism without the premise that makes it necessary.
…local-oz-dual-pill # Conflicts: # app/src/pane_group/pane/terminal_pane.rs # crates/warp_tui/src/orchestration_model.rs
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.



Description
One local
run_agentsdispatch (local: {}, single child, default Oz harness) could render two orchestration pills for the same server run — one local, one remote-flagged — while the server had only one child run and one conversation. The identical omission existed in the TUI front-end, filed as QUALITY-1902 and fixed here too.Root cause, in both front-ends (
warp-server'sEventChildAgentStartedemission for local children is correct and untouched): the local-Oz child launch path stamped the child's task id with a bareconversation.set_task_id(...), which never updates theagent_id_to_conversation_idindex — the only idempotency key the SSE remote-child-placeholder path (ensure_remote_child_placeholder/finish_remote_child_placeholder) checks. Whichever of the two async paths — the local launch's own callback, or the SSE placeholder's task-metadata fetch — resolved second created a duplicate, remote-flagged conversation for the same run id.Changes
The fix closes both race orderings with two changes:
assign_run_id_for_conversationinstead of the bareset_task_id:launch_local_no_harness_child(app/src/pane_group/pane/terminal_pane.rs) via a new shared helper,finish_local_oz_child_conversation(app/src/ai/blocklist/child_agent_launch.rs).register_local_oz_child_session(crates/warp_tui/src/orchestration_model.rs) calls the same shared helper, re-exported viatui_export.rs.assign_run_id_for_conversation(app/src/ai/blocklist/history_model.rs) now checks whether the run id is already mapped to a different conversation; if that conversationis_remote_child(), it's a stale SSE placeholder and gets discarded (discard_stale_placeholder_for_run_id); otherwise the mismatch is logged rather than silently deleted, since that would indicate an unrelated bug. Credit: this centralized, placeholder-gated design is adopted from Matthew Albright's independent fix in #15584, including its regression test.(1) alone would still leave the reverse race open (SSE placeholder wins first); (2) alone is unreachable on the no-harness path, since a launch path that never calls
assign_run_id_for_conversationnever reaches the guard, and the SSE path's own pre-existing idempotency check (conversation_id_for_agent_id) never resolves either, since the run id was never indexed. Both are required together.The TUI's tab bar does not currently render the resulting duplicate conversation (
TuiOrchestrationModel::snapshotfilters children by TUI session presence, and a placeholder has no session), so this closes a latent data-model inconsistency there rather than an observed visible symptom.Known remaining gap, not fixed here:
initialize_output_for_response_stream(history_model.rs, ~1504) also writesagent_id_to_conversation_iddirectly and doesn't go through the new guard. For this fix's case that's harmless (assign_run_id_for_conversationalready runs first, at launch time), but it's an architecturally unguarded writer of the same index for other call paths. Filed as follow-up hardening: QUALITY-1911.Linked Issue
Fixes QUALITY-1897 and QUALITY-1902.
Testing
local_oz_launch_indexes_run_id_before_child_agent_started_sse(GUI,orchestration_event_streamer_tests.rs) — drivesfinish_local_oz_child_conversationdirectly (the exact calllaunch_local_no_harness_childmakes), so a regression there fails the test.sse_placeholder_then_local_launch_converges_on_one_conversation(GUI) — SSE placeholder created first, then the local launch reclaims the run id through the guard; asserts exactly one conversation survives.primary_placeholder_still_created_for_out_of_band_local_children(GUI) — an out-of-band LOCAL child (dispatched via CLI/API on another device) still gets a placeholder, guarding against over-broadly suppressing legitimate placeholders.assign_run_id_for_conversation_discards_stale_remote_placeholder_for_same_run_id(GUI, adopted from Fix duplicate orchestration pills for local child agents #15584) — unit coverage for the guard itself.local_oz_child_session_indexes_run_id_immediately(TUI,orchestration_model_tests.rs) — drivesregister_local_oz_child_sessionitself with a realMaterializedLocalOzChildSession, so a regression at that call site fails the test.Each of the tests above that targets a specific call site was verified by reverting that production line, confirming the test failed, then restoring it.
Ran:
cargo nextest run -p warp(full package): 6257/6260 passed. The 3 failures (missingnsctool; a multibyte-decoration test; a zsh histignorespace test) are pre-existing onmastertoo (confirmed viagit stash+ re-run), unrelated to this change.cargo nextest run -p warp_tui -p warp --lib -E 'package(warp_tui) and test(orchestration_model)': 22/22 passed. (Buildingwarp_tuiin full isolation hits a pre-existing, unrelatedwarpuiplatform-delegate compile gap under thetest-utilfeature; building it alongsidewarpresolves features the way the real workspace build does and avoids it.)./script/formatandcargo clippy -p warp --all-targets --all-features --tests -- -D warnings/cargo clippy -p warp_tui -p warp --lib --tests -- -D warnings— clean, no new diagnostics.Visual verification
This is a background race, not reachable through a deterministic manual repro. A delegated computer-use session built this branch, launched the real Warp GUI, and drove an actual
run_agents(local: {...})dispatch end to end. Result: exactly one pill rendered for the local child, labeled correctly as local, ran to completion.Agent Mode
CHANGELOG-BUG-FIX: Fixed a race that could make a single local Oz
run_agentschild appear as two orchestration pills.