diff --git a/docs/tasks/README.md b/docs/tasks/README.md index e93c3f6b..c6acc30f 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -117,8 +117,9 @@ | `TASK-chat-streaming-ux-20260901.md` | `PROGRESS-chat-streaming-ux-20260901.md` | 流式体验与双会话面统一 | 已验收 | `4dc0c8c7`(BUG-473~478) | | `TASK-unified-loading-20260902.md` | `PROGRESS-unified-loading-20260902.md` | 首页一次等待一次揭幕 | 已验收 | `482796fc`(BUG-479) | | — | — | 初始化后点首页「家庭」报运行合同未完成:本命第一步未强制计算工具、主题卡无入口钉死 | 入口已下线,机制保留供历史会话 | `codex/consultation-family-contract-incomplete-20260909`(BUG-630);主题卡发出点已删,`guided_topic` 枚举仍可读回;走查 `docs/testing/consultation-family-contract-incomplete-20260909.md` 不再可执行 | -| `TASK-chat-reading-load-20260915.md` | `PROGRESS-chat-reading-load-20260915.md` | 首页主题卡下线 + 回答两层可见性 + 折叠层宽表可横滑 + 输入框 16px。内容一字不删。⚠️ BUG-630 走查路径失效 | 待验收 | `codex/chat-reading-load-20260915` | -| `TASK-mobile-touch-and-breakpoints-20260915.md` | `PROGRESS-mobile-touch-and-breakpoints-20260915.md` | 消息操作按钮触屏命中区 27×34、相邻仅隔 1px(BUG-695);CSS 平板上限 900px 与 `sidebarViewportForWidth` 的 1024 不一致,901–1023 是混合态(BUG-696);报告域 720/760/860 三个断点互不对齐,761–860 目录已塌、正文还是桌面(BUG-697)。含断点白名单契约测试。**串行在 chat-reading-load 之后** | 待验收 | `codex/mobile-touch-and-breakpoints-20260915` | +| `TASK-chat-reading-load-20260915.md` | `PROGRESS-chat-reading-load-20260915.md` | 首页主题卡下线 + 回答两层可见性 + 折叠层宽表可横滑 + 输入框 16px。内容一字不删。⚠️ BUG-630 走查路径失效 | 已验收(2 条契约测试未通过,见修复单) | `8144fca2` | +| `TASK-mobile-touch-and-breakpoints-20260915.md` | `PROGRESS-mobile-touch-and-breakpoints-20260915.md` | 消息操作按钮触屏命中区 27×34、相邻仅隔 1px(BUG-695);CSS 平板上限 900px 与 `sidebarViewportForWidth` 的 1024 不一致,901–1023 是混合态(BUG-696);报告域 720/760/860 三个断点互不对齐,761–860 目录已塌、正文还是桌面(BUG-697)。含断点白名单契约测试。**串行在 chat-reading-load 之后** | 已验收 | `6c748d86`(BUG-695~697) | +| `TASK-chat-reading-load-fix-20260915.md` | — | **验收修复单**:`chat-bundle-splitting-contract`(`message-markdown` 包裹数 1→2、`plainParagraphs(spoken)` 字面量已不存在)与 `chat-navigation-a11y-contract`(`window.location.assign("/login")` 4→3,建议问题入口删除带走一处)两条既有断言被本轮打红且未更新;Linux 全量 27→29。只改测试不改产品代码 | 待领取 | `codex/chat-reading-load-fix-20260915` | | `TASK-settings-dialog-size-and-nav-20260915.md` | — | **复发单**:设置弹窗四个分区尺寸仍随内容跳变(BUG-698,复发自 BUG-554——旧防复发只查「有没有写 height」,查不到「写了没生效」);首要嫌疑是 `.settings-modal` 的 `dvh` 没有 `vh` 回退,不支持时整条 `height` 作废退化成内容高度,需先复现确认。另按产品要求去掉分区菜单左侧强调条,并拆开与悬停共用的选中态 | 待领取 | `codex/settings-dialog-size-and-nav-20260915` | ### 个人报告 diff --git a/docs/tasks/TASK-chat-reading-load-fix-20260915.md b/docs/tasks/TASK-chat-reading-load-fix-20260915.md new file mode 100644 index 00000000..3148bfcf --- /dev/null +++ b/docs/tasks/TASK-chat-reading-load-fix-20260915.md @@ -0,0 +1,207 @@ +# TASK · 验收修复单:两条既有契约测试被本轮改动打红且未更新 + +- 日期:2026-09-15 +- 基线 commit:`origin/staging` @ `6c748d86` +- 执行分支:`codex/chat-reading-load-fix-20260915` +- 来源:Claude 对 `8144fca2`(chat-reading-load)与 `6c748d86`(mobile-touch-and-breakpoints)的验收 +- 规模:两条断言 + 两段三栏说明。**不改产品代码。** + +--- + +## 1. 验收结论摘要 + +两单绝大部分通过。门禁在 Linux 上实测: + +| 项 | 结果 | +| --- | --- | +| `tsc --noEmit` | 0 错 | +| `npm run lint` | **0 error** / 120 warning(全部既有) | +| 测试总数 | 基线 `ebd5ba68` 3214 → 改后 3232(**+18**) | +| `npm run build`(Turbopack) | exit 0;`/` 为 `○ Static` | +| 首屏 JS gzip-9 | 585,246 B → 583,660 B,**−1,586 B / −0.27%**(口径同 09-02 / 09-06:预渲染 `index.html` 引用脚本去重求和) | +| 全量测试失败 | 基线 27 → 改后 **29** | + +**两条新增失败就是本单要修的东西。** 基线 27 条失败与改后逐条一致(Docker / DB / 部署 / rsync 环境缺口),没有一条消失也没有一条变形。 + +执行方两份 PROGRESS 都把全量测试与 `next build` 记成了 Windows 环境缺口(`SKILL.md` symlink `EPERM`、CRLF),这个判断对他们的机器成立,但也因此**没有机会看到这两条**。本单把它们补上。 + +顺带澄清:`PROGRESS-mobile-touch-and-breakpoints` 里记的 `membership-page`「login redirects…」CRLF 失败,**在 Linux 上通过**,不是回归,无需处理。 + +--- + +## 2. 事故实证 + +### 2.1 `the pre-markdown fallback renders prose but never raw markdown` + +- 文件:`frontend/tests/chat-bundle-splitting-contract.test.ts` +- 断点:`TestContext. (…:47:10)` +- 报错:`Expected values to be strictly equal: 2 !== 1` + +失败的那一句: + +```js +// And: one stable wrapper serves both paths, so no styling or caret is lost on upgrade. +assert.equal(contentSource.match(/className="message-markdown"/g)?.length, 1); +``` + +`8144fca2` 把回答拆成口语层与折叠层,两层各有一个 `
`(`chat-message-content.tsx` 里现在实测 2 处)。 + +**同一个 test 里还有第二条会接着红**(node:test 首个断言失败即中止,所以 TAP 只报了上面那条): + +```js +assert.match(contentSource, /plainParagraphs\(spoken\) \?\? \[\]/); +``` + +口语层改走 `renderProse(spoken, renderMarkdown)`,`plainParagraphs` 现在只出现在 `renderProse` 内部的 `(plainParagraphs(text) ?? []).map(…)`,字面量 `plainParagraphs(spoken) ?? []` 已不存在。 + +同一个 test 里的 `assert.match(contentSource, /

\{paragraph\}<\/p>/)` **仍然成立**(`renderProse` 里还是这一行),不要动。 + +### 2.2 `auth redirects stay hard document loads so stale session state cannot survive` + +- 文件:`frontend/tests/chat-navigation-a11y-contract.test.ts` +- 断点:`TestContext. (…:68:10)` +- 报错:`Expected values to be strictly equal: 3 !== 4` + +失败的那一句: + +```js +assert.equal(pageSource.match(/window\.location\.assign\("\/login"\)/g)?.length, 4); +``` + +`8144fca2` 删掉了拉建议问题的那个 `useEffect`,其中包含: + +```js +if (caught instanceof OnboardingAuthenticationError) { + window.location.assign("/login"); + return; +} +``` + +调用点从 4 处变成 3 处。这条断言数的是"硬跳转的次数",入口删了所以少一次——**是正确的减少,不是把跳转换成了 `router.push`**。同一个 test 里的 `doesNotMatch(/router\.push\("\/login"\)/)` 与 `doesNotMatch(/router\.replace\("\/login"\)/)` 仍然成立,红线本身没破。 + +### 2.3 性质 + +两条都是**契约测试跟着代码漂了但没更新**,不是产品回归。但 `frontend/AGENTS.md` §7 两条红线各破一次: + +- §7.1「相关测试套件 fail=0」——两套在基线是绿的。 +- §7.3「改任何既有断言必须写『原值 / 新值 / 原因』三栏」——这两条既有断言被改动打红,既没更新也没说明。 + +--- + +## 3. 决策记录 + +1. **改测试,不改产品代码。** 两处的新行为都是任务书明确要求的结果:两层可见性必然有两个 `message-markdown` 包裹;删除建议问题入口必然少一处硬跳转。回退产品代码去迁合测试是反的(`CLAUDE.md`:不要为迎合工具改业务代码)。 +2. 两条断言都**保持"数数"的形式**,只改数字并补三栏说明。不要改成模糊匹配(`>= 1`、`assert.ok`)——它们数的就是"有几个包裹 / 有几次硬跳转",放宽等于把防线拆了。 + +--- + +## 4. 硬红线 + +1. **不得修改 `frontend/src/` 下任何文件。** 本单只动 `frontend/tests/` 与文档。 +2. `tsc --noEmit` 0 错;`npm run lint` 0 error。 +3. 改完全量 `npm test` 的失败数必须回到 **27**,且失败清单与 `ebd5ba68` 基线逐条一致。 +4. 不得放宽断言形式(见 §3.2)。 +5. 不得顺手修 120 条既有 warning。 + +--- + +## 5. 任务分解 + +### 任务 1 · 修 `chat-bundle-splitting-contract.test.ts` + +把那两条断言更新为当前架构,并在**紧邻的注释里**写三栏: + +```js +// 原值: contentSource.match(/className="message-markdown"/g)?.length === 1 +// 新值: 2 +// 原因: 回答改为两层可见性(口语层 + 折叠的完整分析),两层各有一个包裹。 +// 这条仍然数包裹个数,不得放宽成 >= 1——它防的是"升级到 markdown 时换了壳"。 +assert.equal(contentSource.match(/className="message-markdown"/g)?.length, 2); + +// 原值: assert.match(contentSource, /plainParagraphs\(spoken\) \?\? \[\]/) +// 新值: 断言 renderProse 内部的回退路径 +// 原因: 口语层与报告层共用 renderProse,plainParagraphs 只在它内部调用一次。 +assert.match(contentSource, /\(plainParagraphs\(text\) \?\? \[\]\)\.map/); +``` + +**补一条新断言**,把这条 test 原本守的东西守回来——"两个包裹用的是同一套渲染路径": + +```js +// 两层必须都走 renderProse,不得一层 markdown 一层裸文本。 +assert.equal(contentSource.match(/renderProse\(/g)?.length >= 2, true); +``` + +具体写法执行方可调,但**必须让这条 test 重新具备"检测到两层渲染路径分叉"的能力**,不能只是把 1 改成 2 就完事。 + +**验收标准**:`npx tsx --test tests/chat-bundle-splitting-contract.test.ts` fail=0;三栏注释齐全;断言仍是精确计数。 + +### 任务 2 · 修 `chat-navigation-a11y-contract.test.ts` + +```js +// 原值: pageSource.match(/window\.location\.assign\("\/login"\)/g)?.length === 4 +// 新值: 3 +// 原因: 8144fca2 删除建议问题请求,连同它的 OnboardingAuthenticationError 分支里 +// 那一次 window.location.assign("/login")。少的是调用点,不是把硬跳转换成了 +// router.push——同 test 里两条 doesNotMatch 仍然成立。 +assert.equal(pageSource.match(/window\.location\.assign\("\/login"\)/g)?.length, 3); +``` + +顺手核对同一个 test 里其余断言在当前代码下仍然成立(`redirectToLogin`、`persistLoginSessionReturn`、`signOut` 三段),**成立就别动**。 + +**验收标准**:`npx tsx --test tests/chat-navigation-a11y-contract.test.ts` fail=0。 + +### 任务 3 · 全量复核与文档 + +**3.1** 跑全量并逐条比对: + +```bash +npm test > /tmp/head.tap 2>&1 +grep -oE "^ *not ok [0-9]+ - .*" /tmp/head.tap | sed 's/^ *not ok [0-9]* - //' | sort +``` + +必须得到 **27** 条,且与 `ebd5ba68` 的 27 条一字不差。数字与清单写进 PROGRESS。 + +**3.2** 文档: + +- 两份既有 PROGRESS(`PROGRESS-chat-reading-load-20260915.md`、`PROGRESS-mobile-touch-and-breakpoints-20260915.md`)各补一段「验收补正」,写明本单修了哪两条、为什么原来的 Windows 环境缺口判断让它们漏网,并把 Linux 实测的门禁数字抄进去(tsc 0 / lint 0 error / 3232 tests / build exit 0 / `/` Static / gzip −0.27%)。 +- `docs/tasks/PROGRESS-chat-reading-load-fix-20260915.md`:本单自己的进度。 +- **不新增 BUG 编号**:两条都是测试契约漂移,没有用户可感知现象,不进 `docs/BUG_HISTORY.md`。若执行中发现真实回归,从 **BUG-699** 起(BUG-698 已由 `TASK-settings-dialog-size-and-nav-20260915.md` 占用)。 +- 不动 `CHANGELOG.md`(无用户可见行为变化)。 + +--- + +## 6. 让步顺序 + +1. 任务 1 的"新断言"若想不出既精确又稳的写法,**先把两条数字断言改对并写满三栏**,把新断言的缺口写进 PROGRESS 交回。数字改对是底线,加强是加分。 +2. **绝不让步**:不得改 `frontend/src/`;不得把精确计数放宽成模糊匹配;全量失败数必须回到 27。 + +--- + +## 7. 开工前置命令 + +```bash +cd /workspace/Jyotisha +git status -sb | head -1 +git fetch origin --prune +git worktree add -b codex/chat-reading-load-fix-20260915 \ + .worktrees/chat-reading-load-fix-20260915 origin/staging +cd .worktrees/chat-reading-load-fix-20260915/frontend +npm ci +npx tsx --test tests/chat-bundle-splitting-contract.test.ts tests/chat-navigation-a11y-contract.test.ts # 应先看到两条红 +``` + +交付:`git push origin HEAD:staging`,推完核对远端 SHA。 + +--- + +## 8. 未纳入本单的欠账 + +以下在验收中确认仍然欠着,**不属于本单范围**,列在这里免得被当成已完成: + +| 欠账 | 归属 | +| --- | --- | +| 浏览器 / iPhone 真机走查(折叠块、宽表横滑、输入框 16px、44px 触控、断点目视) | `docs/testing/chat-reading-load-20260915.md`、`docs/testing/mobile-touch-and-breakpoints-20260915.md` | +| iOS 键盘遮挡输入框 | `docs/testing/ios-keyboard-composer-20260915.md`,结论出来前不立单 | +| staging 部署核对 | 线上仍是 `8d56d0ab`,两单尚未部署;部署后 `/api/health` 的 `deployment.gitCommit` 应等于含门禁路径改动的最新 staging 提交 | +| 设置弹窗尺寸与分区菜单强调条 | `TASK-settings-dialog-size-and-nav-20260915.md`(BUG-698),待领取 | +| BUG-630 的另一半(本命第一步未强制计算工具) | 与主题卡无关,仍未处理 |