--- phase: 02-integration-layer plan: 02 type: execute wave: 1 depends_on: [] files_modified: - src/gsd_openclaw/response_formatter.py - tests/test_response_formatter.py autonomous: true requirements: - OCLW-04 must_haves: truths: - "Response formatter creates valid Feishu Card JSON 2.0 structure" - "Success cards have green theme" - "Error cards have red theme with guidance buttons" - "Long messages are paginated with [N/M] markers" - "Cards support interactive buttons with callback values" artifacts: - path: "src/gsd_openclaw/response_formatter.py" provides: "Feishu Card message formatting" exports: ["FeishuResponseFormatter", "CardTheme", "CardButton"] - path: "tests/test_response_formatter.py" provides: "Tests for response formatting" exports: ["TestFeishuResponseFormatter"] key_links: - from: "FeishuResponseFormatter" to: "Feishu API" via: "card JSON structure" pattern: '"schema": "2.0"' --- Create FeishuResponseFormatter class that formats responses as Feishu Card JSON 2.0 messages with three-zone structure (header/content/actions). Purpose: Provide channel-adaptive response formatting for Feishu. The formatter converts GSD output into structured Feishu Card messages with proper theming, pagination, and interactive elements. Output: - src/gsd_openclaw/response_formatter.py - Response formatter implementation - tests/test_response_formatter.py - Formatter tests @/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md @/home/starmen1265/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/02-integration-layer/02-CONTEXT.md @.planning/phases/02-integration-layer/02-RESEARCH.md Three-zone layout: 1. header: Title + status color (blue=info, green=success, red=error) 2. elements: Main content area (div, markdown, note components) 3. actions: Interactive buttons (at card bottom) Card JSON structure: ```json { "schema": "2.0", "config": {"wide_screen_mode": true}, "header": { "title": {"tag": "plain_text", "content": "..."}, "template": "green" }, "elements": [ {"tag": "div", "text": {"tag": "lark_md", "content": "..."}} ] } ``` Button structure: ```json { "tag": "action", "actions": [ { "tag": "button", "text": {"tag": "plain_text", "content": "Confirm"}, "type": "primary", "value": {"action": "approve", "project": "my-auth"} } ] } ``` Use `lark_md` tag for markdown content (Feishu's built-in markdown support). Task 1: Create test scaffold for FeishuResponseFormatter tests/test_response_formatter.py - Test 1: format_success returns valid card with green theme - Test 2: format_error returns valid card with red theme - Test 3: format_info returns valid card with blue theme - Test 4: Card with buttons includes action element - Test 5: format_paginated adds [N/M] marker - Test 6: Card has schema 2.0 and wide_screen_mode config Create tests/test_response_formatter.py with test class TestFeishuResponseFormatter: 1. test_format_success_returns_valid_card(): - Create FeishuResponseFormatter() - Call format_success("Test Title", "Test content") - Assert card["schema"] == "2.0" - Assert card["header"]["template"] == "green" - Assert card["header"]["title"]["content"] == "Test Title" 2. test_format_error_returns_red_theme(): - Call format_error("Error", "Something went wrong") - Assert card["header"]["template"] == "red" 3. test_format_info_returns_blue_theme(): - Call format_info("Info", "Status update") - Assert card["header"]["template"] == "blue" 4. test_format_with_buttons_includes_actions(): - Create CardButton("OK", {"action": "ok"}) - Call format_success("Test", "Content", buttons=[button]) - Assert elements contains action with tag="action" - Assert button text is "OK" 5. test_format_paginated_adds_marker(): - Call format_paginated("Title", "Content", page=1, total_pages=3) - Assert "[1/3]" appears in content 6. test_card_has_required_structure(): - Call format_success("Test", "Content") - Assert "config" key exists - Assert config["wide_screen_mode"] == True - Assert elements[0]["tag"] == "div" Import from gsd_openclaw.response_formatter: FeishuResponseFormatter, CardTheme, CardButton Follow test pattern from tests/test_state_manager.py. pytest tests/test_response_formatter.py -v - tests/test_response_formatter.py exists with 6 tests - All tests fail initially (no implementation yet) Task 2: Implement CardTheme enum and CardButton dataclass src/gsd_openclaw/response_formatter.py - Test: CardButton can be created with text, value, button_type - Test: CardTheme enum has SUCCESS, ERROR, INFO, WARNING values Create src/gsd_openclaw/response_formatter.py with: 1. Imports: - from dataclasses import dataclass - from typing import Dict, Any, List, Optional - from enum import Enum 2. CardTheme(Enum): - SUCCESS = "green" - ERROR = "red" - INFO = "blue" - WARNING = "orange" 3. @dataclass CardButton: - text: str - value: Dict[str, Any] - button_type: str = "primary" # primary, default, danger Keep implementation minimal - just these data structures. Tests should now pass for CardTheme and CardButton. pytest tests/test_response_formatter.py::TestFeishuResponseFormatter::test_format_success_returns_valid_card -v 2>&1 | head -20 || echo "Expected: tests still fail (formatter not implemented)" - CardTheme enum exists with SUCCESS, ERROR, INFO, WARNING - CardButton dataclass exists with text, value, button_type Task 3: Implement FeishuResponseFormatter class src/gsd_openclaw/response_formatter.py - Test: All 6 tests in test_response_formatter.py pass Add FeishuResponseFormatter class to response_formatter.py: class FeishuResponseFormatter: """Format responses as Feishu Card messages.""" def format_success(self, title: str, content: str, buttons: Optional[List[CardButton]] = None) -> dict: """Format a success response card with green theme.""" return self._build_card(title, content, CardTheme.SUCCESS, buttons) def format_error(self, title: str, content: str, buttons: Optional[List[CardButton]] = None) -> dict: """Format an error response card with red theme.""" return self._build_card(title, content, CardTheme.ERROR, buttons) def format_info(self, title: str, content: str, buttons: Optional[List[CardButton]] = None) -> dict: """Format an info response card with blue theme.""" return self._build_card(title, content, CardTheme.INFO, buttons) def format_paginated(self, title: str, content: str, page: int, total_pages: int) -> dict: """Format a paginated response with [page/total] marker.""" footer = f"\n\n---\n[{page}/{total_pages}]" return self._build_card(title, content + footer, CardTheme.INFO) def _build_card(self, title: str, content: str, theme: CardTheme, buttons: Optional[List[CardButton]] = None) -> dict: """Build complete card JSON structure.""" card = { "schema": "2.0", "config": {"wide_screen_mode": True}, "header": { "title": {"tag": "plain_text", "content": title}, "template": theme.value }, "elements": [ {"tag": "div", "text": {"tag": "lark_md", "content": content}} ] } if buttons: card["elements"].append(self._build_actions(buttons)) return card def _build_actions(self, buttons: List[CardButton]) -> dict: """Build actions element from button list.""" return { "tag": "action", "actions": [ { "tag": "button", "text": {"tag": "plain_text", "content": btn.text}, "type": btn.button_type, "value": btn.value } for btn in buttons ] } User decision (LOCKED): - Three-zone structure: header + elements + actions - Error cards use red theme - Success cards use green theme - Pagination uses [N/M] format - Interactive buttons support callback values All 6 tests should now pass. pytest tests/test_response_formatter.py -v - FeishuResponseFormatter class implements all format methods - All 6 tests pass - Card JSON uses schema 2.0 with three-zone structure - Run pytest tests/test_response_formatter.py -v (all tests pass) - Verify card structure matches Feishu Card JSON 2.0 specification - Verify theme colors are correct (green=success, red=error, blue=info) 1. FeishuResponseFormatter class exists in src/gsd_openclaw/response_formatter.py 2. format_success, format_error, format_info, format_paginated methods work 3. Card JSON has schema "2.0" with proper header/elements structure 4. Buttons are rendered as action elements with callback values 5. All 6 tests in tests/test_response_formatter.py pass After completion, create `.planning/phases/02-integration-layer/02-02-SUMMARY.md`