diff --git a/packages/pulse/src/e2e/meta-tdd-coding.ts b/packages/pulse/src/e2e/meta-tdd-coding.ts new file mode 100644 index 0000000..b283fba --- /dev/null +++ b/packages/pulse/src/e2e/meta-tdd-coding.ts @@ -0,0 +1,238 @@ +/** + * Meta Workflow e2e — TDD 重构 coding workflow + * + * 让 meta workflow (architect→coder→reviewer→tester→promoter) 把 coding workflow + * 从现有流程改造为 TDD 驱动流程。 + * + * 小橘 🍊 (NEKO Team) + */ + +import { createOpenAiLlmClient } from '../llm-client.js'; +import { createStore } from '../store.js'; +import { createMetaArchitectRole } from '../workflows/roles/meta-architect-llm.js'; +import { createMetaCoderRole } from '../workflows/roles/meta-coder-cursor.js'; +import { createMetaReviewerRole } from '../workflows/roles/meta-reviewer-cursor.js'; +import { createMetaTesterRole } from '../workflows/roles/meta-tester.js'; +import { createMetaPromoterRole } from '../workflows/roles/meta-promoter.js'; +import { createCursorRunner } from '../workflows/roles/agent-executor.js'; +import { createWorkflowRule } from '../workflows/workflow-rule-adapter.js'; +import { createMetaWorkflow } from '../workflows/meta.js'; +import { readFileSync, mkdtempSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +const REPO_DIR = join(import.meta.dir, '../../../..'); + +function readSrc(relPath: string): string { + return readFileSync(join(REPO_DIR, relPath), 'utf-8'); +} + +async function main() { + const dir = mkdtempSync(join(tmpdir(), 'meta-tdd-')); + console.log(`📁 Temp dir: ${dir}`); + console.log(`📂 Repo: ${REPO_DIR}\n`); + + const apiKey = process.env.DASHSCOPE_API_KEY; + if (!apiKey) throw new Error('DASHSCOPE_API_KEY required'); + + const llm = createOpenAiLlmClient({ + apiKey, + baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1', + model: 'qwen-plus', + timeoutMs: 120_000, + }); + + const store = createStore({ + eventsDbPath: join(dir, 'events.db'), + objectsDir: join(dir, 'objects'), + }); + + const cursorRunner = createCursorRunner({ + agentBin: `${process.env.HOME}/.local/bin/agent`, + }); + + const wf = createMetaWorkflow({ + architect: createMetaArchitectRole(llm), + coder: createMetaCoderRole(cursorRunner, llm, REPO_DIR), + reviewer: createMetaReviewerRole(cursorRunner, llm, REPO_DIR), + tester: createMetaTesterRole(llm, { repoDir: REPO_DIR }), + promoter: createMetaPromoterRole({ + repoDir: REPO_DIR, + remote: 'gitflare', + branch: 'main', + }), + }); + + const rule = createWorkflowRule(wf, store, undefined, { cooldownMs: 0 }); + + const taskDescription = `# 重构 coding workflow 为 TDD 驱动流程 + +## 目标 +将现有 coding workflow (architect→coder→reviewer→closer) 重构为完整的 TDD 驱动开发流程。 + +## 新流程设计 +\`\`\` +START + → test-planner (LLM) # 从需求出发写测试方案文档 + → test-reviewer (LLM) # review 测试方案是否覆盖需求 + → rejected → test-planner(带反馈修改) + → approved ↓ + → test-coder (Cursor) # 按测试方案写自动化测试用例(红灯) + → coder (Cursor) # 写功能代码 + 部署/使用说明(绿灯) + → auto-tester (代码) # bun test 跑自动化测试 + → fail → coder(迭代代码) + → pass ↓ + → manual-tester (Agent) # Agent 读测试文档照着操作验证 + → fail → coder(迭代代码) + → pass ↓ + → reviewer (Cursor) # review 功能代码 + 测试用例 + → rejected → coder(迭代) + → approved ↓ + → closer → END +\`\`\` + +## 核心设计原则 +1. **测试文档是一等公民** — test-planner 产出的测试文档贯穿全流程 +2. **测试先行** — test-coder 先写红灯测试,coder 再写代码让它绿 +3. **三处消费测试文档** — test-coder(写自动化测试)、manual-tester(手工验证)、reviewer(审查测试质量) +4. **coder 产出含部署说明** — manual-tester 需要知道怎么操作 +5. **Agent 实操验证** — manual-tester 不是 LLM 读代码,是真正的 Agent 按文档操作 + +## 新 Role 定义 + +### test-planner (LLM, tool_choice: required) +- 输入:需求描述(从 __start__ content) +- 输出 meta:{ testPlan: string(markdown 测试方案), scenarios: string[](测试场景列表)} +- 产出完整测试方案文档,包含:功能测试点、边界条件、异常场景、验收标准 + +### test-reviewer (LLM) +- 输入:test-planner 的测试方案 + 原始需求 +- 输出 meta:{ verdict: 'approved'|'rejected', feedback: string } +- 检查测试方案是否充分覆盖需求 + +### test-coder (Cursor Agent) +- 输入:approved 的测试方案 +- 输出 meta:{ testFiles: string[], testCount: number } +- 按测试方案写自动化测试用例(此时功能代码未实现,测试应全部失败 = 红灯) + +### coder (Cursor Agent) +- 输入:测试方案 + 红灯测试代码 + rejection feedback(如有) +- 输出 meta:{ filesChanged: string[], deploymentGuide: string } +- 写功能代码让测试变绿 + 写部署/使用说明供 manual-tester 使用 + +### auto-tester (代码) +- 输入:coder 完成后 +- 输出 meta:{ pass: boolean, failedTests: string[], output: string } +- 执行 \`bun test\`,解析结果 + +### manual-tester (Agent/LLM) +- 输入:test-planner 的测试文档 + coder 的部署说明 +- 输出 meta:{ pass: boolean, issues: string[] } +- 按测试文档逐项验证(当前阶段用 LLM 静态分析,未来接 OC Agent 实操) + +### reviewer (Cursor Agent) +- 输入:功能代码 + 测试代码 + 测试方案 +- 输出 meta:{ verdict: 'approved'|'rejected', comments: string, codeQuality: string, testQuality: string } +- 同时 review 功能代码质量和测试用例质量 + +### closer (代码) +- 输入:reviewer approved +- 输出 meta:null +- 生成完成报告 + +## Moderator 状态机 +\`\`\`typescript +START → 'test-planner' +'test-planner' → 'test-reviewer' +'test-reviewer' → verdict === 'approved' ? 'test-coder' : 'test-planner' +'test-coder' → 'coder' +'coder' → 'auto-tester' +'auto-tester' → pass ? 'manual-tester' : 'coder' +'manual-tester' → pass ? 'reviewer' : 'coder' +'reviewer' → verdict === 'approved' ? 'closer' : 'coder' +'closer' → END + +// 紧急收敛:remainingRounds <= 1 时跳 closer +\`\`\` + +## limits +\`\`\`typescript +limits: { maxRounds: 25 } +\`\`\` + +## 现有源码(参考) + +### coding.ts (当前 workflow 定义) +\`\`\`typescript +${readSrc('packages/pulse/src/workflows/coding.ts')} +\`\`\` + +### coding.test.ts (当前测试) +\`\`\`typescript +${readSrc('packages/pulse/src/workflows/coding.test.ts')} +\`\`\` + +### workflow-type.ts (接口定义,不要修改) +\`\`\`typescript +${readSrc('packages/pulse/src/workflows/workflow-type.ts')} +\`\`\` + +## 实现约束 +- 不修改 workflow-rule-adapter.ts 和 workflow-type.ts +- 新文件:coding-tdd.ts + coding-tdd.test.ts(不覆盖原 coding.ts) +- Role 先用 mock 实现(后续再接真实 LLM/Cursor) +- 测试用 coding.test.ts 为模板,覆盖所有状态转换 +- Build: \`cd packages/pulse && bun run build\` +- Test: \`bun test packages/pulse/src/workflows/\` +- Commit author: 小橘 +- Commit msg: \`feat: TDD coding workflow (coding-tdd) with test-first pipeline\` +- Push: \`git push gitflare main --no-verify\` + +## 验收标准 +1. coding-tdd.ts 导出 createTddCodingWorkflow() +2. 8 个 role 全部有 mock 实现 +3. moderator 状态机正确(含 test-reviewer rejection 回环、auto-tester/manual-tester fail 回环、reviewer rejection 回环) +4. remainingRounds <= 1 紧急收敛 +5. limits.maxRounds = 25 +6. coding-tdd.test.ts 覆盖所有转换路径 +7. 所有现有测试不 break +8. bun run build 通过 +`; + + const hash = store.putObject(taskDescription); + store.appendEvent({ + occurredAt: Date.now(), + kind: 'meta.__start__', + key: 'tdd-coding-workflow', + hash, + }); + + const MAX_TICKS = 12; + for (let i = 0; i < MAX_TICKS; i++) { + console.log(`\n── tick ${i + 1} ──────────────────────────`); + const result = await rule.tick(); + + if (result.executed.length === 0) { + console.log('⏹️ No more roles to execute. Workflow complete or stuck.'); + break; + } + + for (const ex of result.executed) { + console.log(`✅ ${ex.role} done`); + console.log(` content: ${ex.content.slice(0, 300)}...`); + console.log(` meta: ${JSON.stringify(ex.meta)}`); + } + + const events = store.getAfter(0); + const lastEvent = events[events.length - 1]; + if (lastEvent?.kind === 'meta.promoter') { + console.log('\n🎉 Workflow completed — promoter finished!'); + break; + } + } + + store.close(); + console.log('\n🏁 Done'); +} + +main().catch(console.error);