--- 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` After completion, create `.planning/phases/01-core-infrastructure/01-02-SUMMARY.md`