Files
cc-web/.planning/session-form-switch-race/task_plan.md
2026-07-18 19:47:04 +08:00

2.7 KiB

会话切换后表单不渲染修复计划

目标

修复频繁切换会话后“表单”角标已有待处理数量、但消息区未渲染表单的问题,确保切回会话后无需 F5 即可看到并操作待处理表单。

验收标准

  • 频繁切换包含待处理表单和普通消息的多个会话后,当前会话的表单立即渲染。
  • 旧会话异步请求晚到时,不得覆盖当前会话的消息或表单派生状态。
  • 表单角标数量与消息区实际可见待处理表单保持一致。
  • 页面刷新后的恢复行为与 SPA 内切换行为一致。
  • 新增竞态回归测试,并通过相关前端测试与静态检查。

工作步骤

  1. [DONE] 定位表单计数与消息渲染的会话切换链路
  2. [DONE] 编写可复现竞态的回归测试
  3. [DONE] 运行回归测试并确认当前实现失败
  4. [DONE] 修复会话切换时的表单状态同步与过期请求覆盖
  5. [DONE] 运行前端相关测试并确认回归通过
  6. [IN_PROGRESS] 执行静态检查并审查影响面
  7. [TODO] 使用真实浏览器验证频繁切换无需刷新

关键假设

  • 后端数据完整,因为 F5 后同一待处理表单能够显示。
  • 缺陷位于前端会话切换、消息加载或表单派生渲染状态的同步路径。
  • 修复应优先采用请求归属校验或会话级状态隔离,不依赖强制刷新。

已确认根因与修复边界

  • beginSessionSwitch() 在切换请求刚发起、DOM 尚未替换时提前执行 renderEpoch++,会永久取消当前会话尚未完成的历史消息批次;表单若位于该批次,便形成“缓存角标有 1、DOM 卡片为 0”。
  • session_infomsg.sessionId === currentSessionId 无条件放行,同一会话旧 requestId 响应可能覆盖新加载。
  • 服务端 session_history_chunk 未回传 requestId,前端历史块也只按 sessionId 判断;A→B→A 时旧 A 尾块可能误完成新 A 加载。
  • 修复同时覆盖:渲染代次只在真实 DOM 提交时推进;加载响应按 sessionId + requestId 归属;历史分块透传请求 ID。

风险

  • 消息加载、流式事件和表单计数可能来自不同异步通道,需要避免只修复其中一条路径。
  • 仓库由多个会话共享,不能覆盖现有 .planning/.active_plan、Trellis 当前任务或无关工作树改动。

错误记录

错误 尝试 处理
完整回归的旧静态断言要求保留 requestless idle 分支原字符串 1 将兼容分支拆为具名布尔量,保留语义和可审查契约后重跑
codebase-memory-mcp detect_changes 返回 Transport closed 1 不重复同一失败调用,降级为本地 git diff/rg/语法检查和独立审查代理