# 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 `<guest_home>/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 `<guest_home>/workspace` when enabled, else `guest_home`.
- `workdir` as `guest_path` when enabled, else `guest_home`.
- `has_host_git_dir` from `<host_path>/.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 . <workspace_plan.guest_path>`
- `WORKDIR <workspace_plan.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
`<workspace_plan.guest_path>/.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."<trusted_project_path>"]
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.
