What Actually Runs When You Walk Away: KiroCrew’s Autonomy Modes, Compared


Once a KiroCrew session is running, there are four ways work can proceed without you driving every turn: Autopilot, the Task Runner, Monitor, and /goal. The docs present them as separate features, and I could not tell which one I was supposed to reach for — so I spent some time working through them question by question, reading the installed source as I went rather than trusting the feature list.

They are not four peers. Two of them are the same engine wearing different hats. One of them is not actually about stepping back at all. And the gotcha that broke my first test was an approval gate that had nothing to do with any of the four.

First, a distinction the feature list blurs

Before comparing them it is worth separating two questions that KiroCrew’s documentation tends to answer in the same breath: what starts the work, and how the work proceeds once started.

CategoryAnswersMembers
TriggersWhat starts the work?Scheduled (cron), reactive (webhooks, messaging events), interactive (you typing)
Execution shapesHow does it proceed?Autopilot, Task Runner, Monitor, /goal, subagents

Scheduled jobs are a trigger, not a shape. A cron job can itself run an Autopilot plan, fan out to subagents, or just do one thing and stop. It is orthogonal to the four modes below, which is why I am not comparing it against them.

It is also worth saying that cron is the purest automation of anything here, because a script or command cron runs with no model call at all — deterministic code on a schedule, zero tokens. Everything in the right-hand column is “an agent does the work.” A script cron is “code does the work, on time.” If a job is a fixed sequence with a mechanical decision at the end, that is the tool, and reaching for an agent instead just adds non-determinism and cost. This post is about the four cases where you genuinely do want an agent’s judgement in the loop.

The four, arranged by how much you supervise

ModeShapeWhere you are
AutopilotPlan → you approve → staged executionPresent, gating each stage
Task RunnerExecutes a spec file of steps you wrote in advanceEither — depends how you launch it
Monitor (babysit)Poll something until a condition is metAway
/goalIterate toward an outcome until evidence says doneAway

Monitor and /goal are the two that share an engine. That engine is AutoNudge, and it is worth understanding first because everything built on it inherits its behaviour.

AutoNudge: the engine, not a mode

AutoNudge is a Gateway service, not something you invoke. It does one thing: when a turn completes and the session then sits idle for idle_secs, it injects a configured message as the next turn into the same session — same context, same tools, same conversation. The nudge appears tagged [auto-nudge cycle N] so you can tell it from something you typed.

  • Idle-based, not periodic — for dashboard sessions. The timer measures the gap after your turn ends, so a 300-second interval on a five-minute check wakes you roughly every ten minutes, not every five. Messaging-channel sessions are the exception: they have no dashboard turn-lifecycle hooks, so those loops run on a fixed interval that re-arms right after each fire.
  • Survives restarts. State persists to autonudge.json; active loops are reloaded and their timers re-armed when the Gateway comes back.
  • Bounded three ways — a max_cycles cap, a per-loop STOP sentinel file, and exponential backoff on failed fires (15s base escalating to a 5-minute ceiling, so a wedged slot degrades to a slow poll instead of hammering).
  • Per-slot and opt-in. The service being enabled does not mean anything is looping. One loop per session, created only when something explicitly arms it.

IMPORTANT: Monitor and /goal are not separate engines. Both call the same AutoNudgeService.add() with an interval, a cycle cap, a sentinel path, and a message. The only difference is what that injected message instructs. Monitor says “check this and report real signals.” /goal says “do one atomic step toward the objective.”

That is a genuinely good design — one bounded, restart-safe loop primitive, with the behaviour expressed as instructions rather than as more code. But it means the two features share every limitation, and learning the engine tells you more than reading either feature page.

The /goal name collision

Here is where reading the source paid for itself. The documentation described /goal as a loop with a dedicated completion tool, a default of five iterations, and exponential-backoff dispatch retry. The installed dashboard code says something different: /goal is a thin wrapper over AutoNudge — the handler docstring literally calls it a “v0 self-verdict loop” — with a default cap of 50.

What I could verify is that the dashboard handler builds a plain-English nudge and arms an AutoNudge loop; it never forwards the string to the agent process. What I could not verify is the other half: kiro-cli ships as a compiled binary, so the /goal the documentation describes is something I am inferring from the docs rather than reading. The most likely explanation is two independent implementations sharing a name — but treat that as inference, not a finding.

