# PRD 0045: Workspace Porting Plan - **Status:** Active - **Author:** didericis-codex - **Created:** 2026-06-02 - **Issue:** #116 ## Summary Add a backend-neutral `WorkspacePlan` that describes how the operator's current workspace is represented inside a bottle. Docker and smolmachines should both use this plan for workspace path, working directory, content copy, `.git` copy, ownership, and provider trust configuration instead of rediscovering `/home/node/workspace` in separate launch and provisioning code paths. ## Problem The current `--cwd` behavior is spread across backend-specific code: - Docker builds a derived image that copies the host cwd to `/home/node/workspace`, sets that as `WORKDIR`, and patches Claude trust in the generated Dockerfile. - Docker git provisioning separately copies `.git` into `/home/node/workspace/.git`. - smolmachines git provisioning reconstructs `/workspace/.git`, but does not copy the full working tree. - Codex provider setup trusts `guest_home`, not the copied workspace path. These details create backend drift and make provider-specific workspace fixes easy to hard-code in the wrong layer. ## Goals / Success Criteria - `BottleSpec` remains the CLI intent shape (`copy_cwd`, `user_cwd`), while a resolved `WorkspacePlan` carries the backend-neutral guest workspace contract. - `BottlePlan` exposes `workspace_plan` so shared and backend-specific provisioning paths consume one resolved object. - The default in-bottle workspace path remains `/home/node/workspace` when `--cwd` is enabled. - Docker uses `WorkspacePlan` when building the derived cwd image and when provisioning cwd `.git` state. - smolmachines copies the host cwd contents into the same logical workspace path and uses `WorkspacePlan` when provisioning cwd `.git` state. - Provider trust configuration is written for the workspace path when `--cwd` is enabled, and for the guest home when `--cwd` is disabled. - Unit tests cover plan resolution, provider trust path selection, Docker derived image rendering, and both backends' `.git` copy targets. ## Non-goals - No new user-facing flags for custom workspace paths. - No manifest schema changes. - No redesign of git-gate or `bottle.git` entries. - No switch from Docker image-copy to bind-mount. - No unrelated provider auth changes. ## Scope In scope: - Add a small workspace planning module. - Add `workspace_plan` to `BottlePlan` and populate it in Docker and smolmachines prepare paths. - Thread the trusted project path into provider provisioning. - Replace hard-coded `/home/node/workspace` cwd copy and `.git` copy sites with `WorkspacePlan` values. - Copy full host cwd contents for smolmachines `--cwd` parity. - Update focused unit tests. Out of scope: - Integration tests that launch real Docker containers or smolmachines VMs. - Path customization in the bottle manifest or CLI. - Runtime synchronization after bottle launch; this remains a launch-time copy. ## Design Add `bot_bottle/workspace.py`: ```python @dataclass(frozen=True) class WorkspacePlan: enabled: bool host_path: Path guest_home: str guest_path: str workdir: str owner: str = "node:node" mode: str = "755" copy_contents: bool = True copy_git: bool = True has_host_git_dir: bool = False ``` `workspace_plan(spec, guest_home)` resolves: - `enabled` from `spec.copy_cwd`. - `host_path` from `spec.user_cwd`. - `guest_path` as `/workspace` when enabled, else `guest_home`. - `workdir` as `guest_path` when enabled, else `guest_home`. - `has_host_git_dir` from `/.git`. Backends resolve this in `prepare` using their existing guest-home knobs: - Docker: `BOT_BOTTLE_CONTAINER_HOME`, default `/home/node`. - smolmachines: `BOT_BOTTLE_GUEST_HOME`, default `/home/node`. `BottlePlan` carries the result so launch, git provisioning, and provider provisioning stop consulting `spec.copy_cwd` and hard-coded paths directly. ### Docker Keep the current derived-image transport. Change `build_image_with_cwd(derived, base, cwd)` to accept a `WorkspacePlan` or explicit guest path/workdir fields, then render: - `COPY --chown=node:node . ` - `WORKDIR ` Claude trust should move out of the generated cwd Dockerfile and into provider provisioning so Docker and smolmachines share the same provider trust behavior. ### smolmachines Copy host cwd contents into `workspace_plan.guest_path` during provisioning or VM initialization, then chown the resulting workspace to `node:node`. Continue to copy `.git` through the existing smolvm transport, but target `/.git`. This intentionally closes the current parity gap where smolmachines receives repo metadata without the working tree. ### Provider Trust Extend provider planning with a `trusted_project_path` argument. Callers pass `workspace_plan.workdir`. Codex writes: ```toml [projects.""] trust_level = "trusted" ``` Claude writes or updates `.claude.json` so `projects` includes `trusted_project_path` with `hasTrustDialogAccepted: true`. This provisioning belongs in `AgentProvisionPlan` so both backends apply it through their existing provider file-copy primitives. ## Testing Strategy - Unit-test `workspace_plan()` for enabled and disabled cwd, guest-home overrides, and `.git` detection. - Unit-test Docker cwd image rendering to prove it uses the plan's guest path and workdir. - Unit-test provider planning for Codex and Claude trusted project paths. - Unit-test Docker and smolmachines git provisioning targets using mocked copy and exec primitives. - Unit-test smolmachines workspace content copy target and ownership command. Run: - `python3 -m unittest discover -s tests/unit` ## Open Questions None.