6.2 KiB
6.2 KiB
标题历史定位时间线
目标
当 LLM 通过 ccweb_set_title 修改会话标题时,持久化标题变更事件,并在“定位”列表中按对话顺序显示不可点击的标题分隔项;同时按消息时间插入精确到天的日期分隔项,让用户看出主题在哪一步变化以及对话发生在哪一天。
阶段
| 阶段 | 状态 | 验收标准 |
|---|---|---|
| 1. 建立计划并确认需求边界 | complete | 计划、发现、进度和 TODO CSV 已建立,且不覆盖现有任务文件 |
| 2. 定位标题 MCP、消息存储与定位弹层链路 | complete | 找到精确入口、数据流、现有测试和兼容边界 |
| 3. 补充标题历史及日期定位展示回归 | complete | 新断言覆盖持久化、接口返回、只读展示、历史兼容及浏览器本地 23:59/00:01 跨天边界 |
| 4. 实现标题变更事件持久化与读取 | complete | 成功改名时写入事件,忽略/失败不写,旧数据可读取 |
| 5. 实现定位列表标题与日期时间线展示 | complete | 标题项按步骤排序;日期按天去重并呈现横线中置日期气泡;两类节点均不可点击 |
| 6. 运行专项与全量验证 | complete | 语法、专项回归、全量回归和 diff 检查通过 |
| 7. 审查改动并清理临时跟踪文件 | complete | 代码审查通过,TODO CSV 删除,计划与进度收口 |
关键约束
- 只记录由标题 MCP 实际生效的变更;用户手动锁定导致的 ignored 不产生伪历史。
- 标题历史是时间线元数据,不伪装成普通对话消息,不参与消息定位选中。
- 日期节点只显示
YYYY-MM-DD,按浏览器当前用户本地时区分组,与现有消息时间的前端展示语义一致,不显示时分秒。 - 日期节点采用两侧横线、中间日期气泡的分隔样式;本地时间从 23:59 跨到 00:01 时必须产生新日期节点。
- 兼容已有会话数据;历史中没有标题事件时维持当前行为。
- 不覆盖工作区内与本任务无关的未提交文件和规划状态。
- 测试命令最长 60 秒;未确认无其他 running 会话前不重启服务。
错误记录
| 错误 | 尝试 | 处理 |
|---|---|---|
标题历史专项回归因缺少 normalizeTitleHistory 失败 |
1 | 失败符合 TDD 预期,进入最小后端实现 |
后端实现后专项回归因缺少前端 normalizeOutlineTitleHistory 失败 |
1 | 后端纯函数契约已通过,进入前端实现 |
ccweb_list_conversations(status=running) 60 秒超时 |
1 | 降级检查 PM2、端口、近期会话文件;发现另一条 15:38 收到用户消息的 Codex App 会话,按约束暂缓重启 |
codebase-memory-mcp detect_changes 返回 transport closed |
1 | 不重复同一失败,使用已完成的函数级检索、全量回归与本地 diff 审查收口 |
| 首轮标题前置计划审查要求细化锚点与传输契约 | 1 | 补充字段名、索引基准、合法范围、优先级、降级规则,以及实时/重载/缓存回归要求后重新审查 |
| 标题前置专项回归在后端锚点保留断言失败 | 1 | 符合 TDD 预期,确认现有 normalizeTitleHistory() 会丢弃新字段,进入最小后端实现 |
| 后端实现后专项回归在标题前置顺序断言失败 | 1 | 后端锚点契约已通过,失败符合预期,进入前端锚点排序实现 |
最终 codebase-memory-mcp detect_changes 返回 transport closed |
1 | 不重复失败调用,改用函数级 diff、专项回归、全量回归与 git diff --check 完成影响面审查 |
| 首次全量回归调用未保留最终退出码 | 1 | 确认残留测试进程已结束后,改用可追踪 session id 重新运行并取得 exit 0 与通过标记 |
追加迭代:标题前置锚点(2026-07-29)
| 阶段 | 状态 | 验收标准 |
|---|---|---|
| 1. 梳理现有标题事件与消息索引契约 | complete | 明确现有标题为何排在触发消息之后,并确定不破坏历史数据的锚点字段 |
| 2. 补充标题前置及旧历史兼容回归 | complete | 新标题事件和无锚点旧事件均断言为“日期 → 标题 → 用户消息” |
| 3. 持久化标题事件的触发消息锚点 | complete | LLM 实际改名事件记录 anchorMessageIndex;实时事件、会话重载、前端归一化与 snapshot 缓存均保留该字段,非法数据被清洗 |
| 4. 按锚点重排定位列表展示顺序 | complete | 标题显示在所属用户消息之前,不可点击且不占消息编号;锚点缺失或不可见时按既定回退规则处理 |
| 5. 运行语法检查与专项回归 | complete | server/app/regression 语法检查及标题时间线专项回归通过 |
| 6. 运行全量回归并审查最终差异 | complete | 全量回归、diff check 和影响面审查通过,临时 TODO CSV 已清理 |
追加约束
messageIndex继续表示标题事件实际发生时的消息位置,不复用或改写其历史语义。- 新增可选字段
anchorMessageIndex:零基整数,指向改标题时session.messages中最近一条role === 'user'的消息;合法值必须>= 0且< messageIndex,否则归一化时丢弃该字段但保留合法标题事件。 anchorMessageIndex与messageIndex同时存在时,展示归属优先使用锚点;精确锚点消息未出现在当前定位消息集时,回退到messageIndex之前最近的可见用户消息;仍无候选时保留事件自身的时间位置。- 旧历史没有
anchorMessageIndex时,从定位列表中选择messageIndex之前最近的用户消息作为兼容锚点,不改写磁盘历史。 - 标题归属到触发消息后,日期也采用该用户消息的本地日期,保证固定顺序为“日期 → 标题 → 用户消息”。
- 锚点字段必须贯穿 MCP 返回和持久化、实时
session_renamed.titleEvent、重新加载的session_info.titleHistory、前端normalizeOutlineTitleHistory()与 session snapshot 缓存;实时打开定位列表和重新进入会话都应得到相同顺序。 - 回归至少覆盖:新事件即时归属、重新加载后锚点仍存在、旧事件无锚点回退、跨午夜仍按锚点消息日期分组。
- 不修改
.planning/.active_plan指向的其他在途任务,不触碰其 TODO CSV。