The nudge /goal actually injects tells the agent, in order: stop if the sentinel file exists; stop if the goal is met by concrete evidence (a passing test, a built file, command output — not a guess) and cite it; otherwise do one atomic step of at most five tool calls, and make the deliverable durable before claiming progress. Guardrails in the same text: never push to git, never read credential files, and on a hard blocker say so once and stop.

IMPORTANT: that completion contract lives entirely in the injected prompt text, not in a code-enforced gate. It is a well-written instruction, and for a v0 that is a reasonable place for it. It is still a soft control: the same agent doing the work decides whether the work is done.

Autopilot and /goal decompose at opposite ends

Both drive multi-step work, so I assumed they overlapped. The deepest difference is when the work gets broken down.

Autopilot decomposes up front. It is a per-session mode (internally the orchestrator slot mode) that writes all the stages before any work starts, presents them, and ends the turn. You get three controls: Go (next stage only, then pause again), Go All (all remaining stages without pausing, halting on failure or escalation), or Cancel. Stages are sequential, tasks within a stage can fan out in parallel, and the last stage must be verification.

/goal decomposes lazily. No plan, no approval — one atomic step per idle cycle, route emerging as it goes.

Autopilot/goal
PlanAll stages, reviewable up frontNone — emergent
ApprovalYours, per stage or per runNone
TriggerYour clickIdle timer
RetryUp to 3 rounds within a stage, then checkpoint and ask youRetries differently each cycle up to the cap
Built forYou presentYou absent

I initially had Autopilot down as fire-once-and-stop, which is wrong. A stage can take multiple rounds — spawn a batch of sub-agents, wait, then spawn more if the stage goal is not met — with a documented ceiling of three rounds per stage, after which it checkpoints what it has and asks you. So both modes iterate; the difference is that Autopilot’s iteration is bounded per stage and escalates to a human, while /goal’s runs to its cycle budget and escalates to nobody.

That is the useful selection rule. For “make all the tests pass,” where the path is trial and error and the outcome is mechanically checkable, /goal fits. For “migrate this module,” where the approach is the risky part and a wrong plan wastes a lot of work, Autopilot fits.

Where the Task Runner sits

The Task Runner is the third shape: it executes a markdown spec file of steps you wrote in advance, with live per-step progress and multi-turn refinement afterwards. Not interactive planning like Autopilot, not emergent like /goal — just “here are the steps, run them.”

IMPORTANT: its tool approval depends on how you launch it. From the dashboard, chat, or Slack, non-allowlisted tools prompt interactively. From the standalone CLI (kirocrew run TASK.md) there is no interactive channel, so it is deny-by-default — a tool runs only if it matches hooks.auto_approve_tools, otherwise it is rejected and logged as headless_no_authorization.

That is the first hint of the thing that actually tripped me up.

How you actually start each one

This is the part I found least obvious, and it explains a lot of the confusion: the four are not invoked the same way at all. Only one of them is a command you type.

ModeHow you start it
/goalA slash command you type: /goal <objective>, optionally --max N. /goal clear stops it, /goal status checks it.
AutopilotA per-session toggle in the dashboard. You can also just ask — “autopilot this”, “create a plan” — which overrides the agent’s own judgement about whether the task warrants planning and forces a plan.
Task RunnerPoint it at a spec file: run <path> in chat or Slack, the Tasks page, the task_run tool, or kirocrew run TASK.md from a terminal.
MonitorNo command exists. You ask in plain language — “keep checking…”, “babysit this…”, “let me know when…” — and the agent calls monitor_start on your behalf.

That last row is worth dwelling on. I went looking for a /monitor command and there isn’t one — the dashboard’s slash-command list has /goal and no monitor equivalent. So two features built on the same engine have completely different front doors: one is a typed command, the other is something you request conversationally and the agent wires up.

That asymmetry is probably why they read as unrelated features. /goal feels like a product because it has a command. Monitor feels like a capability because it does not.

And how you stop them

Both loop modes stop the same four ways, because both are AutoNudge underneath: ask the agent (it calls autonudge_stop), use the dashboard’s loop popover, DELETE /api/autonudge/{loop_id} over the REST API, or create the loop’s STOP sentinel file — which halts it on the next cycle. /goal clear is just a convenience wrapper on the same removal.

