| 1 | # TUI deconstruction |
| 2 | |
| 3 | The deliverable is an independently buildable headless runtime and a terminal |
| 4 | client of that runtime. Preserve working behavior while moving ownership out |
| 5 | of `codewhale-tui`. A lower line count alone does not establish the split. |
| 6 | |
| 7 | ## Audited baseline, 2026-09-09 |
| 8 | |
| 9 | Source inspection and offline Cargo metadata at `ce737266683b` found: |
| 10 | |
| 11 | | Source | Physical Rust lines | |
| 12 | | --- | ---: | |
| 13 | | `crates/tui/src` | 971,321 in 817 files | |
| 14 | | `crates/tui/src/tui` | 266,378 | |
| 15 | | `crates/tui/src/tools` | 153,713 | |
| 16 | | `crates/tui/src/core` | 58,378 | |
| 17 | | `crates/tui/src/commands` | 60,951 | |
| 18 | | `crates/tui/src/lib.rs` | 20,327 | |
| 19 | | All of `crates/core/src` | 5,475 | |
| 20 | |
| 21 | Counts include comments, blank lines, tests, and source files that may not be |
| 22 | compiled. Dedicated test-source files account for 191,058 lines within the |
| 23 | TUI total; additional inline tests remain in other files. The directory named |
| 24 | `tui` also contains domain logic. These are ownership clues, not production |
| 25 | LOC, a complete compiler dependency graph, or a language-port estimate. |
| 26 | |
| 27 | The current dependencies explain the blockage: |
| 28 | |
| 29 | - CLI imports TUI for runtime dispatch and route preferences. |
| 30 | - `core/engine.rs` imports approval policy, context thresholds, attachment |
| 31 | parsing, and roster construction from `tui/`. |
| 32 | - `core/events.rs` carries the roster row and `session_manager.rs` persists |
| 33 | the durable context reference; both now name their owning crate |
| 34 | (`crate::agent_roster::AgentRosterRow`, `codewhale_core::ContextReference`) |
| 35 | rather than a `tui::` re-export. |
| 36 | - `tools/subagent` imports engine policy/catalog functions, and implements |
| 37 | its own repeated model-request/tool-result cycle in `run_subagent`. |
| 38 | - `crates/core` owns request construction and some runtime/session services; |
| 39 | the main `Engine::run_turn` remains inside the TUI crate. The comment in |
| 40 | `tui/src/core/mod.rs` claiming the engine has moved is incorrect. |
| 41 | - `core/protocol_parity.rs` exhaustively projects internal operations/events, |
| 42 | but explicitly has no production consumers. Reuse or retire it during the |
| 43 | migration; its existence does not establish client convergence. |
| 44 | |
| 45 | `single_turn_loop.rs` currently counts functions named `run_turn`. It does |
| 46 | not detect `run_subagent`'s execution cycle. A passing name scan is therefore |
| 47 | insufficient evidence of one execution implementation. |
| 48 | |
| 49 | ## Intended ownership |
| 50 | |
| 51 | This is the proposed destination, not a claim that the boundaries exist now. |
| 52 | Reuse existing crates; introduce only the three cohesive runtime libraries |
| 53 | below, with real consumers and all replaced paths migrated in each slice. |
| 54 | |
| 55 | | Owner | Responsibility | |
| 56 | | --- | --- | |
| 57 | | `codewhale-tui` | Terminal lifecycle, rendering, input, pickers, terminal command presentation. No provider I/O, policy decisions, durable store, or agent loop. | |
| 58 | | `codewhale-cli` | Argument parsing, launch/composition, headless command presentation. Existing binary names remain compatible. | |
| 59 | | `codewhale-app-server` | HTTP/SSE and stdio transport adapters over the same runtime. Reconcile the embedded Runtime API and existing app-server; preserve external routes and auth. | |
| 60 | | New `codewhale-runtime` | Session/thread lifecycle, scheduling, recovery, child supervision, and composition of engine and stores. No model/tool execution loop. Used in process by terminal and server hosts. | |
| 61 | | New `codewhale-engine` | The shared parent/child execution implementation, context/compaction, tool dispatch, cancellation, approvals, and typed events. | |
| 62 | | New `codewhale-models` | Provider clients, live catalog/pricing resolution, and model routing against canonical config facts. Consolidate existing `agent` catalog consumers instead of retaining a second seeded registry. | |
| 63 | | Existing `config`, `secrets`, `execpolicy` | Canonical schema/route identity, credential storage/access, and policy decisions. UI labels stay outside these owners. | |
| 64 | | Existing `tools`, `mcp`, `hooks` | Tool contracts and implementations, extension transports, and hook execution. Agent/task tools call runtime capabilities; they do not own another agent loop. | |
| 65 | | Existing `state`, `protocol`, `core` | Persistence, shared wire/domain records, and request construction. Move the existing core runtime service owner into the runtime library as its callers migrate. | |
| 66 | |
| 67 | Dependency direction: terminal/server -> runtime -> engine -> provider/tool |
| 68 | implementations and shared lower-level crates. Tools must not import the |
| 69 | concrete engine or runtime host. Use narrow service capabilities at the |
| 70 | composition boundary where a tool needs scheduling or agent control; do not |
| 71 | introduce a generic service-locator framework or a trait for every helper. |
| 72 | |
| 73 | The TUI can retain in-process channels through the existing `EngineHandle`, |
| 74 | `Op`, and `Event` seams. HTTP remains a transport for external clients, not a |
| 75 | mandatory hop for local terminal use. Keep wire DTOs separate from internal |
| 76 | operations containing reply channels or resolved capabilities. |
| 77 | |
| 78 | ## Model judgment and test-time compute |
| 79 | |
| 80 | Founder clarification, September 9: the harness should let the model decide |
| 81 | when a goal, plan, delegation, further investigation, or verification is useful. |
| 82 | Provide enough reasoning and tool-feedback opportunities for that judgment. |
| 83 | Do not interpret unwanted automatic goals as a request to forbid inferred goals. |
| 84 | |
| 85 | The current goal path has conflicting authorities: `operate_goal_from_prompt` |
| 86 | classifies an instruction with verb/question heuristics before inference, while |
| 87 | `CreateGoalTool::description` tells the model to require explicit goal requests. |
| 88 | `runtime_handoff` additionally tells the model the host already created a goal. |
| 89 | Those three policies must become one model-facing contract with runtime-owned |
| 90 | state transitions. Explicit `/goal` commands remain a direct user control. |
| 91 | |
| 92 | Reference inspection was local, not a claim about every upstream version: |
| 93 | |
| 94 | | Snapshot | Useful evidence | |
| 95 | | --- | --- | |
| 96 | | Codex `45eec73b11` (2026-09-09) | `ext/goal/src/spec.rs` leaves goal tool selection to the model but instructs explicit user/system intent. Base instructions let the model choose when planning helps. Goal state and continuation live outside the terminal renderer. | |
| 97 | | Kimi Code `1414d4602` (2026-08-13; older snapshot) | `agent-core` exposes `CreateGoal` with completion criteria and a separate reusable turn loop. Creation guidance accepts explicit autonomous-outcome requests or host goal intake. Thinking effort is mapped against model capabilities. | |
| 98 | | DSH `c389f96bf3` (2026-09-08) | `goal/tool-goal` explicitly permits inferring a long-running objective from a direct human request. Execution validates top-level human-turn provenance and exact state revisions. Goal state, goal tools, and continuation scheduling are separate consumers. | |
| 99 | |
| 100 | DSH most directly matches the requested goal discretion. Its runtime validates |
| 101 | who may mutate state; the model judges whether persistence benefits the task. |
| 102 | Codewhale should preserve that distinction without copying DSH's package count. |
| 103 | |
| 104 | Implementation packet, to coordinate separately from mechanical extraction: |
| 105 | |
| 106 | 1. Remove host-side semantic goal classification. Present the request, session |
| 107 | state, tools, and existing goal to the model before deciding on persistence. |
| 108 | 2. Revise the existing goal-tool and Operate guidance together: infer a goal |
| 109 | when the requested outcome warrants durable continuation and has a useful |
| 110 | completion criterion; answer, investigate, or perform ordinary multi-step |
| 111 | work without a goal when that suffices. Honor corrections and opt-outs. |
| 112 | Explicit user controls and model actions use the same goal state owner. |
| 113 | 3. Treat test-time compute as reasoning effort, useful tool-feedback rounds, |
| 114 | and evidence-driven revision. `auto_reasoning::select` currently chooses |
| 115 | effort using message keywords; this is another semantic heuristic to |
| 116 | replace. Preserve explicit route/effort choices. Let the lead allocate |
| 117 | supported effort and execution budgets to work, with additional effort |
| 118 | requested for later steps when new evidence makes that useful. Reuse |
| 119 | `RequestTuning` and existing runtime/tool contracts; do not add an |
| 120 | always-on classifier or a second agent loop ahead of every prompt. |
| 121 | 4. Keep accounting, supported provider limits, permission checks, input |
| 122 | provenance, durable state, and cancellation in Rust. Emit current state, |
| 123 | remaining authorized resources, and tool/test results as compact feedback. |
| 124 | A goal does not grant new spend or execution authority. Preserve the pinned |
| 125 | prefix and append changing feedback to history. |
| 126 | 5. Qualify judgment with model-driven sessions, not only deterministic mocks. |
| 127 | Compare matched tasks at explicit effort/resource settings: a greeting, |
| 128 | architecture discussion, one-file repair, large migration, unrelated followup, |
| 129 | mid-run correction, false success evidence, cancellation, and repeated |
| 130 | failure. Judge objective quality, useful continuation, task completion, |
| 131 | verification quality, latency, tokens/cost, and correct stopping. Do not |
| 132 | score a run better merely for creating a goal or taking more steps. |
| 133 | |
| 134 | Start with the existing model's ordinary reasoning/tool loop. Add independent |
| 135 | review or multiple candidate attempts only where measured failures and task |
| 136 | stakes justify the extra compute. No model-evaluation runs, provider spend, or |
| 137 | performance gains were established by this source audit. |
| 138 | |
| 139 | ## Implementation order |
| 140 | |
| 141 | Every packet names the predecessor, all consumers, changed dependency edges, |
| 142 | and its verification. One owner handles shared manifests and integration. |
| 143 | Keep unrelated active work intact; follow the current workspace authority. |
| 144 | |
| 145 | 1. **Remove upward domain dependencies.** Finish the existing `AppMode` and |
| 146 | `ApprovalMode` migration by pointing runtime consumers at their actual |
| 147 | `config`/`execpolicy` owners. Move durable context-reference records out of |
| 148 | file-mention UI; retain composer completion there. Separate worker receipt |
| 149 | data from roster glyphs/layout. Move reasoning preference and approval |
| 150 | policy out of UI modules, preserving exact route/credential identity. |
| 151 | `ApiProvider` and `ProviderKind` currently differ for legacy table identity; |
| 152 | do not replace one with the other through a lossy cast. |
| 153 | 2. **Extract provider and tool foundations by cohesive subsystem.** Consolidate |
| 154 | config schema and catalog facts as each affected consumer migrates. Move |
| 155 | provider adapters with their tests into `codewhale-models`; reuse the |
| 156 | existing model-client seam. Grow `tools`, `mcp`, `hooks`, and `state` in |
| 157 | place. Move ordinary file/shell/MCP capabilities first. Leave agent |
| 158 | orchestration with the execution owner until step 3; moving all of |
| 159 | `tools/subagent` into a leaf tool crate would preserve a dependency cycle. |
| 160 | 3. **Converge parent and child execution.** Inventory and preserve child |
| 161 | budgets, route pins, permissions, tool activation, steering, parking, |
| 162 | checkpoints, nested work, and terminal fan-in. Adapt children to the |
| 163 | existing engine, then remove the old child model/tool cycle. Use actual |
| 164 | parent/child call-path and behavior evidence; the function-name guard |
| 165 | alone is not acceptance. Do not couple this semantic migration with the |
| 166 | mechanical engine file move. |
| 167 | 4. **Move the engine and shared host.** Once the runtime-to-UI dependencies |
| 168 | are gone, move the existing execution implementation into |
| 169 | `codewhale-engine`, with its owning unit tests. Establish one runtime host |
| 170 | for terminal, exec, server, scheduling, and recovery. Migrate the existing |
| 171 | core runtime and thread-manager consumers fully, preserving on-disk |
| 172 | formats, replay cursors, locks, authority, and exact-once terminal events. |
| 173 | No second runtime store or speculative replacement turn loop. |
| 174 | 5. **Finish the clients.** Fold the embedded HTTP API into the existing |
| 175 | app-server transport surface over `codewhale-runtime`. Move argument parsing |
| 176 | and headless command presentation out of TUI `lib.rs` into CLI. Slash |
| 177 | commands retain presentation in TUI and call the same runtime operations. |
| 178 | Prune dependencies, temporary re-exports, and obsolete implementations. |
| 179 | Only then resize remaining UI files according to actual responsibilities. |
| 180 | |
| 181 | The first bounded source packet is step 1's existing mode imports and durable |
| 182 | context-reference records, including every caller. Subsequent packets are |
| 183 | chosen from the remaining dependency graph, not from a target crate count. |
| 184 | |
| 185 | Moving a definition and repointing its internal callers belong to one complete |
| 186 | packet. Mechanical movement and behavioral changes should remain separately |
| 187 | reviewable, but do not land temporary re-export shims with their last consumers |
| 188 | left for an unspecified future migration. Keep shims only for genuine external |
| 189 | compatibility contracts and identify that contract. |
| 190 | |
| 191 | ## Verification and completion |
| 192 | |
| 193 | - Preserve the model-facing runtime receipt, prompt/cache-prefix semantics, |
| 194 | tool names/order, serialized records, and public commands for mechanical |
| 195 | moves. A deliberate behavior fix names and tests the intended difference. |
| 196 | - Move private unit tests with their implementation. A `#[path]` module split |
| 197 | remains in the same compilation unit; it does not reduce the test binary or |
| 198 | establish faster builds. Do not make internals public just to relocate tests. |
| 199 | - Exercise local mock-provider parent and child turns, tool approval and |
| 200 | denial, streaming, cancel/steer, explicit goals, pause/resume, reconnect, |
| 201 | restart recovery, and terminal fan-in at the affected boundaries. |
| 202 | - Goal persistence is independent of Plan/Act/Operate. Let the model decide |
| 203 | when persistent tracking benefits the requested work, using context and |
| 204 | adequate reasoning time. Remove the host verb heuristic; do not replace it |
| 205 | with a blanket explicit-command-only restriction. Explicit user opt-outs, |
| 206 | cancellation, and authorized resource limits remain binding. |
| 207 | - The headless runtime and its tests must build with **no transitive dependency |
| 208 | on `codewhale-tui`, ratatui, or crossterm**. Desktop and terminal consume the |
| 209 | same lifecycle and execution authority. |
| 210 | - Measure warmed edit/build/test cycles and peak memory for a provider edit, |
| 211 | tool edit, terminal-renderer edit, and locale edit before and after. Record |
| 212 | compiler/profile/features/cache state. A leaf change must not recompile the |
| 213 | unrelated TUI library test unit to run that leaf's own tests. Relinking a |
| 214 | final application is a separate cost; no unmeasured speedup promises. |
| 215 | - Use existing `scripts/dev-test.sh` / `scripts/dev-cargo.sh` and focused checks |
| 216 | during packets. Integration uses the required repository gates with actual |
| 217 | counts. Local tests, full gates, hosted CI, installed artifacts, and PTY |
| 218 | behavior remain separate evidence. Follow the workspace's human gates for |
| 219 | publication, deploys, and spend. |
| 220 | |
| 221 | `docs/BUILD_PERFORMANCE.md` retains historical measurements. Its older B3 and |
| 222 | micro-crate candidate lists are superseded by this dependency-led sequence. |
| 223 | The old all-at-once preconditions, test-file-move speed claim, and mandatory |
| 224 | uncompleted two-PR shim sequence are retired. A paused migration reports the |
| 225 | remaining monolith and unresolved consumers explicitly. |
| 226 |