feat(smolmachines): provision_prompt + provision_skills (PRD 0023 chunk 4a) #69
Reference in New Issue
Block a user
Delete Branch "prd-0023-chunk-4a-provision-prompt-skills"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
First slice of chunk 4. Implements the two provisioning methods that don't require agent-image tooling beyond
cpandmkdir—provision_promptandprovision_skills. The remaining three (provision_ca,provision_git,provision_supervise) wait on:PipelockProxyPlan/EgressPlan) threaded through prepare + launch for the CA path, ANDupdate-ca-certificatesin the agent image.gitin the agent image.claudebinary in the agent image so we can runclaude mcp add.The alpine placeholder image used since chunk 2d has
cpandmkdir. That's enough for chunks 4a; the other three provision methods land once the agent-image-conversion gap is closed.What landed
claude_bottle/backend/smolmachines/provision/{__init__,prompt,skills}.py—smolvm machine cp/machine_execrouted implementations. Same contract as the docker backend (provision_promptreturns the in-VM path iff the agent has a non-empty prompt;provision_skillsmkdir + cp per named skill, no-op when empty).prepare.pynow writes the agent's prompt file underagent_state_dir(slug)/prompt.txt(mode 0o600, content from the agent'spromptfield, possibly empty).launch.pycallsprovision(plan, machine_name)aftermachine_start; the returned prompt path threads toSmolmachinesBottlesoexec_claudeadds--append-system-prompt-filewhen relevant.backend.py:provision_prompt/provision_skillswired to the new module;provision_gitis a deliberate stub (waiting on git-gate inner Plan + git tooling).Tests
tests/unit/test_smolmachines_provision.py): argv shape, prompt return-value contract, skills no-op when empty,CLAUDE_BOTTLE_GUEST_SKILLS_DIRoverride, fail-on-missing-skill.tests/integration/test_smolmachines_launch.py::test_prompt_file_lands_in_guest): end-to-end verification against the alpine guest —bottle.exec("cat /root/.claude-bottle-prompt.txt")returns the manifest's prompt text.552 unit + 4 integration tests passing locally (Darwin + smolvm + docker gated).
Roadmap for the rest of chunk 4
PipelockProxyPlan/EgressPlan/GitGatePlan/SupervisePlanthrough prepare + launch so the bundle's daemons run (currentlydaemons_csv="").claude-code+git+curl+ca-certificatesinto the guest. Two paths: build a.smolmachineviasmolvm pack create --from-vmafter manual setup, or push the existing docker image to a registry smolvm can pull from.provision_ca+provision_git+provision_superviseonce 4b + 4c land.First slice of chunk 4: implement the two provisioning methods that don't depend on agent-image tooling beyond `cp` and `mkdir`. provision_ca / provision_git / provision_supervise land once the agent-image gap is solved (chunk 4b+) — they need update-ca-certificates, git, and the claude binary respectively, none of which the chunk-2d alpine placeholder provides. What this PR ships: - `claude_bottle/backend/smolmachines/provision/` subpackage with `prompt.py` + `skills.py`. Each routes through `smolvm.machine_cp` / `machine_exec`. provision_prompt mirrors the docker contract (file always copied; return value drives --append-system-prompt-file iff the agent has a non-empty prompt). provision_skills mkdir + cp per skill, matching the docker backend's loop. - prepare.py now writes the prompt file under agent_state_dir(slug) with the agent's `prompt` body, mode 0o600. The in-guest path is `/root/.claude-bottle-prompt.txt` (alpine has no `node` user; will become `/home/node/...` once the real claude-bottle image lands). - launch.py calls `provision(plan, machine_name)` after machine_start. The returned prompt path threads to SmolmachinesBottle so exec_claude can add --append-system-prompt-file when the agent has a prompt. - backend.py: provision_prompt / provision_skills now real; provision_git is a deliberate stub (waiting on the git-gate inner Plan + git in the agent image). provision_supervise stays the chunk-2d stub. Tests: - 7 new unit cases (test_smolmachines_provision.py): argv shape (mocked smolvm.machine_cp / .machine_exec), prompt return-value contract, no-op-with-no-skills, CLAUDE_BOTTLE_GUEST_SKILLS_DIR override, fail-on-missing-skill. - 1 new integration case in test_smolmachines_launch.py: end-to-end verification that the prompt file lands in the alpine guest at /root/.claude-bottle-prompt.txt with the expected content (via `bottle.exec("cat ...")`). The smoke + the two TSI probes stay green. 552 unit + 4 integration (Darwin+smolvm+docker gated) passing. What's left in chunk 4: - 4b: thread the inner Plans (PipelockProxyPlan / EgressPlan / GitGatePlan / SupervisePlan) through prepare + launch so the bundle daemons actually run (currently daemons_csv=""). - 4c: the agent-image-conversion gap — get claude-code + git + curl + ca-certificates into the guest image (build a .smolmachine via `pack create --from-vm` after manual setup, or push the docker image to a registry smolvm can pull). - 4d: provision_ca + provision_git + provision_supervise once 4b + 4c land. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>@@ -55,0 +53,4 @@# Chunk 4 follow-on: needs the git-gate inner Plan (so the# gitconfig insteadOf URL points at the gate's host) and# the agent image must contain `git`. Stub for chunk 4a.del plan, targetwhy delete these, even as a stub? Why not just do pass?
passnotdelFair — fixed in
085a0c1(passinstead ofdel plan, target). Thedelwas just to silence unused-arg warnings, butpassis the idiomatic stub marker and reads cleaner.