Files
kandev/docs/specs/tasks/subtask-completion-trigger.md
T
2026-07-09 21:37:56 +01:00

239 lines
9.1 KiB
Markdown

---
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.