# 任务执行状态卡片与刷新持久化 测试报告 ## 一、 测试背景 (Background) 对话启动任务后,进度只以气泡内横向 `PhaseEventStrip` 展示:无标题、无耗时、无「当前正在做什么」的突出横幅。`phaseEvents` 不落库,刷新后 completed 任务进度条消失,退化成纯文本。用户要求保留卡片形态,且刷新后不消失、不退化。 ## 二、 测试目标 (Test Objectives) - 任务气泡渲染竖向时间线 `TaskRunCard`(标题 + 状态徽章 + 耗时 + 步骤 + 当前阶段)。 - Finalize 将 `metadata.task_phase_snapshot` 写入助手消息;刷新后窗口 API 带回完整快照,卡片可零请求重建。 - 存量无 snapshot 的消息仍可通过 `replay_only` 扩面重放恢复步骤。 - 验收必须走真实 `pi-agent`(`MiniMax-M2.7-highspeed`),禁止 stub 冒充交付数据。 ## 三、 测试方案与环境 (Test Plan & Environment) - OS:linux。分支 `feature/taskrun-status-card-2026-09-21`,合入本地 `dev` 于集成 worktree `.worktrees/agent-governance-dev-integration`。 - `./scripts/env-service.sh dev rebuild`(worktree 内通过符号链接复用主仓 `.env` / `configs/.env.dev`)。 - 单元:`go test -short ./internal/taskrun ./internal/ctxmgr`;`cd web && npm test && npm run build`。 - 端到端:为用户 398(陈威)签发一次性会话(验收后立即 `revoked_at`),项目 6579 / `sess-p6579-u398` / `task_key=bd_intake` 发起真实任务。 ## 四、 预期结果 (Expected Results) - `/health`:`env=dev`、`agent_backend=pi-agent`、`schema_version=60`。 - 新任务终态助手消息 `metadata.task_phase_snapshot` 含 `title/steps/duration_ms/status`。 - `GET /api/v1/chat/messages/window` 同消息带回 snapshot(模拟刷新)。 - 前端包含 `task-run-card` / `task_phase_snapshot`。 - 旧 run 的 `events?replay_only=1` 仍能吐出 phase 事件。 ## 五、 实际测试结果与判定 (Actual Results & Evaluation) ### 用例 1:健康与真实 PI - **测试输入**:`curl -sf http://127.0.0.1:8080/health`;`curl -sf http://127.0.0.1:18865/health` - **实际输出**: ```json {"status":"ok","env":"dev","agent_backend":"pi-agent","version":"v1.0","shared_kb_port":5432,"kb_ready":true,"database_ready":true,"schema_version":60,"schema_ready":true,"task_store_ready":true} ``` pi-agent:`ok=true`,`model_id=MiniMax-M2.7-highspeed`。 - **评价判定**:PASS - **性能与耗时**:<1s ### 用例 2:真实任务终态写入 phase 快照 - **测试输入**:`POST /api/v1/tasks/runs`,`project_id=6579`,`session_id=sess-p6579-u398`,`task_key=bd_intake`,`user_message=编写用户初始需求收集表(状态卡验收)` - **实际输出**:HTTP 202,`run_id=run_1789978731880379297`,约 4 分钟后 `status=pending_author_confirmation`,`assistant_message_id=4424`。 - DB 取样(`chat_messages.id=4424`): ```text has_snap=t title=用户初始需求收集表 duration_ms=244864 steps=4 steps: squad_assembly / step_executing / quality_gate / advisor_briefing(均为 completed) ``` - **评价判定**:PASS(真实 PI 全链路;快照已持久化) - **性能与耗时**:创建 202 ≈0.2s;终态 ≈244s ### 用例 3:刷新窗口带回卡片数据 - **测试输入**:`GET /api/v1/chat/messages/window?session_id=sess-p6579-u398&days=1`(同一用户一次性会话) - **实际输出**:`window_ok 4424 pending_author_confirmation 用户初始需求收集表 244864 4` - **评价判定**:PASS(刷新路径可重建卡片;前端 `mapRecordToMessage` 单测同步覆盖) - **性能与耗时**:≈0.3s ### 用例 4:存量 run 的 replay_only 扩面 - **测试输入**:`GET /api/v1/tasks/runs/run_1789975357256493376/events?after_seq=0&replay_only=1` - **实际输出**:HTTP 200,`legacy_phase_events=12`(含 `squad_assembly` 等 phase) - **评价判定**:PASS - **性能与耗时**:≈0.2s ### 用例 5:前端包卡片标记 - **测试输入**:容器内 `grep task-run-card / task_phase_snapshot` 于 `/app/web/dist/assets/index-*.js` - **实际输出**:`index-m1o_7k1U.js` 均命中(见 assets/bundle-markers.txt) - **评价判定**:PASS - **性能与耗时**:字符串检查一次完成 ## 六、 自动化测试套件汇总 (Test Suite Execution) - `go vet ./internal/taskrun/... ./internal/ctxmgr/... ./internal/server/...`:通过 - `go test -short ./internal/taskrun ./internal/ctxmgr`:通过 - `web npm test`:468 + vitest 33 通过 - `web npm run build`:通过(tsc + vite) 证据目录:`.doc/test-reports/assets/2026-09-21-taskrun-status-card-persistence/` ## 七、 测试结论与后续优化建议 (Conclusion & Next Steps) 达到合入 `dev` 标准:真实 PI 任务终态写入 `task_phase_snapshot`,刷新窗口可重建竖向时间线卡片;存量消息仍可 SSE 重放。一次性鉴权会话已撤销。test/main 不晋升;不改 `production_ready`;项目 143 未改写。 后续可选:运行中任务刷新时把 live `phaseEvents` 增量写回 metadata(当前依赖 SSE 订阅 + 终态快照);步骤多时时间线折叠。 --- ## 八、 二轮修复验收:收口后 label 冻结为进度中文案(2026-09-21) ### 8.1 背景与缺陷取证 一轮验收后用户反馈:任务显示"已完成",但卡片下方步骤仍读作运行中,出现 "贴身业务参谋正在提炼关键决策摘要与风险雷达...(98%)"。DB 取证(`chat_messages.id=4424`): 步骤 `status` 均为 `completed`,但 `label` 永久冻结为写入时的进度中叙述。 根因:后端 `phase_snapshot.go`(`applyPhasePayload` 收口分支 + `BuildPhaseSnapshot` 终态兜底) 与前端 `phaseEventMapper.ts`(`applyPhaseEvent` 三处收口分支)只翻转 `status`、不改写 `label`; `label` 来自上游 `phase.Message` 原样拼百分比后缀,落库后不再修正。 ### 8.2 修复内容与自动化回归 - `fix(taskrun)`:新增导出 `NormalizeSettledStep`(completed 步骤归一化为短标签 + "已完成", 保留"(第 x/y 步)"结构后缀;failed 有意保留原文案;幂等)。 - `fix(web)`:`settleStep` 对称归一化 + `displayStepLabel` 渲染层兜底(防历史脏数据)。 - `chore(ops)`:`scripts/backfill_task_phase_snapshot_labels.go` 历史回填(幂等,支持 `-dry-run`/`-id`)。 - 单元回归:`go test ./internal/taskrun/... ./internal/ctxmgr/...` 通过(含 `#4424` 复现用例、 幂等用例、多步后缀保留用例);`web npm test` 472+33 全绿;`tsc --noEmit` + `npm run build` 通过。 ### 8.3 历史数据回填实测(dev) - 回填前取样见 `assets/.../backfill-before.txt`(4424/4508 共 8 个 completed 步骤全部冻结为 "正在…(NN%)"文案)。 - 执行:`go run ./scripts/backfill_task_phase_snapshot_labels.go`(先 `-dry-run` 确认目标集) → `backfill complete: scanned_candidates=2 updated=2`(`assets/.../backfill-run.txt`)。 - 回填后取样见 `assets/.../backfill-after.txt`:8 个步骤 label 全部归一化为 "组建小队已完成 / 专员执行已完成 / 质量门禁已完成 / 参谋简报已完成"。 - 幂等验证:二次 `-dry-run` 报告 0 条待变更。 ### 8.4 真实 Agent 全链路验收(红线) - 环境:dev 容器重建后(`env-service.sh dev rebuild`,`/health` `schema_version=61`、 `agent_backend=pi-agent`);pi-runner `MiniMax-M2.7-highspeed`(`shuimu-pi-agent-dev` healthy)。 - 端到端:为用户 398 签发一次性会话(验收后已 `revoked_at`), `POST /api/v1/tasks/runs` → HTTP 202 `run_1789989929668718353` (`project_id=6579, task_key=bd_intake, session_id=sess-p6579-u398`)。 - **判定关键**:本条 run 由**新部署代码的 finalize 前向路径**直接落库,未经任何回填。 新助手消息 `id=4548`(`status=pending_author_confirmation`,耗时 `duration_ms=340510`)快照: ```text steps: squad_assembly completed label=组建小队已完成 step_executing completed label=专员执行已完成 quality_gate completed label=质量门禁已完成 advisor_briefing completed label=参谋简报已完成 ``` 原始"正在…(NN%)"叙述保留在 `detail` 字段(供审计),不再出现在 `label`。 完整快照见 `assets/.../message-4548-snapshot-round2.txt`,run 记录见 `real-task-round2.txt`。 - 刷新恢复(模拟 F5):`GET /api/v1/chat/messages/window?session_id=sess-p6579-u398&days=1` 返回 4548 的 snapshot,4 个 completed 步骤 label 均为静态措辞 (脚本断言无"正在"/"%"残留,`WINDOW_API_LABELS_OK`;见 `window-round2.json`)。 - 部署制品核验:容器 `index.html` 引用 `assets/index-CgkGfOP7.js`,bundle 内含 `参谋简报` / `task-run-card` / `task_phase_snapshot` 标记(`bundle-markers-round2.txt`)。 - **评价判定**:PASS(真实 PI 全链路 + 前向路径 + 刷新恢复 + 历史回填四证齐全) - 一次性会话已撤销(`auth_sessions.revoked_at` 已置)。 ### 8.5 二轮结论 - `production_ready` 不变;test/main 不晋升;项目 143 未触碰。 - 新任务不再产生冻结文案;历史脏数据(4424/4508)已由幂等回填脚本修复; 渲染层兜底保证即使回填遗漏也不会出现"已完成却像还在跑"的文案。