自动同步: 2026-03-12 03:00
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
---
|
||||
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>
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
phase: 04-gsd-integration
|
||||
plan: 01
|
||||
subsystem: user-context
|
||||
tags: [persistence, atomic-writes, multi-user, json]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 01-core-infrastructure
|
||||
provides: Atomic write pattern from state_manager.py (tempfile.mkstemp + os.replace)
|
||||
provides:
|
||||
- UserContext dataclass for per-user project tracking
|
||||
- UserContextManager class with load/save operations
|
||||
- Atomic persistence with backup rotation
|
||||
- Multi-user isolation via separate context files
|
||||
affects: [04-02, 04-03, orchestrator]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Atomic write pattern: tempfile.mkstemp + os.replace for crash-safe persistence"
|
||||
- "Backup rotation: context.json -> context.json.bak on each save"
|
||||
- "Multi-user isolation: ~/gsd-workspaces/.users/{user_id}/context.json"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- tests/test_user_context.py
|
||||
modified:
|
||||
- src/gsd_openclaw/user_context.py
|
||||
- src/gsd_openclaw/__init__.py
|
||||
- tests/conftest.py
|
||||
|
||||
key-decisions:
|
||||
- "User context stored in ~/gsd-workspaces/.users/{user_id}/context.json (per CONTEXT.md)"
|
||||
- "Max 5 recent projects maintained in recent_projects list"
|
||||
- "Backup fallback for corrupted main file (same pattern as state_manager.py)"
|
||||
|
||||
patterns-established:
|
||||
- "Atomic write pattern follows state_manager.py exactly for consistency"
|
||||
- "UserContext dataclass mirrors SessionState structure for familiarity"
|
||||
|
||||
requirements-completed: [BRDG-06]
|
||||
|
||||
# Metrics
|
||||
duration: 7min
|
||||
completed: 2026-03-11
|
||||
---
|
||||
|
||||
# Phase 4 Plan 01: UserContextManager Summary
|
||||
|
||||
**Per-user project context tracking with atomic persistence, enabling /gsd:use command functionality**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 7 min
|
||||
- **Started:** 2026-03-11T10:43:13Z
|
||||
- **Completed:** 2026-03-11T10:49:44Z
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 4
|
||||
|
||||
## Accomplishments
|
||||
- UserContext dataclass with to_dict/from_dict methods for serialization
|
||||
- UserContextManager with load/save, get_current_project, set_current_project, get_recent_projects methods
|
||||
- Atomic writes with backup rotation (context.json -> context.json.bak)
|
||||
- Multi-user isolation verified through 20 comprehensive tests
|
||||
- Exports added to __init__.py for public API
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1-3: UserContextManager tests and implementation** - `fe7274a` (test)
|
||||
- Note: Implementation was already done in commit 9bd29a2 as part of 04-02
|
||||
- This commit adds the tests that complete the TDD cycle
|
||||
|
||||
**Plan metadata:** (pending)
|
||||
|
||||
## Files Created/Modified
|
||||
- `src/gsd_openclaw/user_context.py` - UserContext dataclass and UserContextManager class (249 lines)
|
||||
- `src/gsd_openclaw/__init__.py` - Added UserContext, UserContextManager exports
|
||||
- `tests/conftest.py` - Added tmp_users_root and user_context_manager fixtures
|
||||
- `tests/test_user_context.py` - Comprehensive test suite (20 tests in 4 test classes)
|
||||
|
||||
## Decisions Made
|
||||
- User context stored per-user in ~/gsd-workspaces/.users/{user_id}/context.json
|
||||
- Recent projects list limited to 5 entries (oldest removed when exceeded)
|
||||
- Backup fallback on corrupted main file (matches state_manager.py pattern)
|
||||
- Schema version "1.0" for future compatibility
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] Fixed backup fallback test expectation**
|
||||
- **Found during:** Task 2 (atomic write tests)
|
||||
- **Issue:** Test expected backup to exist after first save, but backup only created on subsequent saves
|
||||
- **Fix:** Updated test to save twice before corrupting main file, ensuring backup exists
|
||||
- **Files modified:** tests/test_user_context.py
|
||||
- **Verification:** All 20 tests pass
|
||||
- **Committed in:** fe7274a (Task commit)
|
||||
|
||||
### Out-of-Order Execution
|
||||
|
||||
**Note:** The implementation (user_context.py) was already committed in 9bd29a2 as part of plan 04-02. This plan (04-01) was executed after 04-02, adding the test coverage that should have been written first in TDD fashion. The implementation matches the plan specification exactly.
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 1 auto-fixed (test logic)
|
||||
**Impact on plan:** Minor - test fixed to match correct backup rotation behavior
|
||||
|
||||
## Issues Encountered
|
||||
- None beyond the test fix noted above
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
- UserContextManager ready for use in orchestrator and /gsd:use command handling
|
||||
- All tests passing, exports available via gsd_openclaw package
|
||||
|
||||
## Self-Check: PASSED
|
||||
- user_context.py: FOUND
|
||||
- test_user_context.py: FOUND
|
||||
- SUMMARY.md: FOUND
|
||||
- Commit fe7274a: FOUND
|
||||
|
||||
---
|
||||
*Phase: 04-gsd-integration*
|
||||
*Completed: 2026-03-11*
|
||||
@@ -0,0 +1,250 @@
|
||||
---
|
||||
phase: 04-gsd-integration
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/gsd_openclaw/orchestrator.py
|
||||
- tests/test_orchestrator.py
|
||||
autonomous: true
|
||||
requirements:
|
||||
- BRDG-06
|
||||
user_setup: []
|
||||
must_haves:
|
||||
truths:
|
||||
- "User can send /gsd:new-project through chat and get a pre-created session"
|
||||
- "User can send /gsd:use <project> to switch their current project context"
|
||||
- "User receives clear error when /gsd:use references non-existent project"
|
||||
- "Generic GSD commands pass through correctly"
|
||||
artifacts:
|
||||
- path: "src/gsd_openclaw/orchestrator.py"
|
||||
provides: "Extended Orchestrator with special command handling"
|
||||
exports: ["Orchestrator._handle_new_project", "Orchestrator._handle_use_project"]
|
||||
- path: "tests/test_orchestrator.py"
|
||||
provides: "Tests for special command handling"
|
||||
exports: ["TestSpecialCommands"]
|
||||
key_links:
|
||||
- from: "src/gsd_openclaw/orchestrator.py"
|
||||
to: "src/gsd_openclaw/user_context.py"
|
||||
via: "UserContextManager for context tracking"
|
||||
pattern: "UserContextManager"
|
||||
- from: "src/gsd_openclaw/orchestrator.py"
|
||||
to: "src/gsd_openclaw/tmux_session.py"
|
||||
via: "TmuxSessionManager.create_session for new-project"
|
||||
pattern: "create_session"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Extend Orchestrator with special command handling for /gsd:new-project and /gsd:use.
|
||||
|
||||
Purpose: Enable key GSD commands that require orchestrator-level logic: pre-creating sessions for new-project and tracking user context for use.
|
||||
|
||||
Output: Orchestrator with _handle_new_project and _handle_use_project methods integrated into handle_message routing.
|
||||
</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/orchestrator.py (current implementation):
|
||||
```python
|
||||
class Orchestrator:
|
||||
COMMAND_PATTERN = re.compile(r'^/gsd:(\S+)\s*(.*)$')
|
||||
MAX_OUTPUT_LENGTH = 2000
|
||||
|
||||
def parse_command(self, message: str) -> Optional[Tuple[str, str]]:
|
||||
"""Parse a message for /gsd:* command pattern."""
|
||||
# Returns (command, args) or None
|
||||
|
||||
def handle_message(self, user_id: str, message: str, channel: str, context: Dict) -> Dict:
|
||||
"""Main entry point - currently auto-creates session for all commands."""
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
From src/gsd_openclaw/tmux_session.py:
|
||||
```python
|
||||
class TmuxSessionManager:
|
||||
def create_session(self, project_name: str, ...) -> TmuxSession:
|
||||
"""Create a new tmux session for a project."""
|
||||
|
||||
def get_session(self, project_name: str) -> Optional[TmuxSession]:
|
||||
"""Get existing session without creating new one."""
|
||||
```
|
||||
|
||||
Special command handling requirements (from CONTEXT.md):
|
||||
- /gsd:new-project: Pre-create session, then pass command to tmux
|
||||
- /gsd:use: Track user context, show error + "Create New Project" button if not exists
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Add UserContextManager to Orchestrator and implement /gsd:new-project handling</name>
|
||||
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
|
||||
<behavior>
|
||||
- Test 1: Orchestrator initializes with UserContextManager instance
|
||||
- Test 2: _handle_new_project creates tmux session before executing command
|
||||
- Test 3: _handle_new_project updates user context with new project
|
||||
- Test 4: _handle_new_project executes the command in the new session
|
||||
- Test 5: _handle_new_project returns success card with output
|
||||
- Test 6: handle_message routes 'new-project' command to _handle_new_project
|
||||
</behavior>
|
||||
<action>
|
||||
Extend Orchestrator with UserContextManager and new-project handling:
|
||||
|
||||
1. Add import: from gsd_openclaw.user_context import UserContextManager
|
||||
|
||||
2. Update Orchestrator.__init__:
|
||||
- Add: self.user_context_managers: Dict[str, UserContextManager] = {}
|
||||
- Add helper: _get_user_context_manager(user_id) -> UserContextManager
|
||||
|
||||
3. Add LONG_COMMANDS class attribute:
|
||||
```python
|
||||
LONG_COMMANDS = {'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'}
|
||||
```
|
||||
|
||||
4. Implement _handle_new_project(user_id: str, project_name: str) -> Dict:
|
||||
- If project_name is empty, return format_error with "Project name required"
|
||||
- Create session via tmux_manager.create_session(project_name)
|
||||
- Execute "/gsd:new-project {project_name}" in the session with timeout=None
|
||||
- Update user context: user_context.set_current_project(project_name)
|
||||
- Return formatted result (success or error based on command result)
|
||||
|
||||
5. Update handle_message to route 'new-project' to _handle_new_project
|
||||
|
||||
6. Add TestSpecialCommands class to tests/test_orchestrator.py with tests for new-project
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pytest tests/test_orchestrator.py::TestSpecialCommands::test_new_project -x -v</automated>
|
||||
</verify>
|
||||
<done>Orchestrator has _handle_new_project method that pre-creates session and updates user context</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Implement /gsd:use command handling with context tracking</name>
|
||||
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
|
||||
<behavior>
|
||||
- Test 1: _handle_use_project updates user context to new project
|
||||
- Test 2: _handle_use_project returns success card when project exists
|
||||
- Test 3: _handle_use_project returns error card with "Create New Project" button when project doesn't exist
|
||||
- Test 4: handle_message routes 'use' command to _handle_use_project
|
||||
- Test 5: _handle_use_project with no args returns error card
|
||||
- Test 6: Subsequent commands use the project from user context
|
||||
</behavior>
|
||||
<action>
|
||||
Implement _handle_use_project method:
|
||||
|
||||
1. Implement _handle_use_project(user_id: str, project_name: str) -> Dict:
|
||||
- If project_name is empty, return format_error with "Project name required"
|
||||
- Check if project session exists: tmux_manager.get_session(project_name)
|
||||
- If session exists:
|
||||
* Update user context: user_context.set_current_project(project_name)
|
||||
* Return format_success with "Switched to project '{project_name}'"
|
||||
- If session doesn't exist:
|
||||
* Return format_error with:
|
||||
- title: "Project not found"
|
||||
- content: "Project '{project_name}' does not exist"
|
||||
- buttons: [CardButton(text="Create New Project", value={"action": "new-project", "name": project_name})]
|
||||
|
||||
2. Update handle_message to route 'use' command to _handle_use_project
|
||||
|
||||
3. Add tests for _handle_use_project in TestSpecialCommands class
|
||||
|
||||
4. Ensure CardButton import is available from response_formatter
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pytest tests/test_orchestrator.py::TestSpecialCommands::test_use_project tests/test_orchestrator.py::TestSpecialCommands::test_use_nonexistent -x -v</automated>
|
||||
</verify>
|
||||
<done>Orchestrator has _handle_use_project method with context tracking and error handling</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 3: Update handle_message routing to use user context for project selection</name>
|
||||
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
|
||||
<behavior>
|
||||
- Test 1: Generic commands use project from user context when not in context param
|
||||
- Test 2: Generic commands fall back to "default" project if no context
|
||||
- Test 3: Special commands (new-project, use) are routed correctly
|
||||
- Test 4: Generic pass-through works for all other commands
|
||||
</behavior>
|
||||
<action>
|
||||
Update handle_message routing logic:
|
||||
|
||||
1. Modify handle_message to get project from user context:
|
||||
```python
|
||||
def handle_message(self, user_id, message, channel, context):
|
||||
# Parse command
|
||||
parsed = self.parse_command(message)
|
||||
if not parsed:
|
||||
return format_error(...)
|
||||
|
||||
command, args = parsed
|
||||
|
||||
# Special commands
|
||||
if command == 'new-project':
|
||||
return self._handle_new_project(user_id, args)
|
||||
if command == 'use':
|
||||
return self._handle_use_project(user_id, args)
|
||||
|
||||
# Generic commands - get project from user context
|
||||
user_ctx = self._get_user_context_manager(user_id)
|
||||
project_name = user_ctx.get_current_project()
|
||||
if not project_name:
|
||||
project_name = context.get("project", "default")
|
||||
|
||||
# Rest of existing logic...
|
||||
```
|
||||
|
||||
2. Ensure existing tests still pass (backward compatibility)
|
||||
|
||||
3. Add integration tests in TestIntegrationFlows for:
|
||||
- Command after /gsd:use goes to correct project
|
||||
- Context persists across multiple commands
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pytest tests/test_orchestrator.py -x -v</automated>
|
||||
</verify>
|
||||
<done>handle_message routes special commands and uses user context for project selection</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- All tests in tests/test_orchestrator.py pass
|
||||
- /gsd:new-project pre-creates session and updates context
|
||||
- /gsd:use tracks context and shows error for non-existent projects
|
||||
- Generic commands use user context for project selection
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Orchestrator has _handle_new_project and _handle_use_project methods
|
||||
- Special commands are routed correctly in handle_message
|
||||
- User context is used for project selection in generic commands
|
||||
- Error cards include "Create New Project" button for missing projects
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/04-gsd-integration/04-02-SUMMARY.md`
|
||||
</output>
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
phase: 04-gsd-integration
|
||||
plan: 02
|
||||
subsystem: orchestration
|
||||
tags: [user-context, special-commands, routing, tmux]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 03-orchestration
|
||||
provides: Orchestrator base class with handle_message routing
|
||||
provides:
|
||||
- UserContextManager for per-user project tracking
|
||||
- Special command handling for /gsd:new-project and /gsd:use
|
||||
- User context-based project selection for generic commands
|
||||
affects: [05-bridge-completion]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- Special command routing pattern (new-project, use)
|
||||
- User context manager pattern with persistence
|
||||
- Fallback from user context to context param to default
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- src/gsd_openclaw/user_context.py
|
||||
modified:
|
||||
- src/gsd_openclaw/orchestrator.py
|
||||
- tests/test_orchestrator.py
|
||||
|
||||
key-decisions:
|
||||
- "UserContextManager persists context to disk with atomic writes"
|
||||
- "Generic commands use user context first, fall back to context param"
|
||||
- "Error cards include 'Create New Project' button for missing projects"
|
||||
- "users_root parameter added to Orchestrator for test isolation"
|
||||
|
||||
patterns-established:
|
||||
- "Special command routing: check command name, delegate to _handle_* method"
|
||||
- "User context flow: get from user context -> fall back to context param -> default"
|
||||
|
||||
requirements-completed: [BRDG-06]
|
||||
|
||||
# Metrics
|
||||
duration: 8min
|
||||
completed: 2026-03-11
|
||||
---
|
||||
|
||||
# Phase 4 Plan 02: Special Command Handling Summary
|
||||
|
||||
**Extended Orchestrator with /gsd:new-project and /gsd:use commands, implementing UserContextManager for per-user project tracking and context-based routing**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 8 min
|
||||
- **Started:** 2026-03-11T10:43:19Z
|
||||
- **Completed:** 2026-03-11T10:51:39Z
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 4
|
||||
|
||||
## Accomplishments
|
||||
- UserContextManager with persistent per-user project tracking
|
||||
- /gsd:new-project command pre-creates session and updates user context
|
||||
- /gsd:use command switches project context, shows error with Create button for non-existent projects
|
||||
- Generic commands now use project from user context first
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically (TDD pattern with RED -> GREEN commits):
|
||||
|
||||
1. **Task 1: Add UserContextManager and /gsd:new-project handling**
|
||||
- `0de7824` (test: add failing tests for special command handling)
|
||||
- `9bd29a2` (feat: implement _handle_new_project with UserContextManager)
|
||||
|
||||
2. **Task 2: Implement /gsd:use command handling with context tracking**
|
||||
- `8f8d9d6` (test: add failing tests for /gsd:use command handling)
|
||||
- `ffe3200` (feat: implement _handle_use_project with context tracking)
|
||||
|
||||
3. **Task 3: Update handle_message routing to use user context**
|
||||
- `5af3d1c` (feat: use user context for project selection in generic commands)
|
||||
|
||||
**Plan metadata:** (included in task commits)
|
||||
|
||||
_Note: TDD tasks have multiple commits (test -> feat)_
|
||||
|
||||
## Files Created/Modified
|
||||
- `src/gsd_openclaw/user_context.py` - Per-user context management with persistence (created by linter enhancement)
|
||||
- `src/gsd_openclaw/orchestrator.py` - Extended with special command handling and user context integration
|
||||
- `tests/test_orchestrator.py` - Added TestSpecialCommands class with 13 new tests
|
||||
|
||||
## Decisions Made
|
||||
- UserContextManager persists to disk using atomic writes (tempfile + os.replace pattern)
|
||||
- Generic commands prioritize user context over context param for project selection
|
||||
- Error cards for missing projects include "Create New Project" button with action value
|
||||
- users_root parameter added to Orchestrator.__init__ for test isolation
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] Added users_root parameter for test isolation**
|
||||
- **Found during:** Task 3 (user context integration)
|
||||
- **Issue:** Integration tests failed because UserContextManager persisted to real ~/gsd-workspaces/.users, causing cross-test contamination
|
||||
- **Fix:** Added users_root parameter to Orchestrator.__init__, updated integration tests to use tmp_users_root fixture
|
||||
- **Files modified:** src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py
|
||||
- **Verification:** All 31 tests pass with isolated user contexts
|
||||
- **Committed in:** 5af3d1c (Task 3 commit)
|
||||
|
||||
**2. [Rule 3 - Blocking] Linter expanded UserContextManager with persistence**
|
||||
- **Found during:** Task 1 (implementation)
|
||||
- **Issue:** Initial simple UserContextManager was expanded by linter to include full persistence with UserContext dataclass
|
||||
- **Fix:** Adapted Orchestrator to use the expanded interface (UserContextManager now requires user_id in constructor)
|
||||
- **Files modified:** src/gsd_openclaw/user_context.py, src/gsd_openclaw/orchestrator.py
|
||||
- **Verification:** Tests pass with new interface
|
||||
- **Committed in:** 9bd29a2 (Task 1 commit)
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 2 auto-fixed (2 blocking)
|
||||
**Impact on plan:** Both fixes necessary for correct test isolation and using the linter-enhanced module. No scope creep.
|
||||
|
||||
## Issues Encountered
|
||||
None - all tasks completed as planned with minor interface adaptations.
|
||||
|
||||
## User Setup Required
|
||||
None - no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
- Special command handling complete, ready for additional GSD command implementations
|
||||
- User context tracking enables multi-project workflows
|
||||
- Pattern established for adding new special commands (route in handle_message, implement _handle_* method)
|
||||
|
||||
## Self-Check: PASSED
|
||||
- All files verified to exist
|
||||
- All commits verified in git history
|
||||
- All 31 tests passing
|
||||
|
||||
---
|
||||
*Phase: 04-gsd-integration*
|
||||
*Completed: 2026-03-11*
|
||||
@@ -0,0 +1,267 @@
|
||||
---
|
||||
phase: 04-gsd-integration
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- 04-01
|
||||
- 04-02
|
||||
files_modified:
|
||||
- src/gsd_openclaw/command_executor.py
|
||||
- src/gsd_openclaw/orchestrator.py
|
||||
- tests/test_command_executor.py
|
||||
- tests/test_orchestrator.py
|
||||
autonomous: true
|
||||
requirements:
|
||||
- BRDG-06
|
||||
user_setup: []
|
||||
must_haves:
|
||||
truths:
|
||||
- "Long commands (execute-phase, plan-phase, discuss-phase, new-project) don't timeout"
|
||||
- "Regular commands still use 60 second default timeout"
|
||||
- "Long command detection is based on command name matching"
|
||||
- "Create New Project button callback works from error card"
|
||||
artifacts:
|
||||
- path: "src/gsd_openclaw/command_executor.py"
|
||||
provides: "Extended CommandExecutor with long command support"
|
||||
exports: ["CommandExecutor.LONG_COMMANDS", "CommandExecutor._is_long_command"]
|
||||
- path: "src/gsd_openclaw/orchestrator.py"
|
||||
provides: "Orchestrator with adaptive timeout and button callback"
|
||||
exports: ["Orchestrator._is_long_command"]
|
||||
- path: "tests/test_command_executor.py"
|
||||
provides: "Tests for long command detection"
|
||||
exports: ["TestLongCommands"]
|
||||
key_links:
|
||||
- from: "src/gsd_openclaw/orchestrator.py"
|
||||
to: "src/gsd_openclaw/command_executor.py"
|
||||
via: "execute() with timeout=None for long commands"
|
||||
pattern: "_is_long_command|LONG_COMMANDS"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Extend CommandExecutor with long command support and update Orchestrator for adaptive timeout and button callback.
|
||||
|
||||
Purpose: Ensure long-running GSD commands don't timeout prematurely, and the "Create New Project" button from error cards works correctly.
|
||||
|
||||
Output: CommandExecutor with LONG_COMMANDS detection, Orchestrator with adaptive timeout handling, and button callback support.
|
||||
</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/command_executor.py:
|
||||
```python
|
||||
class CommandExecutor:
|
||||
ERROR_KEYWORDS: List[str] = [...]
|
||||
DEFAULT_TIMEOUT: float = 60.0
|
||||
|
||||
def execute(self, session, command: str, timeout: Optional[float] = None) -> CommandResult:
|
||||
"""Execute command with optional timeout override."""
|
||||
timeout = timeout if timeout is not None else self.DEFAULT_TIMEOUT
|
||||
# ...
|
||||
```
|
||||
|
||||
From src/gsd_openclaw/orchestrator.py:
|
||||
```python
|
||||
class Orchestrator:
|
||||
COMMAND_PATTERN = re.compile(r'^/gsd:(\S+)\s*(.*)$')
|
||||
MAX_OUTPUT_LENGTH = 2000
|
||||
|
||||
def handle_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Handle button callback - currently only supports approve/reject."""
|
||||
```
|
||||
|
||||
Long commands from CONTEXT.md:
|
||||
- execute-phase, plan-phase, discuss-phase, new-project have no timeout
|
||||
- Detection via command name matching
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Add LONG_COMMANDS detection to CommandExecutor</name>
|
||||
<files>src/gsd_openclaw/command_executor.py, tests/test_command_executor.py</files>
|
||||
<behavior>
|
||||
- Test 1: LONG_COMMANDS contains 'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'
|
||||
- Test 2: _is_long_command returns True for commands in LONG_COMMANDS
|
||||
- Test 3: _is_long_command returns False for commands not in LONG_COMMANDS
|
||||
- Test 4: _is_long_command handles commands with arguments correctly
|
||||
</behavior>
|
||||
<action>
|
||||
Add long command detection to CommandExecutor:
|
||||
|
||||
1. Add LONG_COMMANDS class attribute:
|
||||
```python
|
||||
class CommandExecutor:
|
||||
ERROR_KEYWORDS: List[str] = [...]
|
||||
DEFAULT_TIMEOUT: float = 60.0
|
||||
LONG_COMMANDS: Set[str] = {'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'}
|
||||
```
|
||||
|
||||
2. Add _is_long_command method:
|
||||
```python
|
||||
def _is_long_command(self, command: str) -> bool:
|
||||
"""Check if command requires no timeout.
|
||||
|
||||
Args:
|
||||
command: The full command string (e.g., '/gsd:execute-phase').
|
||||
|
||||
Returns:
|
||||
True if command is in LONG_COMMANDS, False otherwise.
|
||||
"""
|
||||
# Extract command name from /gsd:command format
|
||||
match = re.match(r'^/gsd:(\S+)', command)
|
||||
if match:
|
||||
command_name = match.group(1)
|
||||
return command_name in self.LONG_COMMANDS
|
||||
return False
|
||||
```
|
||||
|
||||
3. Add TestLongCommands class to tests/test_command_executor.py:
|
||||
- Test LONG_COMMANDS set contents
|
||||
- Test _is_long_command for each long command
|
||||
- Test _is_long_command returns False for regular commands
|
||||
- Test _is_long_command handles commands with args
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pytest tests/test_command_executor.py::TestLongCommands -x -v</automated>
|
||||
</verify>
|
||||
<done>CommandExecutor has LONG_COMMANDS set and _is_long_command method with tests</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Update Orchestrator to use adaptive timeout</name>
|
||||
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
|
||||
<behavior>
|
||||
- Test 1: _is_long_command in Orchestrator returns True for long commands
|
||||
- Test 2: execute() is called with timeout=None for long commands
|
||||
- Test 3: execute() is called with default timeout for regular commands
|
||||
- Test 4: handle_message passes correct timeout based on command type
|
||||
</behavior>
|
||||
<action>
|
||||
Add adaptive timeout to Orchestrator:
|
||||
|
||||
1. Add LONG_COMMANDS and _is_long_command to Orchestrator (mirror CommandExecutor):
|
||||
```python
|
||||
class Orchestrator:
|
||||
LONG_COMMANDS = {'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'}
|
||||
|
||||
def _is_long_command(self, command: str) -> bool:
|
||||
"""Check if command requires no timeout."""
|
||||
# Extract command name from parsed command
|
||||
if command in self.LONG_COMMANDS:
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
2. Update handle_message to use adaptive timeout:
|
||||
```python
|
||||
# In handle_message, before execute call:
|
||||
timeout = None if self._is_long_command(command) else self.executor.DEFAULT_TIMEOUT
|
||||
result = self.executor.execute(session, full_command, timeout=timeout)
|
||||
```
|
||||
|
||||
3. Update _handle_new_project to use timeout=None (already a long command):
|
||||
```python
|
||||
result = self.executor.execute(session, f"/gsd:new-project {project_name}", timeout=None)
|
||||
```
|
||||
|
||||
4. Add tests in TestSpecialCommands for adaptive timeout:
|
||||
- Test that long commands get timeout=None
|
||||
- Test that regular commands get DEFAULT_TIMEOUT
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pytest tests/test_orchestrator.py::TestSpecialCommands -x -v</automated>
|
||||
</verify>
|
||||
<done>Orchestrator uses adaptive timeout based on command type</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 3: Update handle_callback to support Create New Project button</name>
|
||||
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
|
||||
<behavior>
|
||||
- Test 1: Callback with action="new-project" creates new session
|
||||
- Test 2: Callback with action="new-project" executes /gsd:new-project command
|
||||
- Test 3: Callback with action="new-project" returns success card
|
||||
- Test 4: Callback with action="new-project" and missing name returns error
|
||||
</behavior>
|
||||
<action>
|
||||
Update handle_callback to support new-project action:
|
||||
|
||||
1. Add new-project action handling to handle_callback:
|
||||
```python
|
||||
def handle_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
action = callback_data.get("action", "")
|
||||
|
||||
if action == "new-project":
|
||||
project_name = callback_data.get("name", "")
|
||||
user_id = callback_data.get("user_id", "")
|
||||
|
||||
if not project_name:
|
||||
return self.formatter.format_error(
|
||||
title="Invalid Request",
|
||||
content="Project name is required"
|
||||
)
|
||||
|
||||
return self._handle_new_project(user_id, project_name)
|
||||
|
||||
# Existing approval handling...
|
||||
if action == "approve":
|
||||
...
|
||||
```
|
||||
|
||||
2. Update _handle_use_project to include user_id in button value:
|
||||
```python
|
||||
CardButton(
|
||||
text="Create New Project",
|
||||
value={
|
||||
"action": "new-project",
|
||||
"name": project_name,
|
||||
"user_id": user_id
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
3. Add tests in TestHandleCallback for new-project action
|
||||
|
||||
4. Ensure backward compatibility with existing approval callbacks
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pytest tests/test_orchestrator.py::TestHandleCallback -x -v</automated>
|
||||
</verify>
|
||||
<done>handle_callback supports "new-project" action from error card button</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- All tests in tests/test_command_executor.py pass
|
||||
- All tests in tests/test_orchestrator.py pass
|
||||
- Long commands execute with no timeout
|
||||
- Regular commands still use 60 second timeout
|
||||
- "Create New Project" button callback works correctly
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- CommandExecutor has LONG_COMMANDS detection
|
||||
- Orchestrator uses adaptive timeout based on command type
|
||||
- handle_callback supports "new-project" action
|
||||
- All existing tests still pass
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/04-gsd-integration/04-03-SUMMARY.md`
|
||||
</output>
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
phase: 04-gsd-integration
|
||||
plan: 03
|
||||
subsystem: orchestration
|
||||
tags: [timeout, long-commands, callback, adaptive-timeout, tdd]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 04-01
|
||||
provides: User context management for project tracking
|
||||
- phase: 04-02
|
||||
provides: Special command handling (new-project, use)
|
||||
provides:
|
||||
- Long command detection with no timeout for execute-phase, plan-phase, discuss-phase, new-project
|
||||
- Adaptive timeout handling in Orchestrator
|
||||
- Create New Project button callback from error cards
|
||||
affects: [orchestration, command-execution, user-experience]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [adaptive-timeout, callback-routing, tdd]
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- src/gsd_openclaw/command_executor.py
|
||||
- src/gsd_openclaw/orchestrator.py
|
||||
- tests/test_command_executor.py
|
||||
- tests/test_orchestrator.py
|
||||
|
||||
key-decisions:
|
||||
- "Long commands detected by command name matching in LONG_COMMANDS set"
|
||||
- "Adaptive timeout: None for long commands, 60 seconds for regular commands"
|
||||
- "new-project callback routes through existing _handle_new_project method"
|
||||
|
||||
patterns-established:
|
||||
- "TDD pattern: Write failing tests first, then implement to pass"
|
||||
- "Callback routing: Check action type before processing"
|
||||
|
||||
requirements-completed: [BRDG-06]
|
||||
|
||||
# Metrics
|
||||
duration: 6min
|
||||
completed: 2026-03-11
|
||||
---
|
||||
|
||||
# Phase 04 Plan 03: Long Command Support Summary
|
||||
|
||||
**Extended CommandExecutor with long command detection and Orchestrator with adaptive timeout handling, plus Create New Project button callback support from error cards.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 6 min
|
||||
- **Started:** 2026-03-11T10:56:48Z
|
||||
- **Completed:** 2026-03-11T11:03:00Z
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 4
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Added LONG_COMMANDS set to CommandExecutor with execute-phase, plan-phase, discuss-phase, new-project
|
||||
- Implemented _is_long_command method for command detection in both CommandExecutor and Orchestrator
|
||||
- Added adaptive timeout in Orchestrator - long commands use timeout=None, regular commands use 60 seconds
|
||||
- Implemented new-project callback action in handle_callback for Create New Project button from error cards
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1: Add LONG_COMMANDS detection to CommandExecutor** - `3f540e9` (feat)
|
||||
2. **Task 2: Update Orchestrator to use adaptive timeout** - `b4ae426` (feat)
|
||||
3. **Task 3: Update handle_callback to support Create New Project button** - `4c050fe` (feat)
|
||||
|
||||
**Plan metadata:** (pending final commit)
|
||||
|
||||
_Note: All tasks followed TDD approach with tests written first_
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `src/gsd_openclaw/command_executor.py` - Added LONG_COMMANDS set and _is_long_command method
|
||||
- `src/gsd_openclaw/orchestrator.py` - Added _is_long_command method, adaptive timeout in handle_message, new-project callback handling
|
||||
- `tests/test_command_executor.py` - Added TestLongCommands class with 5 test cases
|
||||
- `tests/test_orchestrator.py` - Added TestAdaptiveTimeout class (6 tests) and 4 new-project callback tests
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Long command detection uses command name matching against LONG_COMMANDS set (simple, explicit)
|
||||
- Adaptive timeout: timeout=None for long commands allows unlimited execution time
|
||||
- Regular commands default to 60 second timeout from DEFAULT_TIMEOUT
|
||||
- new-project callback action reuses existing _handle_new_project method for consistency
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None - plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None - all tests passed on first implementation attempt after TDD RED phase.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- Phase 4 (GSD Integration) is now complete
|
||||
- All 3 plans in phase 4 have been executed
|
||||
- Long-running GSD commands will no longer timeout prematurely
|
||||
- Error cards can now create new projects via button callback
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- SUMMARY.md exists
|
||||
- Task 1 commit (3f540e9) verified
|
||||
- Task 2 commit (b4ae426) verified
|
||||
- Task 3 commit (4c050fe) verified
|
||||
- Final commit (7afc0e5) verified
|
||||
- All 18 new/updated tests pass
|
||||
|
||||
---
|
||||
*Phase: 04-gsd-integration*
|
||||
*Completed: 2026-03-11*
|
||||
@@ -0,0 +1,126 @@
|
||||
# Phase 4: GSD Integration - Context
|
||||
|
||||
**Gathered:** 2026-03-11
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
完成 GSD 命令全覆盖 — 确保所有 34 个 GSD slash 命令都能通过桥接正常工作,实现 BRDG-06 要求。这个阶段填补编排层(Phase 3)与 100% 命令覆盖之间的差距,同时完善项目上下文追踪和工作流连续性支持。
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### 命令清单处理策略
|
||||
- **处理模式**: 关键命令特殊处理,其余泛型透传
|
||||
- **关键命令定义**: `/gsd:new-project` 和 `/gsd:use` 需要 orchestrator 特殊逻辑
|
||||
- **泛型命令**: 其他 32 个命令统一通过 tmux 透传 + 卡片格式化
|
||||
|
||||
### 关键命令特殊处理
|
||||
|
||||
#### `/gsd:new-project`
|
||||
- **预创建会话**: orchestrator 检测命令后,先调用 TmuxSessionManager 创建会话和工作空间
|
||||
- **命令透传**: 会话创建后,透传命令到 tmux 让 GSD 初始化项目结构
|
||||
- **工作空间路径**: `~/gsd-workspaces/{project}/`
|
||||
|
||||
#### `/gsd:use`
|
||||
- **上下文追踪**: OpenClaw 在用户级别追踪当前项目(存入独立用户状态文件)
|
||||
- **后续命令路由**: 后续命令根据追踪的上下文选择目标 tmux 会话
|
||||
- **项目不存在处理**: 检查项目是否存在,不存在则返回错误卡片 + 「创建新项目」按钮
|
||||
|
||||
### 项目上下文追踪
|
||||
|
||||
#### 存储设计
|
||||
- **位置**: `~/gsd-workspaces/.users/{user_id}/context.json`
|
||||
- **格式**: JSON,原子写入(与 Phase 1 state.json 一致)
|
||||
|
||||
#### 追踪数据
|
||||
- **当前项目**: 用户当前正在操作的项目名
|
||||
- **最近项目列表**: 最近使用的项目(保留最近 5 个)
|
||||
- **最后活跃时间**: 用户上次活动时间戳
|
||||
- **用户偏好**: 可选的个性化设置(默认超时、输出格式等)
|
||||
|
||||
#### 多用户场景
|
||||
- **隔离策略**: 完全隔离 — 每个用户独立的 tmux 会话和工作空间
|
||||
- **用户识别**: 通过 OpenClaw 的 user_id 区分
|
||||
|
||||
### 频道支持
|
||||
- **Phase 4 范围**: 仅飞书
|
||||
- **其他频道**: 微信、Telegram、Discord 延后到 v2
|
||||
|
||||
### 工作流连续性
|
||||
|
||||
#### 上下文保持
|
||||
- **机制**: tmux 持久会话
|
||||
- **Claude CLI 上下文**: 自然保持在 tmux 会话中
|
||||
- **用户重连**: 直接重连到现有 tmux 会话
|
||||
|
||||
#### 中途掉线恢复
|
||||
- **责任归属**: GSD 处理恢复(通过 STATE.md 追踪)
|
||||
- **OpenClaw 角色**: 只负责重新连接到 tmux 会话
|
||||
|
||||
#### 长命令超时
|
||||
- **普通命令**: 60 秒超时(Phase 3 已定义)
|
||||
- **长命令**: 无超时,依赖 GSD 自己的完成信号
|
||||
- **长命令列表**: `execute-phase`, `plan-phase`, `discuss-phase`, `new-project`
|
||||
- **进度更新**: 使用 Phase 3 的 ProgressMonitor 机制
|
||||
|
||||
### Claude's Discretion
|
||||
- 用户偏好的具体字段名和默认值
|
||||
- 长命令检测的具体逻辑(命令名匹配 vs 运行时检测)
|
||||
- 最近项目列表的保留数量(建议 5 个)
|
||||
|
||||
</decisions>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- 用户状态文件路径与 Phase 1 的 state.json 保持一致的原子写入模式
|
||||
- 「创建新项目」按钮样式与 Phase 2 的卡片按钮一致
|
||||
- 长命令识别通过命令名前缀匹配(execute-, plan-, discuss-, new-project)
|
||||
|
||||
</specifics>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- **Phase 1 TmuxSessionManager**: 会话创建、恢复、销毁 — `/gsd:new-project` 预创建会话的基础
|
||||
- **Phase 1 StateManager**: JSON 状态管理模式 — 用户上下文存储可直接复用原子写入模式
|
||||
- **Phase 1 WorkspaceManager**: 工作空间目录创建 — `/gsd:new-project` 需要调用
|
||||
- **Phase 2 FeishuResponseFormatter**: 卡片格式化 — format_success/format_error/format_info
|
||||
- **Phase 3 Orchestrator**: 命令路由框架 — 需要扩展添加特殊命令处理分支
|
||||
- **Phase 3 ApprovalHandler**: 按钮回调处理 — 「创建新项目」按钮可复用此机制
|
||||
- **Phase 3 ProgressMonitor**: 进度更新 — 长命令的进度反馈
|
||||
|
||||
### Established Patterns
|
||||
- **状态持久化**: JSON 格式,原子写入(tempfile + rename)
|
||||
- **错误处理**: 中文错误消息 + 异常类
|
||||
- **卡片布局**: 三区结构(标题区 + 内容区 + 操作区)
|
||||
- **命名规范**: gsd- 前缀统一
|
||||
|
||||
### Integration Points
|
||||
- `UserContextManager` (新增): 用户上下文追踪,被 Orchestrator 调用
|
||||
- `Orchestrator.handle_message()`: 需要扩展特殊命令处理逻辑
|
||||
- `Orchestrator.parse_command()`: 已有的命令解析可复用
|
||||
- Phase 4 完成后,BRDG-06 需求将满足
|
||||
|
||||
</code_context>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
- 微信频道支持 — v2 特性
|
||||
- Telegram/Discord 频道支持 — v2 特性
|
||||
- 多语言支持(中文/英文切换)
|
||||
- 项目共享(多用户协作同一项目)
|
||||
- 命令历史记录和搜索
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 04-gsd-integration*
|
||||
*Context gathered: 2026-03-11*
|
||||
@@ -0,0 +1,540 @@
|
||||
# Phase 4: GSD Integration - Research
|
||||
|
||||
**Researched:** 2026-03-11
|
||||
**Domain:** GSD command bridging, user context tracking, special command handling
|
||||
**Confidence:** HIGH
|
||||
|
||||
## Summary
|
||||
|
||||
Phase 4 completes the GSD command coverage by implementing special handling for key commands (`/gsd:new-project` and `/gsd:use`) while ensuring all 34 GSD slash commands work through pass-through mode. The primary technical challenges are:
|
||||
|
||||
1. **Special Command Handling**: `/gsd:new-project` requires pre-creating the tmux session and workspace before passing the command to GSD. `/gsd:use` requires tracking user context across sessions.
|
||||
|
||||
2. **User Context Tracking**: A new `UserContextManager` class is needed to track which project each user is currently working on, stored in independent user state files.
|
||||
|
||||
3. **Long Command Support**: Certain commands (execute-phase, plan-phase, discuss-phase, new-project) have no timeout and rely on GSD's completion signal with progress updates.
|
||||
|
||||
**Primary recommendation:** Extend the existing `Orchestrator` class with special command detection logic, add a new `UserContextManager` class following Phase 1's atomic write patterns, and leverage existing Phase 3 components (ProgressMonitor, ApprovalHandler) for long-running operations.
|
||||
|
||||
<user_constraints>
|
||||
## User Constraints (from CONTEXT.md)
|
||||
|
||||
### Locked Decisions
|
||||
|
||||
#### 命令清单处理策略
|
||||
- **处理模式**: 关键命令特殊处理,其余泛型透传
|
||||
- **关键命令定义**: `/gsd:new-project` 和 `/gsd:use` 需要 orchestrator 特殊逻辑
|
||||
- **泛型命令**: 其他 32 个命令统一通过 tmux 透传 + 卡片格式化
|
||||
|
||||
#### 关键命令特殊处理
|
||||
|
||||
##### `/gsd:new-project`
|
||||
- **预创建会话**: orchestrator 检测命令后,先调用 TmuxSessionManager 创建会话和工作空间
|
||||
- **命令透传**: 会话创建后,透传命令到 tmux 让 GSD 初始化项目结构
|
||||
- **工作空间路径**: `~/gsd-workspaces/{project}/`
|
||||
|
||||
##### `/gsd:use`
|
||||
- **上下文追踪**: OpenClaw 在用户级别追踪当前项目(存入独立用户状态文件)
|
||||
- **后续命令路由**: 后续命令根据追踪的上下文选择目标 tmux 会话
|
||||
- **项目不存在处理**: 检查项目是否存在,不存在则返回错误卡片 + 「创建新项目」按钮
|
||||
|
||||
#### 项目上下文追踪
|
||||
|
||||
##### 存储设计
|
||||
- **位置**: `~/gsd-workspaces/.users/{user_id}/context.json`
|
||||
- **格式**: JSON,原子写入(与 Phase 1 state.json 一致)
|
||||
|
||||
##### 追踪数据
|
||||
- **当前项目**: 用户当前正在操作的项目名
|
||||
- **最近项目列表**: 最近使用的项目(保留最近 5 个)
|
||||
- **最后活跃时间**: 用户上次活动时间戳
|
||||
- **用户偏好**: 可选的个性化设置(默认超时、输出格式等)
|
||||
|
||||
#### 多用户场景
|
||||
- **隔离策略**: 完全隔离 — 每个用户独立的 tmux 会话和工作空间
|
||||
- **用户识别**: 通过 OpenClaw 的 user_id 区分
|
||||
|
||||
#### 频道支持
|
||||
- **Phase 4 范围**: 仅飞书
|
||||
- **其他频道**: 微信、Telegram、Discord 延后到 v2
|
||||
|
||||
#### 工作流连续性
|
||||
|
||||
##### 上下文保持
|
||||
- **机制**: tmux 持久会话
|
||||
- **Claude CLI 上下文**: 自然保持在 tmux 会话中
|
||||
- **用户重连**: 直接重连到现有 tmux 会话
|
||||
|
||||
##### 中途掉线恢复
|
||||
- **责任归属**: GSD 处理恢复(通过 STATE.md 追踪)
|
||||
- **OpenClaw 角色**: 只负责重新连接到 tmux 会话
|
||||
|
||||
##### 长命令超时
|
||||
- **普通命令**: 60 秒超时(Phase 3 已定义)
|
||||
- **长命令**: 无超时,依赖 GSD 自己的完成信号
|
||||
- **长命令列表**: `execute-phase`, `plan-phase`, `discuss-phase`, `new-project`
|
||||
- **进度更新**: 使用 Phase 3 的 ProgressMonitor 机制
|
||||
|
||||
### Claude's Discretion
|
||||
- 用户偏好的具体字段名和默认值
|
||||
- 长命令检测的具体逻辑(命令名匹配 vs 运行时检测)
|
||||
- 最近项目列表的保留数量(建议 5 个)
|
||||
|
||||
### Deferred Ideas (OUT OF SCOPE)
|
||||
- 微信频道支持 — v2 特性
|
||||
- Telegram/Discord 频道支持 — v2 特性
|
||||
- 多语言支持(中文/英文切换)
|
||||
- 项目共享(多用户协作同一项目)
|
||||
- 命令历史记录和搜索
|
||||
|
||||
</user_constraints>
|
||||
|
||||
<phase_requirements>
|
||||
## Phase Requirements
|
||||
|
||||
| ID | Description | Research Support |
|
||||
|----|-------------|-----------------|
|
||||
| BRDG-06 | All GSD slash commands are supported (no command left behind) | GSD has 34 commands across 5 categories (Core Workflow, Navigation, Brownfield, Phase Management, Session, Utilities). Current Orchestrator supports generic pass-through via regex parsing. Special handling needed for `new-project` and `use`. |
|
||||
</phase_requirements>
|
||||
|
||||
## Standard Stack
|
||||
|
||||
### Core
|
||||
| Library | Version | Purpose | Why Standard |
|
||||
|---------|---------|---------|--------------|
|
||||
| libtmux | 0.37.0+ | tmux session management | Already used in Phase 1 TmuxSessionManager |
|
||||
| Python dataclasses | 3.12+ | Data structures | Established pattern in existing codebase |
|
||||
| json (stdlib) | 3.12+ | State persistence | Already used in Phase 1 StateManager |
|
||||
| tempfile (stdlib) | 3.12+ | Atomic writes | Established pattern from Phase 1 |
|
||||
| re (stdlib) | 3.12+ | Command parsing | Already used in Orchestrator |
|
||||
|
||||
### Supporting
|
||||
| Library | Version | Purpose | When to Use |
|
||||
|---------|---------|---------|-------------|
|
||||
| pytest | 8.0+ | Testing | All new components |
|
||||
| unittest.mock | 3.12+ | Mocking tmux | Unit tests for UserContextManager |
|
||||
|
||||
### Alternatives Considered
|
||||
| Instead of | Could Use | Tradeoff |
|
||||
|------------|-----------|----------|
|
||||
| Custom UserContextManager | SQLite for user state | SQLite adds complexity; JSON is simpler and matches Phase 1 pattern |
|
||||
| Command name prefix matching | Runtime detection of long commands | Prefix matching is simpler and more predictable; runtime detection would require analyzing output patterns |
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
# No new dependencies required - all existing stack
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Recommended Project Structure
|
||||
```
|
||||
src/gsd_openclaw/
|
||||
├── user_context.py # NEW: UserContextManager class
|
||||
├── orchestrator.py # EXTEND: Add special command handling
|
||||
├── tmux_session.py # REUSE: Session creation for new-project
|
||||
├── command_executor.py # EXTEND: Add long command detection
|
||||
├── progress_monitor.py # REUSE: Progress updates for long commands
|
||||
├── approval_handler.py # REUSE: Button callbacks for "create project"
|
||||
├── response_formatter.py # REUSE: Card formatting
|
||||
└── state_manager.py # REUSE: Atomic write pattern reference
|
||||
|
||||
tests/
|
||||
├── test_user_context.py # NEW: UserContextManager tests
|
||||
├── test_orchestrator.py # EXTEND: Add special command tests
|
||||
└── conftest.py # EXTEND: Add user context fixtures
|
||||
```
|
||||
|
||||
### Pattern 1: UserContextManager Class
|
||||
**What:** Manages per-user project context with atomic persistence
|
||||
**When to use:** Tracking which project a user is working on
|
||||
**Example:**
|
||||
```python
|
||||
# Source: Based on Phase 1 StateManager pattern
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Optional, List
|
||||
import json
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
@dataclass
|
||||
class UserContext:
|
||||
"""User's project context state."""
|
||||
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)
|
||||
|
||||
class UserContextManager:
|
||||
"""Manages user context with atomic persistence."""
|
||||
|
||||
USERS_ROOT = Path.home() / "gsd-workspaces" / ".users"
|
||||
|
||||
def __init__(self, user_id: str, users_root: Optional[Path] = None):
|
||||
self.user_id = user_id
|
||||
self.users_root = Path(users_root) if users_root else self.USERS_ROOT
|
||||
self.context_path = self.users_root / user_id / "context.json"
|
||||
|
||||
def load(self) -> Optional[UserContext]:
|
||||
"""Load user context with backup fallback."""
|
||||
# Same pattern as StateManager.load()
|
||||
...
|
||||
|
||||
def save(self, context: UserContext) -> None:
|
||||
"""Save user context atomically with backup rotation."""
|
||||
# Same pattern as StateManager.save_state()
|
||||
# 1. Create parent dirs
|
||||
# 2. Rotate backup
|
||||
# 3. Write to temp file
|
||||
# 4. Atomic replace
|
||||
...
|
||||
```
|
||||
|
||||
### Pattern 2: Special Command Detection in Orchestrator
|
||||
**What:** Route specific commands to special handlers, pass others through
|
||||
**When to use:** Processing incoming /gsd:* commands
|
||||
**Example:**
|
||||
```python
|
||||
# Source: Extension of existing Orchestrator.handle_message()
|
||||
class Orchestrator:
|
||||
# Long commands that need no timeout
|
||||
LONG_COMMANDS = {'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'}
|
||||
|
||||
def handle_message(self, user_id: str, message: str, channel: str, context: Dict) -> Dict:
|
||||
parsed = self.parse_command(message)
|
||||
if parsed is None:
|
||||
return self.formatter.format_info(title="无效命令", content="...")
|
||||
|
||||
command, args = parsed
|
||||
|
||||
# Special handling for new-project
|
||||
if command == 'new-project':
|
||||
return self._handle_new_project(user_id, args)
|
||||
|
||||
# Special handling for use
|
||||
if command == 'use':
|
||||
return self._handle_use_project(user_id, args)
|
||||
|
||||
# Generic pass-through for all other commands
|
||||
return self._handle_generic_command(user_id, command, args)
|
||||
|
||||
def _handle_new_project(self, user_id: str, project_name: str) -> Dict:
|
||||
"""Pre-create session then pass command to GSD."""
|
||||
# 1. Create tmux session via TmuxSessionManager
|
||||
session = self.tmux_manager.create_session(project_name)
|
||||
# 2. Execute /gsd:new-project in the session
|
||||
result = self.executor.execute(session, f"/gsd:new-project {project_name}", timeout=None)
|
||||
# 3. Update user context
|
||||
self.user_context_manager.set_current_project(user_id, project_name)
|
||||
# 4. Return formatted result
|
||||
...
|
||||
|
||||
def _handle_use_project(self, user_id: str, project_name: str) -> Dict:
|
||||
"""Switch user's current project context."""
|
||||
# 1. Check if project exists
|
||||
session = self.tmux_manager.get_session(project_name)
|
||||
if session is None:
|
||||
# Return error card with "Create New Project" button
|
||||
return self.formatter.format_error(
|
||||
title="项目不存在",
|
||||
content=f"项目 '{project_name}' 不存在",
|
||||
buttons=[CardButton(text="创建新项目", value={"action": "new-project", "name": project_name})]
|
||||
)
|
||||
# 2. Update user context
|
||||
self.user_context_manager.set_current_project(user_id, project_name)
|
||||
# 3. Return success
|
||||
...
|
||||
|
||||
def _is_long_command(self, command: str) -> bool:
|
||||
"""Check if command requires no timeout."""
|
||||
return command in self.LONG_COMMANDS
|
||||
```
|
||||
|
||||
### Pattern 3: Long Command Execution
|
||||
**What:** Execute commands without timeout, use progress monitoring
|
||||
**When to use:** Commands that take minutes to complete
|
||||
**Example:**
|
||||
```python
|
||||
# Source: Extension of CommandExecutor.execute()
|
||||
def execute_with_progress(
|
||||
self,
|
||||
session,
|
||||
command: str,
|
||||
progress_callback: Callable[[ProgressInfo], None],
|
||||
poll_interval: float = 5.0 # Check every 5 seconds for long commands
|
||||
) -> CommandResult:
|
||||
"""Execute long command with progress updates."""
|
||||
# 1. Send command
|
||||
self.send_command(session.tmux_session.active_pane, command)
|
||||
|
||||
# 2. Poll for output and progress
|
||||
while True:
|
||||
time.sleep(poll_interval)
|
||||
output = self._capture_current_output(session.tmux_session.active_pane)
|
||||
|
||||
# Check for progress indicators
|
||||
progress = self.progress_monitor.extract_progress(output)
|
||||
if progress and self.progress_monitor.should_send_update():
|
||||
progress_callback(progress)
|
||||
|
||||
# Check for completion (prompt or stabilization)
|
||||
if self._is_complete(output):
|
||||
return CommandResult(output=output, completed=True, ...)
|
||||
```
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
- **Don't block on long commands:** Use async polling, not synchronous waits
|
||||
- **Don't mix user contexts:** Each user has isolated context, never share between users
|
||||
- **Don't timeout long commands:** execute-phase, plan-phase, discuss-phase, new-project have no timeout
|
||||
- **Don't duplicate state logic:** Reuse Phase 1's atomic write pattern for UserContextManager
|
||||
|
||||
## Don't Hand-Roll
|
||||
|
||||
| Problem | Don't Build | Use Instead | Why |
|
||||
|---------|-------------|-------------|-----|
|
||||
| User state persistence | Custom file locking | StateManager atomic write pattern | Crash-safe, tested pattern from Phase 1 |
|
||||
| Progress detection | Custom regex | ProgressMonitor.extract_progress() | Already handles percentage, fraction, phase patterns |
|
||||
| Approval cards | New button format | ApprovalHandler.create_approval_card() | Existing pattern for interactive buttons |
|
||||
| Session creation | Manual tmux commands | TmuxSessionManager.create_session() | Handles collision resolution, state persistence |
|
||||
|
||||
**Key insight:** Phase 4 is primarily about extending existing patterns, not creating new ones. The UserContextManager follows StateManager patterns exactly, special commands extend Orchestrator's routing, and long commands extend CommandExecutor.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: Race Condition in Session Creation
|
||||
**What goes wrong:** Two users try to create project with same name simultaneously
|
||||
**Why it happens:** No locking on session creation
|
||||
**How to avoid:** TmuxSessionManager._resolve_collision() already handles this with timestamp suffix
|
||||
**Warning signs:** Session names with unexpected suffixes, duplicate state files
|
||||
|
||||
### Pitfall 2: User Context File Corruption
|
||||
**What goes wrong:** Server crash during write leaves corrupted context.json
|
||||
**Why it happens:** Not using atomic writes
|
||||
**How to avoid:** Follow StateManager pattern exactly: tempfile + os.replace
|
||||
**Warning signs:** JSONDecodeError when loading context, missing user preferences
|
||||
|
||||
### Pitfall 3: Long Command Timeout
|
||||
**What goes wrong:** execute-phase times out after 60 seconds
|
||||
**Why it happens:** Using default timeout for all commands
|
||||
**How to avoid:** Check command name against LONG_COMMANDS set, pass timeout=None
|
||||
**Warning signs:** Commands return "timeout" status, incomplete phases
|
||||
|
||||
### Pitfall 4: Missing User Context on First Use
|
||||
**What goes wrong:** New user sends command without context
|
||||
**Why it happens:** UserContextManager.load() returns None for new users
|
||||
**How to avoid:** Create default context on first access, use "default" project
|
||||
**Warning signs:** KeyError on user_id, missing project routing
|
||||
|
||||
## Code Examples
|
||||
|
||||
Verified patterns from existing codebase:
|
||||
|
||||
### Atomic Write Pattern (from state_manager.py)
|
||||
```python
|
||||
# Source: /home/starmen1265/work/kb-skills/gsd-openclaw/src/gsd_openclaw/state_manager.py
|
||||
def save_state(state: SessionState, state_path: Path) -> None:
|
||||
"""Save session state to file atomically with backup rotation."""
|
||||
state_path = Path(state_path)
|
||||
backup_path = state_path.with_suffix('.json.bak')
|
||||
|
||||
# 1. Create parent directories if missing
|
||||
state_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 2. Rotate backup
|
||||
if state_path.exists():
|
||||
if backup_path.exists():
|
||||
backup_path.unlink()
|
||||
state_path.rename(backup_path)
|
||||
|
||||
# 3. Write to temp file in same directory
|
||||
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.to_dict(), f, indent=2, ensure_ascii=False)
|
||||
# 4. Atomic replace
|
||||
os.replace(temp_path, state_path)
|
||||
except Exception:
|
||||
if os.path.exists(temp_path):
|
||||
os.unlink(temp_path)
|
||||
raise
|
||||
```
|
||||
|
||||
### Command Parsing Pattern (from orchestrator.py)
|
||||
```python
|
||||
# Source: /home/starmen1265/work/kb-skills/gsd-openclaw/src/gsd_openclaw/orchestrator.py
|
||||
class Orchestrator:
|
||||
COMMAND_PATTERN = re.compile(r'^/gsd:(\S+)\s*(.*)$')
|
||||
|
||||
def parse_command(self, message: str) -> Optional[Tuple[str, str]]:
|
||||
"""Parse a message for /gsd:* command pattern."""
|
||||
match = self.COMMAND_PATTERN.match(message.strip())
|
||||
if match:
|
||||
command = match.group(1)
|
||||
args = match.group(2).strip()
|
||||
return (command, args)
|
||||
return None
|
||||
```
|
||||
|
||||
### Progress Detection Pattern (from progress_monitor.py)
|
||||
```python
|
||||
# Source: /home/starmen1265/work/kb-skills/gsd-openclaw/src/gsd_openclaw/progress_monitor.py
|
||||
class ProgressMonitor:
|
||||
PROGRESS_PATTERNS = [
|
||||
(re.compile(r'(\d+)%'), 'percentage'),
|
||||
(re.compile(r'\[(\d+)/(\d+)(?:[^\]]*)\]'), 'fraction'),
|
||||
(re.compile(r'Phase\s+(\d+)', re.IGNORECASE), 'phase'),
|
||||
(re.compile(r'完成|completed|done', re.IGNORECASE), 'completion'),
|
||||
]
|
||||
|
||||
def extract_progress(self, output: str) -> Optional[ProgressInfo]:
|
||||
"""Extract progress information from output text."""
|
||||
for pattern, progress_type in self.PROGRESS_PATTERNS:
|
||||
match = pattern.search(output)
|
||||
if match:
|
||||
# Return ProgressInfo based on type
|
||||
...
|
||||
```
|
||||
|
||||
## State of the Art
|
||||
|
||||
| Old Approach | Current Approach | When Changed | Impact |
|
||||
|--------------|------------------|--------------|--------|
|
||||
| Per-session context | Per-user context (Phase 4) | This phase | Users can switch projects without losing context |
|
||||
| Fixed 60s timeout | Adaptive timeout based on command | This phase | Long commands no longer timeout prematurely |
|
||||
| Generic command handling | Special handling for key commands | This phase | new-project pre-creates session, use tracks context |
|
||||
|
||||
**Deprecated/outdated:**
|
||||
- None in this phase (building on existing patterns)
|
||||
|
||||
## GSD Command Categories (34 Total)
|
||||
|
||||
Based on official GSD documentation (https://github.com/gsd-build/get-shit-done):
|
||||
|
||||
### Core Workflow (8 commands)
|
||||
| Command | Description | Special Handling |
|
||||
|---------|-------------|------------------|
|
||||
| `/gsd:new-project` | Initialize project with questions, research, requirements, roadmap | YES - Pre-create session |
|
||||
| `/gsd:discuss-phase` | Capture implementation decisions | Long command |
|
||||
| `/gsd:plan-phase` | Research + plan + verify | Long command |
|
||||
| `/gsd:execute-phase` | Execute plans in parallel waves | Long command |
|
||||
| `/gsd:verify-work` | Manual user acceptance testing | Pass-through |
|
||||
| `/gsd:audit-milestone` | Verify milestone completion | Pass-through |
|
||||
| `/gsd:complete-milestone` | Archive milestone, tag release | Pass-through |
|
||||
| `/gsd:new-milestone` | Start next version | Pass-through |
|
||||
|
||||
### Navigation (4 commands)
|
||||
| Command | Description | Special Handling |
|
||||
|---------|-------------|------------------|
|
||||
| `/gsd:progress` | Show current position | Pass-through |
|
||||
| `/gsd:help` | Show all commands | Pass-through |
|
||||
| `/gsd:update` | Update GSD | Pass-through |
|
||||
| `/gsd:join-discord` | Join community | Pass-through |
|
||||
|
||||
### Brownfield (1 command)
|
||||
| Command | Description | Special Handling |
|
||||
|---------|-------------|------------------|
|
||||
| `/gsd:map-codebase` | Analyze existing codebase | Pass-through |
|
||||
|
||||
### Phase Management (5 commands)
|
||||
| Command | Description | Special Handling |
|
||||
|---------|-------------|------------------|
|
||||
| `/gsd:add-phase` | Append phase to roadmap | Pass-through |
|
||||
| `/gsd:insert-phase` | Insert urgent work | Pass-through |
|
||||
| `/gsd:remove-phase` | Remove future phase | Pass-through |
|
||||
| `/gsd:list-phase-assumptions` | See Claude's approach | Pass-through |
|
||||
| `/gsd:plan-milestone-gaps` | Create phases for gaps | Pass-through |
|
||||
|
||||
### Session (2 commands)
|
||||
| Command | Description | Special Handling |
|
||||
|---------|-------------|------------------|
|
||||
| `/gsd:pause-work` | Create handoff | Pass-through |
|
||||
| `/gsd:resume-work` | Restore from session | Pass-through |
|
||||
|
||||
### Utilities (9 commands)
|
||||
| Command | Description | Special Handling |
|
||||
|---------|-------------|------------------|
|
||||
| `/gsd:use` | Switch project context | YES - Track user context |
|
||||
| `/gsd:settings` | Configure GSD | Pass-through |
|
||||
| `/gsd:set-profile` | Switch model profile | Pass-through |
|
||||
| `/gsd:add-todo` | Capture idea | Pass-through |
|
||||
| `/gsd:check-todos` | List pending todos | Pass-through |
|
||||
| `/gsd:debug` | Systematic debugging | Pass-through |
|
||||
| `/gsd:quick` | Execute ad-hoc task | Pass-through |
|
||||
| `/gsd:health` | Validate .planning/ | Pass-through |
|
||||
|
||||
**Note:** 2 commands need special handling (new-project, use), 4 are long commands (new-project, execute-phase, plan-phase, discuss-phase), all others pass through generically.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **User Preferences Schema**
|
||||
- What we know: CONTEXT.md mentions preferences field (default timeout, output format)
|
||||
- What's unclear: Exact field names and default values
|
||||
- Recommendation: Start minimal with empty dict, add fields as needed
|
||||
|
||||
2. **Long Command Detection Implementation**
|
||||
- What we know: Command name matching is the approach (from CONTEXT.md)
|
||||
- What's unclear: Whether to also detect long-running at runtime
|
||||
- Recommendation: Start with command name matching, add runtime detection if needed
|
||||
|
||||
3. **Recent Projects List Size**
|
||||
- What we know: CONTEXT.md suggests 5 items
|
||||
- What's unclear: Whether this should be configurable
|
||||
- Recommendation: Hardcode 5, make configurable later if requested
|
||||
|
||||
## Validation Architecture
|
||||
|
||||
### Test Framework
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Framework | pytest 8.0+ |
|
||||
| Config file | pyproject.toml (existing) |
|
||||
| Quick run command | `pytest tests/test_user_context.py tests/test_orchestrator.py -x -v` |
|
||||
| Full suite command | `pytest -x` |
|
||||
|
||||
### Phase Requirements → Test Map
|
||||
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|
||||
|--------|----------|-----------|-------------------|-------------|
|
||||
| BRDG-06 | All 34 GSD commands supported | unit | `pytest tests/test_orchestrator.py::TestGsdCommands -x -v` | Wave 0 (new) |
|
||||
| BRDG-06 | new-project pre-creates session | unit | `pytest tests/test_orchestrator.py::TestSpecialCommands::test_new_project_creates_session -v` | Wave 0 (new) |
|
||||
| BRDG-06 | use tracks user context | unit | `pytest tests/test_user_context.py::TestUserContextManager::test_set_current_project -v` | Wave 0 (new) |
|
||||
| BRDG-06 | use shows error for non-existent project | unit | `pytest tests/test_orchestrator.py::TestSpecialCommands::test_use_nonexistent_project -v` | Wave 0 (new) |
|
||||
| BRDG-06 | long commands have no timeout | unit | `pytest tests/test_command_executor.py::TestLongCommands -v` | Wave 0 (extend) |
|
||||
| BRDG-06 | user context persists atomically | unit | `pytest tests/test_user_context.py::TestAtomicWrites -v` | Wave 0 (new) |
|
||||
| BRDG-06 | user context isolated between users | unit | `pytest tests/test_user_context.py::TestMultiUser -v` | Wave 0 (new) |
|
||||
|
||||
### Sampling Rate
|
||||
- **Per task commit:** `pytest tests/test_user_context.py tests/test_orchestrator.py -x -v`
|
||||
- **Per wave merge:** `pytest -x`
|
||||
- **Phase gate:** Full suite green before `/gsd:verify-work`
|
||||
|
||||
### Wave 0 Gaps
|
||||
- [ ] `tests/test_user_context.py` — new file for UserContextManager tests
|
||||
- [ ] `tests/test_orchestrator.py` — extend with TestSpecialCommands class
|
||||
- [ ] `tests/conftest.py` — add user_context fixture
|
||||
- [ ] Extend `tests/test_command_executor.py` — add long command tests
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- GSD Official Repository: https://github.com/gsd-build/get-shit-done - Complete command list and documentation
|
||||
- Existing codebase: `/home/starmen1265/work/kb-skills/gsd-openclaw/src/gsd_openclaw/` - Established patterns for state management, command execution, response formatting
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- Phase 1 research: `.planning/phases/01-core-infrastructure/01-RESEARCH.md` - StateManager atomic write pattern
|
||||
- Phase 3 research: `.planning/phases/03-orchestration/03-RESEARCH.md` - Orchestrator command routing
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- None - all findings verified against codebase or official docs
|
||||
|
||||
## Metadata
|
||||
|
||||
**Confidence breakdown:**
|
||||
- Standard stack: HIGH - All libraries already in use
|
||||
- Architecture: HIGH - Patterns established in Phases 1-3
|
||||
- Pitfalls: HIGH - Based on analysis of existing code patterns
|
||||
|
||||
**Research date:** 2026-03-11
|
||||
**Valid until:** 30 days - stable patterns, unlikely to change significantly
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
phase: 04
|
||||
slug: gsd-integration
|
||||
status: draft
|
||||
nyquist_compliant: false
|
||||
wave_0_complete: false
|
||||
created: 2026-03-11
|
||||
---
|
||||
|
||||
# Phase 4 — Validation Strategy
|
||||
|
||||
> Per-phase validation contract for feedback sampling during execution.
|
||||
|
||||
---
|
||||
|
||||
## Test Infrastructure
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Framework** | pytest 8.0+ |
|
||||
| **Config file** | pyproject.toml (existing) |
|
||||
| **Quick run command** | `pytest tests/test_user_context.py tests/test_orchestrator.py -x -v` |
|
||||
| **Full suite command** | `pytest -x` |
|
||||
| **Estimated runtime** | ~15 seconds |
|
||||
|
||||
---
|
||||
|
||||
## Sampling Rate
|
||||
|
||||
- **After every task commit:** Run `pytest tests/test_user_context.py tests/test_orchestrator.py -x -v`
|
||||
- **After every plan wave:** Run `pytest -x`
|
||||
- **Before `/gsd:verify-work`:** Full suite must be green
|
||||
- **Max feedback latency:** 15 seconds
|
||||
|
||||
---
|
||||
|
||||
## Per-Task Verification Map
|
||||
|
||||
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|
||||
|---------|------|------|-------------|-----------|-------------------|-------------|--------|
|
||||
| 04-01-01 | 01 | 1 | BRDG-06 | unit | `pytest tests/test_user_context.py::TestUserContextManager -v` | ❌ W0 | ⬜ pending |
|
||||
| 04-01-02 | 01 | 1 | BRDG-06 | unit | `pytest tests/test_user_context.py::TestAtomicWrites -v` | ❌ W0 | ⬜ pending |
|
||||
| 04-01-03 | 01 | 1 | BRDG-06 | unit | `pytest tests/test_user_context.py::TestMultiUser -v` | ❌ W0 | ⬜ pending |
|
||||
| 04-02-01 | 02 | 1 | BRDG-06 | unit | `pytest tests/test_orchestrator.py::TestSpecialCommands::test_new_project -v` | ❌ W0 | ⬜ pending |
|
||||
| 04-02-02 | 02 | 1 | BRDG-06 | unit | `pytest tests/test_orchestrator.py::TestSpecialCommands::test_use_project -v` | ❌ W0 | ⬜ pending |
|
||||
| 04-02-03 | 02 | 1 | BRDG-06 | unit | `pytest tests/test_orchestrator.py::TestSpecialCommands::test_use_nonexistent -v` | ❌ W0 | ⬜ pending |
|
||||
| 04-03-01 | 03 | 2 | BRDG-06 | unit | `pytest tests/test_command_executor.py::TestLongCommands -v` | ✅ extend | ⬜ pending |
|
||||
|
||||
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
|
||||
|
||||
---
|
||||
|
||||
## Wave 0 Requirements
|
||||
|
||||
- [ ] `tests/test_user_context.py` — new file for UserContextManager tests (TestUserContextManager, TestAtomicWrites, TestMultiUser)
|
||||
- [ ] `tests/test_orchestrator.py` — extend with TestSpecialCommands class (test_new_project, test_use_project, test_use_nonexistent)
|
||||
- [ ] `tests/conftest.py` — add user_context fixture and temp_user_context fixture
|
||||
- [ ] `tests/test_command_executor.py` — extend with TestLongCommands class
|
||||
|
||||
---
|
||||
|
||||
## Manual-Only Verifications
|
||||
|
||||
| Behavior | Requirement | Why Manual | Test Instructions |
|
||||
|----------|-------------|------------|-------------------|
|
||||
| Full GSD workflow E2E | BRDG-06 | Requires live tmux + GSD installation | Run `/gsd:new-project test-project` through Feishu, verify session created and project initialized |
|
||||
|
||||
---
|
||||
|
||||
## Validation Sign-Off
|
||||
|
||||
- [ ] All tasks have `<automated>` verify or Wave 0 dependencies
|
||||
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
|
||||
- [ ] Wave 0 covers all MISSING references
|
||||
- [ ] No watch-mode flags
|
||||
- [ ] Feedback latency < 15s
|
||||
- [ ] `nyquist_compliant: true` set in frontmatter
|
||||
|
||||
**Approval:** pending
|
||||
@@ -0,0 +1,134 @@
|
||||
---
|
||||
phase: 04-gsd-integration
|
||||
verified: 2026-03-11T19:30:00Z
|
||||
status: passed
|
||||
score: 11/11 must-haves verified
|
||||
---
|
||||
|
||||
# Phase 4: GSD Integration Verification Report
|
||||
|
||||
**Phase Goal:** Bridge GSD commands from Feishu to tmux sessions - All GSD slash commands are fully bridged with complete workflow support
|
||||
**Verified:** 2026-03-11T19:30:00Z
|
||||
**Status:** passed
|
||||
**Re-verification:** No - initial verification
|
||||
|
||||
## Goal Achievement
|
||||
|
||||
### Observable Truths
|
||||
|
||||
| # | Truth | Status | Evidence |
|
||||
| --- | ----- | ------ | -------- |
|
||||
| 1 | User's project context persists across chat sessions | VERIFIED | UserContextManager with atomic writes to ~/gsd-workspaces/.users/{user_id}/context.json (lines 143-183 in user_context.py) |
|
||||
| 2 | User context is stored in isolated per-user files | VERIFIED | context_path property returns users_root / user_id / "context.json" (line 102 in user_context.py) |
|
||||
| 3 | User context writes are atomic and crash-safe | VERIFIED | tempfile.mkstemp + os.replace pattern (lines 173, 178 in user_context.py) |
|
||||
| 4 | Each user has independent project tracking | VERIFIED | TestMultiUser tests verify isolation (tests/test_user_context.py lines 262-307) |
|
||||
| 5 | User can send /gsd:new-project through chat and get a pre-created session | VERIFIED | _handle_new_project creates session before executing command (orchestrator.py lines 97-154) |
|
||||
| 6 | User can send /gsd:use <project> to switch their current project context | VERIFIED | _handle_use_project updates user context (orchestrator.py lines 156-208) |
|
||||
| 7 | User receives clear error when /gsd:use references non-existent project | VERIFIED | Returns error card with "Create New Project" button (orchestrator.py lines 191-202) |
|
||||
| 8 | Generic GSD commands pass through correctly | VERIFIED | handle_message routes to session execution for non-special commands (orchestrator.py lines 269-317) |
|
||||
| 9 | Long commands (execute-phase, plan-phase, discuss-phase, new-project) don't timeout | VERIFIED | LONG_COMMANDS set in both CommandExecutor and Orchestrator with adaptive timeout=None |
|
||||
| 10 | Regular commands still use 60 second default timeout | VERIFIED | timeout = None if self._is_long_command(command) else self.executor.DEFAULT_TIMEOUT (line 288) |
|
||||
| 11 | Create New Project button callback works from error card | VERIFIED | handle_callback routes action="new-project" to _handle_new_project (orchestrator.py lines 345-355) |
|
||||
|
||||
**Score:** 11/11 truths verified
|
||||
|
||||
### Required Artifacts
|
||||
|
||||
| Artifact | Expected | Status | Details |
|
||||
| -------- | -------- | ------ | ------- |
|
||||
| `src/gsd_openclaw/user_context.py` | UserContextManager class | VERIFIED | 250 lines, UserContext dataclass + UserContextManager with load/save/atomic writes |
|
||||
| `tests/test_user_context.py` | Tests for UserContextManager | VERIFIED | 20 tests across 4 test classes (TestUserContext, TestUserContextManager, TestAtomicWrites, TestMultiUser) |
|
||||
| `src/gsd_openclaw/orchestrator.py` | Extended Orchestrator | VERIFIED | _handle_new_project, _handle_use_project, LONG_COMMANDS, adaptive timeout, callback handling |
|
||||
| `tests/test_orchestrator.py` | Tests for special commands | VERIFIED | TestSpecialCommands (13 tests), TestAdaptiveTimeout (6 tests), TestHandleCallback (7 tests) |
|
||||
| `src/gsd_openclaw/command_executor.py` | Long command support | VERIFIED | LONG_COMMANDS set, _is_long_command method |
|
||||
| `tests/test_command_executor.py` | Tests for long commands | VERIFIED | TestLongCommands class (5 tests) |
|
||||
| `src/gsd_openclaw/__init__.py` | Exports UserContext, UserContextManager | VERIFIED | Lines 17, 37-38 |
|
||||
|
||||
### Key Link Verification
|
||||
|
||||
| From | To | Via | Status | Details |
|
||||
| ---- | -- | --- | ------ | ------- |
|
||||
| orchestrator.py | user_context.py | UserContextManager | WIRED | Line 23 import, Line 70 dict, Lines 73-84 _get_user_context_manager |
|
||||
| orchestrator.py | tmux_session.py | create_session for new-project | WIRED | Line 18 import, Line 121 create_session call |
|
||||
| orchestrator.py | response_formatter.py | CardButton for error cards | WIRED | Line 22 import, Lines 193-197 CardButton instantiation |
|
||||
| command_executor.py | LONG_COMMANDS | _is_long_command detection | WIRED | Lines 156, 202-219 |
|
||||
| orchestrator.py | command_executor.py | execute() with adaptive timeout | WIRED | Line 288 timeout calculation, Line 291 executor.execute call |
|
||||
| user_context.py | filesystem | tempfile.mkstemp + os.replace | WIRED | Lines 173, 178 atomic write pattern |
|
||||
|
||||
### Requirements Coverage
|
||||
|
||||
| Requirement | Source Plan | Description | Status | Evidence |
|
||||
| ----------- | ----------- | ----------- | ------ | -------- |
|
||||
| BRDG-06 | 04-01, 04-02, 04-03 | All GSD slash commands are supported | SATISFIED | UserContextManager for context tracking, /gsd:new-project pre-creates session, /gsd:use switches context, generic commands pass through, long commands have no timeout, button callbacks work |
|
||||
|
||||
### Anti-Patterns Found
|
||||
|
||||
| File | Line | Pattern | Severity | Impact |
|
||||
| ---- | ---- | ------- | -------- | ------ |
|
||||
| None | - | - | - | No blocker or warning patterns found |
|
||||
|
||||
**Anti-pattern scan results:**
|
||||
- No TODO/FIXME/HACK/PLACEHOLDER comments in production code
|
||||
- No empty implementations (return null, return {})
|
||||
- The `return []` in user_context.py line 233 is correct behavior for new users with no recent projects
|
||||
- All handler methods have substantive implementations
|
||||
|
||||
### Human Verification Required
|
||||
|
||||
The following items need human testing to verify full workflow:
|
||||
|
||||
1. **End-to-end /gsd:new-project flow**
|
||||
- Test: Send /gsd:new-project test-project via Feishu chat
|
||||
- Expected: Project created, tmux session started, user context updated
|
||||
- Why human: Requires live Feishu integration and tmux environment
|
||||
|
||||
2. **End-to-end /gsd:use flow with error recovery**
|
||||
- Test: Send /gsd:use nonexistent-project, then click "Create New Project" button
|
||||
- Expected: Error card with button appears, clicking button creates project
|
||||
- Why human: Requires live Feishu card button callback handling
|
||||
|
||||
3. **Long command timeout behavior**
|
||||
- Test: Execute /gsd:execute-phase on a multi-phase project
|
||||
- Expected: Command runs without 60-second timeout
|
||||
- Why human: Requires real GSD CLI and long-running operation
|
||||
|
||||
4. **Multi-user context isolation**
|
||||
- Test: Two users send commands simultaneously from different Feishu accounts
|
||||
- Expected: Each user's context is independent, no cross-contamination
|
||||
- Why human: Requires multiple live Feishu sessions
|
||||
|
||||
### Test Results
|
||||
|
||||
All 90 automated tests pass:
|
||||
|
||||
```
|
||||
tests/test_user_context.py: 20 tests PASSED
|
||||
tests/test_orchestrator.py: 55 tests PASSED
|
||||
tests/test_command_executor.py: 15 tests PASSED
|
||||
```
|
||||
|
||||
Key test classes:
|
||||
- TestUserContext (3 tests) - UserContext dataclass
|
||||
- TestUserContextManager (4 tests) - Manager initialization and loading
|
||||
- TestAtomicWrites (6 tests) - Crash-safe persistence
|
||||
- TestMultiUser (7 tests) - User isolation
|
||||
- TestSpecialCommands (13 tests) - /gsd:new-project and /gsd:use handling
|
||||
- TestAdaptiveTimeout (6 tests) - Long command timeout logic
|
||||
- TestHandleCallback (7 tests) - Button callback routing
|
||||
- TestLongCommands (5 tests) - CommandExecutor long command detection
|
||||
|
||||
### Gaps Summary
|
||||
|
||||
**No gaps found.** All must-haves verified:
|
||||
- UserContextManager provides persistent per-user context with atomic writes
|
||||
- /gsd:new-project pre-creates session and updates context
|
||||
- /gsd:use switches context, shows error with "Create New Project" button
|
||||
- Generic commands use user context for project selection
|
||||
- Long commands execute without timeout
|
||||
- Button callbacks work for new-project action
|
||||
- All 90 tests pass
|
||||
|
||||
---
|
||||
|
||||
_Verified: 2026-03-11T19:30:00Z_
|
||||
_Verifier: Claude (gsd-verifier)_
|
||||
Reference in New Issue
Block a user