# TASK · 滚动锚两处老问题:直接打开会话时监听未挂上 + 校正长回答被拉到底(2026-09-26) ## 基线 - BUG-1042 修复单(`080ea5ca`,按钮间距)合入后的 `origin/staging`。分支 `codex/scroll-anchor-hook-fixes-20260926`。 - 只改 `frontend/src/hooks/use-conversation-scroll-anchor.ts` 与其调用处;与 `TASK-rectification-dup-question-20260926` 不交叉,可并行。 ## 事故实证(BUG-1042 执行方用 CDP 实测发现) - **BUG-1043(普通咨询)**:页面直接打开一个已有消息的会话(刷新、`?c=` 深链、启动落到最近会话)时,hook 的 scroll 监听与 `ResizeObserver` 从未挂上(插桩计数 0)。表现:短回答也显示「跳到最新」,滚动不更新它,点了也不跟随。先开 `/?new=1` 再点进同一会话则正常。疑似原因:加载屏期间容器为 null,effect 提前返回;揭幕后 `active` / `resetKey` 未变,effect 不重跑。 - **BUG-1044(生时校正长回答)**:钉顶后视口距底部 94 px,落在 hook 的 96 px「已回到底部」阈值内,`measure()` 把它当作读者回到底部 → 释放 `holdUnpin`、`anchored = true` → 长回答被拉到底。普通咨询停在约 198 px,不受影响。改前改后行为一致(非 BUG-1042 引入)。 ## 决策记录 - D1 BUG-1043:监听挂载必须在滚动容器真正出现后发生(容器 ref 变化时重新挂载,或以 callback ref 驱动),不依赖 `active` / `resetKey` 变化。 - D2 BUG-1044:钉顶后在用户**主动**滚动之前,不得因"距底部小于阈值"自动恢复贴底;判断"回到底部"必须来自用户滚动事件,而不是钉顶 / 留白造成的几何位置。 - D3 BUG-930 / 931 / 932 / 1042 的全部行为与测试不得弱化。 ## 硬红线 1. 真实 React 生命周期测试:以"加载屏 → 揭幕"的方式挂载已有消息的会话,断言监听已挂上、短回答不显示「跳到最新」;校正面长回答钉顶后不被拉到底,用户手动滚到底后恢复跟随。 2. 真实浏览器(CDP 假数据)复测两个场景并截图。 3. 全量测试与基线逐条一致、新增 0;改动断言三栏;更新 DESIGN 对应说明。 ## 任务分解 - T1 BUG-1043(D1)+ 测试。 - T2 BUG-1044(D2)+ 测试。 - T3 记录:更新 BUG-1043 / 1044(BUG-1042 单已建号,状态 investigating → 修复后按部署结果改);PROGRESS;`docs/testing/` 清单。