refactor(agent): use agent-neutral runtime names

Assisted-by: Codex
This commit is contained in:
2026-05-28 17:59:24 -04:00
parent c08b09dc9f
commit 1cbedc91c0
23 changed files with 200 additions and 191 deletions
+2 -2
View File
@@ -62,7 +62,7 @@ The feature is **done** when all of the following ship:
`Bottle`) plus a `bot_bottle/backend/docker/` subpackage
containing the `DockerBottleBackend` implementation.
- `DockerBottleBackend.launch(plan)` returns a context manager
yielding a `Bottle` handle exposing `exec_claude(argv, *, tty=True)`,
yielding a `Bottle` handle exposing `exec_agent(argv, *, tty=True)`,
`cp_in(host, ctr)`, and teardown on context exit.
- Every existing `subprocess.run(["docker", ...])` call in
`cli/start.py`, `pipelock.py`, `network.py`, `ssh.py`, and
@@ -205,7 +205,7 @@ and a Docker subpackage:
- **`bot_bottle/cli/start.py`** — replace the inline docker
orchestration with `backend = get_bottle_backend(); plan =
backend.prepare(spec, stage_dir=...); with backend.launch(plan) as
bottle: bottle.exec_claude(...)`. The y/N preflight is rendered by
bottle: bottle.exec_agent(...)`. The y/N preflight is rendered by
`plan.print(...)`.
- **`bot_bottle/manifest.py`** — drop the `runtime` field from the
Bottle dataclass and its validation. Existing manifests with
+1 -1
View File
@@ -312,7 +312,7 @@ existing prefix scan as a fallback for one release.
2. **How does `claude` reach the agent's TTY?** Decided: keep
today's `docker exec -it` model. Agent runs `sleep infinity`
under compose; `DockerBottle.exec_claude` runs
under compose; `DockerBottle.exec_agent` runs
`docker exec -it bot-bottle-<slug> claude ...` exactly like
today. Compose owns the lifecycle (so `compose logs` includes
the agent's stdout, `compose down` tears it down), but the
@@ -154,7 +154,7 @@ Today's flow:
```
./cli.py start agent
└─ with backend.launch(plan) as bottle: ← bottle alive while inside `with`
bottle.exec_claude([...], tty=True) ← blocks until claude exits
bottle.exec_agent([...], tty=True) ← blocks until claude exits
# context exits → compose down → state cleanup
```
@@ -171,7 +171,7 @@ The proposed dashboard-driven flow:
# operator interacts via:
curses.endwin()
bottle.exec_claude([...], tty=True) ← blocks; returns on Ctrl-D
bottle.exec_agent([...], tty=True) ← blocks; returns on Ctrl-D
stdscr.refresh()
# bottle is STILL ALIVE — only the claude process exited
@@ -265,7 +265,7 @@ if modal proves fiddly.
Same handoff pattern the new-agent flow uses. For an agent the
dashboard started this session, the dashboard holds the
`DockerBottle` handle in its `bottles` dict and calls
`bottle.exec_claude(...)`. For an agent it discovered via
`bottle.exec_agent(...)`. For an agent it discovered via
`list_active_slugs` (previous-dashboard or external start),
the dashboard synthesizes a one-shot `DockerBottle` from the
slug — container name is `bot-bottle-<slug>`, no prompt
@@ -304,17 +304,17 @@ acting surface, not a lifetime owner.
Sized for one PR each.
1. **Refactor `_launch_bottle` so the launch + exec_claude
1. **Refactor `_launch_bottle` so the launch + exec_agent
pieces are separable.** Today's `cli/start.py` runs both
inside one function. Extract `prepare_with_preflight(spec,
*, render_preflight, prompt_yes)` and `attach_claude(bottle,
*, render_preflight, prompt_yes)` and `attach_agent(bottle,
*, remote_control)`. The CLI's existing one-shot use binds
them as before; the dashboard binds them with curses-aware
render + prompt callables. No behavior change.
2. **Agent picker modal + new-agent flow.** New key `n` opens
the picker; `prepare_with_preflight` runs against the
selected agent; on Y, `backend.launch(plan)` enters the
dashboard's ExitStack; handoff invokes `attach_claude`.
dashboard's ExitStack; handoff invokes `attach_agent`.
3. **Re-attach via Enter on owned agents-pane row.** Looks up
the slug in the dashboard's `bottles` map; if present →
handoff; else → status-line hint pointing at `./cli.py
@@ -390,7 +390,7 @@ Sized for one PR each.
- PRD 0019 — active-agents pane + selection model (the
agents-pane row the re-attach + stop verbs hook into)
- `docs/research/claude-code-pane-in-dashboard.md` — option 1
(handoff) is what `attach_claude` implements here; options 2
(handoff) is what `attach_agent` implements here; options 2
/ 3 are out of scope for this PRD
- `bot_bottle/cli/start.py:_launch_bottle` — the function
chunk 1 extracts the prepare + attach pieces out of
+5 -5
View File
@@ -163,7 +163,7 @@ def _attach_in_tmux(bottle, slug, *, resume) -> str:
The non-tmux path is unchanged from PRD 0020 — `_attach_via_
handoff` is what those two flows already do today (curses.
endwin → attach_claude → stdscr.refresh).
endwin → attach_agent → stdscr.refresh).
### Pane creation
@@ -230,10 +230,10 @@ def _tmux_pane_exists(pane_id) -> bool:
The tmux helpers need the full docker-exec argv for claude —
specifically including the `--append-system-prompt-file <path>`
flag that `DockerBottle.exec_claude` appends today when the
bottle has a prompt path. Refactor: split `exec_claude` into a
flag that `DockerBottle.exec_agent` appends today when the
bottle has a prompt path. Refactor: split `exec_agent` into a
pure `claude_docker_argv(args, *, tty)` that returns the argv
and a thin `exec_claude` that calls `subprocess.run` on it.
and a thin `exec_agent` that calls `subprocess.run` on it.
Both the tmux path AND the existing foreground path use the
same argv builder.
@@ -272,7 +272,7 @@ Three failure modes worth handling:
Sized small.
1. **`claude_docker_argv` refactor.** Pure-ish split of
`DockerBottle.exec_claude` so both foreground and tmux
`DockerBottle.exec_agent` so both foreground and tmux
paths build on the same argv. No behavior change for the
existing tests.
2. **tmux helpers + pane state.** Add `_in_tmux`,
@@ -176,7 +176,7 @@ bottle so the suite is ~15s wall-clock total instead of
bottle: dev
---
(no prompt — exec_claude isn't called)
(no prompt — exec_agent isn't called)
```
```yaml
+2 -2
View File
@@ -317,7 +317,7 @@ The feature is **done** when all of the following ship:
bot_bottle/backend/smolmachines/
__init__.py re-exports SmolmachinesBottleBackend
backend.py SmolmachinesBottleBackend façade
bottle.py SmolmachinesBottle (exec_claude / exec / cp_in / close)
bottle.py SmolmachinesBottle (exec_agent / exec / cp_in / close)
bottle_plan.py SmolmachinesBottlePlan + .print()
bottle_cleanup_plan.py SmolmachinesBottleCleanupPlan
prepare.py resolve_plan(spec, stage_dir, ...) -> SmolmachinesBottlePlan
@@ -436,7 +436,7 @@ Three changes vs. the Docker backend:
layer enforces it.
3. Provisioning: CA install → prompt → skills → git → supervise
config, each via `smolvm machine exec` / `smolvm machine cp`.
4. Yield a `SmolmachinesBottle` whose `exec_claude` / `exec` /
4. Yield a `SmolmachinesBottle` whose `exec_agent` / `exec` /
`cp_in` all funnel through `smolvm machine exec` /
`smolvm machine cp`.
5. Teardown: stop and delete the VM → stop + remove the bundle