diff --git a/docs/agent-spec.md b/docs/agent-spec.md new file mode 100644 index 0000000..28ece87 --- /dev/null +++ b/docs/agent-spec.md @@ -0,0 +1,212 @@ +# Agent 规范 + +本文件是 FTB 系统中所有 AI Agent 的**唯一权威定义**。任何对 agent 的能力、职责、权限、协作方式的修改,必须先在本文件落地,再同步到 architecture / decisions / workflow / roadmap / glossary。 + +## 何时更新本文件 + +1. **新增 agent**:新建一个独立 agent +2. **agent 职责变化**:输入/输出/调用条件变了 +3. **agent 权限变化**:可读哪些数据、可写哪些数据变了 +4. **多 agent 协作方式变化**:编排顺序、依赖关系、出错回退策略变了 + +不影响以上四类的 prompt 微调、模型版本切换、性能优化不需要更新本文件。 + +## 全局原则 + +1. **AI 不直接动数据,先生草案,用户确认** + - 所有写入实体(DevTask / TestCase)必须带 `aiDraft: true` 标记 + - 用户编辑保存后,标记自动清除(在 `useDevTaskStore.updateTask` / `useTestCaseStore.updateTestCase` 里实现) + +2. **每条产物强制带引用** + - DevTask / TestCase 必须有 `references: Reference[]`,至少 1 条 + - 引用类型:`requirement` / `prototype_note` / `external` + - 不带引用的草案不允许写入 + +3. **AI 输出必须有"对账报告"** + - 拆解任务前,先输出三段对账: + - ✅ 完美对应(需求 ↔ 原型注释 ↔ 任务) + - ⚠️ 仅需求未见原型 / 仅原型未见需求 + - ❓ 注释含糊无法转化 + - 对账报告先呈现给用户,用户审核后才执行写入 + +4. **历史版本不强制存储** + - 系统不要求历史原型 URL 作为基准 + - 拆偏问题靠"对账报告"暴露给用户,由人工补救 + - 这条决定见 decisions.md #17 + +## Agent 列表 + +### Agent 1:Prototype Decompose Agent(原型拆解) + +**目的**:把"产品方案原型 + 关联需求"拆解成开发任务草案 + 测试用例草案。 + +**输入**: +- 当前版本「产品方案」类型 + status=completed 的 VersionPlan,取其 `resultUrl` 作为**原型链接**(约定:产品方案的成果即原型) +- 当前版本关联需求列表(已被加进 Version 的 Requirement[]) +- 版本成员清单(带角色:前端/后端/UI/测试) + +**调用入口**:版本详情页 → 产品方案 Tab → 计划状态 = completed 后,按钮「AI 拆解任务和用例」 + +**触发条件**: +- 当前版本至少有 1 条 type=product 的 VersionPlan 处于 `completed` 且 resultUrl 非空(提交了原型) +- 当前版本至少关联 1 条需求 + +**输出**: +- 对账报告(结构化文本,含"完美对应/单边/含糊"三段) +- DevTask 草案数组(每条带 `aiDraft: true` + `references[]`) +- TestCase 草案数组(每条带 `aiDraft: true` + `references[]`) + +**写入**: +- 用户确认后,调用 `useDevTaskStore.createTask` 和 `useTestCaseStore.createTestCase` +- 写入字段中 `aiDraft: true`、`aiDraftAt: ISO时间戳` + +**权限**: +- 读:Version, Requirement, VersionPlan, Member +- 写:DevTask(仅 aiDraft 草案)、TestCase(仅 aiDraft 草案) +- 不得修改:Requirement、Version、VersionPlan、Member、Bug + +**失败回退**: +- 原型 URL 不可达 → 报告"原型无法访问",不写入任何数据 +- 没有已完成的产品方案计划 → 报告"请先完成产品方案并提交原型成果",不写入 +- 关联需求为空 → 报告"无关联需求,无法对照",不写入 +- 对账报告全是 ❓ → 报告"原型注释含糊度过高,建议人工拆解",不写入 + +**MVP 阶段限制**(V3.1): +- 只生成 DevTask 和 TestCase 草案 +- 不自动分配 assignee(assignee 留给用户从草案编辑时手填) +- 不做"上一版基准 diff" + +### Agent 2:Risk Watch Agent(风险预警)— 待规划 + +仅占位,正式规划见 roadmap.md V3.2。 + +### Agent 3:Schedule Suggest Agent(排期建议)— 待规划 + +仅占位,正式规划见 roadmap.md V3.2。 + +## 多 Agent 协作(V3.2 规划) + +当前 V3.1 只有 Prototype Decompose Agent 单独运行。 +未来 Risk Watch / Schedule Suggest 引入后,编排策略另行设计。 + +## 实现位置 + +V3.1 起,所有 Agent **走 NestJS 后端**(不走 Next.js API Route),原因: +- 与现有 `product` / `requirement` 模块架构一致 +- 共享 Prisma 实例便于后续 AiLog 持久化 +- 多 Agent 引入时统一在 `AiGateway` 管理 prompt / 降级 / 重试 + +代码位置: +``` +apps/server/src/modules/ +├── ai/ +│ ├── ai.module.ts +│ ├── ai.controller.ts # POST /api/v1/ai/decompose +│ ├── ai.service.ts # 抓原型 + 调 LLM + 解析 +│ ├── ai-gateway.service.ts # Anthropic 客户端单例(懒加载,key 改了重建) +│ ├── prompts/decompose.ts # System Prompt + Tool Schema +│ └── dto/decompose.dto.ts # 入参校验 +└── config/ + ├── config.module.ts + ├── ai-config.controller.ts # GET/PATCH /api/v1/config/ai, POST /test + ├── ai-config.service.ts # 读写 data/ai-config.json + └── dto/update-ai-config.dto.ts +``` + +共享类型:`packages/shared/src/agent.ts`(前后端通用) + +## 配置管理 + +**多提供商支持(V3.1.5 起)**:系统支持配置任意数量的 AI 提供商,每个独立的 baseURL / apiKey / model,运行时切换激活。 + +**支持的 API 格式**: +- `anthropic` — Anthropic 官方 + 兼容 Anthropic Messages API 格式的中转站(如 ikuncode 的 anthropic 端点) +- `openai` — OpenAI 官方 + 兼容 OpenAI Chat Completions 格式的中转站 + +**API Key 优先级**:当前激活提供商的 apiKey → 环境变量 `ANTHROPIC_API_KEY`(兜底,仅 anthropic 格式可用) + +**前端配置入口**:`/admin/ai-config`,仅超管(`role.permissions` 含 `'*'`)可见 + +**字段(每个 Provider)**: +- `id`:用户自定义唯一 ID(如 `ikuncode`) +- `name`:显示名 +- `format`:`anthropic` / `openai` +- `baseURL`:完整 API endpoint +- `apiKey`:完整 Key 仅在服务端持有;前端只能拿到 mask 视图 +- `model`:该提供商上使用的模型 ID +- `remark`:可选备注 + +**全局字段**: +- `activeProviderId`:当前激活哪个 provider +- `updatedAt` / `updatedBy`:审计 + +**测试连接**:`POST /api/v1/config/ai/test` 带 `{ id }`,用 16 token 极简调用验证目标 provider 可用性 + +**激活**:`POST /api/v1/config/ai/activate` 带 `{ id }`,切换激活 provider;AiGateway 缓存自动失效,下次调用重新构建客户端 + +**安全约束**: +- 配置文件路径在 .gitignore 里(`apps/server/data/`),不进入版本控制 +- 前端 GET 接口永远不返回完整 Key +- 修改 Key 只能从前端 PATCH 上传新值,不支持读出旧值 +- 编辑提供商时若不传 apiKey,则保留原值 + +**数据迁移**:旧版(V3.1)的 `{ anthropicApiKey, model }` 单 key 结构,自动迁移为多 providers 结构(id 为 `anthropic-official`,自动激活)。 + +## 数据契约 + +### Reference 类型 + +```ts +interface Reference { + type: 'requirement' | 'prototype_note' | 'external'; + id: string; // REQ-008 / QY0007 / 自由文本 + label: string; // 展示用 + url?: string; // 可选跳转链接 +} +``` + +### AI 草案标记 + +```ts +// DevTask 和 TestCase 共有 +aiDraft?: boolean; +aiDraftAt?: string; // ISO 时间戳 +references?: Reference[]; +``` + +### Agent 输入预期 + +```ts +interface DecomposeInput { + version: VersionWithContext; // 上下文(不含 prototypeUrl,原型从产品方案成果取) + prototypeUrl: string; // 从产品方案 completed 计划的 resultUrl 派生 + requirements: Requirement[]; // 当前版本所属项目下已采纳、可用于本次拆解的需求 + members: VersionMember[]; // 该版本参与人员(带角色) +} +``` + +要求:`requirements` 不能从产品级全量需求池直接取,必须经过项目维度和已采纳状态过滤。 + +### Agent 输出预期 + +```ts +interface DecomposeOutput { + report: { + matched: Array<{ reqId: string; noteIds: string[]; taskCount: number }>; + reqOnly: string[]; // 需求 ID 列表 + noteOnly: string[]; // 原型注释 ID 列表 + ambiguous: Array<{ noteId: string; reason: string }>; + }; + devTaskDrafts: Array & { categoryCode: string; aiDraft: true; aiDraftAt: string; references: Reference[] }>; + testCaseDrafts: Array & { categoryCode: string; aiDraft: true; aiDraftAt: string; references: Reference[] }>; +} +``` + +要求:AI 不输出数据库 `categoryId`,只输出稳定 `categoryCode`。前端确认写入时按 `TaskCategory.code` 映射成 `categoryId`;映射失败时使用对应分组的默认类型兜底。 + +## 视觉规范 + +AI 草案在 DevTask / TestCase 列表中的视觉区分: +- 整行加 `border-l-2 border-l-purple-400 bg-purple-50/30` +- 标题旁紫色徽章「AI 草案」(`bg-purple-100 text-purple-600`) +- 用户在详情抽屉里编辑保存任意字段后,徽章和左边线自动消失 diff --git a/docs/architecture.md b/docs/architecture.md index 8b3edfa..d8ed119 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -33,8 +33,8 @@ Requirement Version TestCase | 前端 | Next.js 14 (App Router) | TypeScript + 客户端组件为主 | | UI | Tailwind CSS + Shadcn/ui | 紧凑信息密度、现代风格 | | 状态 | Zustand | 每个领域一个 store | -| 持久化 | localStorage(V1) | 后端 NestJS + Prisma 已搭好但未启用 | -| 后端 | NestJS + Prisma + PostgreSQL(V2) | 暂未启用 | +| 持久化 | PostgreSQL AppData(V2.1) | 业务数据走 NestJS `/data/:key`,不再以浏览器存储为主 | +| 后端 | NestJS + Prisma + PostgreSQL(V2) | 已接入通用数据文档层,后续再逐表关系化 | | AI | Anthropic SDK(V3 远景) | 健康度/风险预警/排期建议 | ## 模块结构 @@ -113,17 +113,14 @@ DevTask 没有"已完成"状态,"已提测"就是终态——开发交付完 ## 数据持久化 -**V1(当前):** 全部 localStorage,每个 store 独立 key -- `ftb_overview_v1` - 产品/项目/版本树 -- `ftb_requirements_v3` -- `ftb_version_plans_v1` -- `ftb_dev_tasks_v1` -- `ftb_test_cases_v1` -- `ftb_bugs_v1` -- `ftb_overtime_v1` -- `ftb_task_categories_v1` +**V2.1(当前):** 通用服务端文档表 `app_data` +- 后端:`apps/server/src/modules/data/` 提供 `GET/PUT /api/v1/data/:key` +- 数据库:Prisma `AppData` 模型,表名 `app_data`,`key` 为主键,`value` 为 JSONB +- 前端:各 Zustand store 保持现有数据形状,通过 `apps/web/lib/server-data.ts` 读写服务端 +- 覆盖范围:产品/项目/版本树、需求池、调研/产品方案/UI 计划、开发任务、测试用例、Bug、成员/角色/部门、任务类型、任务工时日志、加班记录 +- 浏览器仅保留登录会话(`ftb_auth_session` / `ftb_auth_persist`),不再作为业务数据主存储 -**V2(计划):** NestJS + Prisma + PostgreSQL,Schema 已设计但尚未运行 migration。 +**后续 V2.2(计划):** 将 `app_data` 中稳定的数据形状逐步拆成关系表和领域 CRUD API。拆表前不要恢复业务 localStorage 缓存,避免线上部署后出现多端数据分叉。 ## 权限模型(轻量) @@ -134,3 +131,35 @@ V1 仅做前端校验,无后端鉴权: - 版本 `members` 为空时所有人可见(兼容旧数据) V2 接入后端后改为基于 `ProjectMember` 表的 RBAC(Owner/Admin/Member/Viewer)。 + +## AI Agent 层 + +详细规范见 `agent-spec.md`。要点: + +- AI Agent 不是一个独立服务,而是嵌在前端的"特定调用入口"。当前 V3.1 仅 Prototype Decompose Agent。 +- Agent 写入数据时必须带 `aiDraft: true` 标记,列表中视觉区分(紫色边)。用户编辑后自动清除标记。 +- DevTask / TestCase 加入 `references[]` 字段,记录任务/用例的来源(需求 / 原型批注)。Agent 和人工创建均强制至少 1 条引用。 +- 原型链接**不在 Version 上独立存储**,而是来自产品方案 (VersionPlan type=product) 已完成计划的 `resultUrl`。约定:提交产品方案的成果就是原型。 +- AI 服务实现走 **NestJS 后端**(`apps/server/src/modules/ai/`),不走 Next.js API Route。 +- API Key 通过 **`/admin/ai-config` 页面配置**(仅超管可见),存到 `apps/server/data/ai-config.json`,不入 git;环境变量 `ANTHROPIC_API_KEY` 作为兜底。 + +新增涉及 AI 的实体字段: + +| 实体 | 字段 | 类型 | 说明 | +|------|------|------|------| +| DevTask | references | Reference[]? | 引用来源(需求/原型批注) | +| DevTask | aiDraft | boolean? | AI 草案标记 | +| DevTask | aiDraftAt | string? | AI 生成时间戳 | +| TestCase | references | Reference[]? | 同上 | +| TestCase | aiDraft | boolean? | 同上 | +| TestCase | aiDraftAt | string? | 同上 | + +## 版本模块规则层(V2.2 设计约束) + +版本详情里的计划完成、需求候选和任务类型映射必须走规则层: + +- `version-plan-workflow.ts`:调研/产品方案/UI 设计的子任务、需求覆盖、成果提交和完成条件。 +- `requirement-selector.ts`:当前版本所属项目下可关联需求的候选筛选,默认只返回 `status === 'adopted'` 的项目需求。 +- `task-category.ts`:DevTask/TestCase 共用任务类型字典,`id` 用于存储,`code` 用于 AI 语义映射。 + +页面组件只消费规则层输出,不直接拼完成条件或候选筛选条件。 diff --git a/docs/decisions.md b/docs/decisions.md index 9262fe3..232b0b3 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -155,3 +155,117 @@ 4. 简单文案/样式调整 **理由**:brainstorming 适合新功能设计,bug fix 强行套流程浪费时间。 + +## 17. AI 拆解不存历史原型,靠对账报告兜底 + +**问题**:FTB 接入时,禅道里项目可能已经迭代到 V1.6,前面历史原型不在系统里。AI 拆解 V1.6 时如果做"V1.5 → V1.6 diff",需要用户额外提供历史原型 URL。 + +**决策**:**不要求用户提供历史原型 URL**。AI 拆解只用三样输入:当前版本原型 + 关联需求 + 版本成员。 + +**对应风险**:AI 可能把"上版本就有的功能"也当成"本次新做"。 + +**应对**:要求 Agent 输出**对账报告**,三段: +- ✅ 完美对应(需求 ↔ 原型注释 ↔ 任务) +- ⚠️ 仅需求未见原型 / 仅原型未见需求 +- ❓ 注释含糊无法转化 + +**理由**:让历史版本入库的成本远高于让用户对账的成本。对账报告本来就是 AI 拆解的标配,能同时兜住"拆偏"和"拆漏"两类问题。让团队补需求清单,比让团队补历史原型容易。 + +## 18. AI 写入必须带 aiDraft 标记 + 引用来源 + +**问题**:AI 直接写入开发任务/用例,团队无法分辨"这是 AI 拆的还是人手填的",出错时找不到来源。 + +**决策**: +- DevTask / TestCase 加 `aiDraft: boolean` 字段 +- AI 写入时强制 `aiDraft: true` +- 列表中 AI 草案视觉区分(紫色左边线 + AI 草案徽章) +- 用户编辑保存后 `aiDraft` 自动清除(在 store 的 update 方法里实现) +- 同时加 `references: Reference[]` 字段,记录引用来源(需求 / 原型批注) + +**理由**: +- 可识别性:团队一眼分辨 AI 产出 vs 人工产出 +- 可追溯性:每条任务知道是从哪条需求 + 哪条原型批注来的 +- 可信任:用户编辑即认可,自动转正常状态 + +## 19. 引用机制对 AI 和人工一致 + +**问题**:如果只有 AI 任务带 references,人工任务不带,会出现两套规则:用户看到 AI 任务有引用、人工没有,体验割裂;做"任务可追溯"功能时数据缺失。 + +**决策**:人工创建 DevTask / TestCase 时也强制带 references。 +- 表单加「关联需求」(从版本已加需求里多选,自动转 requirement 类型 reference) +- 表单加「原型批注」(手填编号或自由文本,逗号/空格分隔,自动转 prototype_note 类型 reference) +- AI 草案唯一区别只剩 `aiDraft: true` 这一个状态字段 + +**理由**:单一数据形状,UI 复用;可追溯性是任务本身的属性,不该由"谁创建"决定。 + +## 20. AI 配置走页面不走 .env + +**问题**:早期 ANTHROPIC_API_KEY 只能写到 `apps/server/.env`,配置流程对非技术人员不友好;切换 key 要重启 NestJS。 + +**决策**: +- 加 `apps/server/src/modules/config/` 模块,提供 GET/PATCH/POST `/api/v1/config/ai` 接口 +- 配置存到 `apps/server/data/ai-config.json`(在 .gitignore 里) +- 前端 `/admin/ai-config` 页面(仅超管可见)支持修改 Key、切换模型、测试连接 +- AiGateway 优先读文件,其次读环境变量;切换 key 后客户端单例自动重建 +- 前端 GET 接口永远不返回完整 Key,只返回 mask(如 `sk-ant...xxxx`) + +**理由**: +- 运营/管理可以自助维护,不依赖工程师改代码 +- Key 在服务器存储,前端永远拿不到完整内容,安全等同 .env +- 走 .env 的兜底保留,给 CI / 早期部署留路径 + +## 21. 多 AI 提供商抽象,支持中转站 + +**问题**:早期 AiGateway 硬绑定 Anthropic 官方 SDK,无法走 ikuncode 等中转站,也不能切换到 OpenAI 格式提供商。 + +**决策**:引入 Provider 抽象层。 +- `apps/server/src/modules/ai/providers/` 下两个实现:`AnthropicProvider` / `OpenAIProvider` +- 共同实现 `AiProvider` 接口(`callTool` / `ping`),把厂商差异封装在 provider 内部 +- `prompts/decompose.ts` 用格式无关的 JSON Schema,由各 provider 包装成 Anthropic 的 `input_schema` 或 OpenAI 的 `parameters` +- 配置数据结构升级为 `{ providers: [], activeProviderId }`,支持配置多个 provider 运行时切换 +- 旧的单 key 配置自动迁移成 `providers[0]` + +**支持范围**:当前两类格式覆盖 99% 中转站 +- `anthropic`:Anthropic 官方 / 中转站(兼容 Messages API + tool_use) +- `openai`:OpenAI 官方 / 中转站(兼容 Chat Completions + tool calling) + +**理由**: +- 用户可以自由选择厂家或中转站,不被一家锁死 +- 每个 provider 独立 model 字段,因为不同提供商上同名模型不一定可用 +- "激活"概念让多套配置同时存在,方便 A/B 切换 +- 抽象层让上层 AiService 完全不感知厂商差异,未来加 Risk Watch / Schedule Suggest Agent 时复用 + +## 22. 业务数据先迁到 PostgreSQL AppData,再逐步关系化 + +**问题**:V1 业务数据存在浏览器 localStorage 中,用户在 Application Storage 执行 Clear site data 后,产品、项目、版本、需求池、版本详情里的调研/产品方案/UI/开发任务/测试用例/Bug,以及成员、任务类型等都会丢失。系统未来要部署到服务器,浏览器不能作为业务数据主存储。 + +**决策**:先引入服务端通用数据文档层: +- Prisma 增加 `AppData` 模型(表 `app_data`,`key` + JSONB `value`) +- NestJS 增加 `DataModule`,通过 `GET/PUT /api/v1/data/:key` 读写允许的业务数据 key +- 前端 store 保持现有数据形状和业务逻辑,只把持久化从 localStorage 切换到 `/data/:key` +- 覆盖 key:`products-overview`、`requirements`、`version-plans`、`dev-tasks`、`test-cases`、`bugs`、`members`、`task-categories`、`task-worklogs`、`overtime` +- 浏览器只保留登录态,不保存业务主数据 + +**理由**: +- 当前模块多、数据形状仍在快速调整,一次性全量关系化风险高、改动面大 +- JSONB 文档表能立刻解决“清浏览器数据导致业务数据丢失”的线上部署问题 +- 保持 store 数据形状不变,能降低迁移对现有 UI、AI 拆解、工作台聚合的冲击 +- 后续等字段稳定后,再把 `app_data` 中的文档逐步迁入关系表和领域 CRUD API + +## 23. 版本计划完成条件进入工作流引擎,任务类型使用稳定语义码 + +**问题**:版本模块里,调研/产品方案/UI 设计的完成动作散在 `PlanTab` 和 `PlanDetailDrawer`,右上角勾选按钮会绕过子任务和成果提交。开发任务的任务类型太粗,测试用例没有任务类型,AI 拆解只输出 `role`,无法稳定映射到业务分类。 + +**决策**: +- 新增 `version-plan-workflow.ts`,统一判断计划是否可勾选子任务、是否可提交成果、是否可完成、缺少什么条件。 +- 取消计划卡片右上角的“直接完成”勾选按钮,完成只能通过提交成果触发。 +- 调研/产品方案/UI 设计都使用子任务清单;完成时必须满足规则引擎要求并提交链接或文件成果。 +- 新增 `requirement-selector.ts`,所有关联需求候选都从当前版本所属项目下的已采纳需求中取,不从全量需求池取。 +- DevTask 和 TestCase 共用 `TaskCategory` 字典,`TestCase` 增加 `categoryId`。 +- `TaskCategory` 增加稳定 `code`,AI 输出 `categoryCode`,前端再映射为当前系统的 `categoryId`。 + +**理由**: +- 完成条件属于业务规则,不属于按钮组件;集中到工作流引擎后,列表和抽屉能保持一致。 +- 需求候选来源属于领域边界;统一 selector 能避免从需求池、项目需求、版本需求之间误取数据。 +- AI 不应该猜数据库 id;稳定语义码能兼容管理员调整展示名称,也方便后续扩展更多模型。 +- 测试用例带任务类型后,才能知道测试覆盖范围,AI 拆解和人工创建的数据形状也一致。 diff --git a/docs/roadmap.md b/docs/roadmap.md index 77f8ad8..ceb117f 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,11 +1,17 @@ # 开发路线图 -## 当前阶段:V1 — 前端 Mock + 业务流程打磨 +## 当前阶段:V2.1 — 服务端持久化第一阶段 -所有数据用 localStorage 持久化,重点验证业务模型和交互。 +业务流程仍保持 V1 的前端 store 形状,但业务数据主存储已切到 NestJS + PostgreSQL `app_data` 文档表。浏览器只保留登录态,不再保存产品、项目、版本、需求、版本详情、成员、任务类型等业务数据。 ### 已完成(按时间倒序) +**2026-06-24** +- 新增 NestJS `DataModule` + Prisma `AppData`,提供 `GET/PUT /api/v1/data/:key` +- 产品/项目/版本树、需求池、调研/产品方案/UI、开发任务、测试用例、Bug 改为服务端持久化 +- 成员/角色/部门、任务类型、任务工时日志、加班记录改为服务端持久化 +- 登录改为读取服务端成员数据;浏览器只保留登录会话 + **2026-06-16** - 项目模块顶部卡片(总版本数/已开发/需求数/Bug 总数) - 项目模块版本记录与版本详情数据联动(耗时 + 状态胶囊) @@ -47,38 +53,70 @@ ## V2 — 后端接入 -NestJS + Prisma + PostgreSQL Schema 已设计,等业务流程稳定后开始迁移。 +NestJS + Prisma + PostgreSQL 已开始接入。第一阶段先用 `app_data` JSONB 文档表承接现有 store 数据形状,避免浏览器清站点数据导致业务数据丢失;第二阶段再逐步拆成关系表。 ### 关键任务 -1. **运行 Prisma migration**:把现有的 lib/*.ts 类型转为 Prisma schema -2. **API 层**:每个 store 对应一组 CRUD endpoint -3. **localStorage → API 切换**:保留 localStorage 作为离线缓存 -4. **认证**:NextAuth.js + JWT -5. **权限**:RBAC(Owner/Admin/Member/Viewer),按项目/版本级别 +1. **服务端文档层**:`app_data` + `/api/v1/data/:key`(第一阶段已实现) +2. **localStorage → API 切换**:业务主数据不再写浏览器(第一阶段已实现) +3. **运行 Prisma 同步/迁移**:本地和服务器数据库都需要创建 `app_data` +4. **关系化拆表**:把稳定模块从 JSONB 拆成 Product/Project/Version/Task 等领域表 +5. **认证**:NextAuth.js + JWT +6. **权限**:RBAC(Owner/Admin/Member/Viewer),按项目/版本级别 +7. **版本规则引擎收敛**:VersionPlan 完成条件、关联需求候选、TaskCategory 语义码、TestCase.categoryId 统一收束到规则层 ### 数据迁移策略 -提供管理员脚本把当前用户的 localStorage 数据导出为 SQL,导入到 PostgreSQL。 +当前不做本地导入导出。清站点数据后浏览器旧数据无法恢复,后续新增数据直接写入 PostgreSQL。若以后需要迁移旧浏览器数据,再单独做管理员导入工具。 -## V3 — AI 集成 +## V3 — AI Agent 集成 -### 已规划场景 +详细 Agent 规范见 `agent-spec.md`。本节只列规划,不重复 Agent 实现细节。 -1. **需求智能分解**:输入需求描述,AI 拆解为子任务(技术分析+UI 调整+联调+测试) -2. **风险预警**:自动识别延期/阻塞集中/工时偏差大的任务,提前预警 -3. **排期建议**:基于成员负载和历史耗时,建议下一阶段任务分配 -4. **需求转任务**:需求采纳后一键生成 DevTask 草稿 -5. **健康度智能解读**:把数据指标转化为自然语言报告 +### V3.1 — Prototype Decompose Agent(首个 Agent) -### 落地方式 +**目标**:从产品方案的原型 + 关联需求,拆解出开发任务草案 + 测试用例草案。 -`apps/server/src/modules/ai/`: -- `AiGateway` — 统一 prompt 管理、token 计量、降级 -- `AiTaskService.decompose(description)` -- `AiRiskService.analyze(projectId)` -- `AiScheduleService.suggest(projectId)` -- `AiLog` 表存调用记录,便于审计 +**已完成的数据底座**(2026-06): +- DevTask / TestCase 加 references[] + aiDraft + aiDraftAt +- 创建表单加「原型批注」字段 +- 列表中 AI 草案视觉区分(紫色边 + 徽章) +- 编辑后自动清除 aiDraft 标记 +- agent-spec.md / glossary.md 文档落地 +- 约定:原型链接 = 产品方案 (VersionPlan type=product) 已完成计划的 resultUrl,不在 Version 上独立存储 + +**待实现**: +1. 后端 `AiGateway` + `PrototypeDecomposeService`(NestJS module) +2. 前端「AI 拆解任务和用例」按钮(产品方案 Tab) +3. 对账报告组件(弹窗呈现三段:完美对应 / 单边 / 含糊) +4. 用户确认后批量创建 DevTask + TestCase 草案 +5. AiLog 表(调用记录、token 计量、用时) + +**MVP 范围限制**: +- 不自动分配 assignee(留给用户在草案上手填) +- 不做"上一版基准 diff"(按 decisions.md #17 决议) +- 单 Agent 单 Round,不做多 Agent 编排 + +### V3.2 — Risk Watch Agent + Schedule Suggest Agent + +**Risk Watch Agent**:自动识别延期/阻塞集中/工时偏差大的任务,提前预警 + +**Schedule Suggest Agent**:基于成员负载和历史耗时,建议下一阶段任务分配 + +**多 Agent 协作设计**:到 V3.2 才真正涉及,当前 agent-spec.md 仅占位 + +### V3.3 — 其他场景候选 + +1. 需求智能分类(自动归档到产品 / 项目) +2. 健康度智能解读(数据指标 → 自然语言报告) +3. 需求转任务(需求采纳后一键生成 DevTask 草稿) + +### 落地约束 + +- **不直接动数据**:所有 Agent 写入必须带 `aiDraft: true`,用户编辑后才转正 +- **必须有引用**:所有 AI 产物带 references,用户能追溯到源头 +- **必须有对账报告**:拆解类 Agent 输出前端展示三段报告,让用户决策 +- **可降级**:原型不可达 / 输入数据不全 / 模型超时,明确告知用户失败原因,不写入任何数据 ## 不在路线图(明确不做) @@ -95,6 +133,6 @@ NestJS + Prisma + PostgreSQL Schema 已设计,等业务流程稳定后开始 |------|------| | V1 业务流程打磨 | 进行中 | | V1 朋友试用反馈 | 持续中 | -| V2 后端接入 | 等 V1 稳定 | +| V2 后端接入 | 进行中(V2.1 AppData 已实现) | | V3 AI 集成 | 等 V2 数据沉淀 | | 公开发布 | TBD | diff --git a/docs/superpowers/specs/2026-06-25-version-module-rules-design.md b/docs/superpowers/specs/2026-06-25-version-module-rules-design.md new file mode 100644 index 0000000..9086296 --- /dev/null +++ b/docs/superpowers/specs/2026-06-25-version-module-rules-design.md @@ -0,0 +1,226 @@ +# 版本模块规则引擎与任务类型设计 + +## 背景 + +版本详情里的调研、产品方案、UI 设计、开发任务、测试用例已经形成一条执行链路,但有几处规则还散在 UI 组件里: + +- 开发任务的任务类型太粗,测试用例还没有任务类型。 +- AI 拆解只输出 `role`,不能稳定落到当前系统的任务类型字典。 +- 计划任务可以通过右上角勾选直接完成,绕过子任务和成果提交。 +- 关联需求的候选来源不够明确,容易误用全量需求池。 +- 调研、产品方案、UI 设计的关联需求选择缺少全选能力。 + +这次设计的目标是把这些规则收敛到字典、选择器和工作流引擎里,而不是继续在页面组件里散写条件。 + +## 目标 + +1. 任务类型能支撑更细的开发与测试范围。 +2. DevTask 和 TestCase 共用同一套任务类型字典。 +3. AI 拆解必须输出任务类型,并能稳定映射到系统字典。 +4. VersionPlan 的完成条件由统一规则函数判断。 +5. 所有关联需求入口只从当前版本所属项目下的已采纳需求中选择,不从全量需求池选择。 +6. 调研、产品方案、UI 设计的关联需求选择支持全选。 +7. 项目文档明确约束:跨模块联动、状态流转、完成条件、AI 契约必须优先考虑引擎、状态机或统一规则函数。 + +## 非目标 + +- 不做本地数据导入导出。 +- 不在这一步把 `app_data` 全量拆成关系表。 +- 不让 AI 自动分配负责人。 +- 不让 AI 直接写入正式 DevTask/TestCase;仍然只写入草案。 + +## 方案概览 + +新增或调整三个规则层: + +1. `task-category.ts`:任务类型字典扩展为开发、测试、实施、其他,并增加稳定语义码。 +2. `requirement-selector.ts`:统一筛选当前项目下可用于关联的需求。 +3. `version-plan-workflow.ts`:统一计算计划进度、是否能提交成果、是否能完成、缺少什么条件。 + +页面组件只负责展示和触发动作,不直接决定业务规则。 + +## 任务类型字典 + +`TaskCategory` 继续作为开发任务和测试用例的共享字典,但增加一个稳定字段: + +```ts +interface TaskCategory { + id: string; // 存储主键,仍用于 DevTask.categoryId / TestCase.categoryId + code: string; // 稳定语义码,给 AI 和映射逻辑使用 + name: string; // 展示名称,可被管理员调整 + group: 'development' | 'testing' | 'implementation' | 'other'; + color?: string; + sortOrder: number; + isSystem: boolean; +} +``` + +推荐预置分类示例: + +- 开发:前端页面、前端交互、后端接口、后端业务逻辑、数据库/数据结构、权限/流程、第三方集成、接口联调 +- 测试:功能测试、接口测试、异常场景、兼容性测试、回归测试、数据校验 +- 实施:数据处理、实施支持、配置部署 +- 其他:文档、协调、待确认 + +`id` 用于系统存储,`code` 用于 AI 输出和迁移兼容。管理员可以改名称,但不建议随意修改系统预置 `code`。 + +## 测试用例任务类型 + +`TestCase` 增加 `categoryId`: + +```ts +interface TestCase { + categoryId: string; +} +``` + +影响范围: + +- `TestCaseCreateModal` 增加任务类型必填项。 +- `TestCaseRow` 和 `TestCaseDetailDrawer` 展示类型标签。 +- `useTestCaseStore.fetchTestCases` 对旧数据做兼容补齐,默认映射到测试分组的第一个系统类型。 +- AI 草案写入 TestCase 时也要映射 `categoryCode -> categoryId`。 + +## 关联需求选择规则 + +所有关联需求入口统一使用同一套候选规则: + +```ts +requirements.filter((r) => + r.projectId === version.projectId && + r.status === 'adopted' +) +``` + +含义: + +- 候选来自当前版本所属项目下的需求,不从产品级全量需求池直接取。 +- 只有已采纳需求可被新选择。 +- 如果历史计划已经关联了某条需求,但该需求后来不再满足候选条件,编辑时保留旧关联并标记为“历史关联”,避免悄悄丢数据。 +- 版本需求 Tab、调研计划、产品方案、UI 设计、开发任务和测试用例中的需求选择都复用该规则函数。 + +调研、产品方案、UI 设计的关联需求控件增加: + +- 全选当前候选。 +- 清空选择。 +- 已选数量 / 候选数量提示。 +- 候选为空时说明需要先在项目下采纳需求。 + +## VersionPlan 工作流引擎 + +新增 `apps/web/lib/version-plan-workflow.ts`,作为计划完成规则的唯一入口。 + +建议导出: + +```ts +interface PlanCompletionState { + checklistTotal: number; + checklistCompleted: number; + requirementTotal: number; + requirementCompleted: number; + hasResult: boolean; + canSubmitResult: boolean; + canComplete: boolean; + missingReasons: string[]; +} + +function getPlanCompletionState(plan: VersionPlan): PlanCompletionState; +function canTogglePlanChecklist(plan: VersionPlan, now?: Date): boolean; +function canEditPlanRequirementCoverage(plan: VersionPlan, now?: Date): boolean; +``` + +完成规则: + +- 调研、产品方案、UI 设计都使用 `tasks` 作为子任务清单。 +- 子任务必须全部勾选完成。 +- 完成时必须提交成果,成果可以是链接或文件。 +- 产品方案和 UI 设计如果有关联需求覆盖清单,也必须全部勾选覆盖完成。 +- 右上角“直接完成”的勾选按钮取消。 +- `PlanTab` 和 `PlanDetailDrawer` 都只调用工作流引擎,不再各自写完成条件。 + +状态写入仍保持: + +- `pending -> in_progress -> completed` +- `actualStartAt` 在开始时写入。 +- `completedAt` 只能通过提交成果完成时写入。 + +## AI 拆解契约 + +AI 输出不直接返回数据库 `categoryId`,而是返回稳定语义码: + +```ts +interface AgentDevTaskDraft { + title: string; + description?: string; + categoryCode: string; + priority: 'P0' | 'P1' | 'P2' | 'P3'; + estimateHours: number; + references: AgentReference[]; +} + +interface AgentTestCaseDraft { + title: string; + description: string; + categoryCode: string; + priority: 'P0' | 'P1' | 'P2' | 'P3'; + references: AgentReference[]; +} +``` + +映射规则: + +1. 优先按 `TaskCategory.code` 匹配。 +2. 匹配不到时按名称做宽松匹配。 +3. 仍匹配不到时,开发任务落到开发分组默认类型,测试用例落到测试分组默认类型。 +4. 映射失败不阻断 AI 报告展示,但确认写入前必须能得到 `categoryId`。 + +`prompts/decompose.ts` 的 tool schema 要求 devTaskDrafts 和 testCaseDrafts 都必须带 `categoryCode`。 + +## UI 行为 + +- 计划卡片右上角不再显示直接完成的勾选按钮。 +- 完成入口改为“提交成果”,按钮在条件不足时禁用并展示缺失原因。 +- 完成弹窗保留链接/文件两种结果类型。 +- 调研、产品方案、UI 设计新建/编辑时都展示关联需求选择,并支持全选。 +- 测试用例新建弹窗增加任务类型选择。 +- AI 草案确认弹窗展示每条任务/用例的任务类型。 + +## 兼容与迁移 + +当前业务数据仍存于 PostgreSQL `app_data` JSONB。 + +- 新增字段时在 store fetch 阶段做轻量兼容补齐。 +- 旧 TestCase 没有 `categoryId` 时补齐默认测试类型。 +- 旧 TaskCategory 没有 `code` 时按系统预置映射补齐;自定义类型生成稳定 code。 +- 不恢复业务 localStorage 缓存。 + +## 验证计划 + +实现时至少覆盖: + +- `version-plan-workflow` 单元测试: + - 子任务未完成不能完成。 + - 未提交成果不能完成。 + - 子任务完成 + 成果存在才可完成。 + - 产品方案/UI 设计有关联需求覆盖时,未覆盖完不能完成。 +- 任务类型映射测试: + - `categoryCode` 能映射系统类型。 + - 未知 `categoryCode` 有合理 fallback。 + - TestCase 旧数据能补齐类型。 +- AI schema 测试: + - devTaskDrafts 和 testCaseDrafts 都要求 `categoryCode`。 +- 手工 UI 验证: + - 调研/产品/UI 关联需求全选可用。 + - 候选需求只来自当前项目已采纳需求。 + - 顶部直接完成按钮消失。 + - 测试用例创建必须选择任务类型。 + +## 后续实现顺序 + +1. 写规则引擎和选择器测试。 +2. 实现任务类型字典扩展和旧数据兼容。 +3. 给 TestCase 增加 `categoryId`。 +4. 接入 VersionPlan 工作流引擎。 +5. 接入需求选择器和全选 UI。 +6. 升级 AI schema、共享类型和草案写入映射。 +7. 跑类型检查和核心测试。 diff --git a/docs/workflow.md b/docs/workflow.md index 6e992f8..c8e36d5 100644 --- a/docs/workflow.md +++ b/docs/workflow.md @@ -39,7 +39,8 @@ - [ ] 是否需要在 `workspace-engine.ts` 加聚合? - [ ] 删除版本时是否需要清理这类数据? - [ ] 工作台 / 版本详情 / 项目详情三处的统计是否同步? -- [ ] localStorage 旧数据是否需要兼容处理? +- [ ] 是否需要新增 `app_data` key(后端 `data-keys.ts` + 前端 `server-data.ts`)? +- [ ] 是否需要从旧浏览器数据做一次性迁移?(当前不做本地导入导出) ## Drawer(侧边详情)规范 @@ -85,15 +86,19 @@ 朋友拉新代码出现"显示问题"时,按顺序排查: -1. 旧 localStorage 数据格式不兼容 → 让朋友清缓存 -2. 类型定义和实际数据不一致(缺字段) -3. 列宽溢出导致裁切 -4. 派生计算错误(filter 条件错) -5. 跨模块联动断了(store 的 store.getState() 调用时机) +1. 后端是否启动:`GET http://localhost:3001/api/v1/config/ai` 应返回 200 +2. 数据库是否启动并完成 Prisma 同步:`app_data` 表必须存在 +3. 对应 `app_data.key` 是否有值,例如 `products-overview` / `requirements` / `dev-tasks` +4. 前端 store 是否已经调用对应 `fetch*` 方法 +5. 类型定义和实际 JSON 数据不一致(缺字段) +6. 列宽溢出导致裁切 +7. 派生计算错误(filter 条件错) +8. 跨模块联动断了(store 的 store.getState() 调用时机) ## 测试 / 验证流程 - 改动后必须 `npx tsc --noEmit` 通过 +- 涉及服务端数据持久化:`pnpm --filter server exec prisma validate --schema prisma/schema.prisma` - 涉及 UI 改动:`curl http://localhost:3000/` 检查 200 - 不会自动跑 dev server,假定它已经运行 @@ -116,3 +121,66 @@ Workspace 页面(树筛选 + tab 筛选 + 已完成开关) ``` 新增模块时,只需在 `aggregateWorkItems` 中添加聚合逻辑,工作台自动展示。 + +## AI 拆解工作流(V3.1) + +详细 Agent 规范见 `agent-spec.md`。这里是用户视角的工作流: + +``` +1. 创建版本(不需要单独填原型链接) + ↓ +2. 在版本详情 → 产品方案 Tab 创建一条 product 计划 + ↓ +3. 完成产品方案计划,提交「成果」(成果链接即原型链接,附成果标题) + ↓ +4. 从当前项目已采纳需求中关联需求到本版本 + ↓ +5. 点击「AI 拆解任务和用例」按钮(位于产品方案 Tab) + ↓ +6. Agent 从 product 类型的 completed 计划取 resultUrl 作为原型, + 抓取原型 + 关联需求 + 版本成员,输出对账报告 + 任务/用例草案 + ↓ +7. 用户审核对账报告 + ├─ 报告全 ✅:直接确认写入 + ├─ 报告有 ⚠️/❓:选择性放弃部分草案 / 补充信息后重跑 + └─ 报告全 ❓:放弃 AI 拆解,人工创建 + ↓ +8. 确认后,DevTask / TestCase 草案写入对应 Tab,标记 aiDraft: true + ↓ +9. 团队成员在 DevTask Tab 看到紫色边的 AI 草案任务 + ↓ +10. 任意成员编辑任务(改标题/描述/负责人/优先级/时间/分类),保存后 aiDraft 自动清除 + (changeStatus / setBlocked 等用户主动操作也会清除) +``` + +**触发条件**(AI 拆解按钮可点): +- 至少有 1 条 type=product 的产品方案计划处于 completed 且 resultUrl 非空 +- 至少 1 条需求关联到本版本 + +**人工创建任务/用例的引用要求**: +- 创建 DevTask 时已选「关联需求」自动作为 reference 写入 +- 创建表单加「原型批注」字段(手填,逗号或空格分隔,如 `QY0007, QY0023`) +- 至少一类引用非空才能保存(V3.1 暂未做强校验,靠 UI 引导) + +## 高级开发约束:规则先归位 + +涉及以下任意类型的改动时,先判断规则应该放在哪一层,不允许直接在页面组件里散写临时判断: + +- 跨模块联动:例如 Requirement、VersionPlan、DevTask、TestCase、Bug 互相派生状态。 +- 状态流转:例如 `pending -> in_progress -> completed`、`todo -> testing -> submitted`。 +- 完成条件:例如子任务是否完成、是否提交成果、是否允许点击完成。 +- 候选数据来源:例如关联需求只能来自当前项目已采纳需求,不能从全量需求池随手取。 +- AI 写入契约:例如 AI 输出任务类型、引用来源、草案标记。 + +默认落点: + +- 可派生数据进 `*-engine.ts` 或纯函数 helper。 +- 有状态流转的实体要有状态机或 workflow helper。 +- 多个组件共用的候选筛选规则进 selector/helper。 +- AI 输入输出字段先更新 `agent-spec.md` 和 shared type,再改 prompt/schema。 + +版本模块新增规则: + +- `version-plan-workflow.ts` 是调研/产品方案/UI 设计完成条件的唯一入口。 +- `requirement-selector.ts` 是版本内关联需求候选的唯一入口。 +- `TaskCategory.code` 是 AI 和系统任务类型的稳定映射锚点,`id` 只作为存储主键。