diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index c78ab358..93a3f7f2 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -5,7 +5,7 @@ ## 焦点 -1. **任务错误透传**:`fix/taskrun-error-transparency-2026-09-21` 修复 `RunRecord` PascalCase 与前端 `NETWORK_ERROR` 误报(见 [short-term](./short-term/2026-09-21-taskrun-error-transparency.md))。正在占槽合入 dev。 +1. **任务错误透传已合入 dev**:`RunRecord` snake_case 与前端真实失败原因(见 [报告](../test-reports/2026-09-21-taskrun-error-transparency-test-report.md))。test/main 未晋升。 2. **阶段回溯与影响闭环已合入 dev**:T1–T3 与 B1–B3 在 `dev`。空追踪 JSON `null` 已修复,登录后显示「已索引 0 份文档 · 条目 0」(见 [报告](../test-reports/2026-09-21-trace-panel-null-crash-test-report.md))。test/main 未晋升。不改 `production_ready`,不签 D7 对话 ADR。 3. **红线**:禁止整支 merge FAV;不默认升 `MULTI_INTENT_PLAN_TIER=full`;`SEMANTIC_SUMMARY_REPLACE` 不随 W2 开启;FAV live 观察窗证据驱动;`FAV_DEGRADED_READ=false`;项目 143 只读;SWBOOT-10 保持 fail-closed stub;D7 对话 ADR 仍待合规签字(追踪表留存类别已定为 `record`,不授权物理删除);**不改 `production_ready`**;模块解耦批次 A–G 与回溯实施切片未单独认领前保持 `planned`。 @@ -13,7 +13,7 @@ | owner | task | branch | worktree | mutex | status | |-------|------|--------|----------|-------|--------| -| Cursor | 2026-09-21-taskrun-error-transparency | fix/taskrun-error-transparency-2026-09-21 | - | chat+web | in_progress | +| Cursor | 2026-09-21-taskrun-error-transparency | fix/taskrun-error-transparency-2026-09-21 | - | chat+web | done | | Cursor | 2026-09-21-trace-panel-null-crash | fix/trace-panel-null-crash-2026-09-21 | - | trace-index+web | done | | Cursor-Auto | 2026-09-20-stage-backtrack-design | feature/stage-backtrack-design | - | trace-impact-design | done | | Cursor-Auto | 2026-09-20-impact-loop-t1 至 T3/B1–B3 | feature/stage-backtrack-impl | - | trace-index | done | @@ -172,7 +172,7 @@ | holder | phase | since | scope | queue | |--------|-------|-------|-------|-------| -| Cursor | merging | 2026-09-21 | 合入 `fix/taskrun-error-transparency-2026-09-21`:RunRecord snake_case 与前端错误透传;随后 `go vet`、`go test -short`、dev rebuild、`/health`、GET run JSON | - | +| - | idle | 2026-09-21 | 任务错误透传已合入 dev 并 rebuild;`GET /api/v1/tasks/runs/{id}` 为 snake_case;`/health` schema_version=59 | - | **phase**: `idle` | `merging` | `smoke` | `e2e` diff --git a/.doc/ops/short-term/2026-09-21-taskrun-error-transparency.md b/.doc/ops/short-term/2026-09-21-taskrun-error-transparency.md index eac9d2bd..ae092298 100644 --- a/.doc/ops/short-term/2026-09-21-taskrun-error-transparency.md +++ b/.doc/ops/short-term/2026-09-21-taskrun-error-transparency.md @@ -1,7 +1,6 @@ --- title: "任务错误透传:NETWORK_ERROR 误报" -status: in_progress -created: 2026-09-21 +status: done updated: 2026-09-21 owner: Cursor branch: fix/taskrun-error-transparency-2026-09-21 @@ -30,23 +29,23 @@ mutex: chat+web ## 步骤 1. [x] 从 `dev` 检出 `fix/taskrun-error-transparency-2026-09-21` -2. [ ] `RunRecord` json 标签 + 序列化回归测试 -3. [ ] 前端错误透传与用例 -4. [ ] 占集成槽位合入 `dev` 并冒烟(槽位空闲后) -5. [ ] 验收报告 +2. [x] `RunRecord` json 标签 + 序列化回归测试 +3. [x] 前端错误透传与用例 +4. [x] 占集成槽位合入 `dev` 并冒烟(槽位空闲后) +5. [x] 验收报告 ## 验收标准 -- [ ] `GET /api/v1/tasks/runs/{id}` JSON 含 `status`、`error_message`、`run_id`,不含 `Status` -- [ ] 任务 `failed` 且有 `error_message` 时,气泡 code 为 `TASK_FAILED`,不是 `NETWORK_ERROR` -- [ ] `blocked_pending_governance` 仍走治理提示 -- [ ] 未知 4xx 业务码不显示「请检查网络」 +- [x] `GET /api/v1/tasks/runs/{id}` JSON 含 `status`、`error_message`、`run_id`,不含 `Status` +- [x] 任务 `failed` 且有 `error_message` 时,气泡 code 为 `TASK_FAILED`,不是 `NETWORK_ERROR` +- [x] `blocked_pending_governance` 仍走治理提示 +- [x] 未知 4xx 业务码不显示「请检查网络」 ## 相关链接 - 根因:test 环境今晨 natfrp 中继中断是诱因;前端 fallback 与 PascalCase 响应使真实原因不可见 - Long-term: [2026-09-21-http-request-access-log.md](../long-term/2026-09-21-http-request-access-log.md) -- Test report: `.doc/test-reports/2026-09-21-taskrun-error-transparency-test-report.md` +- Test report: [2026-09-21-taskrun-error-transparency-test-report.md](../../test-reports/2026-09-21-taskrun-error-transparency-test-report.md) ## 证据 / 备注 diff --git a/.doc/test-reports/2026-09-21-taskrun-error-transparency-test-report.md b/.doc/test-reports/2026-09-21-taskrun-error-transparency-test-report.md new file mode 100644 index 00000000..78fb8276 --- /dev/null +++ b/.doc/test-reports/2026-09-21-taskrun-error-transparency-test-report.md @@ -0,0 +1,87 @@ +# 任务错误透传(NETWORK_ERROR 误报)测试报告 + +## 一、 测试背景 (Background) + +在 test 环境发起文档编写时,界面返回 `NETWORK_ERROR` / 「网络连接异常,请检查网络后重试」。直接诱因是今晨 natfrp 中继中断导致 SSE 断开;根因是两层缺陷叠在一起: + +- `RunRecord` 没有 json 标签,`GET /api/v1/tasks/runs/{id}` 输出 PascalCase(`Status`、`ErrorMessage`)。前端读 `status` / `error_message` 得到 `undefined`,断线后的完成、治理阻塞、失败分支都不会命中。 +- 失败分支调用 `buildNetworkErrorInfo()` 且不带原因,未知 HTTP 4xx 也落到同一句「请检查网络」。 + +## 二、 测试目标 (Test Objectives) + +- 重建后的 dev 上,`GET /api/v1/tasks/runs/{id}` 使用 snake_case,含 `status`、`error_message`、`run_id`,不含 `Status`。 +- 任务 `failed` 且有 `error_message` 时,前端错误码为 `TASK_FAILED`,不是 `NETWORK_ERROR`。 +- `blocked_pending_governance` 仍先于失败兜底;未知 4xx 不显示「请检查网络」。 +- dev 容器健康,后端为真实 `pi-agent`(`MiniMax-M2.7-highspeed`),不用 stub 充当验收数据。 + +## 三、 测试方案与环境 (Test Plan & Environment) + +- OS:Linux。dev 编排 `agent-dev`,HTTP `:8080`,业务库 `:5433`。合入 `dev@5380cc16` 后执行 `./scripts/env-service.sh dev rebuild`。 +- 后端:`go vet ./...`;`go test -short -count=1 -timeout 20m ./...`;定向 `go test -short ./internal/taskrun ./internal/server`。 +- 前端:`cd web && npm test`(node:test 441 + vitest 32)。 +- 端到端:登录 dev,`GET /api/v1/tasks/runs/run_fresh_lease_031021.771577`;`GET /health`;`GET http://127.0.0.1:18865/health`(pi-agent runner)。 + +## 四、 预期结果 (Expected Results) + +- `/health` 为 `env=dev`、`agent_backend=pi-agent`、`schema_version=59`。 +- 任务 JSON 键为 `run_id`、`status`、`error_message` 等,没有 `Status` / `ErrorMessage`。 +- 前端单测:有 `error_message` 的失败为 `TASK_FAILED`;治理状态不被当成网络错误;`DOC_TASK_DISABLED` 与未知 4xx 不出现「请检查网络」。 + +## 五、 实际测试结果与判定 (Actual Results & Evaluation) + +### 用例 1:dev 健康与真实 pi-agent + +- **测试输入 (Input)**:`curl -sf http://127.0.0.1:8080/health`;`curl -sf http://127.0.0.1:18865/health`。 +- **实际输出 (Actual Output)**: + +```json +{"status":"ok","env":"dev","agent_backend":"pi-agent","version":"v1.0","shared_kb_port":5432,"kb_ready":true,"database_ready":true,"schema_version":59,"schema_ready":true,"task_store_ready":true} +``` + +runner:`ok=true`,`service=shuimu-pi-agent-runner`,`model_id=MiniMax-M2.7-highspeed`,`api=openai-compatible`。 + +证据:[health.json](./assets/2026-09-21-taskrun-error-transparency/health.json) + +- **评价判定 (Evaluation / Verdict)**:PASS。运行中的 dev 后端是 pi-agent,不是 stub。 +- **性能与耗时 (Duration & Latency)**:rebuild 完成后第一次 `/health` 即返回(容器启动后数秒内)。 + +### 用例 2:任务状态 API 为 snake_case + +- **测试输入 (Input)**:管理员登录后 `GET /api/v1/tasks/runs/run_fresh_lease_031021.771577`。 +- **实际输出 (Actual Output)**:键为 `run_id`、`status`、`error_message`、`workspace_id`、`resumable` 等。`status` 为 `interrupted`,`error_message` 为「worker lease 过期,任务已中断(进程重启或 worker 失联)」。响应中没有 `Status` 或 `ErrorMessage`。 + +证据:[get-run.json](./assets/2026-09-21-taskrun-error-transparency/get-run.json) + +- **评价判定 (Evaluation / Verdict)**:PASS。与修复前 test 环境实测的 PascalCase(`Status` / `ErrorMessage`)相反。 +- **性能与耗时 (Duration & Latency)**:登录与 GET 在同一次命令中完成,约 0.3s。 + +### 用例 3:前端错误透传 + +- **测试输入 (Input)**:`npx tsx --test src/hooks/useTaskRunAttempts.test.ts`,以及 `npm test`。 +- **实际输出 (Actual Output)**:定向 4 项通过(失败原因 `TASK_FAILED`、治理状态先于失败兜底、未知 4xx 不写「请检查网络」)。全量 `npm test`:node:test 441 通过,vitest 32 通过。 +- **评价判定 (Evaluation / Verdict)**:PASS。 +- **性能与耗时 (Duration & Latency)**:全量前端测试约 2.2s。 + +### 用例 4:镜像内前端包 + +- **测试输入 (Input)**:在重建后的 `shuimu-agent-app-dev` 中查找 `TASK_FAILED`。 +- **实际输出 (Actual Output)**:`/app/web/dist/assets/index-BkdOgLHC.js` 含该字符串。 +- **评价判定 (Evaluation / Verdict)**:PASS。容器里的是本次前端构建,不是旧包。 +- **性能与耗时 (Duration & Latency)**:`grep` 即时返回。 + +## 六、 自动化测试套件汇总 (Test Suite Execution) + +- `go vet ./...`:通过。 +- `go test -short -count=1 -timeout 180s ./internal/taskrun ./internal/server`:通过(server 约 16.9s,含 `TestGetTaskRunJSONUsesSnakeCase`)。 +- `go test -short -count=1 -timeout 20m ./...`:`go vet` 已通过。全量 short 中与本次无关的 3 个失败仍在,均为开发库约束冲突,不是 JSON 字段回归: + - `internal/docgov` `TestDocGov_CreateOrGetDocument_OptionalDutiesNullFK`:`document_records_project_id_fkey` + - `internal/notification` `TestPG_StageAdvanceReminder24h`、`TestPG_EmitStageReadyDedupe`:`uq_projects_name_global` + - `internal/storage` `TestCAS_DatabaseIntegration`:`uq_projects_name_global` +- `internal/server`、`internal/taskrun`、`internal/stream` 在同一轮 short 中为 ok。 +- 前端 `npm test`:441 + 32 通过,失败 0。 + +## 七、 测试结论与后续优化建议 (Conclusion & Next Steps) + +**结论**:达到合入 dev 的标准。dev 已 rebuild,任务状态 API 为 snake_case,失败原因不再被前端收成网络错误。test / main 未晋升,本次不改 `production_ready`。 + +**后续**:请求级访问日志仍记在 [long-term](../ops/long-term/2026-09-21-http-request-access-log.md)。test 环境要等下次 dev→test 晋升后才会带上本修复;晋升前若再遇到「请检查网络」,先看隧道是否断开,再看响应是前端 camelCase 还是后端 snake_case。 diff --git a/.doc/test-reports/assets/2026-09-21-taskrun-error-transparency/get-run.json b/.doc/test-reports/assets/2026-09-21-taskrun-error-transparency/get-run.json new file mode 100644 index 00000000..2736453d --- /dev/null +++ b/.doc/test-reports/assets/2026-09-21-taskrun-error-transparency/get-run.json @@ -0,0 +1,23 @@ +{ + "run_id": "run_fresh_lease_031021.771577", + "session_id": "sess", + "project_id": 0, + "user_id": 1, + "client_turn_id": "", + "idempotency_key": "", + "task_key": "", + "stage": "", + "workflow_instance_id": 0, + "stage_instance_id": 0, + "workflow_stage_key": "", + "status": "interrupted", + "workspace_id": "", + "assistant_message_id": 0, + "worker_id": "", + "lease_expires_at": null, + "created_at": "2026-09-21T03:10:21.772089Z", + "updated_at": "2026-09-21T03:11:36.413875Z", + "error_message": "worker lease \u8fc7\u671f\uff0c\u4efb\u52a1\u5df2\u4e2d\u65ad\uff08\u8fdb\u7a0b\u91cd\u542f\u6216 worker \u5931\u8054\uff09", + "attempt": 1, + "resumable": false +} diff --git a/.doc/test-reports/assets/2026-09-21-taskrun-error-transparency/health.json b/.doc/test-reports/assets/2026-09-21-taskrun-error-transparency/health.json new file mode 100644 index 00000000..d773e037 --- /dev/null +++ b/.doc/test-reports/assets/2026-09-21-taskrun-error-transparency/health.json @@ -0,0 +1 @@ +{"status":"ok","env":"dev","agent_backend":"pi-agent","version":"v1.0","shared_kb_port":5432,"kb_ready":true,"database_ready":true,"schema_version":59,"schema_ready":true,"task_store_ready":true}