24 KiB
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:
-
Special Command Handling:
/gsd:new-projectrequires pre-creating the tmux session and workspace before passing the command to GSD./gsd:userequires tracking user context across sessions. -
User Context Tracking: A new
UserContextManagerclass is needed to track which project each user is currently working on, stored in independent user state files. -
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:
# 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:
# 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:
# 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:
# 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)
# 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)
# 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)
# 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
-
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
-
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
-
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 teststests/test_orchestrator.py— extend with TestSpecialCommands classtests/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