baa-conductor

git clone 

baa-conductor / bugs / archive
codex@macbookpro  ·  2026-03-27

BUG-010-codexd-turn-status-stuck-inprogress.md

  1# BUG-010: codexd turn 状态停留在 inProgress,未更新为 completed
  2
  3## 当前状态(2026-03-25)
  4
  5已修复。
  6
  7`apps/codexd/src/app-server-transport.ts` 现在会在 stdio 连接关闭前先冲刷尾部缓冲区,确保最后一条没有换行的 JSON-RPC 消息也会被交给 `CodexAppServerClient`。`apps/codexd/src/index.test.js` 新增了顺序两个 session 的回归测试,覆盖第二个 turn 先 `willRetry=true`、再以无换行尾包发出 `turn/completed` 的路径。
  8
  9另外,`codexd` 现在会对“没有合法 completed 就先断流”给出独立诊断:
 10
 11- recent events 记录 `app-server.transport.closed`
 12- 若关闭时 turn 仍未完成,额外记录 `app-server.turn.completed.missing`
 13- 对应 session 会从 `inProgress` 明确收口到 `failed`
 14- 事件 detail 带 `failureClass = "transport_closed_before_turn_completed"`
 15
 16## 现象
 17
 18向 codexd 提交 turn 后,Codex 实际上已经产出完整回复(agentMessage 在 events.jsonl 中有记录),但 session 的 `lastTurnStatus` 始终停留在 `inProgress`,不更新为 `completed` 19
 20同时 recentEvents 里出现 `Reconnecting... N/5`,最终 turn 被标记为 `failed` 或一直挂着。
 21
 22已确认的 agentMessage(通过 events.jsonl 直接读取):
 23- "可以,我现在能稳定工作。"
 24- "1+1等于2。"
 25- 其他多条完整回复
 26
 27说明 Codex 子进程正常生成了答案,问题在 codexd 的状态回传链路上。
 28
 29## 触发路径
 30
 31```text
 32POST /v1/codex/turn { sessionId, input }
 33-> accepted: true,turnId 返回
 34-> app-server.turn.started 事件出现
 35-> Codex 产出 agentMessage(item/completed 事件记录到 events.jsonl)
 36-> Reconnecting... 2/5 开始出现
 37-> turn 状态卡在 inProgress(未收到 completed 信号)
 38-> 轮询 GET /v1/codex/sessions/:id 永远看到 lastTurnStatus: inProgress
 39```
 40
 41## 根因
 42
 43根因已经确认:
 44
 45`codexd` 的 stdio transport 以前只在读到换行时才向上游交付一条 JSON-RPC 消息;如果 app-server 最后一条消息正好是没有换行的 `turn/completed`,随后 `stdout` 结束或 child 退出,transport 会直接 `onClose`,但不会先冲刷尾部缓冲区。
 46
 47结果就是:
 48
 491. agentMessage / retry error 已经到达,说明 turn 实际执行完成或接近完成
 502. 最后一条 `turn/completed` 卡在 transport buffer 里,没有进入 `CodexAppServerClient`
 513. `CodexdDaemon` 没收到完成事件,session 的 `lastTurnStatus` 就停在 `inProgress`
 52
 53这说明问题在 codexd 自己的 transport / lifecycle 收尾边界,不是 Codex 没有生成回复。
 54
 55## 修复落点
 56
 57- `apps/codexd/src/app-server-transport.ts`
 58`stdout end`、stream error、process error、`process exit` 这些关闭路径统一先冲刷尾部缓冲,再触发 `onClose`
 59- `apps/codexd/src/index.test.js`
 60  增加真实 stdio transport 回归测试,覆盖第一个 session 完成后,第二个 session 先收到 `Reconnecting... 2/5`,最终再以无换行尾包发出 `turn/completed` 的路径
 61
 62## 复现步骤
 63
 64```bash
 65# 1. 创建 session
 66curl -s -X POST https://conductor.makefile.so/v1/codex/sessions \
 67  -H 'Content-Type: application/json' \
 68  -H 'Authorization: Bearer <TOKEN>' \
 69  -d '{"purpose":"duplex"}'
 70# 记录 sessionId
 71
 72# 2. 发 turn
 73curl -s -X POST https://conductor.makefile.so/v1/codex/turn \
 74  -H 'Content-Type: application/json' \
 75  -H 'Authorization: Bearer <TOKEN>' \
 76  -d '{"sessionId":"<sessionId>","input":"你好,用一句话介绍自己"}'
 77
 78# 3. 持续轮询 session 状态(等 60s+)
 79curl -s https://conductor.makefile.so/v1/codex/sessions/<sessionId> \
 80  -H 'Authorization: Bearer <TOKEN>'
 81# 预期 lastTurnStatus 卡在 inProgress 或变成 failed
 82
 83# 4. 直接读 events.jsonl 验证 Codex 实际已回复
 84grep agentMessage /Users/george/code/baa-conductor/logs/codexd/codexd/events.jsonl \
 85  | python3 -c "import sys,json; [print(json.loads(l)['detail']['params']['item']['text']) for l in sys.stdin]"
 86# 预期:能看到完整回复内容,证明 Codex 已经回答了
 87```
 88
 89## 修复前影响
 90
 91High —— 虽然 Codex 能正常回答,但调用方(conductor / AI caller)无法通过 session 状态 API 得知 turn 已完成,导致:
 92
 93- 轮询永远得不到 completed 信号
 94- 回复内容只能通过绕路读 events.jsonl 获取,无法走正式 API
 95- conductor 的任务编排无法基于 turn 完成事件触发后续动作
 96
 97## 修复建议
 98
 99已按方案 A 修复;没有引入超时、轮询或额外 completion 兜底。
100
101## 严重程度
102
103High —— 核心链路(发消息 → 得到完成状态)不通,调用方无法可靠地知道 Codex 什么时候回复完了。
104
105## 发现时间
106
1072026-03-24 by Claude
108
109## 备注
110
111- BUG-008 和本 bug 是同一条 transport 收尾缺陷在多 session 路径上的放大表现;见 `BUG-008` 当前状态说明
112- events.jsonl 里已确认的完整 agentMessage:「可以,我现在能稳定工作。」,对应 turn 019d1b95-b947-72c1-b6c2-02ee0a56202e
113- lastTurnStatus 可能的值:inProgress / completed / failed;问题是 completed 从未出现
114
115## Reopen 规则
116
117下面这些仍算 `BUG-010` 已修复范围,不 reopen:
118
119- `turn/completed` 的确发出了,只是恰好落在无换行尾包
120- recent events 中能看到该 turn 的 `app-server.turn.completed`
121- session 最终收口到 `lastTurnStatus: completed`
122
123下面这些应视为新的 bug:
124
125- transport / child 提前断流,recent events 出现 `app-server.transport.closed`
126- 同一 turn 没有任何合法 `app-server.turn.completed`
127- `codexd` 记录 `app-server.turn.completed.missing`
128- 事件 detail 标明 `failureClass = "transport_closed_before_turn_completed"`
129
130## 剩余风险
131
132未来如果再次出现 `stdout` 提前结束,且最终没有合法 `turn/completed`,现在会被 codexd 明确归类为新的 child / transport 故障,不再混入本 bug。