fix: stabilize session list and Codex imports
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
{"file": ".trellis/spec/frontend/quality-guidelines.md", "reason": "检查前端代码质量与回归覆盖"}
|
||||
{"file": ".trellis/spec/frontend/state-management.md", "reason": "检查视图签名与状态同步正确性"}
|
||||
{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "检查消息协议到 DOM 行为没有语义回退"}
|
||||
{"file": ".trellis/tasks/07-30-sidebar-title-refresh-storm/research/root-cause.md", "reason": "按已确认根因和验收重点审查实现"}
|
||||
@@ -0,0 +1,5 @@
|
||||
{"file": ".trellis/spec/frontend/quality-guidelines.md", "reason": "遵循前端质量与测试约束"}
|
||||
{"file": ".trellis/spec/frontend/state-management.md", "reason": "侧栏由客户端状态与服务端快照共同驱动"}
|
||||
{"file": ".trellis/spec/guides/code-reuse-thinking-guide.md", "reason": "复用现有分组、排序与 timeAgo 逻辑"}
|
||||
{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "核验服务端快照到前端 DOM 的完整数据流"}
|
||||
{"file": ".trellis/tasks/07-30-sidebar-title-refresh-storm/research/root-cause.md", "reason": "实现所需的根因证据、边界与测试重点"}
|
||||
48
.trellis/tasks/07-30-sidebar-title-refresh-storm/prd.md
Normal file
48
.trellis/tasks/07-30-sidebar-title-refresh-storm/prd.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 修复侧栏标题刷新风暴
|
||||
|
||||
## 目标
|
||||
|
||||
修复会话或协作状态高频更新期间侧栏会话列表反复整段重建、视觉闪烁且点击目标失效的问题,同时保持标题、排序、运行状态、未读状态、等待状态、搜索与折叠行为实时正确。
|
||||
|
||||
## 已确认根因
|
||||
|
||||
1. 服务端的多类会话事件会发送完整 `session_list`;MCP child-agent 每次增量还会更新父会话的 `updated` 并广播完整列表。
|
||||
2. 前端收到 `session_list` 或 `session_message` 后调用 `renderSessionList()`。
|
||||
3. `renderSessionList()` 无条件执行 `sessionList.innerHTML = ''`,重建所有项目分组、会话项和监听器。
|
||||
4. 当重建发生在指针按下与抬起之间时,原点击节点已被替换,浏览器不会产生有效点击。
|
||||
|
||||
## 需求
|
||||
|
||||
- 在侧栏渲染边界计算“结构视图签名”,只有影响列表结构、排序或交互状态的内容变化时才重建 DOM。
|
||||
- 单纯 `updated` 时间变化且会话顺序未变化时,不得替换现有项目分组或会话项节点。
|
||||
- 跳过结构重建时,原位刷新现有会话项的相对时间文本。
|
||||
- 会话顺序、标题、置顶、运行、未读、等待/回复计数、项目归属、当前会话、当前代理、搜索条件、项目折叠或旧会话展开状态变化时,仍必须重建。
|
||||
- 不改变 WebSocket 消息协议,不删除必要的未读/排序状态广播。
|
||||
- 同一父会话的连续 child-agent 增量应合并整份会话的加载、序列化与落盘;冲刷前必须基于最新父会话合并,不能覆盖同期父 turn 写入。
|
||||
- child-agent 引起的完整 `session_list` 广播应短周期合并,但局部 `ccweb_mcp_child_agent_update` 必须逐次实时发送。
|
||||
- `returned`、`failed`、`interrupted`、`closed` 最终态必须立即可靠冲刷,不能被尾随定时器覆盖或丢失。
|
||||
- 不引入新的前端依赖。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 首次渲染正常建立侧栏 DOM。
|
||||
- [ ] 同一列表连续收到仅 `updated` 不同的快照时,`.session-project-group` 与 `.session-item` 节点身份保持不变。
|
||||
- [ ] 上述快速更新期间,现有点击监听器和点击目标保持有效。
|
||||
- [ ] 跳过结构重建时,`.session-item-time` 文本能原位更新。
|
||||
- [ ] 会话顺序、标题、状态、分组、搜索或折叠状态变化会触发一次正确重建。
|
||||
- [ ] 多次 running child 增量只触发一次父会话保存和一次完整列表广播,同时局部 child 更新逐次发送。
|
||||
- [ ] child 最终态立即落盘并取消残留尾随任务;最终工具结果刷新后可恢复。
|
||||
- [ ] 新增回归测试先失败、修复后通过;现有相关回归通过。
|
||||
|
||||
## 实现约束
|
||||
|
||||
- 优先在 `public/app.js` 内复用现有状态和 `timeAgo()`,避免复制分组/排序逻辑。
|
||||
- 测试沿用 `scripts/regression.js` 的函数提取与最小 DOM stub 方式。
|
||||
- 关键注释使用简体中文,说明为什么 `updated` 不应触发结构重建。
|
||||
- 保留现有搜索、折叠、置顶和会话操作事件语义。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 本次不重构整个侧栏为框架组件。
|
||||
- 本次不修改 child-agent 工具卡协议。
|
||||
- 本次不通过粗暴长延迟隐藏问题。
|
||||
@@ -0,0 +1,33 @@
|
||||
# 侧栏刷新风暴根因记录
|
||||
|
||||
## 数据流
|
||||
|
||||
```text
|
||||
协作/会话事件
|
||||
→ 服务端保存父会话并更新 updated
|
||||
→ sendSessionList / broadcastSessionList
|
||||
→ 前端 handleServerMessage
|
||||
→ renderSessionList
|
||||
→ 清空并重建整个 session-list
|
||||
```
|
||||
|
||||
## 关键证据
|
||||
|
||||
- `renderSessionList()` 第一行清空 `sessionList.innerHTML`,每次重新创建所有项目分组和会话节点。
|
||||
- `session_list` 与 `session_message` 路径都无条件调用该函数。
|
||||
- `sendCcwebMcpChildAgentUpdate()` 每次 child 增量都会调用 `broadcastSessionList()`。
|
||||
- `updatePersistedCcwebMcpChildTool()` 每次增量都会改写父会话的 `updated`,因此完整 payload 字节级比较无法去重。
|
||||
- `waitingOnChildren` 来自跨会话回复队列,不直接来自 MCP child-agent 状态。
|
||||
- 服务端事件循环指标正常,现象不是服务进程阻塞。
|
||||
|
||||
## 技术判断
|
||||
|
||||
仅移除 child 广播会遗漏普通消息等同类来源,也可能延迟未读状态。仅做节流仍会周期性替换点击节点。最稳妥的边界是前端结构视图签名:把会话数组当前顺序和所有结构/状态字段纳入签名,但排除仅用于相对时间展示的 `updated`;签名相同时原位更新时间,签名变化时才重建。
|
||||
|
||||
## 测试重点
|
||||
|
||||
- 首次渲染会清空并建立列表。
|
||||
- 仅更新时间变化时不再次清空列表,并保留原节点身份。
|
||||
- 标题、状态或顺序变化时签名变化并允许重建。
|
||||
- 原位时间刷新使用 `data-id` 精确匹配会话。
|
||||
|
||||
26
.trellis/tasks/07-30-sidebar-title-refresh-storm/task.json
Normal file
26
.trellis/tasks/07-30-sidebar-title-refresh-storm/task.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "sidebar-title-refresh-storm",
|
||||
"name": "sidebar-title-refresh-storm",
|
||||
"title": "修复侧栏标题刷新风暴",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "shiyue",
|
||||
"assignee": "shiyue",
|
||||
"createdAt": "2026-07-30",
|
||||
"completedAt": "2026-07-30T17:20:05+08:00",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
Reference in New Issue
Block a user