chore: rebuild release package
This commit is contained in:
79
.planning/conversation-stale-running-fix/findings.md
Normal file
79
.planning/conversation-stale-running-fix/findings.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# 调研记录
|
||||
|
||||
## 用户现象
|
||||
|
||||
- 助手正文和两个 Shell Command 均已完整结束,工具退出码为 0。
|
||||
- 页面底部仍显示“运行中”,停止按钮仍存在。
|
||||
- 随后发送“还在么”时插入失败,服务端提示:`Codex 任务失败:no active turn to steer`。
|
||||
|
||||
## 初始假设
|
||||
|
||||
- cc-web 的本地/持久化会话仍认为 turn 正在运行,而 Codex app-server 已释放 active turn。
|
||||
- 发送路径依据陈旧运行态选择 `steer`,缺少对 app-server 明确“无活动 turn”响应的状态修复与普通 `turn/start` 降级。
|
||||
|
||||
## 工作区约束
|
||||
|
||||
- 检查开始时 Git 工作树干净。
|
||||
- `.planning/.active_plan` 属于并发中的 `subagent-card-metadata`,本任务使用独立 scoped 目录且不覆盖 active 指针。
|
||||
- Trellis 当前任务也属于其他工作,本任务不切换共享指针。
|
||||
|
||||
## 项目上下文
|
||||
|
||||
- 独立计划审查已通过,无阻塞问题;建议在进度记录中保留实际测试命令与 60 秒超时证据。
|
||||
- `codebase-memory-mcp` 项目 `home-cc-web` 索引状态为 `ready`,当前包含 3246 个节点、7821 条边,无需重新全量索引。
|
||||
|
||||
## 初步代码命中
|
||||
|
||||
- 后端 `handleCodexAppSteerMessage` 直接读取 `activeCodexAppTurns` 中的 `threadId/turnId`,请求 `turn/steer`;失败分支目前只发送 `failed` 状态与 `codexapp_steer_failed` 错误,未见状态自愈或普通 turn 降级。
|
||||
- 前端 `handleServerMessage` 只把 `codex_app_steer_status` 映射成 pending/inserted/failed 展示;是否运行的权威来源仍需沿 `done`、`session_info` 和 `session_list` 消息继续确认。
|
||||
- `handleLoadSession` 返回的 `isRunning` 来自服务端 `isSessionRunning(sessionId)`,说明截图底部“运行中”不是单纯 DOM 残留,核心漂移大概率位于后端活动 turn Map 或其终态通知处理。
|
||||
- mock 已有 `turn/steer` 和 `turn/completed` 基础能力,`scripts/regression.js` 也已有运行中 steer 正常路径,可在其附近追加竞态回归。
|
||||
|
||||
## 状态机证据
|
||||
|
||||
- `isSessionRunning` 直接以 `activeCodexAppTurns.has(sessionId)` 判定 Codex App 运行态;只要 Map 条目没有删除,所有页面和再次发送都会继续认为 turn 活跃。
|
||||
- `processCodexAppNotification` 只有收到 `turn/completed` 或 `error` 才返回 `done: true`;`handleCodexAppNotification` 随后调用 `handleCodexAppTurnComplete` 删除活动条目并广播 `done`。
|
||||
- `handleMessage` 在持久化新普通消息之前先检查 `activeCodexAppTurns`,存在条目便无条件转入 `handleCodexAppSteerMessage`。
|
||||
- steer 路径会先把用户消息持久化,再异步请求 `turn/steer`。失败分支目前仅显示错误,不删除陈旧活动条目,也不为已经持久化的用户消息启动新 turn,因此会同时造成“持续运行中”和“消息插入失败”。
|
||||
- 最小安全修复应只识别 app-server 的权威终态/turn 不匹配错误:先收敛仍指向同一旧 entry 的本地状态,再用已持久化的用户消息启动新 turn;不能再次经过 `handleMessage`,否则会重复写入用户历史。
|
||||
|
||||
## 前端恢复态证据
|
||||
|
||||
- 前端用 `isGenerating` 控制停止按钮和“插入”发送分支,用 `currentSessionRunning` 控制“运行中”徽标,两者不是同一状态源。
|
||||
- 正常 `done` 会调用 `finishGenerating` 同时清理两者;但断线/切后台后收到权威 `resume_session_result(isRunning=false)` 时,仅清理徽标并重新读取仍为 true 的 `isGenerating`,因此停止按钮和 steer 视觉状态可继续残留。
|
||||
- 前端最小修复是在当前会话的 idle 恢复结果中调用 `finishGenerating`;该函数已有空 streaming bubble 与历史计数保护,无需单独操作停止按钮。
|
||||
- 本次先不扩大到 resume requestId/epoch 的乱序重构;现有服务端会以同一 requestId 先发 `resume_generating` 再发最终 `resume_session_result`,直接在前一个事件清 pending 会让简单匹配方案误拒绝最终响应。
|
||||
|
||||
## 修复范围决策
|
||||
|
||||
- 后端:只对 `no active turn to steer` 这类明确“无活动 turn”的权威错误自动收敛并新开 turn;不把一般 expectedTurnId 不匹配、超时或网络错误视为安全降级条件。
|
||||
- 前端:用权威 idle 恢复结果收敛本地生成态,解决漏收 `done` 后的停止按钮残留。
|
||||
- 暂不扩展 `thread/status/changed`;其非 systemError 状态可能出现在 turn 开始前,笼统作为终态会误结束真实任务。
|
||||
|
||||
## 测试入口
|
||||
|
||||
- 项目完整回归入口为 `timeout 60s npm run regression`。
|
||||
- 现有 `scripts/regression.js --target` 只有 composer、未路由通知和子代理卡片三个定向目标;本次应新增独立 stale-turn 目标,避免每次红绿循环都启动完整回归。
|
||||
- 前端已有 `assertFrontendGenerationControlsContract` 与 `assertSessionSwitchResilienceContract` 源码合同,可在前者或本次新目标中断言 idle resume 必须调用完整生成态收敛函数。
|
||||
|
||||
## 实现结果
|
||||
|
||||
- mock 新增确定性场景:app-server 内部 turn 已结束但故意不发 `turn/completed`,下一次 steer 返回真实 `no active turn to steer`。
|
||||
- 后端新增窄匹配分类器,仅在旧 entry 身份仍一致时调用现有完成流程保存输出并清理状态,再直接以已持久化消息启动新 turn;一般 expectedTurnId 不匹配继续报错。
|
||||
- 前端 idle `resume_session_result` 改为调用 `finishGenerating`,同步清理 `isGenerating`、停止按钮、streaming 状态与运行徽标。
|
||||
- 定向回归覆盖消息/输出不重复、自动转新 turn、最终 idle、无失败 toast,以及 expectedTurnId 不匹配负例。
|
||||
|
||||
## 独立审查修正
|
||||
|
||||
- 首轮实现会在 follow-up 用户消息之后追加旧 turn 助手输出,刷新后历史顺序错误;现通过 timestamp+content 定位,把旧助手消息插入 follow-up 之前,并补严格索引顺序断言。
|
||||
- 完成旧 turn 时同步 flush 跨会话回复可能先创建新 entry;现仅在 stale replacement 期间延迟 flush,并在替代 turn 启动前再次确认 Map 为空。
|
||||
- `done` 会短暂清掉前端 `isGenerating`;现替代 turn 创建成功后、首个 delta 前发送空 `resume_generating`,恢复停止/插入控制状态。
|
||||
- 替代 turn 优先沿用旧 entry 的 `mcpContext`,避免线程级 MCP 上下文退化。
|
||||
- 独立质量审查最终通过,无阻塞问题。
|
||||
|
||||
## 部署验证
|
||||
|
||||
- 重启前通过 ccweb 会话列表确认只有当前对话处于 `running`,不存在会被中断的其他会话。
|
||||
- `pm2 restart ccweb --update-env` 导致调用连接中断,但 PM2 进程创建时间、restart 计数与启动日志确认重启已经执行。
|
||||
- 重启后 `ccweb` 状态为 `online`,unstable restart 为 0,事件循环 P95 约 1.61 ms。
|
||||
- 本地 `http://127.0.0.1:8002/` 返回 HTTP 200,探针耗时约 3.9 ms。
|
||||
20
.planning/conversation-stale-running-fix/progress.md
Normal file
20
.planning/conversation-stale-running-fix/progress.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 进度日志
|
||||
|
||||
- 2026-07-17:读取项目指令、Trellis 工作流及 `planning-with-files`、`todo-list-csv` 技能。
|
||||
- 2026-07-17:确认工作树干净,已有共享规划任务正在使用 active 指针,本任务采用独立 scoped 计划。
|
||||
- 2026-07-17:根据截图固化状态漂移与 `steer` 失败的初始因果假设,建立实施与验收计划。
|
||||
- 2026-07-17:独立计划审查通过;代码索引就绪,开始并行定位后端终态与前端发送判定链路。
|
||||
- 2026-07-17:初步定位 `handleCodexAppSteerMessage`、`isSessionRunning` 与现有 steer 回归入口;确认失败分支缺少明显的状态自愈。
|
||||
- 2026-07-17:完成核心状态机梳理;根因收敛为“终态通知缺失/乱序后 Map 陈旧,steer 权威拒绝未触发本地收敛与新 turn 降级”。
|
||||
- 2026-07-17:前后端并行排查完成;另确认前端 idle 恢复只清徽标、不清 `isGenerating`,会保留停止按钮与插入视觉状态。
|
||||
- 2026-07-17:定位阶段完成,进入失败回归编写;计划仅窄匹配明确无 active turn,保留其他 steer 错误原行为。
|
||||
- 2026-07-17:确认完整回归入口和现有定向 target 结构;实施代理将新增本问题专用 target 以保留红绿测试证据。
|
||||
- 2026-07-17:实施代理完成测试先行修复;旧实现定向回归按预期失败于 steer 错误和 done 超时,修复后连续三次通过。
|
||||
- 2026-07-17:主线程复跑 `timeout 60s node scripts/regression.js --target codexapp-stale-running` 通过;四个修改文件语法检查和 `git diff --check` 通过。
|
||||
- 2026-07-17:进入完整回归与独立边界审查阶段。
|
||||
- 2026-07-17:首轮独立审查发现历史顺序与跨会话 flush 重入问题,已补实现及红绿回归。
|
||||
- 2026-07-17:最终复审进一步指出 done→running 的短暂 UI 窗口;已发送 `resume_generating` 收敛并保留旧 MCP 上下文。
|
||||
- 2026-07-17:主线程再次运行定向回归、四文件语法检查、`git diff --check` 和 `timeout 60s npm run regression`,全部通过;独立审查最终放行。
|
||||
- 2026-07-17:进入最终差异、部署条件与临时跟踪文件清理。
|
||||
- 2026-07-17:重启前确认只有当前对话 running;执行 cc-web 重启后 PM2 online、HTTP 200、事件循环与进程状态正常。
|
||||
- 2026-07-17:最终行号、差异统计和 `git diff --check` 核验完成,任务闭环。
|
||||
35
.planning/conversation-stale-running-fix/task_plan.md
Normal file
35
.planning/conversation-stale-running-fix/task_plan.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# 对话结束后仍显示运行中修复计划
|
||||
|
||||
## 目标
|
||||
|
||||
修复 Codex App 对话实际已结束但 cc-web 仍显示“运行中”的偶发状态漂移,确保后续消息不会误走 `steer` 并触发 `no active turn to steer`。
|
||||
|
||||
## 阶段
|
||||
|
||||
1. [complete] 定位运行状态结束与再次发送的完整调用链
|
||||
2. [complete] 补充卡住运行态与 steer 失败的回归用例
|
||||
3. [complete] 实现终态事件驱动的运行状态收敛
|
||||
4. [complete] 实现无活动 turn 时的安全发送降级
|
||||
5. [complete] 运行定向回归与语法静态检查
|
||||
6. [complete] 执行完整回归并独立审查边界
|
||||
7. [complete] 核对差异风险并清理临时跟踪文件
|
||||
|
||||
## 验收标准
|
||||
|
||||
- turn 正常完成、失败或中断后,会话最终稳定为非运行态。
|
||||
- 状态漂移时再次发送不会把用户消息永久失败在 `steer`。
|
||||
- 不引入重复 turn、重复消息或吞掉真实 steer 错误。
|
||||
- 回归测试覆盖终态缺失/乱序和 `no active turn to steer` 竞态。
|
||||
- 定向测试、语法检查与完整回归均通过,单次测试最长 60 秒。
|
||||
|
||||
## 约束
|
||||
|
||||
- 代码理解优先使用 `codebase-memory-mcp`,`rg` / `nl` 仅做校验。
|
||||
- 不覆盖现有 `.planning` 任务,不切换共享 Trellis 当前任务指针。
|
||||
- 未确认其他会话均非 `running` 前不重启 cc-web。
|
||||
- 代码注释与新增文档使用简体中文。
|
||||
|
||||
## 错误记录
|
||||
|
||||
| 错误 | 尝试 | 处理 |
|
||||
|---|---:|---|
|
||||
Reference in New Issue
Block a user