# 工作流程

记录用户的协作偏好和系统化流程，方便后续模型理解项目运作方式。

## 用户协作风格

- **直接修复明确 bug**，不强制走 brainstorming 流程
- **方案确认后立即执行**，不重复讨论
- **不喜欢自动 push**：commit 由模型完成，**push 需要用户明说"推/push"**
- **要求高级开发思维**：考虑数据联动、引擎层抽象，而不是分散写 ad-hoc 逻辑
- **重视一致性**：所有 Drawer 阴影、所有时间格式、所有状态计算都要统一
- **省略多余对话**：能直接做的不要问，只在真有歧义时用 AskUserQuestion 给选项
- **Brainstorming 适用于新功能**，bug fix 和已批准方案的延续不走 brainstorming

## 实际工时计算流程

所有"实际开始时间"和"实际完成时间"都是 ISO 时间戳：

```
状态变更时机：
- DevTask: todo → in_progress 时记 startDate
            testing → submitted 时记 completedAt
- TestCase: pending → running 时记 startedAt
            running → passed/failed/blocked 时记 completedAt
- VersionPlan: pending → in_progress 时记 actualStartAt
                in_progress → completed 时记 completedAt

计算：
- 实际工时 = (completedAt - startDate) / 3600000，精确到 0.5h
- 阶段日历耗时 = min(start) → max(end) 跨度（项目维度，不重复）
- 个人耗时 = 每个任务独立累加（个人维度）
```

## 开发任务延后原因判断

开发任务从“待开发”切换到“开发中”时，只有当前时间已经超过 `expectedEndAt`（预计截止）才要求填写延后原因。超过预计开始时间但仍未超过预计截止时间，不视为延后。

## AI 草案领取与计划流程

开发任务和测试用例由 AI 生成后，版本详情里不再显示独立的“待排期”状态：

1. DevTask / TestCase 没有负责人时显示“待领取”。
2. 点击“领取并填写计划”时同时写入当前用户为负责人，并填写计划起止时间。
3. 已采纳推荐负责人的 AI 草案已经有负责人，不需要领取，但开始开发/测试前仍必须点击“填写计划”补齐计划起止时间。
4. DevTask 开始开发前必须同时具备 `assigneeId`、`expectedStartAt`、`expectedEndAt`；TestCase 开始测试前必须同时具备 `assigneeId`、`plannedTestAt`、`plannedEndAt`。
5. 计划起止时间自动按工作日历计算 `estimateHours`。AI 预估仍保留在 `aiEstimateHours`，不代表负责人已确认排期。

## 开发任务提测流程

1. DevTask 从“自测”转为“已提测”前，任务不能处于阻塞中。
2. 如果 `isBlocked=true`，必须先解除阻塞并清空阻塞原因，再允许提测。
3. “已提测”仍然是 DevTask 终态；后续测试通过或失败不回写 DevTask 状态。

## 测试轮次流程

测试用例支持按版本开启多轮测试：

1. 旧测试用例或未写入 `roundNo` 的测试用例默认属于第 1 轮。
2. 点击“开启新一轮测试”时，系统从第 1 轮复制全部测试用例到下一轮，包含 AI 创建和人工创建的用例。
3. 只有最新一轮全部测完（状态为通过、不通过或阻塞）后，才能开启下一轮；第 1 轮未测完不能开启第 2 轮。
4. 新轮次保留用例范围与估时信息：关联需求、任务类型、优先级、负责人、引用来源、AI 预估、执行预估。
5. 新轮次清空执行记录：状态回到待测试，不带开始/完成时间、执行人、失败原因、阻塞原因。
6. 测试用例列表、当前轮次进度、无 Bug 通过率按选中的轮次展示。
7. 顶部耗时汇总按当前版本全部轮次累计，实际耗时和人力投入会包含新一轮执行产生的耗时。
8. 测试用例按所属需求分组展示开发提测标签：该需求下所有开发任务都已提测时显示“已提测”，否则显示“待提测”。

## 数据联动检查清单

新加模块或字段时，检查以下点：

- [ ] 是否需要在 `linkage-engine.ts` 加派生函数？
- [ ] 是否需要在 `workspace-engine.ts` 加聚合？
- [ ] 删除版本时是否需要清理这类数据？
- [ ] 工作台 / 版本详情 / 项目详情三处的统计是否同步？
- [ ] 是否需要新增 `app_data` key（后端 `data-keys.ts` + 前端 `server-data.ts`）？
- [ ] 是否需要从旧浏览器数据做一次性迁移？（当前不做本地导入导出）

