Linux 上补了执行方在 Windows 上做不到的两项:全量 npm test 与 next build。
通过:tsc 0 错;lint 0 error / 120 既有 warning;测试总数 3214 → 3232;
next build exit 0、/ 仍是 ○ Static;首屏 JS gzip-9 585,246 → 583,660 B,
-1,586 B / -0.27%(口径同 09-02、09-06)。任务书逐条对照,删除范围与保留
范围都对,guided_topic 枚举保留、录入链路未被误删、复制仍走全文。
未通过 2 条,同一类——契约测试跟着代码漂了没更新,不是产品回归:
chat-bundle-splitting-contract 断言 message-markdown 包裹恰好 1 个,两层
可见性后是 2 个(同一 test 里 plainParagraphs(spoken) 字面量也已不存在);
chat-navigation-a11y-contract 断言 window.location.assign("/login") 恰好
4 处,删建议问题入口带走一处变成 3。基线 27 条失败与改后逐条一致,新增
的就是这两条。
执行方把全量测试记成 Windows 环境缺口(SKILL.md symlink EPERM、CRLF),
判断对他们的机器成立,也因此没机会看到这两条。修复单只改测试、禁止碰
frontend/src,要求失败数回到 27。
顺带澄清:PROGRESS 里记的 membership-page「login redirects」CRLF 失败在
Linux 上通过,不是回归。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
208 lines
9.9 KiB
Markdown
208 lines
9.9 KiB
Markdown
# 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.<anonymous> (…: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` 把回答拆成口语层与折叠层,两层各有一个 `<div className="message-markdown">`(`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, /<p key=\{index\}>\{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.<anonymous> (…: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 的另一半(本命第一步未强制计算工具) | 与主题卡无关,仍未处理 |
|