# Architecture Patterns **Domain:** tmux-Claude CLI integration + OpenClaw skill bridge **Researched:** 2026-03-10 ## Recommended Architecture The gsd-openclaw skill acts as a bridge between OpenClaw's multi-channel messaging system and GSD's Claude CLI-based project management. The architecture follows a **session-orchestrator-bridge** pattern. ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ USER INTERFACES │ │ WhatsApp / Telegram / Slack / Discord / Feishu / WeChat / WebChat │ └─────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ OpenClaw Gateway │ │ (WebSocket Control Plane) │ │ ws://127.0.0.1:18789 │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Channel │ │ Session │ │ Skill │ │ │ │ Handlers │ │ Manager │ │ Registry │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ Skill Invocation (SKILL.md trigger) ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ gsd-openclaw Skill Layer │ │ ~/.openclaw/workspace/skills/gsd-openclaw/ │ │ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ SKILL.md │ │ │ │ - Trigger patterns: "/gsd:*", "gsd*", project management keywords │ │ │ │ - Routes to orchestrator.py with parsed intent │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ orchestrator.py │ │ │ │ - Command routing and validation │ │ │ │ - User/project authentication │ │ │ │ - State coordination │ │ │ │ - Response formatting for messaging channels │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ tmux_session_manager.py │ │ │ │ - Create/destroy tmux sessions per user-project │ │ │ │ - Send commands to Claude CLI │ │ │ │ - Capture output (sync/async) │ │ │ │ - Session health monitoring │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ state_manager.py │ │ │ │ - Persistent state storage (JSON) │ │ │ │ - Project workspace tracking │ │ │ │ - User session mapping │ │ │ │ - Conversation history (for context recovery) │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ libtmux / tmux commands ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ tmux Session Layer │ │ │ │ ┌─────────────────────┐ ┌─────────────────────┐ ┌────────────────────┐ │ │ │ tmux:gsd-user1-projA│ │ tmux:gsd-user1-projB│ │ tmux:gsd-user2-projC│ │ │ │ (Claude CLI) │ │ (Claude CLI) │ │ (Claude CLI) │ │ │ │ workspace: ~/projA │ │ workspace: ~/projB │ │ workspace: ~/projC│ │ │ └─────────────────────┘ └─────────────────────┘ └────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ │ │ Slash commands (/gsd:*) ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ GSD System │ │ ~/.claude/get-shit-done/ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ /gsd:new- │ │ /gsd:plan │ │ /gsd:ship │ │ /gsd:status │ │ │ │ project │ │ │ │ │ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ GSD Core │ │ │ │ - Project scaffolding (.planning/ structure) │ │ │ │ - Roadmap generation │ │ │ │ - Task management │ │ │ │ - Git integration │ │ │ └──────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### Component Boundaries | Component | Responsibility | Communicates With | |-----------|---------------|-------------------| | **SKILL.md** | Trigger pattern matching, skill description | OpenClaw Gateway | | **orchestrator.py** | Command routing, user auth, response formatting | state_manager, tmux_session_manager | | **tmux_session_manager.py** | tmux session lifecycle, I/O with Claude CLI | tmux (via libtmux/subprocess), orchestrator | | **state_manager.py** | Persistent state, workspace tracking, user mapping | orchestrator (reads/writes JSON) | | **prompts/** | LLM prompt templates for different scenarios | orchestrator (loads as needed) | ### Data Flow ``` 1. User sends message: "Create a new project for user authentication" │ ▼ 2. OpenClaw Gateway receives via channel (WhatsApp/Telegram/etc.) │ ▼ 3. Gateway matches trigger pattern → invokes gsd-openclaw skill │ ▼ 4. orchestrator.py: a. Extracts intent: "new-project" command b. Validates user authentication c. Checks state_manager for existing sessions │ ▼ 5. tmux_session_manager.py: a. Gets or creates tmux session for user+project b. Sends "/gsd:new-project user authentication" to Claude CLI │ ▼ 6. Claude CLI processes in tmux (persistent context): a. GSD system scaffolds project b. Generates initial planning files c. Outputs progress/response │ ▼ 7. tmux_session_manager captures output (async polling) │ ▼ 8. orchestrator formats response for channel: - Compact for mobile (WhatsApp/Telegram) - Rich for Slack/Discord │ ▼ 9. Response sent back through OpenClaw Gateway to user │ ▼ 10. state_manager updates: - Session last activity - Project state changes - Conversation checkpoint ``` ## Patterns to Follow ### Pattern 1: Session-per-Project Isolation **What:** Each user-project combination gets its own tmux session with isolated Claude context. **When:** Always, for any project-specific work. **Why:** Prevents context pollution between projects, enables parallel work. ```python # tmux_session_manager.py class TmuxSessionManager: def get_or_create_session(self, user_id: str, project_id: str) -> TmuxSession: session_name = f"gsd-{user_id}-{project_id}" # Check if session exists session = self.find_session(session_name) if session: return session # Create new session with project workspace workspace = self.state_manager.get_project_workspace(user_id, project_id) return self.create_session( name=session_name, working_dir=workspace, command="claude" # Start Claude CLI ) ``` ### Pattern 2: Async Response Handling **What:** Non-blocking output capture with callback-based response delivery. **When:** Any command that may take time (GSD operations can be slow). **Why:** Messaging channels expect timely responses; Claude operations may take minutes. ```python # orchestrator.py async def handle_command(self, user_id: str, command: str, context: dict): session = self.tmux_manager.get_or_create_session( user_id, context.get("project_id") ) # Send command session.send_keys(command) # Start async output capture output_future = self.tmux_manager.capture_output_async( session, timeout=300, # 5 min max idle_timeout=30 # 30s of no output = done ) # Return immediately with "processing" status # Callback will send response when complete return Response(status="processing", estimated_time="1-2 minutes") ``` ### Pattern 3: State Checkpointing **What:** Periodic state snapshots to enable recovery from interruption. **When:** After each significant GSD operation, every N minutes during long operations. **Why:** Network interruptions, server restarts, user timeouts should not lose work. ```python # state_manager.py def checkpoint_session(self, session_id: str, claude_output: str): state = self.load_state() state["sessions"][session_id]["last_checkpoint"] = { "timestamp": datetime.now().isoformat(), "claude_context_summary": self._extract_context_summary(claude_output), "project_phase": self._detect_current_phase(claude_output), "pending_actions": self._extract_pending_actions(claude_output) } self.save_state(state) ``` ### Pattern 4: Channel-Adaptive Formatting **What:** Same content rendered differently based on target channel. **When:** Every response going back to user. **Why:** WhatsApp has 4096 char limit, Discord supports markdown, mobile needs compactness. ```python # response_formatter.py def format_response(self, content: dict, channel: str) -> str: formatters = { "whatsapp": self._format_compact, "telegram": self._format_markdown_lite, "slack": self._format_rich_slack, "discord": self._format_rich_discord, "feishu": self._format_feishu_card } formatter = formatters.get(channel, self._format_generic) return formatter(content) ``` ## Anti-Patterns to Avoid ### Anti-Pattern 1: Shared Global tmux Session **What:** One tmux session for all users/projects. **Why bad:** Context mixing, race conditions, security issues (users see each other's data). **Instead:** Session-per-project isolation (Pattern 1). ### Anti-Pattern 2: Synchronous Blocking on Claude Output **What:** Wait for Claude to complete before returning any response. **Why bad:** Timeouts on messaging channels (often 30-60s), poor UX, gateway hangs. **Instead:** Async with status updates (Pattern 2). ### Anti-Pattern 3: State in Memory Only **What:** Keep all state in Python process memory. **Why bad:** Lost on restart, cannot scale horizontally, no recovery from crash. **Instead:** Persistent JSON state with checkpointing (Pattern 3). ### Anti-Pattern 4: Direct GSD Code Modification **What:** Modify GSD source to add OpenClaw-specific features. **Why bad:** Breaks GSD updates, creates maintenance burden, diverges from upstream. **Instead:** Pure bridge pattern - send slash commands, capture output, no GSD modifications. ### Anti-Pattern 5: Claude CLI in Non-Interactive Mode **What:** Use Claude CLI with `--no-interactive` or pipe-only input. **Why bad:** Loses conversational context, breaks GSD's interactive prompts, no approval flows. **Instead:** Full tmux session with pseudo-terminal. ## Scalability Considerations | Concern | At 1 user | At 10 users | At 100 users | |---------|-----------|-------------|--------------| | **tmux sessions** | 1-5 per user | 10-50 total | 100-500 total (consider session pooling/LRU eviction) | | **Memory** | ~100MB (Claude) | ~1GB (10x Claude) | ~10GB (need session limits, swap to disk) | | **State storage** | ~1MB JSON | ~10MB | ~100MB (consider SQLite migration) | | **Response latency** | Immediate | Slight queue | Need job queue, async workers | | **Channel limits** | N/A | Monitor rate limits | Implement rate limiting, batching | ## Directory Structure ``` ~/.openclaw/workspace/skills/gsd-openclaw/ ├── SKILL.md # Skill definition and triggers ├── orchestrator.py # Main orchestration logic ├── tmux_session_manager.py # tmux session lifecycle ├── state_manager.py # Persistent state management ├── response_formatter.py # Channel-adaptive formatting ├── prompts/ │ ├── context_recovery.md # Prompt for recovering session context │ ├── command_mapping.md # Natural language to GSD commands │ └── error_recovery.md # Prompt for handling errors └── state/ ├── sessions.json # Active sessions mapping ├── users.json # User project mappings └── projects/ # Per-project state snapshots ├── user1-projA.json └── user1-projB.json ``` ## Build Order Implications Based on component dependencies, recommended build order: 1. **Phase 1: Core Infrastructure** - `state_manager.py` (no dependencies, needed by all) - `tmux_session_manager.py` (depends on state_manager for workspace paths) 2. **Phase 2: Integration Layer** - `response_formatter.py` (standalone, needed by orchestrator) - `prompts/` (static files, needed by orchestrator) 3. **Phase 3: Orchestration** - `orchestrator.py` (depends on all above) - `SKILL.md` (defines triggers, depends on orchestrator interface) 4. **Phase 4: Testing & Polish** - Integration tests - Channel-specific formatting refinement - Error handling edge cases ## Key Integration Points ### OpenClaw Gateway Integration - Skill is auto-loaded from `~/.openclaw/workspace/skills/gsd-openclaw/` - SKILL.md frontmatter defines trigger patterns - Gateway calls orchestrator with: user_id, message, channel, context ### GSD Integration - **No code changes to GSD** - pure bridge pattern - All interaction via tmux + slash commands - Workspace paths from GSD conventions (`~/projects/{name}`) ### tmux Integration Options 1. **libtmux (Python)** - Recommended, cleaner API 2. **subprocess + tmux commands** - Fallback, more control ```python # Option 1: libtmux import libtmux server = libtmux.Server() session = server.new_session("gsd-user1-projA", attach=False) pane = session.attached_window.attached_pane pane.send_keys("/gsd:status") # Option 2: subprocess import subprocess subprocess.run(["tmux", "new-session", "-d", "-s", "gsd-user1-projA"]) subprocess.run(["tmux", "send-keys", "-t", "gsd-user1-projA", "/gsd:status", "Enter"]) ``` ## Sources - OpenClaw GitHub Repository: https://github.com/openclaw/openclaw (HIGH confidence - official source) - OpenClaw Website: https://openclaw.ai/ (HIGH confidence - official documentation) - dev-workflow skill reference implementation (HIGH confidence - existing pattern in same ecosystem) - GSD project context from PROJECT.md (HIGH confidence - project requirements) ## Confidence Assessment | Area | Confidence | Reason | |------|------------|--------| | OpenClaw skill structure | HIGH | Direct reference from dev-workflow, official docs | | tmux integration patterns | MEDIUM | Standard Python/tmux patterns, needs validation with Claude CLI specifics | | Async response handling | HIGH | Standard pattern for messaging bots, well-established | | GSD bridge approach | HIGH | Project requirement explicitly states "bridge not modify" | | Scalability estimates | LOW | Theoretical, needs real-world testing |