## Drawer（侧边详情）规范

所有侧边详情遵循：

- `fixed inset-0 z-50 flex justify-end`
- 背景 `bg-black/40`
- Drawer 容器 `w-full max-w-md h-full bg-[var(--bg)] border-l shadow-2xl flex flex-col`
- 顶部上下文条（可选）：`px-5 py-2 bg-[var(--bg-subtle)] text-[11px]`
- 顶栏：`h-14 border-b bg-[var(--bg-card)]`
- 操作按钮按颜色编码：blue=进行/转交、emerald=完成、red=删除/失败、orange=关闭/提BUG

## Modal（弹窗）规范

- 居中 `flex items-center justify-center bg-black/40`
- `rounded-2xl bg-[var(--bg-card)] border shadow-md`
- 提交 BUG 等图片密集型用 `max-w-2xl`，普通表单 `max-w-md` 或 `max-w-lg`

## 字段命名规范

- 实际开始：`startDate` (DevTask) / `startedAt` (TestCase) / `actualStartAt` (VersionPlan)
- 实际完成：`completedAt`（统一）
- 派生进度：在 lib 层提供函数，不存储

历史原因导致命名不完全一致（DevTask 用 startDate 是因为最早是日期字段），但行为一致。

## 提交信息规范

- 中文 commit message
- 格式：`类型(模块): 描述`
  - feat / fix / refactor / docs / test
- 描述列出关键改动点，特别是跨模块影响
- Co-Authored-By 行带版本号

## 文档维护流程

- **新增/改动核心架构** → 更新 `architecture.md`
- **关键设计决策** → 追加到 `decisions.md`，包含"为什么"
- **新功能/路线图变更** → 更新 `roadmap.md`
- **不影响架构的功能性改动** → 不需要更新文档

## Bug 排查流程

朋友拉新代码出现"显示问题"时，按顺序排查：

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/<path>` 检查 200
- 不会自动跑 dev server，假定它已经运行

## AppData 并发写入流程

- 前端通过 `loadServerData(key)` 读取业务文档时，必须缓存响应里的 `version`。
- 前端通过 `saveServerData(key, value)` 保存已读取过的 key 时，必须把最近一次成功读取/保存得到的 `version` 一起提交。
- 后端只在 `key + updatedAt(version)` 匹配时更新；如果其他用户已经先保存，返回 `409 APP_DATA_CONFLICT`，响应包含当前服务端 `currentVersion` 和 `currentValue`。
- 收到 `ServerDataConflictError` 时，不要自动重试覆盖。当前处理策略是阻止静默覆盖，后续 UI 冲突合并能力再单独补。

## 与我相关（Workspace）数据流

```
useProductStore → versions
useRequirementStore → requirements (with versionId)
useDevTaskStore → tasks (with versionId; legacy with requirementId)
useTestCaseStore → testCases (with versionId)
useBugStore → bugs (with versionId)
useVersionPlanStore → plans (with versionId, owner)
useAuthStore → user (filter by current user)
        ↓
workspace-engine.aggregateWorkItems(...)
        ↓
WorkItem[] (统一格式)
        ↓
Workspace 页面（树筛选 + tab 筛选 + 已完成开关）
```

新增模块时，只需在 `aggregateWorkItems` 中添加聚合逻辑，工作台自动展示。

## AI 拆解工作流（V3.1）

详细 Agent 规范见 `agent-spec.md`。这里是用户视角的工作流：

```
1. 创建版本（不需要单独填原型链接）
   ↓
2. 在版本详情 → 产品方案 Tab 创建一条 product 计划
   ↓
3. 完成产品方案计划，提交「成果」（成果链接即原型链接，附成果标题）
   ↓
4. 从当前项目已采纳需求中关联需求到本版本
   ↓
5. 点击「AI 拆解开发任务」或「AI 拆解测试用例」按钮（位于产品方案 Tab）
   ↓
6. Agent 从 product 类型的 completed 计划取 resultUrl 作为原型，
   抓取原型 + 关联需求 + 版本成员，输出对账报告 + 任务/用例草案
   （关联需求优先；原型中明确可拆但没有 REQID 的内容进入“无需求ID”分组）
   - 生成中状态和完成结果保存在前端 AI 拆解会话中；用户离开版本详情再返回时，未结束的生成继续保持“拆解中”，已完成的生成显示“查看结果”。
   - 生成结果只有在用户点击「采纳选中」或「不采纳」后才清空；单纯关闭弹窗或切到其他页面不会丢失结果。
   ↓
