From d01f4b66133bb5425940b19fd772589d04b6fb8f Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 2 Jun 2026 16:54:50 +0000 Subject: [PATCH] docs(prd): add workspace porting plan --- docs/prds/0045-workspace-porting-plan.md | 167 +++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 docs/prds/0045-workspace-porting-plan.md diff --git a/docs/prds/0045-workspace-porting-plan.md b/docs/prds/0045-workspace-porting-plan.md new file mode 100644 index 0000000..0607c3b --- /dev/null +++ b/docs/prds/0045-workspace-porting-plan.md @@ -0,0 +1,167 @@ +# PRD 0045: Workspace Porting Plan + +- **Status:** Draft +- **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.