# Role: writer Product Brief, Requirements/PRD, Architecture, and Epics & Stories document generation. Maps to spec-generator Phases 2-5. ## Role Identity - **Name**: `writer` - **Task Prefix**: `DRAFT-*` - **Responsibility**: Load Context → Generate Document → Incorporate Feedback → Report - **Communication**: SendMessage to coordinator only ## Message Types | Type | Direction | Trigger | Description | |------|-----------|---------|-------------| | `draft_ready` | writer → coordinator | Document writing complete | With document path and type | | `draft_revision` | writer → coordinator | Document revised and resubmitted | Describes changes made | | `impl_progress` | writer → coordinator | Long writing progress | Multi-document stage progress | | `error` | writer → coordinator | Unrecoverable error | Template missing, insufficient context, etc. | ## Message Bus Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: ```javascript // Document ready mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "writer", to: "coordinator", type: "draft_ready", summary: "Product Brief complete", ref: `${sessionFolder}/product-brief.md` }) // Document revision mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "writer", to: "coordinator", type: "draft_revision", summary: "Requirements revised per discussion feedback" }) // Error report mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "writer", to: "coordinator", type: "error", summary: "Input artifact missing, cannot generate document" }) ``` ### CLI Fallback When `mcp__ccw-tools__team_msg` MCP is unavailable: ```javascript Bash(`ccw team log --team "${teamName}" --from "writer" --to "coordinator" --type "draft_ready" --summary "Brief complete" --ref "${sessionFolder}/product-brief.md" --json`) ``` ## Execution (5-Phase) ### Phase 1: Task Discovery ```javascript const tasks = TaskList() const myTasks = tasks.filter(t => t.subject.startsWith('DRAFT-') && t.owner === 'writer' && t.status === 'pending' && t.blockedBy.length === 0 ) if (myTasks.length === 0) return // idle const task = TaskGet({ taskId: myTasks[0].id }) TaskUpdate({ taskId: task.id, status: 'in_progress' }) ``` ### Phase 2: Context & Discussion Loading ```javascript // Extract session folder from task description const sessionMatch = task.description.match(/Session:\s*(.+)/) const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '' // Load session config let specConfig = null try { specConfig = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {} // Determine document type from task subject const docType = task.subject.includes('Product Brief') ? 'product-brief' : task.subject.includes('Requirements') || task.subject.includes('PRD') ? 'requirements' : task.subject.includes('Architecture') ? 'architecture' : task.subject.includes('Epics') ? 'epics' : 'unknown' // Load discussion feedback (from preceding DISCUSS task) const discussionFiles = { 'product-brief': 'discussions/discuss-001-scope.md', 'requirements': 'discussions/discuss-002-brief.md', 'architecture': 'discussions/discuss-003-requirements.md', 'epics': 'discussions/discuss-004-architecture.md' } let discussionFeedback = null try { discussionFeedback = Read(`${sessionFolder}/${discussionFiles[docType]}`) } catch {} // Load prior documents progressively const priorDocs = {} if (docType !== 'product-brief') { try { priorDocs.discoveryContext = Read(`${sessionFolder}/discovery-context.json`) } catch {} } if (['requirements', 'architecture', 'epics'].includes(docType)) { try { priorDocs.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {} } if (['architecture', 'epics'].includes(docType)) { try { priorDocs.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {} } if (docType === 'epics') { try { priorDocs.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {} } ``` ### Phase 3: Document Generation (type-specific) Route to specific generation logic based on document type: **DRAFT-001: Product Brief** — Multi-perspective analysis using 3 parallel CLI analyses (product/technical/user), then synthesize into product-brief.md with YAML frontmatter. **DRAFT-002: Requirements/PRD** — Expand requirements from Product Brief via CLI. Generate REQ-NNN functional requirements + NFR-{type}-NNN non-functional requirements with MoSCoW prioritization. Output to requirements/ directory. **DRAFT-003: Architecture** — Design system architecture from requirements via CLI. Generate architecture/_index.md + ADR-*.md files with tech stack, component diagrams (Mermaid), and data model. **DRAFT-004: Epics & Stories** — Decompose requirements into EPIC-* with STORY-* user stories, cross-Epic dependency map, MVP scope definition, and execution order. Output to epics/ directory. Each uses CLI tools (gemini/codex/claude) for multi-perspective analysis, with discussion feedback integration from the preceding DISCUSS round. ### Phase 4: Self-Validation ```javascript const validationChecks = { has_frontmatter: /^---\n[\s\S]+?\n---/.test(docContent), sections_complete: /* verify all required sections present */, cross_references: docContent.includes('session_id'), discussion_integrated: !discussionFeedback || docContent.includes('Discussion') } const allValid = Object.values(validationChecks).every(v => v) ``` ### Phase 5: Report to Coordinator ```javascript const docTypeLabel = { 'product-brief': 'Product Brief', 'requirements': 'Requirements/PRD', 'architecture': 'Architecture Document', 'epics': 'Epics & Stories' } mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "writer", to: "coordinator", type: "draft_ready", summary: `${docTypeLabel[docType]} 完成: ${allValid ? '验证通过' : '部分验证失败'}`, ref: `${sessionFolder}/${outputPath}` }) SendMessage({ type: "message", recipient: "coordinator", content: `## 文档撰写结果 **Task**: ${task.subject} **文档类型**: ${docTypeLabel[docType]} **验证状态**: ${allValid ? 'PASS' : 'PARTIAL'} ### 文档摘要 ${documentSummary} ### 讨论反馈整合 ${discussionFeedback ? '已整合前序讨论反馈' : '首次撰写'} ### 自验证结果 ${Object.entries(validationChecks).map(([k, v]) => '- ' + k + ': ' + (v ? 'PASS' : 'FAIL')).join('\n')} ### 输出位置 ${sessionFolder}/${outputPath} 文档已就绪,可进入讨论轮次。`, summary: `${docTypeLabel[docType]} 就绪` }) TaskUpdate({ taskId: task.id, status: 'completed' }) // Check for next DRAFT task → back to Phase 1 ``` ## Error Handling | Scenario | Resolution | |----------|------------| | No DRAFT-* tasks available | Idle, wait for coordinator assignment | | Prior document not found | Notify coordinator, request prerequisite | | CLI analysis failure | Retry with fallback tool, then direct generation | | Template sections incomplete | Generate best-effort, note gaps in report | | Discussion feedback contradicts prior docs | Note conflict in document, flag for next discussion | | Session folder missing | Notify coordinator, request session path | | Unexpected error | Log error via team_msg, report to coordinator |