docs(04-gsd-integration): create phase 4 plans for GSD command bridging

This commit is contained in:
2026-03-11 18:37:30 +08:00
parent caf205819f
commit 25ab3315da
4 changed files with 765 additions and 8 deletions
+13 -8
View File
@@ -14,7 +14,7 @@ Decimal phases appear between their surrounding integers in numeric order.
- [x] **Phase 1: Core Infrastructure** - State persistence and tmux session lifecycle management
- [x] **Phase 2: Integration Layer** - SKILL.md definition, response formatting, and prompt templates
- [ ] **Phase 3: Orchestration** - Command routing, user authentication, and bidirectional bridging
- [x] **Phase 3: Orchestration** - Command routing, user authentication, and bidirectional bridging
- [ ] **Phase 4: GSD Integration** - Full slash command bridging, approval flows, and progress reporting
## Phase Details
@@ -65,9 +65,9 @@ Plans:
**Plans**: 3 plans
Plans:
- [ ] 03-01-PLAN.md — Command executor with output capture and error detection (BRDG-02, BRDG-03)
- [ ] 03-02-PLAN.md — Approval handler and progress monitor (BRDG-04, BRDG-05)
- [ ] 03-03-PLAN.md — Orchestrator integration with command routing (BRDG-01, OCLW-03, OCLW-05, OCLW-06)
- [x] 03-01-PLAN.md — Command executor with output capture and error detection (BRDG-02, BRDG-03)
- [x] 03-02-PLAN.md — Approval handler and progress monitor (BRDG-04, BRDG-05)
- [x] 03-03-PLAN.md — Orchestrator integration with command routing (BRDG-01, OCLW-03, OCLW-05, OCLW-06)
### Phase 4: GSD Integration
**Goal**: All GSD slash commands are fully bridged with complete workflow support
@@ -76,7 +76,12 @@ Plans:
**Success Criteria** (what must be TRUE):
1. All GSD slash commands work through the bridge (no command left behind)
2. Complete GSD workflow is accessible from any supported chat channel
**Plans**: TBD
**Plans**: 3 plans
Plans:
- [ ] 04-01-PLAN.md — UserContextManager for per-user project context tracking (BRDG-06)
- [ ] 04-02-PLAN.md — Orchestrator special command handling for /gsd:new-project and /gsd:use (BRDG-06)
- [ ] 04-03-PLAN.md — Long command support and button callback for Create New Project (BRDG-06)
## Progress
@@ -87,9 +92,9 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4
|-------|----------------|--------|-----------|
| 1. Core Infrastructure | 4/4 | Complete | 2026-03-11 |
| 2. Integration Layer | 3/3 | Complete | 2026-03-11 |
| 3. Orchestration | 0/3 | Not started | - |
| 4. GSD Integration | 0/TBD | Not started | - |
| 3. Orchestration | 3/3 | Complete | 2026-03-11 |
| 4. GSD Integration | 0/3 | Not started | - |
---
*Roadmap created: 2026-03-10*
*Last updated: 2026-03-11 - Planned Phase 3 orchestration*
*Last updated: 2026-03-11 - Planned Phase 4 GSD Integration*
@@ -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,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,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>