docs(reports): record BUG-601, progress notes and the manual walkthrough
Renumbered from BUG-599: staging took 599 and 600 from other sessions while this branch was in review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016P5RoqzmUQEbeC2qjAkeGr
This commit is contained in:
co-authored by
Claude Fable 5
parent
848e39e61f
commit
5565b6324e
@@ -0,0 +1,72 @@
|
||||
# PROGRESS · 报告生成分章进度(2026-09-09)
|
||||
|
||||
- 分支:`codex/report-progress-20260909`,基线 `origin/staging` = `a43a6db8`
|
||||
- 模式:直接执行模式(用户指定 subagent 执行),无独立任务书;设计口径来自会话内定稿
|
||||
- BUG 编号:**BUG-601**(开工核对当时最大号 598,原写 599;交付前 rebase 发现 staging 已有别的会话占用 599「回访登录自动打开生时校正」与 600「新用户保存账户资料 500」,故顺延到 601)
|
||||
|
||||
## 一、做了什么
|
||||
|
||||
把 `/reports/[reportId]` 的生成等待屏从「spinner + 秒表」改成分章进度。
|
||||
|
||||
### 后端(本轮唯一的服务端改动)
|
||||
|
||||
1. `app/api/reports/[reportId]/route.ts`:`listSections` 原先只返回 `{status, lastErrorCode}`,现补上 `sectionId` 与 `attemptCount`。两者都只在服务端消费。
|
||||
2. `lib/personal-report-route-core.ts`:
|
||||
- 分章行原先**只在 `row.status === "failed"` 时才读**,`reportView` 也不含任何分章字段——也就是说生成中根本没有章节数据离开服务端。现改为 `failed` 与 `generating` 两种状态都读;`ready` 路径不变,不增加查询。
|
||||
- 新增 `sectionProgress` 推导,随 `reportView` 第四参数下发,只带 `{id, state}`。
|
||||
|
||||
### 新增共享层
|
||||
|
||||
3. `lib/personal-report-progress.ts`(新):
|
||||
- `REPORT_THEME_LABELS` / `reportThemeLabel`:从 `personal-report-document-view.tsx` 提取,原处改为 import,**不留第二份副本**。
|
||||
- `deriveSectionProgressState(status, attemptCount)`:行状态 → `done / failed / writing / waiting`。这是全仓唯一读 `attemptCount` 的地方,且只在服务端跑。
|
||||
- `classifyReportProgressStage(phase)`:`preparing / writing / finishing`。
|
||||
- `describeReportProgress()`:纯函数,产出标题句与章节清单。
|
||||
- `reportProgressSignature()`:进度指纹,用于停滞判定。
|
||||
- `REPORT_PROGRESS_STALL_MS = 90_000`。
|
||||
|
||||
### 前端
|
||||
|
||||
4. `components/personal-report/personal-report-progress-panel.tsx`(新):按章分格的进度条 + 章节清单。无 percent、无定时器、无 transition。
|
||||
5. `components/personal-report/personal-report-page.tsx`:解析 `sections`(带白名单校验,形状不对就丢弃而不是渲染成谜之行);用 `progressMark` 记录进度指纹与时刻,停滞判定复用既有的一秒 tick(`waitStartedAt + waitedMs` 即"现在"),**未新增定时器、未新增 effect**;写作阶段隐藏 spinner 换成面板。
|
||||
6. `app/globals.css`:新增等待屏进度样式。
|
||||
7. `frontend/DESIGN.md` §9 新增「报告生成等待态」小节。
|
||||
|
||||
## 二、三条实现事实(做错任一条功能就是错的)
|
||||
|
||||
1. **`section:<id>` 命名的是刚写完的那一章。** `emitProgress` 在 `complete()`/`block()` 之后调用(`personal-report-generation.ts:3459 / 3485 / 3496`)。
|
||||
2. **"已完成数 + 1"同样会指错。** `sectionService.list()` 按 `section_id` **字典序**返回(`personal-report-section-service-core.ts:113`),不是写作顺序。所以顺推出来的"下一章"与真正在写的那一章无关。
|
||||
→ 因此本轮改用**行状态**判定:`start_personal_report_section` 只 `attempt_count + 1` 并保持 `status='pending'`(迁移 `20260830020000` 第 102–111 行),所以"正在写"精确等于 `pending && attemptCount > 0`,不是推断。测试里专门构造了字典序会指错的用例(正确答案 `marriage`,顺推会得到 `timing`)。
|
||||
3. **blocked 计入完成数**,进度条照常前进;收尾走既有 `summarizePersonalReportFailure()`。
|
||||
|
||||
## 三、测试数字
|
||||
|
||||
| 项 | 基线(a43a6db8) | 改后 | 结论 |
|
||||
| --- | --- | --- | --- |
|
||||
| `tsc --noEmit` | 0 错 | 0 错 | 通过 |
|
||||
| `npm run lint` | 0 error / 108 warning | 0 error / 108 warning | 通过;warning 数不变,且无一条在本轮改动的文件里 |
|
||||
| `npm test` tests | 2929 | 2945 | +16 |
|
||||
| `npm test` pass | 2887 | 2903 | +16 |
|
||||
| `npm test` fail | 28 | 28 | 清单 `diff` 逐条一致 |
|
||||
| `npm test` skipped | 14 | 14 | 不变 |
|
||||
| `next build` `/` | `○ Static` | `○ Static` | 通过 |
|
||||
| `globals.css` gzip | 33,324 B | 33,641 B | +317 B / +0.95%,在 ±2% 内 |
|
||||
|
||||
28 条失败全部是无 Docker 的数据库/部署/迁移套件,基线即红。失败清单已逐条 `diff` 比对,**完全一致,无新增、无消失**。
|
||||
|
||||
新增测试:`frontend/tests/personal-report-progress.test.ts` 16 项,覆盖任务要求的六类——off-by-one(含字典序陷阱)、blocked 计入进度、全 ready 与部分 blocked 两种收尾、90 秒停滞文案切换且不出现「第 N 次尝试」、准备/写作/收尾三分支渲染、以及后台字段不出服务端。
|
||||
|
||||
**未改动任何既有断言**,因此无「原值 / 新值 / 原因」三栏。特别说明一条:`tests/report-polling-contract.test.ts:105` 有 `assert.doesNotMatch(pageSource, /章节/)`,本轮把章节渲染整体放进独立组件 `personal-report-progress-panel.tsx`,`pageSource` 里确实不出现「章节」,该断言原样通过,未被弱化或删除。
|
||||
|
||||
## 四、偏离与判断
|
||||
|
||||
1. **任务交代「`sections[]` 目前被剥掉了 `sectionId`,把它加回去」,实际情况更严重一层。** 分章数组根本不在 `generating` 的响应里——`resolveReportRead` 只在 `failed` 时读分章行,`reportView` 也没有该字段。所以只补 `sectionId` 不够,必须同时让生成中也读分章行并下发。这是比交代范围略大的服务端改动,但不做则整个功能无数据可用。`ready` 路径未加查询。
|
||||
2. **停滞判定没有用 `progressPhase` 的持续时长,而是用完整进度指纹**(phase + percent + 各章状态)。理由:重试时 phase 与 percent 都不动,但若同期别的信号变了,仍不该报"停滞"。指纹变化才重置计时,更严格。
|
||||
3. **面板不重复渲染标题句。** 标题句(「已完成 N / M 章」)只出现在页面的 `<p role="status">` 里,面板只画条与清单。这样读屏软件只播报一次,也不会出现同一句话上下各一遍。
|
||||
4. **`--type-body-xs` 这个 token 不存在**,写样式时误用后已改掉,改用继承字号。未新增 token。
|
||||
|
||||
## 五、没做的
|
||||
|
||||
- **真实报告生成一次都没跑过**(无登录态、无 Chrome、无模型凭据)。写作阶段是否真的出现、章节名是否错位、慢章节文案是否真的切换,全部无运行时证据。已写成 `docs/testing/report-progress-20260909.md` 八节清单,并记入 `BLOCKED.md`。
|
||||
- **`npm run test:db` 未跑**(无 Docker,基线同样红)。本轮未动数据库结构、未加迁移。
|
||||
- 未 push(按会话纪律,代码分支不自行推送)。
|
||||
Reference in New Issue
Block a user