9.1 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 | |||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-gsd-integration | 01 | execute | 1 |
|
true |
|
|
Purpose: Enable users to have persistent project context across chat sessions, supporting the /gsd:use command requirement.
Output: UserContextManager class with load/save operations, atomic writes, and multi-user isolation.
<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/04-gsd-integration/04-CONTEXT.md @.planning/phases/04-gsd-integration/04-RESEARCH.mdFrom src/gsd_openclaw/state_manager.py (atomic write pattern to follow):
def save_state(state: SessionState, state_path: Path) -> None:
"""Save session state to file atomically with backup rotation."""
# 1. Create parent directories if missing
state_path.parent.mkdir(parents=True, exist_ok=True)
# 2. Rotate backup
# 3. Write to temp file in same directory
fd, temp_path = tempfile.mkstemp(dir=state_path.parent, suffix='.tmp')
# 4. Atomic replace
os.replace(temp_path, state_path)
From src/gsd_openclaw/response_formatter.py:
@dataclass
class CardButton:
"""Interactive button for Feishu Card actions."""
text: str
value: Dict[str, Any]
button_type: str = "primary" # primary, default, danger
User context storage location (from CONTEXT.md):
- Location:
~/gsd-workspaces/.users/{user_id}/context.json - Format: JSON, atomic writes (same as Phase 1 state.json)
User context data structure (from CONTEXT.md):
- current_project: str - User's current project
- recent_projects: List[str] - Recent 5 projects
- last_active_at: str - ISO timestamp
- preferences: dict - Optional settings
-
Create UserContext dataclass with fields:
- schema_version: str = "1.0"
- user_id: str = ""
- current_project: str = ""
- recent_projects: List[str] = field(default_factory=list)
- last_active_at: str = ""
- preferences: dict = field(default_factory=dict)
-
Add to_dict() and from_dict() methods (follow SessionState pattern exactly)
-
Create UserContextManager class:
- USERS_ROOT = Path.home() / "gsd-workspaces" / ".users"
- init(self, user_id: str, users_root: Optional[Path] = None)
- context_path property: users_root / user_id / "context.json"
- load() -> Optional[UserContext] method
- save(context: UserContext) -> None method (stub for next task)
-
Create tests/test_user_context.py with TestUserContextManager class
-
Follow state_manager.py patterns exactly for consistency pytest tests/test_user_context.py -x -v UserContext dataclass and UserContextManager skeleton exist with passing tests
-
Follow state_manager.py save_state() pattern exactly:
- Create parent directories: context_path.parent.mkdir(parents=True, exist_ok=True)
- Rotate backup: if context_path exists, rename to context.json.bak
- Write to temp file: tempfile.mkstemp(dir=context_path.parent, suffix='.tmp')
- Atomic replace: os.replace(temp_path, context_path)
- Exception handling: clean up temp file on failure
-
Add TestAtomicWrites class to tests/test_user_context.py with tests for:
- Atomic file creation
- Parent directory creation
- Backup rotation
- Cleanup on failure
-
Ensure load() has backup fallback (same as load_state in state_manager.py)
-
Add imports: tempfile, os, json pytest tests/test_user_context.py::TestAtomicWrites -x -v UserContextManager.save() implements atomic writes with backup rotation, matching state_manager.py pattern
-
Add to UserContextManager:
- get_current_project() -> Optional[str]: Returns current_project or None
- set_current_project(project: str) -> None:
- Creates default context if none exists
- Updates current_project
- Adds to recent_projects (max 5, remove oldest if needed)
- Updates last_active_at to now
- Calls save()
- get_recent_projects() -> List[str]: Returns recent_projects list
- create_default_context() -> UserContext: Creates new context with defaults
-
Add TestMultiUser class to tests/test_user_context.py:
- Test isolated context paths for different users
- Test changes to one user don't affect another
-
Add fixtures to tests/conftest.py:
- tmp_users_root(tmp_path): Temp directory for user contexts
- user_context_manager(tmp_users_root): UserContextManager with temp root
-
Update src/gsd_openclaw/__init__.py:
- Add: from gsd_openclaw.user_context import UserContext, UserContextManager
- Add to all: "UserContext", "UserContextManager" pytest tests/test_user_context.py::TestMultiUser tests/test_user_context.py::TestUserContextManager -x -v UserContextManager has convenience methods, multi-user isolation works, exports added to __init__.py
<success_criteria>
- UserContextManager class exists with load/save/get_current_project/set_current_project methods
- Atomic writes with backup rotation work correctly
- Multi-user isolation verified by tests
- Exports added to __init__.py </success_criteria>