Files
kandev/docs/decisions/2026-08-08-task-autopilot-contract.md
T
2026-08-23 17:34:52 +00:00

112 lines
6.1 KiB
Markdown

# ADR-2026-08-08-task-autopilot-contract: Make Task Autopilot a Creation-Time Runtime Contract
**Status:** accepted
**Date:** 2026-08-08
**Area:** backend, agentctl, frontend, protocol, workflow
**Related issue:** [#2425](https://github.com/kdlbs/kandev/issues/2425)
**Related spec:** [Task Autopilot Mode](../specs/tasks/requirements/autopilot-mode.md)
## Context
Kandev task agents currently receive a task-mode system prompt and an MCP tool
inventory when their runtime session is launched. An agent can ask the operator a
clarification through `ask_user_question_kandev`, but that call waits for the
operator's answer. This is unsuitable for delegated autonomous work: a child can
block while its parent is busy, the question can time out, and the operator can be
shown a question that the parent agent could have answered.
Autopilot changes both behavioral guidance and the capabilities an agent should
discover. Allowing the setting to change during a running task would require an
atomic prompt and tool-inventory replacement across active, waiting, resumed, and
restarted sessions. A partially applied change would create a split-brain session
whose prompt and available tools disagree.
## Decision
1. Autopilot is an immutable, persisted property of a task in its first release.
It is selected at creation, defaults to `false`, and is exposed in task DTOs.
Editing an existing task cannot change it.
2. Creation APIs, including `create_task_kandev`, accept an explicit `autopilot`
boolean. The value does not implicitly inherit from the parent when omitted.
An autopilot agent that wants autonomous delegation must create its child with
`autopilot: true` deliberately.
3. The backend derives the session's behavioral prompt and MCP capability set from
the persisted task property. Agentctl enforces the supplied capability set; it
does not infer autopilot from prompts or parentage.
4. An autopilot child with a direct parent does not discover
`ask_user_question_kandev`. It instead discovers `ask_parent_question_kandev`,
which records a structured question and sends it to the task's current direct
parent without waiting for an answer. The parent target is resolved by the
backend and is never caller supplied. An autopilot root receives no question
tool.
5. `ask_parent_question_kandev` is a terminal action for the current logical turn.
The tool response tells the agent to end its turn, and the autopilot system
prompt requires the tool to be its last action. At turn completion the child
remains waiting on the recorded parent question rather than completing its
workflow transition or draining unrelated queued prompts.
6. A parent answers through `message_task_kandev` with a correlated
`reply_to_question_id`. The backend accepts the answer only from the recorded
direct parent and routes it to the recorded child session. Duplicate replies
are idempotent.
7. Pending parent questions use durable message metadata as their source of truth.
They survive restart, project to the existing clarification pending-action
category, and therefore reuse the sidebar question indicator without exposing
an operator clarification card. They leave the pending state when answered,
superseded by explicit new child input, or made stale by terminal task/session
state.
8. A top-level autopilot task has no human-question escape hatch. It proceeds with
best judgment; calling the parent-question tool fails closed with a clear
no-parent result. Nested autopilot hierarchies may relay an essential question
one direct-parent hop at a time.
9. Autopilot is initially supported only for persistent task-mode sessions whose
configured agent runtime supports Kandev MCP injection. Creation with an
incompatible profile is rejected instead of silently providing weaker
semantics. Quick chat, Office-owned sessions, config sessions, and utility
sessions are outside this contract.
## Consequences
- Creation-time immutability makes every launch and resume deterministic, at the
cost of requiring a new task to change modes.
- The backend gains a typed parent-question lifecycle and a correlated extension to
peer messaging, but no request blocks on another agent or on the operator.
- Existing non-autopilot tasks and root clarification behavior remain compatible.
- The UI can show autopilot as task identity and pending questions as transient task
state using existing shared desktop/mobile task rows.
- Supporting live toggles later requires a separate decision covering versioned
runtime configuration and in-flight question migration.
## Alternatives Considered
### Allow the setting to change while a task is running
Deferred because prompt guidance is injected at turn/session boundaries while MCP
discovery has its own runtime update path. A correct mutable design needs a
versioned configuration snapshot, atomic capability replacement, and explicit
semantics for an already-pending operator or parent question. The first release
avoids that split-brain state.
### Keep `ask_user_question_kandev` and rely only on prompt wording
Rejected because prompts are advisory. The blocking tool would remain callable and
could still time out or route a delegated question directly to the operator.
### Implement parent questions by calling `message_task_kandev` directly
Rejected because a free-form peer message has no correlation ID or durable waiting
state. A dedicated request tool is needed to enforce direct-parent routing,
idempotent answers, restart recovery, and the sidebar pending indicator.
### Route every question directly to the root task
Rejected for the initial contract because the immediate parent owns the delegated
work and may already know the answer. Direct-parent hops also preserve the existing
authorization boundary for parent-to-child control. An autopilot parent can relay a
question upward if it genuinely cannot decide.
### Add a separate parent-question table
Rejected because task messages already provide durable, ordered, restart-safe
records and pending-action projection. A typed hidden message plus metadata adds the
needed state without establishing a second conversation store.