6.3 KiB
status, created, owner
| status | created | owner |
|---|---|---|
| draft | 2026-08-05 | Kandev |
Workflow Step Agent Start Ownership
Why
A workflow step whose on_enter combines reset_agent_context and
auto_start_agent can have two independent paths start the same agent
execution. The second start is rejected, the task is marked FAILED, the agent
is force-killed, and the step prompt that was already written to chat history is
never delivered. Recovery boots a fresh agent, so the user is left looking at a
healthy "Resumed agent" banner above a session that will never do anything.
Observed in a local run, where the whole sequence — step move, double start, force-kill, idle reboot — completed in about 13 seconds.
Broken behavior
resetAgentContext decides whether to restart purely on the presence of an
agent execution ID. A CREATED session whose execution is workspace-only —
prepared but never started — therefore has its subprocess started by the reset
path, which reaches agentctl as a restart.
markIdleAfterReset only flips sessions in RUNNING/STARTING, so the session
stays CREATED. Its own comment documents the opposite invariant: that
resetAgentContext early-returns for CREATED "without restarting".
autoStartStepPrompt then reads CREATED, concludes the agent was never
started, and calls StartCreatedSession → StartAgentProcess against the
now-running execution. agentctl's Manager.Configure rejects it with
cannot configure while agent is running; the executor marks the session
FAILED and force-stops the agent.
The prompt recorded by recordAutoStartMessage is not requeued on that failure
— only a taken handoff message is — so it is lost. Recovery's session/load
then fails with -32002 Resource not found because the ACP session died with
the killed process, and the fallback session/new boots an agent that is idle
and unprompted.
What
- On a step entry, exactly one path starts the agent subprocess for a session.
- Context reset performs no start or restart for a session that has never been
prompted (
CREATED). Such a session has no agent conversation to clear, and the process the auto-start path launches already begins on a new ACP session. - Context reset for a session that has been prompted continues to restart the
subprocess and clear
acp_session_idexactly as it does today. - When a
CREATEDlaunch fails synchronously with a busy or already-running condition, the recorded prompt is queued for the session so the existing boot-ready drain delivers it once the session is promptable again. Permanent rejections (Office scheduler guard, missing agent profile) are not queued — nothing would ever drain them. - A failed start still marks the session
FAILEDand surfaces the error.
Failure modes
- Reset is skipped for a
CREATEDsession and auto-start then fails for an unrelated reason: the session isFAILEDwith the error surfaced, and the prompt is queued rather than orphaned. - The step has
reset_agent_contextbut noauto_start_agentand the session isCREATED: no restart occurs and the session remains promptable. No agent context is lost, because none exists. - The session is passthrough and
CREATED: the same skip applies; the CLI has no conversation to reset before its first prompt. - Execution lookup fails or returns an empty ID: unchanged — reset is skipped, as today.
- Queueing the prompt after a failed start itself fails: the failure is logged
and the session's
FAILEDstate is unchanged.
Scenarios
- GIVEN a session in
CREATEDwith a prepared, not-yet-started agent execution, WHEN the task enters a step whoseon_enterhas bothreset_agent_contextandauto_start_agent, THEN the reset performs no subprocess restart, auto-start performs the single start, and the step prompt reaches the agent. - GIVEN a session in
CREATEDwith a prepared execution, WHEN the task enters a step withreset_agent_contextand noauto_start_agent, THEN no restart occurs and the session remains promptable. - GIVEN a session in
RUNNINGwith a started agent execution, WHEN the task enters a step withreset_agent_context, THEN the subprocess is restarted andacp_session_idis cleared, unchanged from today. - GIVEN a passthrough session in
CREATED, WHEN the task enters a step withreset_agent_context, THEN no restart occurs. - GIVEN a
CREATEDsession with no in-memory execution (the shape after a backend restart), WHEN its auto-start launch fails synchronously because a concurrent path already owns the start, THEN the recorded prompt is queued for the session rather than dropped, and the session's later promptable transition delivers it. - GIVEN a
CREATEDlaunch rejected permanently (Office scheduler guard), WHEN the failure is handled, THEN the error is returned and no message is queued.
Known gap
Prompt preservation across an asynchronous start failure is not covered
here. startAgentOnExistingWorkspace ends with startAgentProcessAsync and
returns nil, so a session with a prepared execution — the incident's own shape
— never surfaces a synchronous error for the clause above to catch. That failure
is handled entirely by handleAgentProcessStartFailure →
Service.handleAgentStartFailed, which has no access to the prompt the launch
was carrying and therefore cannot re-queue it.
Task 01 removes the only known trigger for that path on a first-turn launch, so the incident cannot recur through it. The residual gap — any other async start failure on a first-turn launch drops the step prompt — is a distinct pre-existing defect. Closing it needs a pending-prompt handle the async failure path can read, which is a design change beyond this repair.
Out of scope
- Relaxing agentctl's
Manager.Configurerunning-process guard. Reconfiguring command or environment under a live subprocess is unsafe; the guard is correct and stays as-is. - Native ACP
session/resetsupport in the adapter. The reset-unsupported-fallback-to-restart path is unchanged. - The
-32002 Resource not foundresume failure, which is a consequence of the force-kill rather than an independent defect. - Changing task
FAILEDsemantics, the reconciliation path, or the lazy-recovery boot that follows a backend restart.