What happened?
In a session that delegates most of its work to foreground subagents, the persisted Todo plan froze for 55m44s while the real work advanced through four plan nodes.
The plan file itself was never wrong — it faithfully mirrored the last todo_write call. The problem is that nothing feeds a delegated execution back into plan state, and the nudge that is supposed to compensate for that never fired once in the whole session.
Observed sequence (relative offsets; the session took one small bugfix from triage to PR):
| T |
What actually happened |
Persisted plan |
| +0s |
todo_write registers 6 nodes (node 2 in_progress) and, in the same assistant turn, launches a foreground subagent with todo_id pointing at node 3 |
accurate |
| +0s → +39m |
The subagent implements, tests and commits the fix (commit lands at +37m42s) |
frozen: node 2 in_progress, nodes 3–6 pending |
| +39m39s |
The todo_write response for the call issued at +0s is only recorded now — it was queued behind its long-running sibling tool call in the same batch |
frozen |
| +39m45s |
The user sends a progress query → a new user turn |
frozen |
| +41m47s |
The parent delegates review to a second foreground subagent, todo_id for node 5 |
frozen |
| +55m44s |
First todo_write in 55m44s: flips nodes 2–5 to completed in one batched call, node 6 in_progress |
caught up |
| +59m03s |
Node 6 completed |
done |
Per-node lag at the moment of that catch-up write: node 2 ≈ 50 min, node 3 ≈ 18 min, node 4 ≈ 16 min. Four nodes that actually finished 50 / 18 / 16 / 0 minutes apart were reported as having completed together.
Practical impact: anything that reads plan state — the Todo pill, the plan-execution view, an operator watching a long session — shows the task stuck at "create worktree" while the fix is already committed and under review. Asking the agent "how is progress going?" mid-flight makes it worse, because that very turn throws the plan context away (point 4 below).
What did you expect to happen?
Plan state should track delegated progress within a bounded lag. At minimum:
- When a delegated execution that carries
todo_id finishes, the model should be re-prompted to update that plan node, instead of the reminder budget being consumed by parent tool turns that delegation never produces.
- A new user turn should not discard the context of a plan that still has unfinished items. Today the plan nudge is destroyed by exactly the message that asks about the plan.
Why it happens
Verified against main @ 661f41eef06.
- Only
todo_write can change plan state. The sidecar's sole writer is writeTodosToFile() (packages/core/src/tools/todoWrite.ts:133, called at :491). A subagent execution has no path back into the parent's plan.
todo_id is not load-bearing for status. It is declared (packages/core/src/tools/agent/agent.ts:224), documented (:861), shape-validated (:994-999) and used as an admission gate inside an approved Session Workflow (:1002-1013). Its only other consumer is display grouping in the Web Shell plan view (packages/web-shell/client/components/messages/PlanExecutionView.tsx:448). The feature's own design doc states it is observational and "does not schedule, retry, unblock, or complete work" (docs/design/ordinary-session-plan-execution.md).
- The compensating nudge is turn-budgeted on the parent chain.
todo_write registers an "unfinished todo items" reminder for the current prompt (todoWrite.ts:277-295). It is re-injected only every ACTIVE_TODO_REMINDER_REFRESH_TURNS = 3 parent tool turns (packages/core/src/config/config.ts:292; takeActiveTodoReminder at :7557, consumed on the ToolResult path at packages/core/src/core/client.ts:3911). The forced variant is reserved for Retry / Cron / Notification / Teammate turns (client.ts:3832). A parent that delegates to a foreground subagent spends roughly one turn per tens of minutes of real work, so the budget is never reached.
- Any new user turn discards it.
SendMessageType.UserQuery calls startActiveTodoWorkChain(prompt_id) with no continuedFrom (client.ts:3387), which clears the reminder, the chain owners and the turn budget (config.ts:7584-7599). Only Retry continues the chain (client.ts:3390). Nothing re-registers the reminder until the next todo_write.
Measurement note (corrected after filing): injection is not observable from the session transcript. The reminder is appended to the outgoing request after that turn's tool-result record has already been written, so a transcript search for the reminder string only ever matches a tool's own output echoing it. This report originally claimed the reminder was "injected 0 times" across the transcript of the session above and of a second, unrelated ~7.5 MB session; that inference was invalid and is retracted, and whether the reminder fired in those two sessions is not recoverable from their transcripts. The valid oracle is the outgoing request payload (--openai-logging), and both rules proposed below have since been measured that way against real builds — see the Before/After evidence on #10963. The staleness timeline above is unaffected: it comes from the plan sidecar's mtime and the todo_write call timestamps, not from reminder injection.
Reproduction
Needs a delegation-heavy session; the timing is the only non-deterministic part.
- Start a session and let the model create a multi-node plan with
todo_write.
- In the same turn, delegate one node to a foreground subagent with
todo_id set, and give that subagent enough work to run for several minutes.
- Send any user message while it runs.
- Inspect the plan sidecar (
<runtime-dir>/todos/<session-id>.json) and the transcript: node statuses stay at their pre-delegation values until the model spontaneously calls todo_write again, and the string The current task still has unfinished todo items never appears in the transcript.
Possible directions
- (a) When a top-level Agent execution carrying
todo_id completes, make the reminder due immediately — takeActiveTodoReminder(promptId, true) already supports forcing, so this is one call site at the agent-result boundary. Cheapest, and it puts the nudge exactly where the information arrives.
- (b) On a user turn, continue the previous work chain instead of clearing it when a reminder is still registered (the existing
continuedFrom path already does the re-keying). A registered reminder implies unfinished items, since todo_write deletes it when the plan completes.
- (c) Make the cadence time-aware as well as turn-aware — a turn count is meaningless when one turn is a 40-minute subagent run.
- (d) Make
todo_id load-bearing for status (auto in_progress on launch, prompt or auto-complete on finish). This contradicts the "observational" contract in the design doc, so it needs a deliberate decision rather than a drive-by change.
- (e) Separate defect, same session: record a tool response when the tool executes, not after the batch's slowest sibling. Here a
todo_write response was recorded 39m39s late, which makes transcript-based auditing of plan timing wrong by tens of minutes.
(a) + (b) look like the smallest change that removes the observed failure without touching the observational contract; (c) is a cheap follow-up; (d) and (e) deserve their own discussion.
Client information
Observed on a locally built CLI (version string 0.23.0) running from a git worktree of this repository, macOS (arm64). The four code paths above are all present in current main (661f41eef06), so this is not specific to that build. Login method is not relevant to the defect and is omitted.
Forensic identifiers (local-only, for maintainer follow-up)
These identifiers are only meaningful on the machine that produced them; no paths, hostnames, credentials or task content are included.
- Session ID:
d6d0a49e-1a8a-47b5-83cf-c7e1129b3480
- Plan ID:
0df766f9-1573-4647-a028-be7c9cff14a4
todo_write call IDs, in order: call_a419080da6114d198f4971ed, call_c1c8f5d2c28147f1979539ce, call_a39fce0868d7475d972adf0d, call_49533d90cedf41979a2817f3 (the delayed one), call_04d0e4aa6adc4ca9a9e46828, call_ecda4fcdd50046cc97a7abd1
- Delegated executions:
call_91ee484db5e54e9093dd68ec (todo_id → node 3), call_b93d8dc2b48649b583efe39c (todo_id → node 5)
- Absolute UTC timestamps for the table above:
2026-09-03T15:40:09Z (plan write + delegation), 2026-09-03T16:19:48Z (delayed response), 2026-09-03T16:21:56Z (second delegation), 2026-09-03T16:35:53Z (catch-up write), 2026-09-03T16:39:12Z (plan completed)
- Plan sidecar mtime always equalled the last
todo_write timestamp, which is what rules out a persistence defect.
中文
现象
在一个把大部分工作委派给前台子 agent 的会话里,持久化的 Todo plan 冻结了 55 分 44 秒,而实际工作已经推进了四个节点。
plan 文件本身没有写错——它忠实反映了最后一次 todo_write。问题在于:委派出去的执行没有任何回灌 plan 状态的通路,而本该起补偿作用的提醒在整场会话里 一次都没有触发。
时间线(相对时间;该会话把一个小修复从 triage 走到了 PR):
| T |
实际发生 |
持久化的 plan |
| +0s |
todo_write 写入 6 个节点(节点 2 为 in_progress),并在同一个 assistant turn 里启动前台子 agent,todo_id 指向节点 3 |
准确 |
| +0s → +39m |
子 agent 实现、测试并提交修复(提交落在 +37m42s) |
冻结:节点 2 in_progress,节点 3–6 pending |
| +39m39s |
+0s 那次 todo_write 的响应此刻才被记录——它被同一批次里长跑的兄弟工具调用堵住了 |
冻结 |
| +39m45s |
用户发了一句询问进展的消息 → 新的 user turn |
冻结 |
| +41m47s |
父会话把 review 委派给第二个前台子 agent,todo_id 指向节点 5 |
冻结 |
| +55m44s |
55m44s 以来第一次 todo_write:一次性把节点 2–5 刷成 completed,节点 6 in_progress |
追上 |
| +59m03s |
节点 6 completed |
完成 |
补刷那一刻的各节点滞后:节点 2 约 50 分钟、节点 3 约 18 分钟、节点 4 约 16 分钟。四个实际相隔 50 / 18 / 16 / 0 分钟完成的节点,被报告成"一起完成"。
实际影响:任何读取 plan 状态的地方——Todo pill、plan-execution 视图、盯着长会话的人——都会看到任务卡在"建 worktree",而修复其实早已提交并在 review 中。中途问一句"进展如何"还会让情况更糟,因为正是那一轮把 plan 上下文丢掉了(见下面第 4 点)。
期望行为
plan 状态应当以有界的滞后跟随委派出去的进展。至少要满足:
- 带
todo_id 的委派执行结束时,模型应当被重新提醒去更新对应节点,而不是让提醒预算被委派根本不会产生的父会话 tool turn 消耗掉。
- 新的 user turn 不应该丢弃仍有未完成项的 plan 上下文。今天恰恰是"询问 plan 的那条消息"销毁了 plan 提醒。
原因
已在 main @ 661f41eef06 上复核。
- 只有
todo_write 能改 plan 状态。 sidecar 的唯一写入者是 writeTodosToFile()(packages/core/src/tools/todoWrite.ts:133,在 :491 被调用)。子 agent 的执行没有回到父会话 plan 的通路。
todo_id 对状态不承载语义。 它被声明(packages/core/src/tools/agent/agent.ts:224)、被文档描述(:861)、做形状校验(:994-999),并在已审批的 Session Workflow 里作为准入门禁(:1002-1013)。唯一的另一个消费方是 Web Shell plan 视图里的展示分组(packages/web-shell/client/components/messages/PlanExecutionView.tsx:448)。该功能自己的设计文档写明它是观测性的,"does not schedule, retry, unblock, or complete work"(docs/design/ordinary-session-plan-execution.md)。
- 补偿提醒的预算按父会话链的 turn 计。
todo_write 为当前 prompt 注册"未完成 todo"提醒(todoWrite.ts:277-295)。它只在每 ACTIVE_TODO_REMINDER_REFRESH_TURNS = 3 个父会话 tool turn 才重新注入一次(packages/core/src/config/config.ts:292;takeActiveTodoReminder 在 :7557,ToolResult 路径消费点在 packages/core/src/core/client.ts:3911)。强制注入的变体只留给 Retry / Cron / Notification / Teammate 轮次(client.ts:3832)。而把活委派给前台子 agent 的父会话,几十个分钟的真实工作才烧掉大约一个 turn,预算永远凑不满。
- 任何新的 user turn 都会把它丢掉。
SendMessageType.UserQuery 调用 startActiveTodoWorkChain(prompt_id) 且不带 continuedFrom(client.ts:3387),这会清空提醒、链归属和 turn 预算(config.ts:7584-7599)。只有 Retry 会续链(client.ts:3390)。在下一次 todo_write 之前没有任何东西会重新注册提醒。
测量口径更正(提交后修订):注入这件事无法从会话 transcript 观测。提醒是在该轮 tool-result 记录写盘之后才追加到出站请求上的,所以在 transcript 里搜这句提醒文本,只会命中某个工具自己输出里的回声。本报告最初写的「上述会话整份 transcript(约 1.6 MB)里注入 0 次、另一份无关的约 7.5 MB 会话里也是 0 次」这个推断无效,现予撤回;那两场会话里提醒究竟有没有触发,已无法从其 transcript 还原。有效口径是出站请求体(--openai-logging),下面提出的两条规则后来已按此口径在真实构建上完成测量,见 #10963 上的 Before/After 证据。上面的滞后时间线不受影响:它来自 plan sidecar 的 mtime 与 todo_write 调用时间戳,与提醒注入无关。
复现
需要一个委派密集的会话;唯一不确定的是耗时。
- 起一个会话,让模型用
todo_write 建一个多节点 plan。
- 在同一轮里,把某个节点委派给一个前台子 agent 并设置
todo_id,给足让它跑几分钟的工作量。
- 在它运行期间发送任意一条用户消息。
- 检查 plan sidecar(
<runtime-dir>/todos/<session-id>.json)和 transcript:节点状态会停在委派前的值,直到模型自发地再次调用 todo_write;并且 transcript 里始终不会出现 The current task still has unfinished todo items 这个字符串。
可能的方向
- (a) 带
todo_id 的顶层 Agent 执行结束时,让提醒立即到期——takeActiveTodoReminder(promptId, true) 已经支持强制注入,所以这只是 agent 结果边界上的一个调用点。成本最低,而且把提醒放在了信息真正到达的位置。
- (b) 用户轮次到来时,如果仍有已注册的提醒,就续上上一条工作链而不是清空(现有的
continuedFrom 路径已经会重新挂载归属)。提醒存在即意味着有未完成项,因为 plan 完成时 todo_write 会把它删掉。
- (c) 让注入节奏同时感知时间,而不只感知 turn——当一个 turn 就是 40 分钟的子 agent 运行时,turn 计数没有意义。
- (d) 让
todo_id 真正承载状态(启动即 in_progress,结束时提示或自动完成)。这与设计文档里的"观测性"契约冲突,需要一个明确的决策,不适合顺手改。
- (e) 同一会话里暴露的另一个独立缺陷:工具响应应在工具执行时记录,而不是等同批次里最慢的兄弟调用。这里一次
todo_write 响应晚记录了 39m39s,导致基于 transcript 的 plan 时序审计会差几十分钟。
(a) + (b) 看起来是能在不触碰观测性契约的前提下消除该故障的最小改动;(c) 是便宜的后续;(d) 和 (e) 值得单独讨论。
What happened?
In a session that delegates most of its work to foreground subagents, the persisted Todo plan froze for 55m44s while the real work advanced through four plan nodes.
The plan file itself was never wrong — it faithfully mirrored the last
todo_writecall. The problem is that nothing feeds a delegated execution back into plan state, and the nudge that is supposed to compensate for that never fired once in the whole session.Observed sequence (relative offsets; the session took one small bugfix from triage to PR):
todo_writeregisters 6 nodes (node 2in_progress) and, in the same assistant turn, launches a foreground subagent withtodo_idpointing at node 3in_progress, nodes 3–6pendingtodo_writeresponse for the call issued at +0s is only recorded now — it was queued behind its long-running sibling tool call in the same batchtodo_idfor node 5todo_writein 55m44s: flips nodes 2–5 tocompletedin one batched call, node 6in_progresscompletedPer-node lag at the moment of that catch-up write: node 2 ≈ 50 min, node 3 ≈ 18 min, node 4 ≈ 16 min. Four nodes that actually finished 50 / 18 / 16 / 0 minutes apart were reported as having completed together.
Practical impact: anything that reads plan state — the Todo pill, the plan-execution view, an operator watching a long session — shows the task stuck at "create worktree" while the fix is already committed and under review. Asking the agent "how is progress going?" mid-flight makes it worse, because that very turn throws the plan context away (point 4 below).
What did you expect to happen?
Plan state should track delegated progress within a bounded lag. At minimum:
todo_idfinishes, the model should be re-prompted to update that plan node, instead of the reminder budget being consumed by parent tool turns that delegation never produces.Why it happens
Verified against
main@661f41eef06.todo_writecan change plan state. The sidecar's sole writer iswriteTodosToFile()(packages/core/src/tools/todoWrite.ts:133, called at:491). A subagent execution has no path back into the parent's plan.todo_idis not load-bearing for status. It is declared (packages/core/src/tools/agent/agent.ts:224), documented (:861), shape-validated (:994-999) and used as an admission gate inside an approved Session Workflow (:1002-1013). Its only other consumer is display grouping in the Web Shell plan view (packages/web-shell/client/components/messages/PlanExecutionView.tsx:448). The feature's own design doc states it is observational and "does not schedule, retry, unblock, or complete work" (docs/design/ordinary-session-plan-execution.md).todo_writeregisters an "unfinished todo items" reminder for the current prompt (todoWrite.ts:277-295). It is re-injected only everyACTIVE_TODO_REMINDER_REFRESH_TURNS = 3parent tool turns (packages/core/src/config/config.ts:292;takeActiveTodoReminderat:7557, consumed on the ToolResult path atpackages/core/src/core/client.ts:3911). The forced variant is reserved for Retry / Cron / Notification / Teammate turns (client.ts:3832). A parent that delegates to a foreground subagent spends roughly one turn per tens of minutes of real work, so the budget is never reached.SendMessageType.UserQuerycallsstartActiveTodoWorkChain(prompt_id)with nocontinuedFrom(client.ts:3387), which clears the reminder, the chain owners and the turn budget (config.ts:7584-7599). OnlyRetrycontinues the chain (client.ts:3390). Nothing re-registers the reminder until the nexttodo_write.Measurement note (corrected after filing): injection is not observable from the session transcript. The reminder is appended to the outgoing request after that turn's tool-result record has already been written, so a transcript search for the reminder string only ever matches a tool's own output echoing it. This report originally claimed the reminder was "injected 0 times" across the transcript of the session above and of a second, unrelated ~7.5 MB session; that inference was invalid and is retracted, and whether the reminder fired in those two sessions is not recoverable from their transcripts. The valid oracle is the outgoing request payload (
--openai-logging), and both rules proposed below have since been measured that way against real builds — see the Before/After evidence on #10963. The staleness timeline above is unaffected: it comes from the plan sidecar's mtime and thetodo_writecall timestamps, not from reminder injection.Reproduction
Needs a delegation-heavy session; the timing is the only non-deterministic part.
todo_write.todo_idset, and give that subagent enough work to run for several minutes.<runtime-dir>/todos/<session-id>.json) and the transcript: node statuses stay at their pre-delegation values until the model spontaneously callstodo_writeagain, and the stringThe current task still has unfinished todo itemsnever appears in the transcript.Possible directions
todo_idcompletes, make the reminder due immediately —takeActiveTodoReminder(promptId, true)already supports forcing, so this is one call site at the agent-result boundary. Cheapest, and it puts the nudge exactly where the information arrives.continuedFrompath already does the re-keying). A registered reminder implies unfinished items, sincetodo_writedeletes it when the plan completes.todo_idload-bearing for status (autoin_progresson launch, prompt or auto-complete on finish). This contradicts the "observational" contract in the design doc, so it needs a deliberate decision rather than a drive-by change.todo_writeresponse was recorded 39m39s late, which makes transcript-based auditing of plan timing wrong by tens of minutes.(a) + (b) look like the smallest change that removes the observed failure without touching the observational contract; (c) is a cheap follow-up; (d) and (e) deserve their own discussion.
Client information
Observed on a locally built CLI (version string
0.23.0) running from a git worktree of this repository, macOS (arm64). The four code paths above are all present in currentmain(661f41eef06), so this is not specific to that build. Login method is not relevant to the defect and is omitted.Forensic identifiers (local-only, for maintainer follow-up)
These identifiers are only meaningful on the machine that produced them; no paths, hostnames, credentials or task content are included.
d6d0a49e-1a8a-47b5-83cf-c7e1129b34800df766f9-1573-4647-a028-be7c9cff14a4todo_writecall IDs, in order:call_a419080da6114d198f4971ed,call_c1c8f5d2c28147f1979539ce,call_a39fce0868d7475d972adf0d,call_49533d90cedf41979a2817f3(the delayed one),call_04d0e4aa6adc4ca9a9e46828,call_ecda4fcdd50046cc97a7abd1call_91ee484db5e54e9093dd68ec(todo_id→ node 3),call_b93d8dc2b48649b583efe39c(todo_id→ node 5)2026-09-03T15:40:09Z(plan write + delegation),2026-09-03T16:19:48Z(delayed response),2026-09-03T16:21:56Z(second delegation),2026-09-03T16:35:53Z(catch-up write),2026-09-03T16:39:12Z(plan completed)todo_writetimestamp, which is what rules out a persistence defect.中文
现象
在一个把大部分工作委派给前台子 agent 的会话里,持久化的 Todo plan 冻结了 55 分 44 秒,而实际工作已经推进了四个节点。
plan 文件本身没有写错——它忠实反映了最后一次
todo_write。问题在于:委派出去的执行没有任何回灌 plan 状态的通路,而本该起补偿作用的提醒在整场会话里 一次都没有触发。时间线(相对时间;该会话把一个小修复从 triage 走到了 PR):
todo_write写入 6 个节点(节点 2 为in_progress),并在同一个 assistant turn 里启动前台子 agent,todo_id指向节点 3in_progress,节点 3–6pendingtodo_write的响应此刻才被记录——它被同一批次里长跑的兄弟工具调用堵住了todo_id指向节点 5todo_write:一次性把节点 2–5 刷成completed,节点 6in_progresscompleted补刷那一刻的各节点滞后:节点 2 约 50 分钟、节点 3 约 18 分钟、节点 4 约 16 分钟。四个实际相隔 50 / 18 / 16 / 0 分钟完成的节点,被报告成"一起完成"。
实际影响:任何读取 plan 状态的地方——Todo pill、plan-execution 视图、盯着长会话的人——都会看到任务卡在"建 worktree",而修复其实早已提交并在 review 中。中途问一句"进展如何"还会让情况更糟,因为正是那一轮把 plan 上下文丢掉了(见下面第 4 点)。
期望行为
plan 状态应当以有界的滞后跟随委派出去的进展。至少要满足:
todo_id的委派执行结束时,模型应当被重新提醒去更新对应节点,而不是让提醒预算被委派根本不会产生的父会话 tool turn 消耗掉。原因
已在
main@661f41eef06上复核。todo_write能改 plan 状态。 sidecar 的唯一写入者是writeTodosToFile()(packages/core/src/tools/todoWrite.ts:133,在:491被调用)。子 agent 的执行没有回到父会话 plan 的通路。todo_id对状态不承载语义。 它被声明(packages/core/src/tools/agent/agent.ts:224)、被文档描述(:861)、做形状校验(:994-999),并在已审批的 Session Workflow 里作为准入门禁(:1002-1013)。唯一的另一个消费方是 Web Shell plan 视图里的展示分组(packages/web-shell/client/components/messages/PlanExecutionView.tsx:448)。该功能自己的设计文档写明它是观测性的,"does not schedule, retry, unblock, or complete work"(docs/design/ordinary-session-plan-execution.md)。todo_write为当前 prompt 注册"未完成 todo"提醒(todoWrite.ts:277-295)。它只在每ACTIVE_TODO_REMINDER_REFRESH_TURNS = 3个父会话 tool turn 才重新注入一次(packages/core/src/config/config.ts:292;takeActiveTodoReminder在:7557,ToolResult 路径消费点在packages/core/src/core/client.ts:3911)。强制注入的变体只留给 Retry / Cron / Notification / Teammate 轮次(client.ts:3832)。而把活委派给前台子 agent 的父会话,几十个分钟的真实工作才烧掉大约一个 turn,预算永远凑不满。SendMessageType.UserQuery调用startActiveTodoWorkChain(prompt_id)且不带continuedFrom(client.ts:3387),这会清空提醒、链归属和 turn 预算(config.ts:7584-7599)。只有Retry会续链(client.ts:3390)。在下一次todo_write之前没有任何东西会重新注册提醒。测量口径更正(提交后修订):注入这件事无法从会话 transcript 观测。提醒是在该轮 tool-result 记录写盘之后才追加到出站请求上的,所以在 transcript 里搜这句提醒文本,只会命中某个工具自己输出里的回声。本报告最初写的「上述会话整份 transcript(约 1.6 MB)里注入 0 次、另一份无关的约 7.5 MB 会话里也是 0 次」这个推断无效,现予撤回;那两场会话里提醒究竟有没有触发,已无法从其 transcript 还原。有效口径是出站请求体(
--openai-logging),下面提出的两条规则后来已按此口径在真实构建上完成测量,见 #10963 上的 Before/After 证据。上面的滞后时间线不受影响:它来自 plan sidecar 的 mtime 与todo_write调用时间戳,与提醒注入无关。复现
需要一个委派密集的会话;唯一不确定的是耗时。
todo_write建一个多节点 plan。todo_id,给足让它跑几分钟的工作量。<runtime-dir>/todos/<session-id>.json)和 transcript:节点状态会停在委派前的值,直到模型自发地再次调用todo_write;并且 transcript 里始终不会出现The current task still has unfinished todo items这个字符串。可能的方向
todo_id的顶层 Agent 执行结束时,让提醒立即到期——takeActiveTodoReminder(promptId, true)已经支持强制注入,所以这只是 agent 结果边界上的一个调用点。成本最低,而且把提醒放在了信息真正到达的位置。continuedFrom路径已经会重新挂载归属)。提醒存在即意味着有未完成项,因为 plan 完成时todo_write会把它删掉。todo_id真正承载状态(启动即in_progress,结束时提示或自动完成)。这与设计文档里的"观测性"契约冲突,需要一个明确的决策,不适合顺手改。todo_write响应晚记录了 39m39s,导致基于 transcript 的 plan 时序审计会差几十分钟。(a) + (b) 看起来是能在不触碰观测性契约的前提下消除该故障的最小改动;(c) 是便宜的后续;(d) 和 (e) 值得单独讨论。