This repository has been archived on 2026-08-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
openclaw/skills/gsd-openclaw/.planning/phases/04-gsd-integration/04-01-PLAN.md
T

236 lines
9.1 KiB
Markdown

---
phase: 04-gsd-integration
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/gsd_openclaw/user_context.py
- src/gsd_openclaw/__init__.py
- tests/test_user_context.py
- tests/conftest.py
autonomous: true
requirements:
- BRDG-06
user_setup: []
must_haves:
truths:
- "User's project context persists across chat sessions"
- "User context is stored in isolated per-user files"
- "User context writes are atomic and crash-safe"
- "Each user has independent project tracking"
artifacts:
- path: "src/gsd_openclaw/user_context.py"
provides: "UserContextManager class for per-user project tracking"
min_lines: 100
- path: "tests/test_user_context.py"
provides: "Tests for UserContextManager"
exports: ["TestUserContextManager", "TestAtomicWrites", "TestMultiUser"]
key_links:
- from: "src/gsd_openclaw/user_context.py"
to: "~/gsd-workspaces/.users/{user_id}/context.json"
via: "atomic write pattern (tempfile + os.replace)"
pattern: "tempfile\\.mkstemp|os\\.replace"
---
<objective>
Create UserContextManager class for per-user project context tracking with atomic persistence.
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.
</objective>
<execution_context>
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
</execution_context>
<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.md
<interfaces>
<!-- Key types and contracts from existing codebase -->
From src/gsd_openclaw/state_manager.py (atomic write pattern to follow):
```python
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:
```python
@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
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Write UserContext dataclass and UserContextManager interface</name>
<files>src/gsd_openclaw/user_context.py, tests/test_user_context.py</files>
<behavior>
- Test 1: UserContext can be created with all fields (user_id, current_project, recent_projects, last_active_at, preferences)
- Test 2: UserContext.to_dict() returns complete dict with all fields
- Test 3: UserContext.from_dict() reconstructs from dict, ignoring unknown fields
- Test 4: UserContextManager.__init__ sets users_root and creates context_path correctly
- Test 5: UserContextManager.load() returns None for new users (no existing context)
- Test 6: UserContextManager.load() returns UserContext for existing context file
</behavior>
<action>
Create UserContext dataclass and UserContextManager class skeleton in src/gsd_openclaw/user_context.py:
1. 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)
2. Add to_dict() and from_dict() methods (follow SessionState pattern exactly)
3. 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)
4. Create tests/test_user_context.py with TestUserContextManager class
5. Follow state_manager.py patterns exactly for consistency
</action>
<verify>
<automated>pytest tests/test_user_context.py -x -v</automated>
</verify>
<done>UserContext dataclass and UserContextManager skeleton exist with passing tests</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Implement atomic save with backup rotation</name>
<files>src/gsd_openclaw/user_context.py, tests/test_user_context.py</files>
<behavior>
- Test 1: save() creates file atomically (no temp files remain after save)
- Test 2: save() creates parent directories if missing
- Test 3: save() rotates backup (context.json -> context.json.bak)
- Test 4: save() overwrites old backup on subsequent saves
- Test 5: save/load roundtrip preserves all data
- Test 6: load() falls back to backup when main file is corrupted
</behavior>
<action>
Implement UserContextManager.save() with atomic write pattern:
1. 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
2. Add TestAtomicWrites class to tests/test_user_context.py with tests for:
- Atomic file creation
- Parent directory creation
- Backup rotation
- Cleanup on failure
3. Ensure load() has backup fallback (same as load_state in state_manager.py)
4. Add imports: tempfile, os, json
</action>
<verify>
<automated>pytest tests/test_user_context.py::TestAtomicWrites -x -v</automated>
</verify>
<done>UserContextManager.save() implements atomic writes with backup rotation, matching state_manager.py pattern</done>
</task>
<task type="auto" tdd="true">
<name>Task 3: Implement multi-user isolation and convenience methods</name>
<files>src/gsd_openclaw/user_context.py, tests/test_user_context.py, tests/conftest.py, src/gsd_openclaw/__init__.py</files>
<behavior>
- Test 1: Two users have completely isolated context files
- Test 2: get_current_project() returns current_project or None
- Test 3: set_current_project() updates current_project and adds to recent_projects
- Test 4: set_current_project() maintains only last 5 recent projects
- Test 5: get_recent_projects() returns list of recent projects
- Test 6: New user gets default context created on first access
</behavior>
<action>
Add convenience methods and multi-user support:
1. 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
2. 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
3. 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
4. Update src/gsd_openclaw/__init__.py:
- Add: from gsd_openclaw.user_context import UserContext, UserContextManager
- Add to __all__: "UserContext", "UserContextManager"
</action>
<verify>
<automated>pytest tests/test_user_context.py::TestMultiUser tests/test_user_context.py::TestUserContextManager -x -v</automated>
</verify>
<done>UserContextManager has convenience methods, multi-user isolation works, exports added to __init__.py</done>
</task>
</tasks>
<verification>
- All tests in tests/test_user_context.py pass
- UserContext follows SessionState pattern for consistency
- Atomic write pattern matches state_manager.py exactly
- Multi-user isolation verified through tests
</verification>
<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>
<output>
After completion, create `.planning/phases/04-gsd-integration/04-01-SUMMARY.md`
</output>