5e0130b56f
When starting a smolmachines agent from the dashboard the docker-build output rendered on top of the curses preflight modal — the build was kicked off before the operator had confirmed launch. The docker backend's `prepare` is pure resolution (no docker calls); smolmachines was inconsistent because `prepare` called `_ensure_smolmachine` which ran `docker build` → `docker save` → `crane push` → `smolvm pack create`, several seconds of stderr noise rendered before the y/N prompt. Move the pipeline: - `_ensure_smolmachine` (+ `_SMOLMACHINE_CACHE_DIR` + `_REPO_DIR` + the local-registry / smolvm imports) moves from `backend/smolmachines/prepare.py` to `backend/smolmachines/launch.py`. Called right before `_smolvm.machine_create` so the resulting `.smolmachine` sidecar path lands as a local in `launch`, not on the plan. - `SmolmachinesBottlePlan.agent_from_path: Path` becomes `agent_image_ref: str`. `prepare` stashes only the docker tag (`$CLAUDE_BOTTLE_IMAGE` || `claude-bottle:latest`); `launch` resolves it into the artifact at bringup. This puts smolmachines on the same prepare-vs-launch boundary the docker backend uses: the preflight summary in the dashboard prints, the operator confirms, then `launch` runs — and its stderr is routed via `_route_op_to_right_pane` (in tmux) or via `curses.endwin` (foreground handoff) so the build output lands cleanly. Tests: - `tests/unit/test_smolmachines_prepare_image.py` → `tests/unit/test_smolmachines_launch_image.py`, updated to import `_ensure_smolmachine` from `launch` rather than `prepare`. - `test_smolmachines_provision.py`: plan fixture switches `agent_from_path` → `agent_image_ref`. 593 unit tests pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>