diff --git a/BLOCKED.md b/BLOCKED.md index 81eb5d97..d1459ed4 100644 --- a/BLOCKED.md +++ b/BLOCKED.md @@ -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 项因建网失败。 diff --git a/CHANGELOG.md b/CHANGELOG.md index 689cfb14..2f9f05f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 印度占星 Skill 更新日志 +## 2026-09-09 — 报告生成改成按章报进度,不再只有一个转圈 + +生成个人报告时,等待页不再只显示一个转圈和已等待时间。准备证据时仍是一句「正在准备你的星盘证据」;开始写章节后换成「已完成 N / M 章」,下面是一条按章分格的进度条和章节清单(事业、婚恋、财富、应期、健康),每章标着已完成 / 正在写 / 待写 / 写作失败;最后收尾时显示「正在整理成文」。某一章写超过 90 秒会改成「用时较长,仍在写」,不再让人以为卡死。进度条按章分格而不是画百分比,也不做平滑动画——后端没推进,屏幕就不动。写作失败的章仍然计入完成数,但结尾会说明哪几个主题没写成,不会只说一句完成。Skill 版本不变。 + + ## 2026-09-08 — 交付卡改成至多三列对照,点「更像这个」采用 生时校正给出范围后,卡片改成一行至多三列。每列一个候选分钟,写相对可能性、按该分钟推出的性格处事、经历对照计数,以及往后 12 个月的事件窗;没有窗就写「未来一年没有明显的窗口」。点「更像这个」即采用该列分钟,不预先标「排盘用」。助手正文仍只说范围、对照了几件经历、以及这不是已确认的唯一出生分钟。八法验证报告仍在「查看验证报告」里,默认收起。Skill 版本 10.0.18。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 1b19bcad..5adf2dc7 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -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:` 30–85 → `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→2945(pass 2887→2903),失败 28 条与基线逐条一致(全部为无 Docker 的数据库/部署套件)。`tsc --noEmit` 0 错,`npm run lint` 0 error。 +- 防复发:三条写进 `frontend/DESIGN.md` §9「报告生成等待态」并由上述测试锁死。其一,进度条按章分格,不得改画百分比条——job 的 percent 在 0→30 与 90→100 是瞬间跳变,线性条会演出后端没做的动作。其二,"正在写哪一章"只能由行状态(`pending` 且已认领)判定,不得由 `section:` 的 phase 名或"已完成数 + 1"推导:前者命名的是刚写完的那一章,后者在字典序列表上会指错。其三,界面上不得出现按定时器推进的插值动画或预计剩余时间。 +- 相关记录:BUG-043(禁止把后台评分状态渲染成用户需要管理的面板。本处边界不同且不冲突:等待屏是只读的,展示的是用户交付物自身的章节结构,`attemptCount`、原始错误码、lease、job id、payload 一律不出服务端)、BUG-576、BUG-574 +- 复发自:无 +- 修复版本:待发布 diff --git a/docs/tasks/PROGRESS-report-progress-20260909.md b/docs/tasks/PROGRESS-report-progress-20260909.md new file mode 100644 index 00000000..64251458 --- /dev/null +++ b/docs/tasks/PROGRESS-report-progress-20260909.md @@ -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:` 命名的是刚写完的那一章。** `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 章」)只出现在页面的 `

