268 lines
9.1 KiB
Markdown
268 lines
9.1 KiB
Markdown
---
|
|
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>
|