Skip to content

Stopping a run

Suspending an AI System stops everything inside it. On a shared assistant serving a whole team that is not a control anyone will use, so in practice the answer to “can you stop it?” was “yes, by stopping everything”.

An abort stops one run. Same enforcement as autonomy — the same five governed surfaces, the same refusal, the same audit shape — scoped to a single run rather than an agent or a system.

It stops the run’s next governed action. The next model call, tool call, delegation or skill execution on that run is refused at the gateway.

It does not interrupt a call already in flight. A provider request that has already been sent completes. Streaming responses are cut at the next chunk boundary; a non-streaming call finishes.

It does not undo anything. If the agent has already sent the email, aborting the run does not unsend it. Rollback is a different, much larger problem.

It does not touch other runs. That is the entire point.

From the Admin console, open AI Systems → system → Runs, open a run, and choose Stop run. A reason is required: an intervention with no stated reason is not evidence, and “why did this run stop?” is the first question afterwards.

Through the API:

Terminal window
curl -X POST http://localhost:5050/v1/admin/ai-systems/runs/{root_task_id}/abort \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason": "looping on the same tool call since 14:02"}'

Requires the run:abort permission, which is separate from resourcegroup:update on purpose — can view runs and can stop runs are different jobs, so a SOC operator can pull the handle without also being able to reconfigure what they are watching.

You may only stop a run you can see. The containment rule matches the one the gateway applies to end_run: stopping someone else’s run is not a read, it writes a terminal state into assurance evidence.

Aborting a run twice returns the intervention already in force rather than an error. A second abort is the same instruction, not a new one.

Terminal window
curl "http://localhost:5050/v1/admin/ai-systems/runs/live" \
-H "Authorization: Bearer $ADMIN_TOKEN"

There is no live-run table. ai_system_runs is a rollup written when a run finalizes, and adding a live table would put a write on the hot path for every run when almost none are ever intervened on. The list is derived on read from the proxy logs instead, over the same index the run sweeper uses.

“I clicked stop” and “the run stopped” are different claims, and the record keeps them apart:

Field Meaning
status: requested The abort is written. No gateway has refused anything yet.
status: effective A gateway has refused at least one action on this run.
first_blocked_at When the first refusal happened. null means it has not bitten.
steps_blocked How many actions have been refused.

steps_blocked: 0 on a finished run is not a failure — it usually means the agent had already stopped when you clicked, so no action ever came along to refuse. The run still finalizes as cancelled.

An abort reaches every gateway replica by three independent paths: a pg_notify broadcast, a Redis key, and reconciliation on the sweeper’s tick. An abort that fails to arrive would let a runaway continue, which is the wrong direction for a safety control to fail.

Once an abort bites:

  • The refused action is logged with subtype run_aborted, kept distinct from autonomy_block so “I stopped this run” and “the system is suspended” stay separable in the audit trail.
  • Any approvals still pending on that run are expired, so an approver cannot approve an action on a stopped run an hour later and resurrect it.
  • The run finalizes as cancelled, closed by operator. The idle sweeper will not relabel it abandoned: the operator’s label wins over the timeout’s guess.

The intervention record itself survives as evidence. It appears in the run’s audit trail and in the Assurance Report, and a system aborted repeatedly is a signal worth reading rather than a problem to manage quietly.