docs(tasks): 回答定位开头验收未通过,修复单 BUG-931/932

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
This commit is contained in:
Jesse_Chen
2026-09-17 14:13:43 +00:00
co-authored by Claude Fable 5.1
parent 11c0028d59
commit b5e16b96f4
2 changed files with 70 additions and 1 deletions
+2 -1
View File
@@ -132,7 +132,8 @@
| `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` |
| `TASK-consult-followup-tool-contract-20260917.md` | `PROGRESS-consult-followup-tool-contract-20260917.md` | 真机:申报时段会话连发「?」「你在说什么鬼」都 `run.failed runtime_contract_incomplete`,回执无任何 `tool` 步骤。根因是 Agent 系统指令写明「简单追问可复用已有 packet / context、不调工具」,而 `contractReady()` 要求每次请求恰好一次成功排盘调用;「已有 packet」跨请求并不存在(缓存只在单次请求内)。本命与窗口两个 Agent 同构。**产品拍板方案 1**:每轮必调工具(BUG-922 删例外句 + BUG-923 第 0 步 `toolChoice: required`);否决「没调工具就走不扣点纯对话」。第一轮正经问题为何失败留 T4 取证(回执只在 web 容器日志) | 待验收 | `codex/consult-followup-tool-contract-20260917` |
| `TASK-session-list-single-source-20260917.md` | `PROGRESS-session-list-single-source-20260917.md` | 会话列表一处数据源:本地 PG 兼容层 `order()` 只保留最后一键,`/api/sessions` 实际按 `id` 排、与游标不一致;`/` 与次级页两份数据源、`/` 每次回来重启动(产品拍板:首页与三个次级页进同一路由组,侧栏外壳与列表 provider 常驻 layout,不做 store);空「新对话」落库堆积(首页 50 条里 28 条);标题类别在后、同名靠墙钟 HH:MM。串行在 composer-guard 单之后。BUG 段 926 起 | 待验收(T1–T4;延迟落库让步) | `codex/session-list-single-source-20260917` |
| `TASK-consultation-answer-start-anchor-20260917.md` | `PROGRESS-consultation-answer-start-anchor-20260917.md` | 主会话回答落在结尾:`useConversationScrollAnchor` 是贴底跟随,流式期间视口钉在最后一个字,回答开头滚出视口;改为发送后问题钉顶、回答向下长、长出视口显示「跳到最新」、末尾动态留白;产品追加拍板:校正面同一语义(推翻 BUG-041/048 贴底),本轮开头 = 用户行或新助手行。BUG 段 930 起 | 验收 | `worktree/green-harbor-5be3` |
| `TASK-consultation-answer-start-anchor-20260917.md` | `PROGRESS-consultation-answer-start-anchor-20260917.md` | 主会话回答落在结尾:`useConversationScrollAnchor` 是贴底跟随,流式期间视口钉在最后一个字,回答开头滚出视口;改为发送后问题钉顶、回答向下长、长出视口显示「跳到最新」、末尾动态留白;产品追加拍板:校正面同一语义(推翻 BUG-041/048 贴底),本轮开头 = 用户行或新助手行。BUG 段 930 起 | 验收未通过:主会话钉顶/跳到最新/切会话(Chrome 真实布局 S1–S4)通过;P1 无用户行的一轮(校正点选项/开场)留白自指坍缩、钉不住并回退贴底(BUG-931);P2 闲置会话上滑后子元素尺寸变化被写留白(BUG-932)。tsc/build 红继承自 e4e73f56。修复单 `TASK-consultation-answer-start-anchor-fix-20260917.md` | `worktree/green-harbor-5be3` |
| `TASK-consultation-answer-start-anchor-fix-20260917.md` | — | 验收修复单:F1 头就是留白行时留白按整视口算(BUG-931);F2 留白只在钉住期间存在(BUG-932);前置:先修 e4e73f56 的两处 TS 错否则门禁不过 | 待领取 | — |
### 个人报告
@@ -0,0 +1,68 @@
# 验收修复单 · 回答定位到开头:无用户行的一轮钉不住、闲置会话被留白(2026-09-17)
## 0. 基线
- 验收对象:`11c0028d``origin/staging` head,执行方变基后快进推送)。任务书:`TASK-consultation-answer-start-anchor-20260917.md`
- 分支:`codex/consultation-answer-start-anchor-fix-20260917`,从 `origin/staging` 起。
- 范围:只有 `frontend/src/hooks/use-conversation-scroll-anchor.ts` 与其测试、`docs/BUG_HISTORY.md``docs/tasks/`。不动 `page.tsx`、不动组件、不动 CSS。
- BUG 段:**BUG-931 起**(基线最大号 BUG-930)。
- **前置**staging 自 `e4e73f56`(会话列表单)起 `tsc --noEmit``next build` 都红(`src/lib/session-sidebar-row.ts(30,41)``tests/chart-library-session.test.ts(89,42)``Expected 1 arguments, but got 2`),门禁过不了,`/api/health` 仍停在 `dc2f2a16`。那两处属于会话列表单的验收,另行处理;本单执行时若 staging 仍红,先在本分支顺手修这两行并在进度记录写明,否则本单也发不出去。
## 1. 验收实证
验收环境:LinuxNode 20.19Google Chrome 151 无头(本机有 Chrome,真实布局;无登录态,所以用真实 hook + 与 `globals.css` 同参数的骨架页做场景,不是 staging 页面)。
| 项 | 结果 |
| --- | --- |
| `tsc --noEmit` | 2 错,**均继承自 `e4e73f56`**,本提交 0 新增 |
| `npm run lint` | 0 error / 117 warning |
| `npm test` | 3455 条,39 红;与父提交 `e4e73f56`(3450 条,39 红)逐条一致,**0 新红、+5 新测试全绿**。`e4e73f56` 自身比 `5203f9f0` 多 4 条红(会话列表单的事,另记) |
| `next build --webpack` | 编译通过,`Failed to type check`(同上两处继承错误);`/` Static 与 gzip 因此未核 |
| `page.tsx` 行数 | 1830 → 1830 |
| 文档 | BUG-930、BUG-041/048 各补指回行、CHANGELOG、DESIGN「Answer start anchor」、真机清单、PROGRESS 齐 |
| T4 思考块 | 执行方核实「按步骤条数有界、结算后折一行、未改」,与代码一致,接受 |
真实布局场景(容器 600px 高,`.message-list``.message``globals.css` 同一组 padding,最后一轮的 `min-height` 规则原样):
| 场景 | 结果 | 结论 |
| --- | --- | --- |
| S1 长历史、读者在底部、发问 | 钉顶后用户行距容器顶 16px;流式增高 6 步 `scrollTop` 纹丝不动;尾部超出 96px 后 `latestBelowFold` 为真;按「跳到最新」后贴底并跟随 | 通过 |
| S2 短历史(不溢出)、发问 | 留白让用户行到顶(16px),增高不动 | 通过 |
| S3 读者先滚到顶部再发问 | 钉顶 16px,增高不动 | 通过 |
| S4 切换会话 | 落底,`anchored` 为真 | 通过 |
| S5 闲置历史会话,读者上滑 400px,某条旧消息高度变化 30px | **最后一条助手行被写入留白**(底部从 936 变 1320`--conversation-viewport` / `--latest-turn-head-height` 被写上),`scrollTop` 漂 30px | **P2BUG-932** |
| S6 本轮没有用户行(校正点选项 / 开场 / 自动续轮):追加新助手行后 `pinLatestTurn()` | **钉不住**:新助手行停在距顶 480px`--latest-turn-head-height` 被写成 740px;随后第一步增高 `anchored` 翻成真,视口回到贴底跟随 | **P1BUG-931** |
S6 的机理(按符号):`resolveTurnHead` 正确选到新助手行;但 `applyTurnSpacer(element, head)``head.offsetHeight``--latest-turn-head-height`,而这个 head **就是**承载 `min-height: calc(viewport head)` 的最后一条助手行——前一帧 `follow()` 的非贴底分支已经给它写过一次留白,于是量到的高度是「内容 + 留白」,留白公式自指后坍缩为 0,`scrollTo` 目标被 `scrollHeight` 夹住,头钉不到顶。接着 `measure()``distance ≤ 96` 解除 `holdUnpin``nextAnchorState` 判回 `anchored = true`,后续增高就是 BUG-930 之前的贴底行为。校正面用 `ChatMessageRow`,行也是 `.message-assistant`,所以真实页面同样命中。
S5 的机理:`follow()` 的非贴底分支对**任何** `anchored = false` 的容器都 `applyTurnSpacer`,不管有没有正在进行的一轮。读者在旧会话里上滑后,任何子元素尺寸变化(窗口宽度、图片、思考块折叠)都会给最后一条助手行补一段留白,还会把 `latestBelowFold` 顶成真。
## 2. 任务分解
### F1 头就是留白行时,留白按整视口算(BUG-931)
- `applyTurnSpacer(container, head)`:若 `head``lastTurnTail(container)` 是同一个元素(本轮无用户行),`--latest-turn-head-height``0px`,让该行 `min-height` = 整个视口;否则照旧写 `head.offsetHeight`。或者在量高之前 `clearTurnSpacer` 再量,两者取其一,进度记录写明选哪种及原因。
- `pinLatestTurn``applyTurnSpacer` 之后、`scrollTo` 之前必须能把 head 钉到 `offset space4`;不得靠 `holdUnpin` 之外的新状态。
- 验收:
- 行为测试(fake scroller 或 jsdom)新增:容器内 `[u][a][u][a][a-new]``pinLatestTurn()``scrollTop === offset(a-new) 16``--latest-turn-head-height === "0px"`;随后增高 3 步 `anchored` 仍为假、`scrollTop` 不变。
- 现有主会话场景(S1–S3)断言不变。
### F2 留白只在本轮钉住期间存在(BUG-932)
- `follow()` 的非贴底分支只在 `pinnedHeadRef.current !== null``applyTurnSpacer`;读者单纯上滑(无 pin)不写留白。`anchorToLatest()``resetKey` 变化时清 `pinnedHeadRef` 与留白(现在已清)。
- `latestBelowFold` 在无 pin、读者上滑时仍按尾部超出 96px 判定(保留「读者上滑后可跳到最新」)。
- 验收:行为测试新增:长历史落底 → 上滑 400px → 改某条旧消息高度 → 容器 `style``--conversation-viewport``scrollTop` 不变(允许 ±1px)。
### F3 记录
- `docs/BUG_HISTORY.md` 新增 BUG-931、BUG-932(关联 BUG-930);BUG-930 的「验证」补一句真实布局场景已由验收方跑过。
- `docs/tasks/PROGRESS-consultation-answer-start-anchor-fix-20260917.md`;状态板行。
- 真机清单第 5 条拆成两条:点选项后新助手行在顶部;开场轮在顶部。
## 3. 硬红线
沿用原单 §4:滚动逻辑只在 hook 内;单一 hook、单一按钮;不 sticky;改断言写三栏;`page.tsx` 不增行。
## 4. 验收口径
`tsc --noEmit` 0 错(含前置两行修复后);`npm run lint` 0 error`npm test` 失败清单与 `11c0028d` 逐条一致、新增测试全绿;`next build` 通过、`/` Static、gzip ±2%(此项因门禁红一直没核,本单必须补上)。验收方会用同一套 Chrome 骨架页复跑 S1–S6。