7. 用户审核对账报告
   ├─ 系统先自动过滤已采纳过的重复 DevTask/TestCase 草案
   ├─ 报告全 ✅：直接确认写入
   ├─ 报告有无需求ID分组：确认后写入任务/用例，但不创建需求池记录
   ├─ 报告有 ⚠️/❓：选择性放弃部分草案 / 补充信息后重跑
   └─ 报告全 ❓：放弃 AI 拆解，人工创建
   ↓
8. 确认后，DevTask / TestCase 草案写入对应 Tab，标记 aiDraft: true，并写入 aiEstimateHours；
   无需求ID草案按 `无需求ID · 需求名称` 分组展示，不额外显示“原型发现”标记
   ↓
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 输出任务类型、引用来源、草案标记。
- AI 无需求ID分组：原型中明确可拆但没有匹配关联需求的内容，只写入 DevTask/TestCase 的 `requirementName`，不进入需求池或版本关联需求列表。

默认落点：

- 可派生数据进 `*-engine.ts` 或纯函数 helper。
- 有状态流转的实体要有状态机或 workflow helper。
- 多个组件共用的候选筛选规则进 selector/helper。
- AI 输入输出字段先更新 `agent-spec.md` 和 shared type，再改 prompt/schema。

AI 估时约束：
- `aiEstimateHours` 是 AI 建议工时，只能由 AI 拆解写入。
- `estimateHours` 是执行人预估，只能由人工创建/编辑或负责人确认排期时写入。
- 统计进度优先取 `estimateHours`，没有时取 `aiEstimateHours`，避免 AI 草案在未确认前失去统计权重。

版本模块新增规则：

- `version-plan-workflow.ts` 是调研/产品方案/UI 设计完成条件的唯一入口。
- `requirement-selector.ts` 是版本内关联需求候选的唯一入口。
- `TaskCategory.name` 是 AI 草案的可复用任务类型锚点；可复用开发类型可自动入库，测试用例未知类型回退到已有测试分类。`TaskCategory.code` 仅作为可选兼容映射，`id` 只作为存储主键。
- DevTask 新数据必须有 `versionId`；`requirementId` 作为正式需求语义标签可选。版本级聚合走 `versionId`，需求级进度只统计带 `requirementId` 的任务。
- 产品方案和 UI 设计的引用需求不再用 checkbox 直接标记完成，必须通过 `requirementCoverage[]` 记录 `not_started / partial / completed`、本次已完成内容和剩余内容；只有 `completed` 计入成果提交门禁。
- 产品/UI 计划右侧展示计划日志，需求进度更新和 AI 拆解触发/完成/失败都写入 `VersionPlan.logs[]`，页面只消费日志数据，不临时拼历史。
- 调研/产品方案/UI 设计的计划级 `actualStartAt` 只表示计划容器已开始，不直接作为具体任务日报耗时。具体调研方向或引用需求需要先点击「开始任务」，写入当前行的 `currentWorkStartedAt`；提交「记录」时日志和 `work-activities` 保留 `workStartedAt`，日报耗时按 `workStartedAt -> 记录提交时间` 计算，提交后清空当前行的开始时间。完全完成可直接提交结束本次耗时，部分完成才需要填写本次已完成内容和剩余未完成内容。
## Work Activity Daily Report Flow (2026-06-26)

The daily report flow uses mixed evidence:

1. Automatic evidence is written when a user performs a successful domain action:
   - VersionPlan created, started, or completed.
   - DevTask created, started, moved to self-test, submitted to test, blocked, or unblocked.
   - TestCase created, started, passed, failed, or blocked.
   - Bug created, moved to fixing, fixed, closed, or transferred.
2. Manual progress notes are used for multi-day work that does not change status today.
   - DevTask「今日进展」必须填写 `nextStartAt`，默认值为第二天 09:30。
   - 带 `nextStartAt` 的进展记录按「上一条记录的 `nextStartAt` → 本次记录时间」计算当日耗时；如果任务是当天开始，才回退到当天实际开始时间。
3. `/workspace` shows only the current logged-in user's report.
4. Project-owner and management views will reuse the same `work-activities` data later, but are not part of the personal workspace panel.

Implementation convention:

- Activity wording and category mapping belong in `apps/web/lib/work-activity-factory.ts`.
- Daily report grouping belongs in `apps/web/lib/workspace-daily-report.ts`.
- Page components should consume report output, not rebuild report rules.

## 小宝预警工作流

小宝预警位于主导航“工作区 / 小宝预警”，展示在“与我相关”上方。可见范围由角色权限控制：

