---
phase: 01-core-infrastructure
plan: 02
type: execute
wave: 1
depends_on: []
files_modified:
- src/gsd_openclaw/workspace.py
- tests/test_workspace.py
autonomous: true
requirements:
- SESS-06
user_setup: []
must_haves:
truths:
- "Each project has its own isolated workspace directory"
- "Workspace directory structure follows the defined layout"
- "Workspace can be created on demand"
- "Workspace can be destroyed (cleanup on session destroy)"
artifacts:
- path: "src/gsd_openclaw/workspace.py"
provides: "Workspace directory management"
exports: ["Workspace", "WorkspaceManager"]
min_lines: 60
- path: "tests/test_workspace.py"
provides: "Test coverage for workspace operations"
contains: "test_workspace_isolation"
key_links:
- from: "src/gsd_openclaw/workspace.py"
to: "~/gsd-workspaces/"
via: "Path.home() / gsd-workspaces"
pattern: "Path\\.home.*gsd-workspaces"
---
Implement workspace directory management for project isolation.
Purpose: Each project gets its own isolated workspace with the defined directory structure. This ensures SESS-06 (isolated workspace per project).
Output: Working workspace.py with directory creation, structure validation, and cleanup.
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-core-infrastructure/01-CONTEXT.md
@.planning/phases/01-core-infrastructure/01-RESEARCH.md
**Directory layout:**
```
~/gsd-workspaces/{project}/
├── .gsd/
│ ├── state.json # Session state
│ ├── state.json.bak # State backup
│ └── session-dump/ # Session dump
├── code/ # Project code (optional)
└── io-logs/
└── {session-id}.log # I/O logs
```
**Key paths:**
- Root: `Path.home() / "gsd-workspaces" / project_name`
- State: `workspace_root / ".gsd" / "state.json"`
- I/O logs: `workspace_root / "io-logs"`
Task 1: Implement Workspace dataclass with path properties
src/gsd_openclaw/workspace.py, tests/test_workspace.py
- Test 1: Workspace creates correct paths for all directories
- Test 2: Workspace paths are consistent (same root)
- Test 3: Workspace handles project names with special characters safely
Create Workspace dataclass that represents a project workspace:
```python
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
@dataclass
class Workspace:
project_name: str
root: Path = None # Computed property
def __post_init__(self):
if self.root is None:
self.root = Path.home() / "gsd-workspaces" / self.project_name
@property
def gsd_dir(self) -> Path:
return self.root / ".gsd"
@property
def state_path(self) -> Path:
return self.gsd_dir / "state.json"
@property
def state_backup_path(self) -> Path:
return self.gsd_dir / "state.json.bak"
@property
def session_dump_dir(self) -> Path:
return self.gsd_dir / "session-dump"
@property
def code_dir(self) -> Path:
return self.root / "code"
@property
def io_logs_dir(self) -> Path:
return self.root / "io-logs"
def io_log_path(self, session_id: str) -> Path:
return self.io_logs_dir / f"{session_id}.log"
```
For testing, allow injecting a custom root path (for tmp_path isolation).
pytest tests/test_workspace.py::test_workspace_paths -x
Workspace dataclass exists with correct path properties for all directories
Task 2: Implement WorkspaceManager with create/destroy operations
src/gsd_openclaw/workspace.py, tests/test_workspace.py
- Test 1: create() creates all required directories
- Test 2: create() is idempotent (calling twice doesn't fail)
- Test 3: destroy() removes entire workspace directory
- Test 4: exists() returns True after create, False after destroy
- Test 5: Two projects have isolated workspaces (different roots)
Create WorkspaceManager class:
```python
class WorkspaceManager:
WORKSPACES_ROOT = Path.home() / "gsd-workspaces"
def __init__(self, project_name: str, root: Optional[Path] = None):
self.project_name = project_name
self.root = root or self.WORKSPACES_ROOT
self.workspace = Workspace(project_name, self.root / project_name)
def create(self) -> Workspace:
"""Create workspace directory structure. Idempotent."""
# Create all directories
self.workspace.gsd_dir.mkdir(parents=True, exist_ok=True)
self.workspace.session_dump_dir.mkdir(exist_ok=True)
self.workspace.code_dir.mkdir(exist_ok=True)
self.workspace.io_logs_dir.mkdir(exist_ok=True)
return self.workspace
def destroy(self) -> None:
"""Remove entire workspace directory."""
import shutil
if self.workspace.root.exists():
shutil.rmtree(self.workspace.root)
def exists(self) -> bool:
"""Check if workspace exists."""
return self.workspace.root.exists()
def get_workspace(self) -> Workspace:
"""Get workspace object (does not create)."""
return self.workspace
```
The root parameter allows testing with tmp_path instead of home directory.
pytest tests/test_workspace.py::test_workspace_create -x
pytest tests/test_workspace.py::test_workspace_destroy -x
pytest tests/test_workspace.py::test_workspace_isolation -x
WorkspaceManager creates/destroys workspace directories, multiple projects are isolated
Task 3: Create test fixtures for workspace testing
tests/conftest.py
Add workspace fixtures to conftest.py:
```python
import pytest
from pathlib import Path
from gsd_openclaw.workspace import Workspace, WorkspaceManager
@pytest.fixture
def tmp_workspaces_root(tmp_path: Path) -> Path:
"""Temporary root for workspace testing."""
return tmp_path / "gsd-workspaces"
@pytest.fixture
def workspace_manager(tmp_workspaces_root: Path) -> WorkspaceManager:
"""WorkspaceManager with temporary root for isolation."""
manager = WorkspaceManager("test-project", tmp_workspaces_root)
yield manager
# Cleanup
if manager.workspace.root.exists():
import shutil
shutil.rmtree(manager.workspace.root)
```
This ensures tests don't pollute the real ~/gsd-workspaces directory.
pytest tests/test_workspace.py -v
Test fixtures exist for isolated workspace testing
Run full test suite:
```bash
pytest tests/test_workspace.py -v
```
Verify:
- Directory structure created correctly
- Idempotent create (no error on second call)
- Destroy removes all files
- Isolation between projects
- Workspace dataclass with all path properties exists
- WorkspaceManager creates directory structure
- WorkspaceManager destroys workspace cleanly
- Multiple projects have isolated workspaces
- All tests pass: `pytest tests/test_workspace.py -v`