ADR-0083: The agent runs commands: gated, asynchronous, time-bounded
- Status: Accepted; amended by ADR-0084
- Date: 2026-09-09
- Deciders: jka
- Decision: D83
Context
The last deferred part of the agent's tools was running commands: tests, linters,
builds, git. ADR-0053 had placed this in scope provided a human approves. The existing
exec.run blocks the single-threaded core until the child exits, so a timer-based
timeout could never fire while it waited.
Decision
A run_command tool, of a new kind exec, goes through the same propose-and-approve
gate as writes.
- Always gated. Auto-accept applies to edits only. A command always waits for a human, and no separate "auto-run commands" setting exists.
- Asynchronous.
rio::exec::start {argv cwd stdin timeout_ms donecmd}spawns the child and returns immediately. Standard output is read as it arrives, standard error goes to a temporary file, and the exit code is taken fromclose. The agent's turn yields until completion. The core stays responsive while a command runs. - Time-bounded. The default timeout is 120 seconds and the maximum 600; there is no
unlimited setting. An overrunning child is killed (
kill -TERMon Unix,taskkill /F /Ton Windows) and reported as timed out. Resetting or superseding a turn kills a running command. - Constraints. Argument vector only, no shell. The working directory is confined
to the project root. Any argument Tcl's
execwould interpret as redirection or a pipe (<,>,2>,|,&and similar) is refused. A non-zero exit is a successful run whose code is data; only a failure to launch or a timeout is an error.
agent.propose gains a kind (edit or command), and a command proposal carries the
command, its display form and a project-relative working directory instead of a diff.
Consequences
- The agent can run tests with a human watching, and no provider needed changes.
- Command output is returned when the command ends. Streaming output and a per-command cancel are deferred.
exec.runremains synchronous for git's short commands.- Re-approving the same test command every few minutes was tedious, which led to the allow-list of ADR-0084.