Task Tree 統一子任務管理:詳細設計同 PR 拆分計劃
上游參考: Zoo-Code-Org/Zoo-Code#856(Task Tree Abstraction design spec;Zoo-Code-Org#849 / Zoo-Code-Org#850 / Zoo-Code-Org#854 已於 2026-07-07 標記為 duplicate 關閉)
本 issue 係 Zoo-Code-Org#856 喺呢個 fork 嘅實施計劃:將 spec 對照 main(e064cf059)實際代碼驗證過,修正同現實唔符嘅部分,並拆成每個 ≤1000 行改動(連測試) 嘅 PR。
一、已驗證現狀(main @ e064cf0)
| 事實 |
位置 |
Task 只有單向鏈:readonly parentTaskId? + childTaskId?,冇 depth 欄位(全倉 grep 零匹配) |
src/core/task/Task.ts:171-172, 505-506 |
HistoryItem 已有 rootTaskId / parentTaskId / status("active"|"completed"|"delegated"|"interrupted") / delegatedToId / childIds[] / awaitingChildId / completedByChildId / completionResultSummary |
packages/types/src/history.ts:10-29 |
委派流程強制 single-open invariant:flush tool results → removeClineFromStack() → provider 級 handleModeSwitch()(全局 mode)→ createTask(startTask:false) → atomicReadAndUpdate 寫 parent 後設資料(含 rollback)→ scheduleTask(child) |
src/core/webview/ClineProvider.ts:3644-3810 |
new_task 委派前彈 askApproval("tool", ...) 批准框 |
src/core/tools/NewTaskTool.ts:~107 |
子任務完成:AttemptCompletionTool.delegateToParent() → askFinishSubTaskApproval()(實現在 presentAssistantMessage.ts:553)→ reopenParentFromDelegation(),返回 "delegated"|"denied"|"continue" |
src/core/tools/AttemptCompletionTool.ts:219-240 |
持久化:每 task 一個目錄(taskMetadata()),HistoryItem 經帶鎖 atomicReadAndUpdate 寫 globalState |
src/core/task-persistence/taskMetadata.ts |
webview 已有按 parentTaskId 分組嘅歷史視圖 hook |
webview-ui/src/components/history/useGroupedTasks.ts |
關鍵結論:spec 講「數據模型已支持樹」只係譜系層面對;真正瓶頸係 single-open invariant + provider 級全局 mode,呢兩樣先係平行執行嘅架構障礙。
二、範圍決定
| Phase |
內容 |
風險 |
| P1 |
depth 追蹤 + maxNestingDepth / autoFlattenOnLimit 設定 + auto-flatten inline 模式 |
低 |
| P2 |
樹形持久化加固(depth 回填、循環保護)+ UI 樹視圖擴充 |
中 |
| P3 |
平行子任務 — 本計劃唔排期:阻塞於 per-task mode 隔離 spike(handleModeSwitch 係 provider 級全局狀態,兩個 Task 無法同時各持一個 mode)。spike 完成前所有設計按 sequential-only |
高 |
| P4 |
會話級 checkpoint(只做手動觸發) |
中 |
明確剷走:
- cross-model context analysis — spec 嘅
confidenceScore 假設 LLM 輸出校準過嘅置信度,實際上唔成立;仲要設計多 provider 路由同成本估算,spec 完全冇覆蓋 → 另開實驗性 issue
- AI-driven inline escalation 啟發式(
countDistinctGoals、detectedParallelNeeds 等)— 冇任何機制令子任務向 runtime 發出「我變複雜咗」訊號;改做用戶手動觸發(P2 UI 按鈕,單向、唔可逆)
- checkpoint 自動觸發(depth-based / change detection / stuck detection)— spec 嗰套閾值純拍頭,先做手動
三、詳細設計
P1.1 數據模型
packages/types/src/history.ts:
// 新增可選欄位 — zod .optional() 令舊資料自動兼容
depth: z.number().int().min(0).optional(), // 巢狀層級,root = 0;建立時寫入 parent.depth + 1
- 唔加
siblingIds(可由 parent 嘅 childIds 反查,存兩份只會造成同步負擔)
- inline 狀態唔持久化:只係 Task 實例內存欄位。理由:inline 子任務冇獨立 Task 實例、冇獨立 historyItem,重啟後會話自然當普通 parent task 繼續 — 冇孤兒狀態要修復
src/core/task/Task.ts(line ~171-172, ~505):
depth: number // constructor: historyItem?.depth ?? (parentTask ? parentTask.depth + 1 : 0)
inlineSubtask?: { message: string; todos: TodoItem[] } // 純內存,唔寫入任何持久層
CreateTaskOptions(packages/types/src/task.ts:86)加 depth?: number 透傳
- depth 回填:舊任務 depth=undefined → 按 parentTaskId 鏈回溯補算一次,下次
atomicReadAndUpdate 時順帶寫入;唔需要一次性遷移腳本
P1.2 設定 round-trip(跟 AGENTS.md「Persisted Setting Checklist」)
// packages/types/src/global-settings.ts
maxNestingDepth: number // default 2,範圍 0–5;0 = 禁止委派(全部 inline)
autoFlattenOnLimit: boolean // default true;false 時超深直接拒絕 new_task
完整 round-trip:global-settings.ts → ExtensionState(vscode-extension-host.ts)→ SettingsView 綁定 local cachedState(唔好綁 live useExtensionState(),會 race)→ handleSubmit() payload → webviewMessageHandler → contextProxy.setValue() → ClineProvider.getState() + getStateToPostToWebview() 兩邊都要加。測試覆蓋 true / false / 未設定三種情況。
P1.3 Auto-flatten inline 模式(核心,重新設計)
決策:inline = 同一個 Task 實例 + 階段標記,唔建子 Task。
Spec 講「virtual child」但冇定義 runtime 點樣區分子任務 turn 同 parent turn。本倉庫有三個硬約束逼出唯一乾淨方案:
- single-open invariant(
removeClineFromStack)— parent 離開 stack 先開到子 tab
handleModeSwitch 係 provider 級全局狀態,兩個 Task 無法同時各持一個 mode
- inline 若建真 Task 實例但「唔開啟」,直接違反不變量一、二
所以 inline 子任務 = parent Task 自己嘅一個執行階段。new_task 被 flatten 時唔彈批准框、唔建 Task、唔開 tab — tool_result 本身就係 inline prompt:
模型調用 new_task(message, mode)
│
NewTaskTool.execute():
childDepth = task.depth + 1
if (childDepth > maxNestingDepth && autoFlattenOnLimit) {
task.inlineSubtask = { message, todos } // 設階段標記
pushToolResult(
`[auto-flattened: nesting limit ${maxNestingDepth} reached — executing inline]
You are now executing this subtask INLINE in the current conversation.
Subtask instruction: ${message}
Todos: ${todos}
Execute it with your available tools. When done, call attempt_completion
with a summary of what you did.`)
return // 唔彈 askApproval,唔建 child
}
...現有流程不變(askApproval → delegateParentAndOpenChild)
好處:
- 零合成訊息:parent 自己嘅會話就係 bounded context,唔使 spec 嗰套「截取最後 15 條 + extractParentContext」prompt 組裝
- 冇
resumeAfterDelegation():parent 從未離開 task loop,唔需要任何狀態重置 — 比 spec「inline 同 tab 共用 resume 邏輯」簡單一個量級
- 取消語義免費解決:用戶中途 cancel → 清走
task.inlineSubtask 標記,會話繼續當普通 parent。冇孤兒 child、冇 cancellation cascade(spec 列為 PR-stage 未決問題,呢度直接消失)
attempt_completion 喺 inline 階段(AttemptCompletionTool.handle(),delegateToParent 之前加分支):
if (task.inlineSubtask) {
task.inlineSubtask = undefined // 清標記
pushToolResult(`[inline subtask completed]\n${result}\nThe parent conversation continues.`)
return // loop 自然進入下一次 API call
}
// ...現有 askFinishSubTaskApproval → reopenParentFromDelegation 流程
即 inline 完成唔彈 askFinishSubTaskApproval(用戶已經喺同一會話睇到全部過程,再彈框係雙重摩擦 — spec 呢點判斷正確)。presentAssistantMessage.ts:553 嗰個 approval callback 完全唔使改。
嵌套規則:inline 階段期間模型再調 new_task → 直接拒絕(pushToolResult error),P1 明確禁止。Escalation 留 P2 做用戶手動觸發。
autoFlattenOnLimit: false:超深 → pushToolResult error「Nesting limit reached — continue working directly」,模型自己繼續做。
四、PR 拆分計劃(每個 ≤1000 行改動,連測試)
全部喺 main-task-tree base branch(= origin/main @ e064cf0)順序開發;每個 PR 由 main-task-tree 開出,merge 後 rebase 繼續。
PR-1:depth 追蹤 + schema(~450 行)
| 檔案 |
改動 |
packages/types/src/history.ts |
+depth: z.number().int().min(0).optional() |
packages/types/src/task.ts |
CreateTaskOptions.depth? |
src/core/task/Task.ts |
constructor depth 計算;inlineSubtask 欄位(先加,PR-3 用);depth 回填邏輯 |
src/core/webview/ClineProvider.ts |
createTask 透傳 depth + 寫入 historyItem |
| 循環保護 helper |
沿 parentTaskId 祖先鏈檢查 |
| 測試 |
unit:depth 計算 / 回填 / 循環保護(~250 行) |
依賴:無。風險:低(全部 additive,舊資料兼容)。
PR-2:設定 round-trip(~480 行)
| 檔案 |
改動 |
packages/types/src/global-settings.ts |
+maxNestingDepth(default 2, 0–5)、+autoFlattenOnLimit(default true) |
packages/types/src/vscode-extension-host.ts |
ExtensionState 加兩欄位 |
webview-ui/.../SettingsView |
cachedState 綁定 + control + handleSubmit payload(唔好綁 live useExtensionState()) |
src/core/webview/webviewMessageHandler.ts |
設定正規化 |
src/core/webview/ClineProvider.ts |
getState() + getStateToPostToWebview() 兩邊加 |
| 測試 |
UI binding/save spec、persistence/normalization、round-trip(true / false / 未設定)(~300 行) |
依賴:PR-1。風險:低。
PR-3:auto-flatten inline 模式(~780 行)
| 檔案 |
改動 |
src/core/tools/NewTaskTool.ts |
depth check 喺 askApproval 之前;flatten 分支設 task.inlineSubtask + pushToolResult directive;inline 階段中再 new_task → 拒絕;autoFlattenOnLimit=false → error result |
src/core/tools/AttemptCompletionTool.ts |
inline 完成分支(清標記、無 approval、loop 繼續) |
src/core/task/Task.ts |
cancel / abort 路徑清走 inlineSubtask |
| 測試 |
unit:flatten 決策純函數;integration:擴充 provider-delegation.spec.ts / nested-delegation-resume.spec.ts(超深 → 無 child Task、無 approval ask、tool_result 含 directive;inline attempt_completion → 標記清除、loop 繼續、冇 askFinishSubTaskApproval 調用)(~450 行) |
依賴:PR-1 + PR-2。風險:中(核心行為變更,但 tab / sequential 現有流程完全唔變)。
PR-4:樹形持久化加固 + UI(~520 行)
| 檔案 |
改動 |
src/core/webview/ClineProvider.ts |
webview message 帶 inlineSubtaskActive;取消級聯(cancel parent → children interrupted,接入 abandonSubtask 路徑) |
webview-ui/src/components/history/useGroupedTasks.ts |
depth badge + 狀態指示 + inline 標記 |
| 測試 |
useGroupedTasks spec、取消級聯 integration(~280 行) |
依賴:PR-3。風險:中低。
PR-5:手動會話 checkpoint(~760 行)
| 檔案 |
改動 |
src/core/checkpoints/(新子模組 conversation-checkpoint.ts) |
task 目錄下 checkpoints/<timestamp>.json 存儲 + 載入 |
create_checkpoint tool |
手動觸發,保存 fullHistory + summary |
| UI:CheckpointMenu 擴充或獨立節點按鈕 |
顯示 checkpoint 列表、restore 會話 |
| 測試 |
storage round-trip、tool 行為、UI(~380 行) |
依賴:PR-1。風險:中。可同 PR-4 並行。
唔喺本計劃內
- P3 平行模式:先開獨立 spike issue 做 per-task mode 隔離(
handleModeSwitch / initializeTaskMode 層重構);spike 完成前所有設計按 sequential-only,schema 唔預留 awaitingChildIds
- cross-model analysis、AI escalation 啟發式、checkpoint 自動觸發 — 見「範圍決定」
五、測試計劃總覽(跟 AGENTS.md test placement)
| 層 |
位置 |
場景 |
| unit |
src/__tests__/(同 new-task-delegation.spec.ts 同層) |
depth 計算 / 回填;flatten 決策純函數(depth+1 > max、autoFlatten on/off、inline 階段中再委派被拒);循環保護 |
| integration |
擴充 provider-delegation.spec.ts / nested-delegation-resume.spec.ts |
flatten 路徑全鏈;inline attempt_completion;舊任務 depth=undefined 載入回填 |
| webview-ui |
webview-ui/src/components/history/__tests__/useGroupedTasks.spec.ts + SettingsView spec |
depth badge、inline 標記渲染;設定 binding/save/round-trip |
| e2e |
apps/vscode-e2e/src/suite/subtasks.test.ts(已有委派 fixture) |
smoke:depth=2 時再委派 → 同 tab 內完成,tab 數量唔增加 |
六、對上游 spec 嘅明確修正清單
- 「數據模型已支持樹」→ 只係譜系層面;執行層要動 single-open invariant(P3)
- inline「virtual child + resumeAfterDelegation 兼容證明」→ 改為同實例階段標記,連
resumeAfterDelegation 都唔使碰
- AI-driven escalation 啟發式 → 剷走,改用戶手動(PR-4)
- cross-model analysis → 剷出核心範圍
- checkpoint change/stuck detection → 延後,先做手動觸發
zooCode.* VSCode settings → 改用本倉庫 globalSettings + contextProxy round-trip(AGENTS.md checklist)
- 「取代 tab 嘅樹視圖」→ 並存,擴充現有
useGroupedTasks
七、分支策略
Task Tree 統一子任務管理:詳細設計同 PR 拆分計劃
上游參考: Zoo-Code-Org/Zoo-Code#856(Task Tree Abstraction design spec;Zoo-Code-Org#849 / Zoo-Code-Org#850 / Zoo-Code-Org#854 已於 2026-07-07 標記為 duplicate 關閉)
本 issue 係 Zoo-Code-Org#856 喺呢個 fork 嘅實施計劃:將 spec 對照 main(
e064cf059)實際代碼驗證過,修正同現實唔符嘅部分,並拆成每個 ≤1000 行改動(連測試) 嘅 PR。一、已驗證現狀(main @ e064cf0)
Task只有單向鏈:readonly parentTaskId?+childTaskId?,冇 depth 欄位(全倉 grep 零匹配)src/core/task/Task.ts:171-172, 505-506HistoryItem已有rootTaskId / parentTaskId / status("active"|"completed"|"delegated"|"interrupted") / delegatedToId / childIds[] / awaitingChildId / completedByChildId / completionResultSummarypackages/types/src/history.ts:10-29removeClineFromStack()→ provider 級handleModeSwitch()(全局 mode)→createTask(startTask:false)→atomicReadAndUpdate寫 parent 後設資料(含 rollback)→scheduleTask(child)src/core/webview/ClineProvider.ts:3644-3810new_task委派前彈askApproval("tool", ...)批准框src/core/tools/NewTaskTool.ts:~107AttemptCompletionTool.delegateToParent()→askFinishSubTaskApproval()(實現在presentAssistantMessage.ts:553)→reopenParentFromDelegation(),返回"delegated"|"denied"|"continue"src/core/tools/AttemptCompletionTool.ts:219-240taskMetadata()),HistoryItem 經帶鎖atomicReadAndUpdate寫 globalStatesrc/core/task-persistence/taskMetadata.tsparentTaskId分組嘅歷史視圖 hookwebview-ui/src/components/history/useGroupedTasks.ts關鍵結論:spec 講「數據模型已支持樹」只係譜系層面對;真正瓶頸係 single-open invariant + provider 級全局 mode,呢兩樣先係平行執行嘅架構障礙。
二、範圍決定
maxNestingDepth/autoFlattenOnLimit設定 + auto-flatten inline 模式handleModeSwitch係 provider 級全局狀態,兩個 Task 無法同時各持一個 mode)。spike 完成前所有設計按 sequential-only明確剷走:
confidenceScore假設 LLM 輸出校準過嘅置信度,實際上唔成立;仲要設計多 provider 路由同成本估算,spec 完全冇覆蓋 → 另開實驗性 issuecountDistinctGoals、detectedParallelNeeds等)— 冇任何機制令子任務向 runtime 發出「我變複雜咗」訊號;改做用戶手動觸發(P2 UI 按鈕,單向、唔可逆)三、詳細設計
P1.1 數據模型
packages/types/src/history.ts:siblingIds(可由 parent 嘅childIds反查,存兩份只會造成同步負擔)src/core/task/Task.ts(line ~171-172, ~505):CreateTaskOptions(packages/types/src/task.ts:86)加depth?: number透傳atomicReadAndUpdate時順帶寫入;唔需要一次性遷移腳本P1.2 設定 round-trip(跟 AGENTS.md「Persisted Setting Checklist」)
完整 round-trip:
global-settings.ts→ExtensionState(vscode-extension-host.ts)→SettingsView綁定 localcachedState(唔好綁 liveuseExtensionState(),會 race)→handleSubmit()payload →webviewMessageHandler→contextProxy.setValue()→ClineProvider.getState()+getStateToPostToWebview()兩邊都要加。測試覆蓋 true / false / 未設定三種情況。P1.3 Auto-flatten inline 模式(核心,重新設計)
決策:inline = 同一個 Task 實例 + 階段標記,唔建子 Task。
Spec 講「virtual child」但冇定義 runtime 點樣區分子任務 turn 同 parent turn。本倉庫有三個硬約束逼出唯一乾淨方案:
removeClineFromStack)— parent 離開 stack 先開到子 tabhandleModeSwitch係 provider 級全局狀態,兩個 Task 無法同時各持一個 mode所以 inline 子任務 = parent Task 自己嘅一個執行階段。
new_task被 flatten 時唔彈批准框、唔建 Task、唔開 tab — tool_result 本身就係 inline prompt:好處:
resumeAfterDelegation():parent 從未離開 task loop,唔需要任何狀態重置 — 比 spec「inline 同 tab 共用 resume 邏輯」簡單一個量級task.inlineSubtask標記,會話繼續當普通 parent。冇孤兒 child、冇 cancellation cascade(spec 列為 PR-stage 未決問題,呢度直接消失)attempt_completion喺 inline 階段(AttemptCompletionTool.handle(),delegateToParent之前加分支):即 inline 完成唔彈
askFinishSubTaskApproval(用戶已經喺同一會話睇到全部過程,再彈框係雙重摩擦 — spec 呢點判斷正確)。presentAssistantMessage.ts:553嗰個 approval callback 完全唔使改。嵌套規則:inline 階段期間模型再調
new_task→ 直接拒絕(pushToolResult error),P1 明確禁止。Escalation 留 P2 做用戶手動觸發。autoFlattenOnLimit: false:超深 → pushToolResult error「Nesting limit reached — continue working directly」,模型自己繼續做。四、PR 拆分計劃(每個 ≤1000 行改動,連測試)
全部喺
main-task-treebase branch(= origin/main @ e064cf0)順序開發;每個 PR 由main-task-tree開出,merge 後 rebase 繼續。PR-1:depth 追蹤 + schema(~450 行)
packages/types/src/history.tsdepth: z.number().int().min(0).optional()packages/types/src/task.tsCreateTaskOptions.depth?src/core/task/Task.tsinlineSubtask欄位(先加,PR-3 用);depth 回填邏輯src/core/webview/ClineProvider.ts依賴:無。風險:低(全部 additive,舊資料兼容)。
PR-2:設定 round-trip(~480 行)
packages/types/src/global-settings.tsmaxNestingDepth(default 2, 0–5)、+autoFlattenOnLimit(default true)packages/types/src/vscode-extension-host.tswebview-ui/.../SettingsViewsrc/core/webview/webviewMessageHandler.tssrc/core/webview/ClineProvider.ts依賴:PR-1。風險:低。
PR-3:auto-flatten inline 模式(~780 行)
src/core/tools/NewTaskTool.tstask.inlineSubtask+ pushToolResult directive;inline 階段中再 new_task → 拒絕;autoFlattenOnLimit=false → error resultsrc/core/tools/AttemptCompletionTool.tssrc/core/task/Task.tsprovider-delegation.spec.ts/nested-delegation-resume.spec.ts(超深 → 無 child Task、無 approval ask、tool_result 含 directive;inline attempt_completion → 標記清除、loop 繼續、冇 askFinishSubTaskApproval 調用)(~450 行)依賴:PR-1 + PR-2。風險:中(核心行為變更,但 tab / sequential 現有流程完全唔變)。
PR-4:樹形持久化加固 + UI(~520 行)
src/core/webview/ClineProvider.tsinlineSubtaskActive;取消級聯(cancel parent → children interrupted,接入 abandonSubtask 路徑)webview-ui/src/components/history/useGroupedTasks.ts依賴:PR-3。風險:中低。
PR-5:手動會話 checkpoint(~760 行)
src/core/checkpoints/(新子模組 conversation-checkpoint.ts)checkpoints/<timestamp>.json存儲 + 載入create_checkpointtool依賴:PR-1。風險:中。可同 PR-4 並行。
唔喺本計劃內
handleModeSwitch/initializeTaskMode層重構);spike 完成前所有設計按 sequential-only,schema 唔預留awaitingChildIds五、測試計劃總覽(跟 AGENTS.md test placement)
src/__tests__/(同new-task-delegation.spec.ts同層)provider-delegation.spec.ts/nested-delegation-resume.spec.tswebview-ui/src/components/history/__tests__/useGroupedTasks.spec.ts+ SettingsView specapps/vscode-e2e/src/suite/subtasks.test.ts(已有委派 fixture)六、對上游 spec 嘅明確修正清單
resumeAfterDelegation都唔使碰zooCode.*VSCode settings → 改用本倉庫 globalSettings + contextProxy round-trip(AGENTS.md checklist)useGroupedTasks七、分支策略
main-task-tree(= origin/main @ e064cf0,已 push)main-task-tree開出獨立 branch(如pr-1-depth-tracking),base =main-task-treemain-task-tree→ upstream main(一個總 PR)