237 lines
8.8 KiB
Markdown
237 lines
8.8 KiB
Markdown
# Runtime backend rationale: process by default, tmux as debug/manual mode
|
|
|
|
Date: 2026-05-13
|
|
|
|
Status: informational note, not a normative architecture spec.
|
|
|
|
This document captures the reasoning discussed during launch-runtime stabilization work. It may contain small inaccuracies or outdated external-project details, especially about third-party projects. Treat it as context and rationale, not as the source of truth. Current implementation, tests, and upstream project docs remain authoritative.
|
|
|
|
## Short version
|
|
|
|
We intentionally moved the desktop app toward **process backend by default** for app-launched teammates, while keeping **tmux as an explicit debug/manual mode**.
|
|
|
|
The reason is not that tmux is bad. The reason is that our product is not primarily a terminal multiplexer. It is an app-owned team runtime with UI state, launch diagnostics, restart/retry controls, provider auth handling, bootstrap proofs, notifications, and artifact packs.
|
|
|
|
For that product shape, the default runtime should be controlled by the app, not by a human attaching to panes.
|
|
|
|
## What tmux gives
|
|
|
|
tmux is useful when the product expects live terminal sessions:
|
|
|
|
- A human can attach to a pane and see exactly what the CLI sees.
|
|
- If the CLI asks for input, the user can manually press Enter or answer prompts.
|
|
- Panes can survive some app restarts.
|
|
- TTY behavior is closer to running the CLI manually.
|
|
- Debugging auth/login/TTY problems is easier because the terminal is visible.
|
|
|
|
This is why tmux is a natural default for terminal-first systems.
|
|
|
|
## Why not tmux like gastown/gascity
|
|
|
|
Based on the external-project research snapshot from this thread, `gastown` and `gascity` appear to be more terminal/session-oriented. This is an interpretation of their public docs/issues at the time of research, not a maintained compatibility claim:
|
|
|
|
- Their interaction model leans heavily on attachable sessions.
|
|
- Their session layer historically expects pane-like targets and terminal observation.
|
|
- In `gascity`, tmux appears as a default provider in session configuration.
|
|
- They use tmux because their flow values live interactive sessions, attach/revive/nudge, and human terminal control.
|
|
|
|
That is a valid design for a terminal-first product.
|
|
|
|
It is not automatically the best default for us because our desktop app has different ownership boundaries:
|
|
|
|
- We need reliable UI state for each member.
|
|
- We need deterministic launch success/failure state.
|
|
- We need structured diagnostics, not only "look at the pane".
|
|
- We need restart/retry/cleanup to be owned by the app.
|
|
- We need provider auth and tool approval to be modeled explicitly.
|
|
- We need headless teammate behavior to work without a terminal being open.
|
|
|
|
tmux also has known operational costs in this class of products:
|
|
|
|
- zombie sessions;
|
|
- broken pane targets;
|
|
- socket/version split-brain after upgrades;
|
|
- platform limitations, especially Windows;
|
|
- ambiguity between "pane exists" and "agent is actually ready";
|
|
- harder cleanup when app state and terminal state diverge.
|
|
|
|
So the difference is product shape:
|
|
|
|
- `gastown/gascity`: terminal/session-first, so tmux default is understandable.
|
|
- `claude_team`: desktop/app-owned lifecycle-first, so process default is more aligned.
|
|
|
|
## What process backend gives us
|
|
|
|
The process backend lets the app own the lifecycle:
|
|
|
|
- Runtime identity is represented as process metadata, not only pane id.
|
|
- `backendType: process` and `tmuxPaneId: process:<pid>` preserve compatibility with older shapes while making the backend explicit.
|
|
- Launch state can distinguish `spawned`, `bootstrap_submitted`, `bootstrap_confirmed`, `failed_to_start`, `bootstrap_stalled`, and provider failures.
|
|
- Diagnostics can be surfaced in member cards, notifications, launch summaries, and artifact packs.
|
|
- Restart and cleanup can target launch-owned processes instead of broad terminal state.
|
|
- App-managed bootstrap can avoid relying on the model to manually discover and call setup tools.
|
|
|
|
This is a better foundation for stable desktop launches than treating a pane as the primary runtime truth.
|
|
|
|
## Interactive prompts are still real
|
|
|
|
The main argument for tmux is valid: real CLIs sometimes ask interactive questions.
|
|
|
|
Examples:
|
|
|
|
- "Press Enter to continue"
|
|
- "Do you want to proceed? [y/N]"
|
|
- "Enter API key"
|
|
- "Please login"
|
|
- OAuth token expired
|
|
- provider quota or key limit prompt
|
|
- tool approval prompt
|
|
|
|
Our answer should not be "ignore all interaction". The correct answer is to split interaction into categories.
|
|
|
|
## How our architecture should handle interaction
|
|
|
|
### Structured approvals
|
|
|
|
Tool approvals should use structured protocol:
|
|
|
|
- CLI emits a `control_request`;
|
|
- app shows an approval UI or notification;
|
|
- app sends `control_response` through the owned channel;
|
|
- decision is persisted in runtime state.
|
|
|
|
This is better than asking the user to attach to tmux and press a key manually.
|
|
|
|
### Auth and login prompts
|
|
|
|
Auth/login prompts should usually be handled before launch:
|
|
|
|
- preflight provider auth;
|
|
- validate subscription/API-key mode;
|
|
- validate required settings/env;
|
|
- fail fast with actionable UI if auth is missing or expired.
|
|
|
|
Hidden teammate processes should not block waiting for a browser login or secret input.
|
|
|
|
### Safe known prompts
|
|
|
|
Some prompts can be handled through an allowlisted interactive prompt gate:
|
|
|
|
- exact "Press Enter to continue" style prompt;
|
|
- exact yes/no confirmation where the action is known and safe;
|
|
- one prompt at a time per process;
|
|
- timeout if user does not respond;
|
|
- event recorded in diagnostics/artifact pack.
|
|
|
|
For a lead process, the desktop app already owns `child.stdin`, so writing a newline is technically possible.
|
|
|
|
For teammate process backend, the desktop app may not directly own the child handle. The robust design is:
|
|
|
|
- detect prompt in process backend/orchestrator;
|
|
- surface structured prompt state to desktop;
|
|
- user chooses action in UI;
|
|
- the runtime owner writes to the teammate stdin;
|
|
- event is persisted.
|
|
|
|
Do not blindly write to arbitrary process stdin by PID.
|
|
|
|
### Unknown prompts
|
|
|
|
Unknown prompts should not be answered automatically.
|
|
|
|
Correct behavior:
|
|
|
|
- mark the member as waiting/blocked with a diagnostic;
|
|
- show the relevant output excerpt;
|
|
- suggest fixing auth/settings or using tmux debug mode;
|
|
- avoid sending random newline/yes/no input.
|
|
|
|
This prevents dangerous accidental confirmation and avoids hiding provider setup bugs.
|
|
|
|
## Why tmux remains useful
|
|
|
|
tmux should stay available as an explicit mode:
|
|
|
|
```bash
|
|
CLAUDE_TEAM_TEAMMATE_MODE=tmux pnpm dev
|
|
```
|
|
|
|
or via extra CLI args:
|
|
|
|
```bash
|
|
--teammate-mode tmux
|
|
```
|
|
|
|
Use it for:
|
|
|
|
- debugging unknown TTY behavior;
|
|
- reproducing provider CLI prompts manually;
|
|
- investigating strange live CLI output;
|
|
- cases where human terminal control matters more than app-owned lifecycle.
|
|
|
|
tmux is an escape hatch, not the production default.
|
|
|
|
## Why not full arbitrary terminal emulation
|
|
|
|
Trying to support all possible interactive terminal behavior inside process backend would be risky.
|
|
|
|
Problems:
|
|
|
|
- prompts are provider-specific and change over time;
|
|
- pressing Enter may be safe in one context and dangerous in another;
|
|
- stdin might be structured JSON, not text;
|
|
- a newline can land during an active model turn;
|
|
- secrets should not be requested through generic stdin;
|
|
- the app can accidentally mask auth or provider integration failures.
|
|
|
|
The safer contract is:
|
|
|
|
- app-managed launch should be non-interactive by default;
|
|
- known safe prompts may be handled through structured UI;
|
|
- auth/setup should be preflighted;
|
|
- unknown TTY needs tmux/manual debug mode.
|
|
|
|
## Current strategic choice
|
|
|
|
Recommended runtime policy:
|
|
|
|
1. Production default: process backend.
|
|
2. Provider setup: preflight and actionable diagnostics.
|
|
3. Tool approvals: structured app UI.
|
|
4. Known safe prompts: bounded interactive prompt gate.
|
|
5. Unknown prompts: fail/block visibly with diagnostics.
|
|
6. Debug/manual: explicit tmux mode.
|
|
|
|
This keeps the app in control of lifecycle state while preserving tmux where it is genuinely useful.
|
|
|
|
## Tradeoff summary
|
|
|
|
### Process default + tmux debug mode
|
|
|
|
Confidence: 9.3/10
|
|
Reliability: 9/10
|
|
Complexity: 6/10
|
|
|
|
Best fit for desktop/app-owned agent teams. Requires strong diagnostics and provider preflight.
|
|
|
|
### tmux default + process fallback
|
|
|
|
Confidence: 6.5/10
|
|
Reliability: 6.5/10
|
|
Complexity: 4/10
|
|
|
|
Good for terminal-first workflows. Less aligned with deterministic app-owned launch state.
|
|
|
|
### Fully abstract runtime providers
|
|
|
|
Confidence: 7/10
|
|
Reliability: 7.5/10
|
|
Complexity: 9/10
|
|
|
|
Potentially useful later, but too broad as a launch-stability fix.
|
|
|
|
## Bottom line
|
|
|
|
We did not reject tmux entirely. We rejected tmux as the default runtime truth for app-launched teams.
|
|
|
|
The desktop product should make teammate launch reliable through app-owned process lifecycle, structured evidence, diagnostics, and controlled recovery. tmux remains valuable for debug/manual sessions, especially when an unknown CLI prompt requires a real terminal.
|