9.4 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | user_setup | must_haves | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-core-infrastructure | 01 | execute | 1 |
|
true |
|
|
Purpose: Provides the foundation for all session state management. Other components depend on this for storing and recovering session metadata.
Output: Working state_manager.py with atomic save, load with validation, and automatic backup rotation.
<execution_context> @/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md @/home/starmen1265/.claude/get-shit-done/templates/summary.md </execution_context>
@.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 @.planning/phases/01-core-infrastructure/01-VALIDATION.mdSessionState dataclass:
@dataclass
class SessionState:
schema_version: str = "1.0"
session_id: str = ""
project_name: str = ""
tmux_session_name: str = ""
tmux_pane_id: str = ""
created_at: str = ""
last_active_at: str = ""
command_count: int = 0
last_output_snapshot: Optional[str] = None
display_name: Optional[str] = None
Atomic write pattern:
def save_state(state: dict, state_path: Path) -> None:
fd, temp_path = tempfile.mkstemp(dir=state_path.parent, suffix='.tmp')
try:
with os.fdopen(fd, 'w', encoding='utf-8') as f:
json.dump(state, f, indent=2, ensure_ascii=False)
os.replace(temp_path, state_path)
except Exception:
if os.path.exists(temp_path):
os.unlink(temp_path)
raise
Implement to_dict() using dataclasses.asdict() and from_dict() with field filtering.
Also create the package __init__.py with version and exports.
Do NOT add validation logic yet - that comes in Task 3. pytest tests/test_state_manager.py::test_session_state_creation -x pytest tests/test_state_manager.py::test_session_state_serialization -x SessionState dataclass exists with all fields, to_dict() and from_dict() methods work correctly
Task 2: Implement atomic state save with backup rotation src/gsd_openclaw/state_manager.py, tests/test_state_manager.py - Test 1: save_state creates file atomically (temp file + rename) - Test 2: save_state creates parent directories if missing - Test 3: save_state rotates backup (state.json -> state.json.bak) - Test 4: save_state cleans up temp file on failure - Test 5: Temp file is created in same directory as target (for atomic rename) Implement save_state(state: SessionState, state_path: Path) function:- Create parent directories with mkdir(parents=True, exist_ok=True)
- If state_path exists, rotate backup:
- Delete old state.json.bak if exists
- Rename state_path to state.json.bak
- Write to temp file using tempfile.mkstemp(dir=state_path.parent, suffix='.tmp')
- Use os.fdopen to write JSON with indent=2, ensure_ascii=False
- Atomic replace with os.replace(temp_path, state_path)
- On exception, clean up temp file if it exists
Use the pattern from RESEARCH.md - this is the atomic write pattern proven to prevent corruption.
Do NOT use file locking - os.replace is atomic on POSIX systems. pytest tests/test_state_manager.py::test_atomic_write -x pytest tests/test_state_manager.py::test_backup_rotation -x State saves atomically, backup rotates correctly, temp files cleaned up on failure
Task 3: Implement state load with validation and fallback src/gsd_openclaw/state_manager.py, tests/test_state_manager.py, tests/conftest.py - Test 1: load_state returns None for missing file - Test 2: load_state returns SessionState for valid file - Test 3: load_state raises ValueError for unsupported schema version - Test 4: load_state raises ValueError for missing required fields - Test 5: load_state falls back to backup when main file is corrupted (JSONDecodeError) - Test 6: load_state falls back to backup when validation fails Implement load_state(state_path: Path) -> Optional[SessionState] function:- If state_path doesn't exist, try backup (state_path.with_suffix('.json.bak'))
- If neither exists, return None
- Read JSON with proper encoding (utf-8)
- Validate:
- schema_version == "1.0" (raise ValueError if not)
- Required fields present: session_id, project_name, tmux_session_name
- On JSONDecodeError or ValueError, try backup (if backup != state_path)
- Return SessionState.from_dict(data)
Also create tests/conftest.py with pytest fixtures:
- tmp_state_dir fixture using pytest's tmp_path
- sample_state fixture returning a valid SessionState instance pytest tests/test_state_manager.py::test_save_load_state -x pytest tests/test_state_manager.py::test_state_validation -x pytest tests/test_state_manager.py::test_backup_fallback -x State loads with validation, corrupted files fall back to backup, missing files return None
class StateManager:
WORKSPACES_ROOT = Path.home() / "gsd-workspaces"
def __init__(self, project_name: str):
self.project_name = project_name
self.workspace_path = self.WORKSPACES_ROOT / project_name
self.state_path = self.workspace_path / ".gsd" / "state.json"
def save(self, state: SessionState) -> None:
save_state(state, self.state_path)
def load(self) -> Optional[SessionState]:
return load_state(self.state_path)
def get_state_path(self) -> Path:
return self.state_path
This provides a clean interface for other components to use state persistence without managing paths directly. pytest tests/test_state_manager.py::test_state_manager_class -x StateManager class provides clean interface for state persistence with correct workspace paths
Run full test suite to verify all state operations: ```bash pytest tests/test_state_manager.py -v ```All tests must pass. Coverage should include:
- Atomic write pattern
- Backup rotation
- Validation logic
- Fallback to backup
- StateManager convenience methods
<success_criteria>
- SessionState dataclass with all required fields exists
- Atomic save_state function prevents corruption
- load_state validates schema and required fields
- Backup rotation works (state.json.bak)
- Corrupted files fall back to backup automatically
- StateManager class provides clean interface
- All tests pass:
pytest tests/test_state_manager.py -v</success_criteria>