- `xiaobao.warning:manage`：查看所有未结束版本的预警。
- `xiaobao.warning:view`：仅查看当前用户在 `version.members` 中的未结束版本。

页面打开时会聚合版本下的计划、开发任务、测试用例、Bug、日报和工作活动，计算当前风险并保存当天快照。页面使用 `buildXiaobaoWorkItems` 做版本级聚合，不使用个人工作台的 `aggregateWorkItems(userName, ...)` 过滤。快照按同版本同日节流保存：重大变化立即保存，普通变化 10 分钟内不重复写入。

AI 解读不由人工按钮触发。`at_risk`、`likely_delayed`、`blocked` 自动触发；`attention` 在风险分明显上升、趋势连续上升、关键 Bug 增加、测试失败、阻塞增加、静默风险增加、置信度下降或预测发版日延后时触发。缓存命中时复用解读；同版本最近 6 小时内已有解读时进入 cooldown，不重复请求，风险等级升级时可绕过；缓存保存时间使用客户端时间，不信任模型返回的 `generatedAt` 作为缓存新鲜度。

静默风险包括长期无更新、无日报、无活动、进行中事项无人处理等信号。日报和工作活动是风险解释的重要证据，必须进入 AI 解读输入。

## 日期选择与计划时间

- 调研、产品方案、UI 设计、开发任务、测试用例、Bug 创建时使用统一工作日日期时间选择器。
- 日期选择器接入中国节假日日历。当前内置 2026 年国务院办公厅放假调休安排；其他年份先按周末/工作日兜底。
- 非工作日只提示，不阻止保存；调休工作日按工作日提示。
- 测试用例计划测试时间字段为 `plannedTestAt` / `plannedEndAt`；Bug 计划修复时间字段为 `plannedFixAt`。
- 测试轮次复制用例时保留计划测试起止时间、AI 预估和执行预估，清空实际执行记录。

## 工时统计口径

- 正常任务耗时使用 `calcWorkHours` / `calcActualElapsedHours`，按中国工作日历和 9:00-12:00、13:00-18:00 工作时段计算。
- 法定节假日和周末不计入正常任务耗时；调休上班日计入正常任务耗时。
- AI 预估工时和手填执行预估工时本身是小时数，不再按日期过滤；只有从计划起止时间自动推导的执行预估会按工作日历计算。
- 加班记录使用 `overtime.calcDuration`，不受节假日过滤，但按项目管理口径计算：每个自然日默认最多 8h，跨天中间日期按 8h，结束晚于 18:00 的当天额外计入超出时长，避免把夜间空档当作打卡工时。
- 新建加班记录也使用工作日历日期选择器；节假日和周末可选，只提示不阻止。

## 生产部署流程

生产部署以 `docs/deployment.md` 为准，标准顺序如下：

本地服务器/局域网部署：

1. 复制 `.env.local-server.example` 为 `.env.local-server`，按需把 `LOCAL_ACCESS_HOST` 和 `NEXTAUTH_URL` 改为本机局域网 IP。
2. 运行 `pnpm deploy:verify`。
3. 运行 `pnpm deploy:local:build` 构建镜像。
4. 运行 `pnpm deploy:local:up` 启动服务，默认访问 `http://localhost:8080`。
5. 首次部署或升级后运行 `docker compose --env-file .env.local-server -f docker-compose.local.yml exec server pnpm --filter server db:deploy`。
6. 用 `curl http://localhost:8080/api/v1/config/ai` 验证 Nginx 到后端链路。

云服务器部署：

1. 复制 `.env.production.example` 为 `.env.production`，替换密码、域名、`NEXTAUTH_SECRET`、AI key 等真实值。
2. 运行 `pnpm deploy:verify`，确认生产 Docker、Compose、Nginx、env 示例和文档齐全。
3. 运行 `docker compose --env-file .env.production -f docker-compose.prod.yml build` 构建镜像。
4. 运行 `docker compose --env-file .env.production -f docker-compose.prod.yml up -d` 启动服务。
5. 首次部署或升级后运行 `docker compose --env-file .env.production -f docker-compose.prod.yml exec server pnpm --filter server db:deploy`。
6. 用 `curl http://localhost/api/v1/config/ai` 验证 Nginx 到后端链路。
7. 用 `docker compose --env-file .env.production -f docker-compose.prod.yml ps` 和 `logs -f server/web/nginx` 排查健康状态。

生产环境不提交 `.env.production`。HTTPS 默认由外层负载均衡、CDN、宿主机证书工具或外层 Nginx 终止；Compose 内置 Nginx 只承担同域 HTTP 反代。
