docs(reports): record BUG-601, progress notes and the manual walkthrough
Independent Staging Quality Gate / validate (push) Successful in 9m50s
Independent Staging Quality Gate / publish (push) Successful in 1m59s

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:
Jesse_Chen
2026-09-09 03:30:39 +00:00
co-authored by Claude Fable 5
parent 848e39e61f
commit 5565b6324e
5 changed files with 166 additions and 0 deletions
+7
View File
@@ -1,5 +1,12 @@
# BLOCKED
## 报告生成分章进度(2026-09-09,分支 `codex/report-progress-20260909`BUG-601
- **真实报告生成一次都没跑过:执行环境无登录态、无 Chrome、无模型凭据。** 因此"写作阶段真的会出现""章节名不错位""慢章节文案真的会切换"这三条**没有任何运行时证据**,只有纯函数测试与源码锁(`frontend/tests/personal-report-progress.test.ts` 16 项)。逐条走查清单见 `docs/testing/report-progress-20260909.md`,交给有真实账号的人。
- **分章行在生成中是否真的可读,未在真库上验证。** `personal_report_sections` 的 RLS 给了 `authenticated` 对自己行的 select,路由用的也是已认证客户端,逻辑上成立;但本机无 Docker/Postgres`npm run test:db` 的相关套件(`database-personal-report-sections`)在基线就因缺 Docker 失败,本轮同样没跑。**若上线后等待屏始终停在准备阶段,第一嫌疑就是这里**——分章行读不到时代码会安静回落到准备态,不会报错。
- **`next build` 的 First Load JS 分路由数字取不到。** Next 16.3.1 的 Turbopack 构建输出只有路由表(`○ /` 已确认 Static),没有 Size / First Load 两列,`build-manifest.json` 也不含按路由的 CSS 清单。本轮改用可直接测量的口径:`/` 唯一受影响的产物是 `globals.css`gzip 33,324 → 33,641 B+317 B+0.95%),在 ±2% 内;新增 JS(`personal-report-progress.ts``personal-report-progress-panel.tsx`)只被 `/reports` 路由引用,已用反查确认 `/` 的组件树不引入任何 personal-report 模块。
## 普通咨询上下文窗口与模型缓存(2026-09-06,分支 `codex/consultation-context-and-cache-20260906`
- **`npm run test:db` 未绿:Docker 用户自定义网络地址池耗尽。** `docker compose` 起 postgres fixture 时报 `all predefined address pools have been fully subnetted`。本机 Docker daemon 可用,且已有十余个遗留 `jyotisha-postgres-*` 容器占着网络;本单未做 `docker network prune`(会动共享宿主状态)。`test:db` 36 项里 19 项不需要新网络(备份路径、env 校验等)通过,17 项因建网失败。
+5
View File
@@ -1,5 +1,10 @@
# 印度占星 Skill 更新日志
## 2026-09-09 — 报告生成改成按章报进度,不再只有一个转圈
生成个人报告时,等待页不再只显示一个转圈和已等待时间。准备证据时仍是一句「正在准备你的星盘证据」;开始写章节后换成「已完成 N / M 章」,下面是一条按章分格的进度条和章节清单(事业、婚恋、财富、应期、健康),每章标着已完成 / 正在写 / 待写 / 写作失败;最后收尾时显示「正在整理成文」。某一章写超过 90 秒会改成「用时较长,仍在写」,不再让人以为卡死。进度条按章分格而不是画百分比,也不做平滑动画——后端没推进,屏幕就不动。写作失败的章仍然计入完成数,但结尾会说明哪几个主题没写成,不会只说一句完成。Skill 版本不变。
## 2026-09-08 — 交付卡改成至多三列对照,点「更像这个」采用
生时校正给出范围后,卡片改成一行至多三列。每列一个候选分钟,写相对可能性、按该分钟推出的性格处事、经历对照计数,以及往后 12 个月的事件窗;没有窗就写「未来一年没有明显的窗口」。点「更像这个」即采用该列分钟,不预先标「排盘用」。助手正文仍只说范围、对照了几件经历、以及这不是已确认的唯一出生分钟。八法验证报告仍在「查看验证报告」里,默认收起。Skill 版本 10.0.18。
+15
View File
@@ -9318,3 +9318,18 @@
- 复发自:BUG-039(全球地点列漏授 service_role 的同类权限缺口)
- 修复版本:迁移 `20260909010000`,待发布
## BUG-601 | 报告生成进度后端已产出,前端解析后一字未渲染,8 分钟只见转圈
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:`/reports/[reportId]` 生成等待屏、`personal-report-page.tsx``personal-report-route-core.ts`
- 用户现象:生成个人报告后停在等待页,屏幕上只有一个 `InlineSpinner`、一句固定文案「正在生成报告,大约 10–30 秒」和「已等待 X 分 Y 秒」。实际耗时以分钟计,轮询预算 8 分钟。用户无从判断是在推进还是已经卡死,普遍在三分钟左右刷新或离开。
- 触发条件:任何一次个人报告生成,必现。
- 根因:两段断链。其一,`personal-report-page.tsx``classifyReportEnvelope` 已经把 `progressPercent` / `progressPhase` 解析进 `generating` 状态对象(原第 97–102 行),但该分支的渲染(原第 231–246 行)完全没有引用这两个字段,等待屏因此与后端进度无关。其二,`resolveReportRead` 只在 `row.status === "failed"` 时读取分章行,`reportView` 也不含分章字段,所以生成中根本没有任何章节状态离开服务端——即便前端想渲染也无数据可用。worker 侧的阶梯(`loading_context` 10 → `generating_report` 30 → `section:<id>` 3085 → `persisting_report` 90)一直在正常写库,只是没有读者。
- 修复:分章进度成为等待屏的主体。服务端在 `generating``failed` 两种状态下读取分章行(`ready` 路径不变,不增加查询);新增 `personal-report-progress.ts` 把行状态映射为只读的四态 `done / failed / writing / waiting` 并随 `reportView` 下发,只带 `id``state`。等待屏按 phase 分三段:准备阶段保留 spinner,写作阶段换成"已完成 N / M 章"加按章分格的进度条与章节清单,收尾阶段回到 spinner。停滞满 90 秒(`REPORT_PROGRESS_STALL_MS`)时当前章文案改为「用时较长,仍在写」,该计时复用既有的一秒 tick,未新增定时器或 effect。
- 验证:`frontend/tests/personal-report-progress.test.ts` 16 项全绿,覆盖:认领态判定为「正在写」而非按完成数顺推(字典序返回时顺推会指向 `timing` 而正确答案是 `marriage`)、phase 命名的章已完成、blocked 计入完成数且 `hasFailure` 为真、全 ready 与部分 blocked 两种收尾、90 秒停滞文案切换且不出现「第 N 次尝试」、准备/写作/收尾三分支、分章行缺失时回落准备态而非渲染「已完成 0 / 0 章」、面板无 percent/定时器/预计剩余、`attemptCount` 等后台字段不出服务端。全量 `npm test` 2929→2945pass 2887→2903),失败 28 条与基线逐条一致(全部为无 Docker 的数据库/部署套件)。`tsc --noEmit` 0 错,`npm run lint` 0 error。
- 防复发:三条写进 `frontend/DESIGN.md` §9「报告生成等待态」并由上述测试锁死。其一,进度条按章分格,不得改画百分比条——job 的 percent 在 0→30 与 90→100 是瞬间跳变,线性条会演出后端没做的动作。其二,"正在写哪一章"只能由行状态(`pending` 且已认领)判定,不得由 `section:<id>` 的 phase 名或"已完成数 + 1"推导:前者命名的是刚写完的那一章,后者在字典序列表上会指错。其三,界面上不得出现按定时器推进的插值动画或预计剩余时间。
- 相关记录:BUG-043(禁止把后台评分状态渲染成用户需要管理的面板。本处边界不同且不冲突:等待屏是只读的,展示的是用户交付物自身的章节结构,`attemptCount`、原始错误码、lease、job id、payload 一律不出服务端)、BUG-576、BUG-574
- 复发自:无
- 修复版本:待发布
@@ -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` 第 102111 行),所以"正在写"精确等于 `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(按会话纪律,代码分支不自行推送)。
+67
View File
@@ -0,0 +1,67 @@
# 报告生成进度:真实环境走查清单(2026-09-09BUG-601
执行环境没有登录态、没有 Chrome、没有模型凭据,因此**没有任何一次真实报告生成被观察过**。下面每一条都需要一位有真实账号的人在 staging 上照做。自动化替代证据见 `frontend/tests/personal-report-progress.test.ts`16 项,纯函数 + 源码锁)。
前置:staging 已部署含本轮改动的 SHA;账号点数足以生成一份个人报告。
## 1. 三个阶段各自出现过
生成一份个人报告,停在 `/reports/<id>` 不要离开,全程录屏或每 30 秒截一张。
- [ ] **准备阶段**:屏幕上是一个转圈 + 「正在准备你的星盘证据」。没有章节清单,没有「已完成 0 / 0 章」。
- [ ] **写作阶段**:转圈消失,换成「已完成 N / M 章」大字 + 一条按章分格的进度条 + 章节清单。M 等于本次生成的主题数(默认 5:事业、婚恋、财富、应期、健康)。
- [ ] **收尾阶段**:清单消失,回到转圈 + 「正在整理成文」。
- [ ] 完成后自动跳到报告正文。
如果**从头到尾没有出现过写作阶段**,说明分章行没有下发,这是本轮最可能的失败模式,请把 `/api/reports/<id>` 的响应体(去掉正文)贴给开发。
## 2. 章节名对得上,而且不错位
这是本轮最容易做错的一条,请特别留意。
- [ ] 清单里同一时刻**至多一章**显示「正在写」。
- [ ] 那一章的名字,与几十秒后变成「已完成」的那一章,**是同一章**。(如果它写完后变成已完成的却是清单里的另一行,就是错位,必须报回来。)
- [ ] 已完成的章按完成先后逐个变成「已完成」,不会一次跳变两章以上(除非确实很快)。
- [ ] 章节名是中文(事业 / 婚恋 / 财富 / 应期 / 健康),不是 `career` 这样的英文 id。
## 3. 进度条与清单不打架
- [ ] 进度条的格数 **等于** 清单的行数。
- [ ] 深色格数 = 清单里「已完成」+「写作失败」的行数。
- [ ] 进度条不会自己平滑地往前爬——它只在某一章状态变化时跳一格。盯着看 30 秒,没有变化时它应该纹丝不动。
## 4. 慢章节的文案
需要碰到一次某章写得久(超过 90 秒)。可遇不可求,遇到就记下来。
- [ ] 该章文案从「正在写」变成「用时较长,仍在写」。
- [ ] 该文案**没有**出现「第 2 次尝试」「重试」这类字样。
- [ ] 恢复推进后,下一章回到正常的「正在写」。
## 5. 有章节失败时
需要一次部分失败的生成(不易构造,若一直没遇到请在清单上写「未遇到」,不要打勾)。
- [ ] 失败的那一章显示「写作失败」。
- [ ] 进度条对应格仍然是深色(失败也算完成的工作),整体继续前进。
- [ ] 最终页面出现「M 个主题中 N 个写作失败:…」这类说明,**不是**只说一句「完成」就跳走。
## 6. 长等待与离开
- [ ] 等到超过 8 分钟:页面出现「生成时间超出预期」,有「继续等待」和「返回报告中心」两个按钮。
- [ ] 点「继续等待」后,等待计时归零,进度重新开始显示(不是空白)。
- [ ] 中途切到别的标签页再切回来:进度继续,没有重复计时或倒退。
- [ ] 中途刷新页面:进度接着显示,不是从头开始。
## 7. 手机上
- [ ] iPhone Safari 竖屏:章节清单每行「主题名 — 状态」左右对齐,不换行错位、不溢出。
- [ ] 进度条在窄屏下每格仍然可见(5 格不会挤成一条)。
## 8. 深色模式
- [ ] 系统切到深色:已完成 / 正在写 / 待写 三种颜色仍然区分得开,进度条深色格与底色对比足够。
---
未打勾的项一律写成「未验证」,不要因为"看起来应该没问题"就打勾。发现错位、卡住不动、或章节名不对,直接把现象和 `/api/reports/<id>` 响应(去掉正文)贴回。