# TASK · 报告阅读页切换语言后正文不换 · 2026-09-30 > 执行方:coding agent。验收:Claude。 > 分支 `codex/report-language-switch-stale-20260930`,worktree `.worktrees/report-language-switch-stale-20260930`,基线 `origin/staging`。 > BUG 编号:**BUG-1126**(Claude 写单时已登记为 investigating;开工时核对 `docs/BUG_HISTORY.md` 最大号,若已被占用顺延并同步改本单)。 > 纯前端改动,不需要 AGENTS §9 预检。 ## 0. 基线 - 写单时 `origin/staging` = `da3ed2f3`(staging `/api/health` `deployment.gitCommit` 同为 `da3ed2f3`)。 - 开工前先 `git fetch origin --prune`,以实测 `origin/staging` 为准,写进 `docs/tasks/PROGRESS-report-language-switch-stale-20260930.md`。 ## 1. 事故实证 产品 2026-09-30 在 staging 实测,桌面 Dia 与 iPhone 两台设备都能复现: - 进报告页后点「English」:地址栏会变成 `?lang=en`,开关也显示 English 已选中,但正文、目录(标题仍是「目录」)还是中文版。 - 直接带 `?lang=en` 打开:正文是英文;这时点「中文」,开关会变,正文仍是英文。 - 导出面板里的「中文 / English」和导出的英文文件都正常,说明英文版数据已经完整送到浏览器。 Claude 复现情况(staging 线上 JS + 无头 Chrome;报告接口用虚构出生资料的真实引擎输出替换,也伪造了登录态): | 响应里有什么 | 点切换后 | | --- | --- | | 只有 `longformMarkdown` / `longformMarkdownEn` | 正常:正文区块始终 1 份,目录变 Contents | | 再加上 `factTables` / `factTablesEn`(真实报告都有) | **复现**:`.personal-report-md-layout` 1 → 2 → 3 份,旧语言那份一直留在最上面;开发版 React 报 `Encountered two children with the same key` | ## 2. 根因 `frontend/src/components/personal-report/personal-report-page.tsx` → `PersonalReportPage` 的 markdown-ready 分支,在 `.personal-report-reader-body` 下同级渲染: ```tsx {state.calculationCharts && } {factTables && } ``` 两个同级元素的 key 都是 `shown`("zh" / "en"),发生重复。每次换语言,React 按 key 对齐时旧节点清不掉,旧语言的正文留在页面上,新语言的正文追加到后面,所以读者看到的一直是第一次渲染的那个语言。开关读的是同一个 `shown`,所以只有开关在变。 - 引入提交:`b2ad772e`(英文版上线,两处 `key={shown}` 同时加入)。 - 既有测试没拦住:`frontend/tests/report-english-edition.test.tsx` 只单独测了 `ReportActions`(开关)、`classifyReportEnvelope` 和导出面板,没有把整页连同核对表一起渲染再切换。 **Claude 已验证修复方向**:把两个 key 改成互不相同(例如 `markdown-${shown}` / `facts-${shown}`)后,同一复现环境里切换一次就换成英文、正文区块 1 份、0 个含汉字的章节。验证后已还原,没有提交任何代码。 ## 3. 决策记录 1. 产品 2026-09-30 反馈「切换不管用」。修复只动 key,不改英文版的生成、存储、接口,也不改切换的交互。 2. 沿用 `TASK-report-english-edition-20260929.md` 决策 6:切换只换正文、目录、图盘标签、核对表,应用外壳保持中文。本单不推翻任何既有决策。 ## 4. 硬红线 1. 只改 `personal-report-page.tsx` 里这两处 key,加测试和记录;不顺手重构阅读页,不动 `PersonalReportMarkdownView` 的懒渲染和保留章节逻辑(BUG-1106~1108)。 2. 不改 `report-english-edition.test.tsx` 的既有断言;如果必须改,写「原值 / 新值 / 原因」三栏。 3. 测试 fixture 用仓库里已有的真实引擎虚构盘 golden(`report-reader-main-fictional*.json`、`report-density-fictional-engine.json`),不手造形状(AGENTS §7-4)。 4. 不动数据库、不升级依赖、不改 workflow。 ## 5. 任务分解 ### T1 · 去掉重复 key `PersonalReportMarkdownView` 与 `ReportFactTables` 的 key 改成互不相同,且仍随语言变化(保证换语言时两者都会重新挂载)。同一父级下其他带 key 的子元素一并检查。 验收:`personal-report-page.tsx` 中 `.personal-report-reader-body` 的直接子元素没有重复 key。 ### T2 · 整页切换回归测试(先红后绿) 新增测试:渲染 `PersonalReportPage`,mock `fetch` 返回 ready 报告,同时带 `longformMarkdown`、`longformMarkdownEn`、`factTables`、`factTablesEn`(用 golden 组装),然后: 1. 以 `initialLanguage="zh"` 打开 → 点 English → 断言正文区块只有 1 份、目录标题是 Contents、第一个 h2 是英文、正文不含汉字; 2. 再点中文 → 正文区块只有 1 份、目录是「目录」; 3. 以 `initialLanguage="en"` 打开 → 点中文 → 同样断言。 验收:在未修复的基线上这条测试是**红**的(进度记录贴失败输出),修复后是绿的。如果现有测试台(`react-client-lifecycle-test-support`)跑不了整页,说明原因并换可行的方案,不得退回只测组件。 ### T3 · 记录 - `docs/BUG_HISTORY.md` 的 BUG-1126 补齐修复、验证、防复发,状态改为 resolved(待部署、真机待产品)。 - `CHANGELOG.md` 加一条:报告阅读页中英切换恢复正常。 - `docs/testing/report-english-edition-20260929.md` 追加一条真机步骤:先中文打开,切 English,再切回中文,每次正文与目录都要跟着变,正文不能出现两份。 - 本单不改 UI 外观,`DESIGN.md` 不用更新。 ## 6. 验收口径 - `./node_modules/.bin/tsc --noEmit` 0 错;`npm run lint` 0 error。 - `npm test` 失败清单与基线逐条一致,总数不少于基线加新增条数。 - `next build` 通过,`/` 仍是 Static;首屏 gzip 在 ±2% 内(预期不变)。 - 交付:快进推 `origin/staging`,核对远端 SHA;部署后 `/api/health` 的 `deployment.gitCommit` 等于该提交。 ## 7. 让步顺序 时间不够时按这个顺序保:T1 → T2 → T3。T1 和 T2 必须一起交付,不接受只改代码不加回归测试。 ## 8. 开工前置命令 ```bash cd /workspace/Jyotisha && git status -sb | head -1 git fetch origin --prune git worktree add -b codex/report-language-switch-stale-20260930 .worktrees/report-language-switch-stale-20260930 origin/staging cd .worktrees/report-language-switch-stale-20260930/frontend && npm ci # 各 worktree 需自己装依赖;Node 22 在 /exec-daemon/node grep -o "BUG-[0-9]\{3,4\}" ../docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1 ```