Skip to content

fix(runtime): keep subagent mirror turns balanced across eviction and late items - #328

Merged
Tryanks merged 1 commit into
mainfrom
fix/mirror-turn-lifecycle
Sep 5, 2026
Merged

Tryanks merged 1 commit into
mainfrom
fix/mirror-turn-lifecycle

Conversation

@Tryanks

@Tryanks Tryanks commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Problem

Observed in a live run with real Codex native subagents after #324. Mirror session event logs held e.g. 11 turn_started and 0 turn_completed (37 vs 5 across eight mirrors) and the sidebar kept a blue working dot on those mirrors after everything had finished.

Three defects in crates/runtime/src/app/subagents.rs:

  • sync_mirror_turn decided open/closed from the resident mirror's timeline.turn_running. Once the mirror was evicted (or its timeline still loading), resident() is None, so every rerouted child item appended a new TurnStarted and a TurnCompleted could never be emitted.
  • A child item arriving after the Subagent item had completed reopened the mirror with nothing left to close it.
  • The parent's TurnCompleted did not close its mirrors; only SessionClosed did.

Fix

Track the synthetic turn state in a small AppState map that survives eviction; a late child item gets its own start/item/complete sequence so it still renders inside a turn; the parent's TurnCompleted now runs the mirror cleanup. Mirror creation no longer pre-sets turn_in_flight.

Tests

Five new runtime tests (written first, three failed before the fix): resident, evicted, async metadata reload, late item, and parent-turn-end variants, all asserting exactly one start/completion pair in the persisted log and a non-working final status.

cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo build, cargo test --workspace pass locally.

… late items

Mirror sessions for provider-native subagents synthesize their turn
boundaries, and the open/closed decision was read from the resident
mirror's timeline. Once the mirror was evicted (or its timeline was still
loading) every rerouted child item appended another turn_started and no
turn_completed could ever follow, so mirrors kept a stale working dot and
their event logs held dozens of open turns. A child item arriving after
the Subagent item had completed reopened the mirror for good, and the
parent's own turn end did not close its mirrors.

Track the synthetic turn state on AppState so it survives eviction, wrap a
late child item in its own start/complete pair, and close every mirror
when the parent turn completes.
@Tryanks
Tryanks merged commit 8a2f88f into main Sep 5, 2026
3 checks passed
@Tryanks
Tryanks deleted the fix/mirror-turn-lifecycle branch September 5, 2026 09:53
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