From 149f697c9c87350d0ae07e8f1249e9a17e236433 Mon Sep 17 00:00:00 2001 From: yiliang114 Date: Sun, 2 Aug 2026 01:18:01 +0800 Subject: [PATCH 1/2] fix(todo): preserve compatibility outside active plans --- .../core/__snapshots__/prompts.test.ts.snap | 32 +++++++++---------- packages/core/src/core/prompts.test.ts | 6 ++++ packages/core/src/core/prompts.ts | 2 +- packages/core/src/tools/todoWrite.test.ts | 21 ++++++++++++ packages/core/src/tools/todoWrite.ts | 3 +- .../messages/PlanExecutionView.test.tsx | 14 ++++++++ .../components/messages/PlanExecutionView.tsx | 2 +- 7 files changed, 61 insertions(+), 19 deletions(-) diff --git a/packages/core/src/core/__snapshots__/prompts.test.ts.snap b/packages/core/src/core/__snapshots__/prompts.test.ts.snap index 3ecd1ca596f..37387b5b46e 100644 --- a/packages/core/src/core/__snapshots__/prompts.test.ts.snap +++ b/packages/core/src/core/__snapshots__/prompts.test.ts.snap @@ -23,7 +23,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -219,7 +219,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -431,7 +431,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -622,7 +622,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -813,7 +813,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -1004,7 +1004,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -1195,7 +1195,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -1386,7 +1386,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -1577,7 +1577,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -1768,7 +1768,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -1982,7 +1982,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -2262,7 +2262,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -2476,7 +2476,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -2752,7 +2752,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -2943,7 +2943,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. @@ -3134,7 +3134,7 @@ You have access to the todo_write tool to keep user-visible progress for work th When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. diff --git a/packages/core/src/core/prompts.test.ts b/packages/core/src/core/prompts.test.ts index 33e120dc3ac..72f3e4e3043 100644 --- a/packages/core/src/core/prompts.test.ts +++ b/packages/core/src/core/prompts.test.ts @@ -168,6 +168,12 @@ describe('Core System Prompt (prompts.ts)', () => { expect(prompt).toContain( 'rather than one item per error, file, command, or minor edit', ); + expect(prompt).toContain( + 'When an active Todo plan covers work delegated through top-level Agent calls', + ); + expect(prompt).not.toContain( + 'For complex work delegated through top-level Agent calls, create the relevant todo first', + ); expect(prompt).not.toContain('VERY frequently'); expect(prompt).not.toContain('EXTREMELY helpful'); expect(prompt).not.toContain('write 10 items to the todo list'); diff --git a/packages/core/src/core/prompts.ts b/packages/core/src/core/prompts.ts index c039abf5f46..46c626ae6dd 100644 --- a/packages/core/src/core/prompts.ts +++ b/packages/core/src/core/prompts.ts @@ -317,7 +317,7 @@ You have access to the ${ToolNames.TODO_WRITE} tool to keep user-visible progres When you create a todo list: - Keep it short and outcome-oriented. Use a few meaningful, logically ordered, verifiable steps rather than one item per error, file, command, or minor edit. -- For complex work delegated through top-level Agent calls, create the relevant todo first and pass its ID as \`todo_id\` so the execution can be associated with the plan node. +- When an active Todo plan covers work delegated through top-level Agent calls, pass the matching Todo ID as \`todo_id\` so the execution can be associated with that plan node. Do not create a Todo solely to wrap a delegation that does not otherwise need task tracking. - Keep at most one item in_progress. Keep the list current, mark finished work completed, and revise it when the scope or approach changes. When work completes together, update multiple statuses in one tool call rather than making bookkeeping-only calls. - Do not repeat the full todo list in prose after calling the tool; briefly communicate only important context or the next step. diff --git a/packages/core/src/tools/todoWrite.test.ts b/packages/core/src/tools/todoWrite.test.ts index 0359de14594..f8775dc4330 100644 --- a/packages/core/src/tools/todoWrite.test.ts +++ b/packages/core/src/tools/todoWrite.test.ts @@ -356,6 +356,27 @@ describe('TodoWriteTool', () => { expect(display.planId).not.toBe('finished-plan'); }); + it('should start a new plan for a distinct all-completed snapshot', async () => { + mockFs.readFile.mockResolvedValue( + JSON.stringify({ + planId: 'finished-plan', + todos: [{ id: '1', content: 'Done', status: 'completed' }], + }), + ); + mockFs.mkdir.mockResolvedValue(undefined); + mockAtomicWrite.mockResolvedValue(undefined); + + const result = await tool + .build({ + todos: [{ id: '2', content: 'Already done', status: 'completed' }], + }) + .execute(mockAbortSignal); + const display = result.returnDisplay as { planId?: string }; + + expect(display.planId).toEqual(expect.any(String)); + expect(display.planId).not.toBe('finished-plan'); + }); + it('should clear persisted plan identity while identifying the cleared plan', async () => { mockFs.readFile.mockResolvedValue( JSON.stringify({ diff --git a/packages/core/src/tools/todoWrite.ts b/packages/core/src/tools/todoWrite.ts index 931433e36bf..4add2e8ac45 100644 --- a/packages/core/src/tools/todoWrite.ts +++ b/packages/core/src/tools/todoWrite.ts @@ -370,7 +370,8 @@ class TodoWriteToolInvocation extends BaseToolInvocation< finalTodos.length > 0 && (oldTodos.length === 0 || (oldTodos.every((todo) => todo.status === 'completed') && - finalTodos.some((todo) => todo.status !== 'completed'))); + (finalTodos.some((todo) => todo.status !== 'completed') || + finalTodos.some((todo) => !oldTodosMap.has(todo.id))))); const activePlanId = finalTodos.length === 0 ? undefined diff --git a/packages/web-shell/client/components/messages/PlanExecutionView.test.tsx b/packages/web-shell/client/components/messages/PlanExecutionView.test.tsx index 1fbc2862483..f546e4c22b1 100644 --- a/packages/web-shell/client/components/messages/PlanExecutionView.test.tsx +++ b/packages/web-shell/client/components/messages/PlanExecutionView.test.tsx @@ -135,6 +135,20 @@ describe('PlanExecutionView', () => { }); }); + it('does not block a todo on an unknown dependency', () => { + const todo: TodoItem = { + id: 'standalone', + content: 'Standalone', + status: 'pending', + blockedBy: ['missing'], + }; + + expect(getPlanNodeState(todo, new Map([[todo.id, todo]]), [], [])).toEqual({ + status: 'ready', + attention: false, + }); + }); + it('restores cancellation from replay output after the live task leaves', () => { const cancelled = { ...agentTool('build'), diff --git a/packages/web-shell/client/components/messages/PlanExecutionView.tsx b/packages/web-shell/client/components/messages/PlanExecutionView.tsx index 220d8029156..922c5e9f936 100644 --- a/packages/web-shell/client/components/messages/PlanExecutionView.tsx +++ b/packages/web-shell/client/components/messages/PlanExecutionView.tsx @@ -228,7 +228,7 @@ function getPlanNodeStateFromIndex( return { status: 'paused', attention }; if (todo.status === 'completed') return { status: 'completed', attention }; const blocked = (todo.blockedBy ?? []).some( - (id) => todosById.get(id)?.status !== 'completed', + (id) => todosById.has(id) && todosById.get(id)?.status !== 'completed', ); if (blocked) return { status: 'blocked', attention }; if (todo.status === 'in_progress') From 2e10f4776dbc92e5cee8b2898f8b16c753159a60 Mon Sep 17 00:00:00 2001 From: yiliang114 Date: Sun, 2 Aug 2026 02:01:57 +0800 Subject: [PATCH 2/2] fix(todo): distinguish completed plans with reused IDs --- packages/core/src/tools/todoWrite.test.ts | 6 +++--- packages/core/src/tools/todoWrite.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/tools/todoWrite.test.ts b/packages/core/src/tools/todoWrite.test.ts index f8775dc4330..bc3ab1bdb49 100644 --- a/packages/core/src/tools/todoWrite.test.ts +++ b/packages/core/src/tools/todoWrite.test.ts @@ -327,7 +327,7 @@ describe('TodoWriteTool', () => { const result = await tool .build({ - todos: [{ id: '1', content: 'Still done', status: 'completed' }], + todos: [{ id: '1', content: 'Done', status: 'completed' }], }) .execute(mockAbortSignal); @@ -348,7 +348,7 @@ describe('TodoWriteTool', () => { mockAtomicWrite.mockResolvedValue(undefined); const result = await tool - .build({ todos: [{ id: '2', content: 'New', status: 'pending' }] }) + .build({ todos: [{ id: '1', content: 'New', status: 'pending' }] }) .execute(mockAbortSignal); const display = result.returnDisplay as { planId?: string }; @@ -368,7 +368,7 @@ describe('TodoWriteTool', () => { const result = await tool .build({ - todos: [{ id: '2', content: 'Already done', status: 'completed' }], + todos: [{ id: '1', content: 'Already done', status: 'completed' }], }) .execute(mockAbortSignal); const display = result.returnDisplay as { planId?: string }; diff --git a/packages/core/src/tools/todoWrite.ts b/packages/core/src/tools/todoWrite.ts index 4add2e8ac45..72f09baa1c7 100644 --- a/packages/core/src/tools/todoWrite.ts +++ b/packages/core/src/tools/todoWrite.ts @@ -8,6 +8,7 @@ import type { ToolResult } from './tools.js'; import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js'; import type { FunctionDeclaration } from '@google/genai'; import { randomUUID } from 'node:crypto'; +import { isDeepStrictEqual } from 'node:util'; import * as fs from 'fs/promises'; import * as fsSync from 'fs'; import * as path from 'path'; @@ -370,8 +371,7 @@ class TodoWriteToolInvocation extends BaseToolInvocation< finalTodos.length > 0 && (oldTodos.length === 0 || (oldTodos.every((todo) => todo.status === 'completed') && - (finalTodos.some((todo) => todo.status !== 'completed') || - finalTodos.some((todo) => !oldTodosMap.has(todo.id))))); + !isDeepStrictEqual(finalTodos, oldTodos))); const activePlanId = finalTodos.length === 0 ? undefined