IMPORTANT: there is one loop per session. Arming a monitor replaces an existing /goal loop on that slot, and vice versa — they are competing for the same single slot on the same engine. If you want both a standing goal and a watch running, they need to be in different sessions.

Stopping deliberately matters more than it sounds, because a loop survives a Gateway restart — its state is reloaded and the timer re-armed. Walking away from a live loop does not end it.

The gotcha that actually bit me

I armed a /goal loop as a test. It stopped almost immediately and asked me to approve running ls — which defeats the entire point of a loop designed to run while I am away.

My first assumption was a misconfiguration, because my config has:

"agent": {
"approval_mode": "auto"
}

If tool calls auto-approve, why the prompt? Tracing it produced a distinction I had simply had wrong: there are two separate approval gates, for two separate classes of tool.

Tool classExamplesGate
Agent-nativeexecute_bash, fs_read, fs_write, grep, globthe agent JSON’s allowedTools
MCP server tools@kirocrew-core/*, @kirocrew-cron/*, custom MCP serversKiroCrew’s agent.approval_mode, via its MCP gateway

config.agent.approval_mode is consumed by KiroCrew’s MCP gateway — passed to the MCP stub as --approval-mode and folded into its pool key alongside the sandbox mode. It governs tools that route through that gateway. ls runs via execute_bash, an agent-native tool that does not go through the MCP gateway, so that setting has no say over it.

Reading my own agent config confirmed it. execute_bash appears in tools (available) but not in allowedTools (auto-approved) — and neither do fs_read, fs_write, grep or glob. The prompt was correct behaviour, not a bug.

One honest limit on this: I traced approval_mode to the MCP-gateway boundary in KiroCrew’s own source, but allowedTools is enforced inside the agent binary, so the fine detail of how its entries are matched comes from documentation rather than code I read.

This matters more than a stray click, because approval prompts auto-decline after ten minutes. An unattended loop needing a non-allowlisted tool will fire a cycle, wait, get declined, and burn the cycle having achieved nothing — repeating until it exhausts its budget. So the fix is not approval_mode. It is one of:

  • Session-scoped trust — the approval path has trust tiers above plain interactive prompting, so trust granted for a session stops the prompting for that session without changing anything on disk. Suitable for a one-off unattended run.
  • A dedicated agent whose allowedTools auto-approves only the tools that job needs. More setup, but it is the least-privilege answer and it survives across runs.

Widening allowedTools on your main agent also works, and it is the option I would think hardest about: it auto-approves execute_bash for every conversation from then on. That is a permanent change to what runs without asking, in exchange for fixing one afternoon’s convenience problem.

Takeaways

  • Two of the four share an engine. AutoNudge is infrastructure, not a mode — Monitor and /goal are different instructions injected into the same bounded loop. Learn the engine’s limits (cap, sentinel, backoff, idle timing) and you understand both features.
  • Choose on supervision, not capability. Autopilot when the approach needs reviewing. /goal when the outcome is verifiable but the path is trial and error. Task Runner when you already know the steps.
  • Both iterate — they escalate differently. Autopilot retries up to three rounds per stage then asks you; /goal retries to its cycle budget and asks nobody. That, not the presence of retry, is the real difference.
  • They are not invoked alike. Only /goal is a typed command. Autopilot is a session toggle, the Task Runner takes a spec file, and Monitor has no command at all — you ask for it and the agent arms it. Two features on one engine, with two completely different front doors.
  • One loop per session. Monitor and /goal compete for the same slot, so arming one replaces the other. And a loop survives a Gateway restart — stop it deliberately.
  • Know which approval gate applies to which tool class before trusting a loop to run unattended. Agent-native tools answer to allowedTools; MCP tools answer to approval_mode. Getting this wrong produces a loop that silently burns its budget on declined prompts.
  • A self-verdict is a soft control. /goal’s evidence requirement is prompt text, not enforced code — fine for a v0, as long as you are not treating it as a guarantee.

The general lesson is not specific to KiroCrew: when several features look like they solve the same problem, the naming rarely tells you the difference, and the documentation sometimes describes a different build than the one in front of you. I also got two things wrong on my first pass through this — I had AutoNudge filed as a feature rather than the plumbing, and I had Autopilot down as fire-once when it has a three-round budget per stage. Both errors came from reading the shape of the docs instead of the behaviour of the code.