feat(task): auto-flatten subtasks inline when the nesting limit is reached - #12
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(task): auto-flatten subtasks inline when the nesting limit is reached
Part of the Task Tree unified subtask management plan (issue #9, P1.3). When a
new_taskcall would exceedmaxNestingDepth, it no longer opens a child tab — it runs inline in the current conversation as an execution phase of the same Task instance.Design: inline = same Task instance + in-memory phase marker (no virtual child)
The repo has three hard constraints that rule out a "virtual child Task":
removeClineFromStack) — parent leaves the stack before a child tab openshandleModeSwitchis provider-global state; two Tasks can't each hold a modeSo an inline subtask is the parent's own execution phase: no approval popup, no child Task, no new tab — the
tool_resultdoubles as the inline prompt.attempt_completionduring an inline phase clears the marker and pushes a[inline subtask completed]tool_result — noaskFinishSubTaskApprovalpopup (the user is already watching this same conversation; a second approval would be double friction). The loop naturally continues to the next API call. Cancel/abort clears the marker so the task resumes as an ordinary parent.Changes
src/core/tools/inlineSubtask.ts(new)decideInlineFlatten()+ directive builder — unit-testable without Task/vscodesrc/core/task/Task.tsInlineSubtaskinterface + non-persistedinlineSubtask?phase marker; cleared inabortTask()src/core/tools/NewTaskTool.tsaskApproval; flatten/reject branches per the decision; delegate path unchangedsrc/core/tools/AttemptCompletionTool.tsBehavior guarantees
autoFlattenOnLimit: true→ inline phase; zero synthetic messages (the parent's own conversation IS the bounded context); noresumeAfterDelegation()needed since the parent never left its task loopautoFlattenOnLimit: false→ rejected with an error result so work continues directlynew_taskduring an inline phase → rejected (P1 forbids recursion into a second inline subtask)Tests (~450 lines per plan)
core/tools/__tests__/inlineSubtask.spec.ts(unit, 11 cases): delegate within/at limit; flatten over-limit with directive content + todos; reject-limit when auto-flatten disabled; maxNestingDepth=0 → always inline; nested rejection precedence (active phase wins even when over limit); todo rendering in/outcore/tools/__tests__/newTaskInlineFlatten.spec.ts(integration, 6 cases): delegate path unchanged (approval + child opened); flatten path (no approval, no child, marker set, directive as tool_result); reject-limit error; nested rejection preserves existing marker; inline attempt_completion clears marker with noaskFinishSubTaskApprovalcall and pushes the continue result; non-inline completion does not take the inline branchnew-task-delegation,provider-delegation,nested-delegation-resume,Task.spec.tsall pass unchangedVerification
pnpm run check-types— 14/14 packages pass--max-warnings=0) clean; zero eslint suppression count increases (test doubles use a single documentedas unknown as Taskcast, matching the existingnew-task-delegation.spec.tspattern)Out of scope (later PRs per issue #9)
Webview inline-phase indicator + cancel cascade (PR-4); manual conversation checkpoints (PR-5). Escalation stays user-manual in P2; no AI-driven heuristics.