Skip to content

v0.9.2: Split RuntimeThreadManager into store, executor, events, and types #3313

Description

@Hmbown

Problem

crates/tui/src/runtime_threads.rs is a 7,133-line module holding four concerns: the persistent thread store, the RuntimeThreadManager orchestrator, turn execution/monitoring, and the DynamicToolExecutor bridge. The numbers have grown since this was filed: impl RuntimeThreadManager now spans ~4,900 lines (~90 methods) and monitor_turn alone is ~1,035 lines. The precondition called out at filing — extract tests first — has landed (runtime_threads/tests.rs, 7,718 lines, 119 tests), so the module split is unblocked. This code owns persisted runtime state, so the split must be pure code motion with the persisted JSON shape untouched.

Current evidence

Measured at v0.9.1 (2026-07-18), crates/tui/src/runtime_threads.rs:

  • Constants, test fault-injection, stream-delta batching, prompt-summary merge helpers: lines 55–320 (coalesce_stream_delta at 149, merge_summary_into_prompt at 208, validated_record_id at 243, CURRENT_RUNTIME_SCHEMA_VERSION = 2 at 272).
  • Persisted record types: lines 324–585 (RuntimeTurnStatus 324, TurnItemKind 335, ThreadRecord 359, TurnRecord 422, TurnItemRecord 474, RuntimeEventRecord 495, RuntimeEventReplay 509, RuntimeStoreState 525, event-append error types 541–585).
  • RuntimeThreadStore + impl: lines 587–1163. On-disk layout: <root>/{threads,turns,items,events}/<id>.json[l] plus state.json (see RuntimeThreadStore::open, line 603).
  • Manager config + request/response/pending types: lines 1164–1615 (RuntimeThreadManagerConfig 1164, CreateThreadRequest 1205, StartTurnRequest 1249, ThreadDetail 1276, ActiveThreads 1433, pending approval/user-input/dynamic-tool entries 1546–1615).
  • impl RuntimeThreadManager: lines 1616–6517, including monitor_turn (5263–6297), start_turn (4303), seed_thread_from_messages (3613), ensure_engine_loaded (4848), event emission (emit_event 2474, append_and_broadcast_event 2491, events_since 4805, replay_events 4825, subscribe_events 2461), and recovery (recover_interrupted_state 6358).
  • DynamicToolExecutor bridge: helpers at 6519–6568 and impl crate::tools::spec::DynamicToolExecutor for RuntimeThreadManager at 6569–6722 (trait defined at crates/tui/src/tools/spec.rs:35).
  • Free helpers at the tail: LRU (6723–6773), parse_mode (6774), tool_kind_for_name (6787), AgentRebindHint/collect_agent_rebind_hints (6810–6857), summarize_text (6858), and the security-sensitive store path-safety family — checked_runtime_store_root (6895), checked_existing_runtime_store_dir (6926), reject_symlinked_store_file (6951), rollback_failed_event_append (6964), reject_symlinked_store_dir (6978).
  • Tests already extracted: crates/tui/src/runtime_threads/tests.rs (7,718 lines, 119 tests, use super::*). Schema/back-compat guards exist: store_load_thread_rejects_newer_schema_version (tests.rs:1703), current_runtime_schema_version_is_two_on_v066 (tests.rs:2214), torn-event-log repair coverage around tests.rs:1952.
  • External consumers: crates/tui/src/runtime_api.rs, crates/tui/src/runtime_api/sessions.rs, crates/tui/src/task_manager.rs.

Scope

Land as small PRs in this order (each step is one reviewable move; runtime_threads.rs stays the module root, submodules join runtime_threads/tests.rs):

  1. runtime_threads/types.rs — persisted record/request/response/pending structs and enums (lines 324–585 and 1164–1615 region types). Serde attributes move verbatim.
  2. runtime_threads/store.rsRuntimeThreadStore + impl (587–1163) together with the path-safety and event-log-repair helpers from the tail (6895–7132); these are load-bearing security checks and must move whole, not be reimplemented.
  3. runtime_threads/events.rs — event projection/emission: RuntimeEventRecord-adjacent replay types, coalesce_stream_delta, the emit_*/append_and_broadcast_event/events_since/replay_events cluster, and collect_agent_rebind_hints.
  4. runtime_threads/dynamic_tools.rs — the DynamicToolExecutor impl and its helpers (6519–6722).
  5. runtime_threads/turn_executor.rsmonitor_turn (5263–6297) plus the claimed-turn monitors (monitor_claimed_turn 4168, spawn_claimed_turn_monitor 4209, settle_claimed_turn_failure 4000) split out of the manager impl last.
  6. Whatever remains of impl RuntimeThreadManager (thread CRUD, start/steer/interrupt/compact, engine loading, recovery) stays in the root or moves to runtime_threads/manager.rs as a final, mechanical step.

Key files

  • crates/tui/src/runtime_threads.rs
  • crates/tui/src/runtime_threads/tests.rs
  • crates/tui/src/tools/spec.rs
  • crates/tui/src/runtime_api.rs
  • crates/tui/src/task_manager.rs

Acceptance criteria

  • Persistent-store code (RuntimeThreadStore + path-safety helpers) lives in its own module, separate from runtime turn execution.
  • Turn event mapping/emission is separated from manager orchestration.
  • The DynamicToolExecutor impl lives outside the manager block/module.
  • All 119 tests in runtime_threads/tests.rs pass unmodified (they use use super::*; keep moved items reachable via pub(crate)/re-exports rather than widening to pub).
  • Persisted JSON shape and store layout (threads/, turns/, items/, events/, state.json, schema_version: 2) are byte-for-byte unchanged; the schema-guard tests at tests.rs:1703 and tests.rs:2214 still pass without edits.
  • runtime_api and task_manager compile without import-path churn (root re-exports preserve crate::runtime_threads::X paths).

Verification

cargo test -p codewhale-tui --bin codewhale-tui --locked runtime_threads
cargo test -p codewhale-tui --bin codewhale-tui --locked runtime_api
cargo fmt

Out of scope

  • Any change to persisted thread/turn/item/event JSON, file naming, or directory layout (that requires its own issue with migration tests).
  • Behavior changes to approval/user-input/dynamic-tool settlement timeouts or turn lifecycle.
  • task_manager.rs / runtime_api.rs refactors beyond import updates.

Related

Triage note: body restructured for agent execution on 2026-07-18; prior comment refinements folded in. Original wording preserved in edit history.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readySelf-contained for a fresh-clone cloud agent; implement in a tested PR with no live credentialscleanupCode cleanup, refactor, or maintenance workenhancementNew feature or requestreliabilityReliability, flaky behavior, retries, fallbacks, and robustnessrustPull requests that update rust codetuiTerminal UI behavior, rendering, or interactionv0.9.2Targeting v0.9.2

    Projects

    Status
    Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions