--- status: draft created: 2026-06-23 owner: cfl --- # Subtask Completion Trigger ## Why Parent agents can delegate work by creating subtasks with `create_task_kandev` and `parent_id`, but today they must poll `list_related_tasks_kandev` to learn when all delegated workstreams are done. Polling burns agent turns and pushes orchestration logic into the parent prompt instead of the workflow system. ## What - A workflow step can define an `on_children_completed` event. - The event fires on a parent task's current workflow step when every direct, active child task has reached terminal completion. - Terminal task states are `COMPLETED`, `FAILED`, and `CANCELLED`. - A child also counts as terminal when it is in a final workflow step named `Done`, `Complete`, `Completed`, or `Approved`; entering that step persists `tasks.state = COMPLETED`. - The event is driven by task state changes and workflow-step moves into a terminal step, including subtasks created through `create_task_kandev` with `parent_id`. - The event runs the step actions configured on `on_children_completed`, such as queueing a parent-agent run or moving the parent to a verification step. - Workflow authors can configure the event from the workflow step editor with the "When Child Tasks Complete" transition selector. The editor explains that the trigger belongs on the parent step, waits for direct active children only, treats terminal task states and terminal workflow-step membership as terminal, and ignores archived or ephemeral child tasks. - The event fires at most once for the same completed child set. If a child is reopened and later returns to a terminal state, the parent can receive a new event for the new completion cycle. - The trigger considers direct children only. Grandchildren complete their own parent before any higher-level parent can react. - Parent tasks with no active or primary session do not auto-create a session for this event; the event is skipped and logged. - Existing workflows that do not configure `on_children_completed` behave unchanged. ## Data model No new persistent table is required. ### `tasks` The existing `tasks.parent_id` relationship defines the direct child set. Children with `archived_at` set or `is_ephemeral = 1` are ignored by this feature, matching existing active-child listing semantics. The existing `tasks.state` field defines terminal child completion. The terminal state set for this feature is: ```text COMPLETED FAILED CANCELLED ``` `tasks.workflow_step_id` is also considered. A child task is complete when its current step is the final step in that workflow and the step name is `Done`, `Complete`, `Completed`, or `Approved`. This keeps visual board completion and API state consistent without adding a new persistent terminal-step flag. ### Workflow operation idempotency The existing workflow engine operation-id store records each synthesized `on_children_completed` trigger. The operation id is deterministic for the parent and completed direct-child set, including enough child completion version data to let a reopen-and-complete cycle fire again. ## API surface No new HTTP route, WebSocket event, MCP tool, or CLI command is introduced. Workflow step event JSON accepts and returns `on_children_completed` in the existing `events` object: ```json { "events": { "on_children_completed": [ { "type": "queue_run", "config": { "target": "primary", "task_id": "this", "reason": "children_completed" } } ] } } ``` The workflow settings UI exposes the transition-oriented subset of this event: ```json { "events": { "on_children_completed": [ { "type": "move_to_step", "config": { "step_id": "verification-step-id" } } ] } } ``` The event uses the existing workflow-engine trigger payload: ```json { "child_summaries": [ { "task_id": "child-task-id", "status": "COMPLETED", "summary": "optional child completion summary", "pr_links": ["https://github.com/org/repo/pull/123"] } ] } ``` `summary` and `pr_links` are best-effort enrichment. A missing summary or PR link does not block the trigger. ## State machine - A child enters terminal completion when its task state changes from a non-terminal state to `COMPLETED`, `FAILED`, or `CANCELLED`, or when it moves into a terminal workflow step. - On that transition or move, the system checks the child's parent. - If the child has no parent, no parent event is considered. - If any active direct child of the parent is still non-terminal, no parent event fires. - If every active direct child is terminal, the parent task receives `on_children_completed` at its current workflow step. - If configured actions transition the parent step, the normal workflow transition lifecycle applies: `on_exit`, persisted step change, data patch persistence, then `on_enter`. - If a child later moves from terminal to non-terminal, the parent is considered waiting again. A later transition of that child back to terminal can produce a new event. ## Permissions This feature adds no new permission boundary. - Agents and users can only create child tasks through existing task creation permissions and MCP tool exposure. - Agents cannot fire `on_children_completed` directly; they can only cause it by changing task state or moving a child task through existing task completion paths. - Workflow authors control the parent reaction by configuring `on_children_completed` actions on the parent step. ## Failure modes - If the child-state event cannot be decoded, the event is dropped and a warning is logged. - If sibling lookup fails, no parent trigger fires for that event and a warning is logged. A later child state transition can retry the check. - If terminal step lookup fails, the child is evaluated by task state only. - If the parent has no active or primary session, no new parent session is created. The skip is logged at debug/info level. - If the workflow engine rejects or fails a configured action, the error is logged and the child task's terminal state remains unchanged. - If multiple children finish concurrently, engine operation idempotency prevents duplicate parent actions for the same completed child set. ## Persistence guarantees - Child task state, parent-child relationships, workflow step configuration, and workflow operation ids survive backend restarts. - A child completion event that was already applied before restart is not re-applied for the same completed child set. - In-memory event delivery itself is not replayed after restart. A future child state transition or reopen-and-complete cycle can synthesize the trigger again. ## Scenarios - **GIVEN** a parent task has two direct children and one child is still `IN_PROGRESS`, **WHEN** the first child changes to `COMPLETED`, **THEN** the parent's `on_children_completed` event does not fire. - **GIVEN** a parent task has two direct children and the first child is `COMPLETED`, **WHEN** the second child changes from `IN_PROGRESS` to `COMPLETED`, **THEN** the parent receives one `on_children_completed` trigger. - **GIVEN** a child task still has `state = REVIEW`, **WHEN** it moves into a final workflow step named `Done`, **THEN** the child persists `state = COMPLETED` and the parent completion check treats that child as terminal. - **GIVEN** a parent step configures `on_children_completed` with `move_to_next`, **WHEN** all direct children are terminal, **THEN** the parent moves to the next workflow step and that step's `on_enter` actions run normally. - **GIVEN** a parent step configures `on_children_completed` with `queue_run` for the primary agent, **WHEN** all direct children are terminal, **THEN** a parent run is queued with the children-completed reason and child summary context. - **GIVEN** one child finishes as `FAILED` and all other direct children are terminal, **WHEN** the failed child transitions from non-terminal to `FAILED`, **THEN** the parent receives `on_children_completed`. - **GIVEN** a child is already `COMPLETED`, **WHEN** a duplicate update attempts to set it to `COMPLETED` again, **THEN** no duplicate parent trigger fires. - **GIVEN** all direct children are terminal and the parent trigger has fired, **WHEN** one child is reopened to `IN_PROGRESS` and later changes back to `COMPLETED`, **THEN** the parent can receive a new `on_children_completed` trigger for the new completion cycle. - **GIVEN** a child has its own child, **WHEN** the grandchild completes, **THEN** only the direct parent of that grandchild is evaluated for `on_children_completed`. - **GIVEN** a parent has no active or primary session, **WHEN** all direct children become terminal, **THEN** no session is created automatically and the skip is logged. ## Out of scope - Adding a second event name such as `on_all_children_complete` or `on_subtasks_done`. - Recursive descendant aggregation. - Creating parent sessions automatically when no parent session exists. - Adding a new MCP wait primitive or long-poll API. - Requiring every child agent to know whether it is the last sibling.