` 里,面板只画条与清单。这样读屏软件只播报一次,也不会出现同一句话上下各一遍。 +4. **`--type-body-xs` 这个 token 不存在**,写样式时误用后已改掉,改用继承字号。未新增 token。 + +## 五、没做的 + +- **真实报告生成一次都没跑过**(无登录态、无 Chrome、无模型凭据)。写作阶段是否真的出现、章节名是否错位、慢章节文案是否真的切换,全部无运行时证据。已写成 `docs/testing/report-progress-20260909.md` 八节清单,并记入 `BLOCKED.md`。 +- **`npm run test:db` 未跑**(无 Docker,基线同样红)。本轮未动数据库结构、未加迁移。 +- 未 push(按会话纪律,代码分支不自行推送)。 diff --git a/docs/testing/report-progress-20260909.md b/docs/testing/report-progress-20260909.md new file mode 100644 index 00000000..1f8cee48 --- /dev/null +++ b/docs/testing/report-progress-20260909.md @@ -0,0 +1,67 @@ +# 报告生成进度:真实环境走查清单(2026-09-09,BUG-601) + +执行环境没有登录态、没有 Chrome、没有模型凭据,因此**没有任何一次真实报告生成被观察过**。下面每一条都需要一位有真实账号的人在 staging 上照做。自动化替代证据见 `frontend/tests/personal-report-progress.test.ts`(16 项,纯函数 + 源码锁)。 + +前置:staging 已部署含本轮改动的 SHA;账号点数足以生成一份个人报告。 + +## 1. 三个阶段各自出现过 + +生成一份个人报告,停在 `/reports/` 不要离开,全程录屏或每 30 秒截一张。 + +- [ ] **准备阶段**:屏幕上是一个转圈 + 「正在准备你的星盘证据」。没有章节清单,没有「已完成 0 / 0 章」。 +- [ ] **写作阶段**:转圈消失,换成「已完成 N / M 章」大字 + 一条按章分格的进度条 + 章节清单。M 等于本次生成的主题数(默认 5:事业、婚恋、财富、应期、健康)。 +- [ ] **收尾阶段**:清单消失,回到转圈 + 「正在整理成文」。 +- [ ] 完成后自动跳到报告正文。 + +如果**从头到尾没有出现过写作阶段**,说明分章行没有下发,这是本轮最可能的失败模式,请把 `/api/reports/` 的响应体(去掉正文)贴给开发。 + +## 2. 章节名对得上,而且不错位 + +这是本轮最容易做错的一条,请特别留意。 + +- [ ] 清单里同一时刻**至多一章**显示「正在写」。 +- [ ] 那一章的名字,与几十秒后变成「已完成」的那一章,**是同一章**。(如果它写完后变成已完成的却是清单里的另一行,就是错位,必须报回来。) +- [ ] 已完成的章按完成先后逐个变成「已完成」,不会一次跳变两章以上(除非确实很快)。 +- [ ] 章节名是中文(事业 / 婚恋 / 财富 / 应期 / 健康),不是 `career` 这样的英文 id。 + +## 3. 进度条与清单不打架 + +- [ ] 进度条的格数 **等于** 清单的行数。 +- [ ] 深色格数 = 清单里「已完成」+「写作失败」的行数。 +- [ ] 进度条不会自己平滑地往前爬——它只在某一章状态变化时跳一格。盯着看 30 秒,没有变化时它应该纹丝不动。 + +## 4. 慢章节的文案 + +需要碰到一次某章写得久(超过 90 秒)。可遇不可求,遇到就记下来。 + +- [ ] 该章文案从「正在写」变成「用时较长,仍在写」。 +- [ ] 该文案**没有**出现「第 2 次尝试」「重试」这类字样。 +- [ ] 恢复推进后,下一章回到正常的「正在写」。 + +## 5. 有章节失败时 + +需要一次部分失败的生成(不易构造,若一直没遇到请在清单上写「未遇到」,不要打勾)。 + +- [ ] 失败的那一章显示「写作失败」。 +- [ ] 进度条对应格仍然是深色(失败也算完成的工作),整体继续前进。 +- [ ] 最终页面出现「M 个主题中 N 个写作失败:…」这类说明,**不是**只说一句「完成」就跳走。 + +## 6. 长等待与离开 + +- [ ] 等到超过 8 分钟:页面出现「生成时间超出预期」,有「继续等待」和「返回报告中心」两个按钮。 +- [ ] 点「继续等待」后,等待计时归零,进度重新开始显示(不是空白)。 +- [ ] 中途切到别的标签页再切回来:进度继续,没有重复计时或倒退。 +- [ ] 中途刷新页面:进度接着显示,不是从头开始。 + +## 7. 手机上 + +- [ ] iPhone Safari 竖屏:章节清单每行「主题名 — 状态」左右对齐,不换行错位、不溢出。 +- [ ] 进度条在窄屏下每格仍然可见(5 格不会挤成一条)。 + +## 8. 深色模式 + +- [ ] 系统切到深色:已完成 / 正在写 / 待写 三种颜色仍然区分得开,进度条深色格与底色对比足够。 + +--- + +未打勾的项一律写成「未验证」,不要因为"看起来应该没问题"就打勾。发现错位、卡住不动、或章节名不对,直接把现象和 `/api/reports/` 响应(去掉正文)贴回。