feat(web): an automation's queued run starts its coding agent as the deployment acting for the owner, under its guards (automations 3/7)
Automations 3/7, stacked on #2512. An automation run now starts its coding agent: the minute sweep acts on the runs it queued and on any run a trigger left queued. Nothing can create an automation until the API in 4/7.
Run execution (hosted/automation-runs.ts)
A run is a Start with a task, on the same path a Start takes (coder-host/start.ts):
- The plan, its repository, the owner's access to it through the Luke GitHub App, and the model are checked exactly as for the developer's own Start.
- The agent runs as the owner, on the owner's keys.
- eve is reached as the deployment acting for the owner under
CRON_SECRET. That is the opening the coder channel already admits for the voice brain (CODER_DEPLOYMENT_TURNS[OPEN][TYPED]), so the channel is unchanged. - The run's id is the Start's idempotency key. If two sweeps, or a sweep and a trigger, both act on one run, only one agent starts.
- An automation's model choice does not become the account's default. This adds a new
StartAsk.rememberChoice; the route and the voice tool are unaffected. - The task is the automation's prompt plus a closing line saying which automation started the run, why it fired, and that no one is watching it run.
Guards, checked before the start:
- Daily cap: runs started in the last 24 h.
- Concurrency: this automation's agents that are still starting or running, read with
codingAgentStatusOfexactly as each agent's tab reads it.
A run past either guard is skipped, not queued.
Failures:
- A refused start is a failed run with one fixed word:
plan-unreachable,no-repository,repository-unreachable,github-sign-in,modelorunavailable. - Three failed starts in a row pause the automation:
paused_reason: failures, andnext_run_atis cleared. - If the store fails partway through, the run stays queued and the next sweep retries it under the same key.
Sweep (hosted/automation-sweep.ts, maintenance-app.ts)
Each sweep claims due schedules (2/7), then acts on runs still queued from the last hour: at most 12 per sweep, 4 at a time, oldest first. The sweep's answer is now automations: { fired, started, skipped, failed }.
If the deployment has no CRON_SECRET or no eve origin, every run it acts on fails as unavailable.
The agent's questions go to the Inbox and its end raises the finished-agent notification, as for any agent; neither path changes.
Tests
apps/web/tests/automation-runs.test.ts, over PGlite, a fake eve, a scripted GitHub and a fixed catalog:
- A due schedule starts exactly one agent, opened for the owner and handed the prompt as its task, on the automation's model. A second sweep starts nothing, and the account default stays unset.
- A second run while the first agent is still starting is skipped as
concurrency; with a daily cap of 1, the second run is skipped asdaily-cap. Only the started runs open a session. - Three runs on a plan with no repository fail as
no-repositoryand pause the automation asfailures, withnext_run_atcleared.
pnpm check:affected passes. There are no UI changes in this PR, so there is no Mac evidence and verify.sh was not run.
š¤ Generated with Claude Code
Summary by cubic
An automation's queued run now starts its coding agent in the minute sweep, as the deployment acting for the owner, under the automation's guards. This is automations 3/7; nothing can create an automation until the API lands in 4/7.
New Features
- A run is a Start with a task on the same path as a developer's Start, using the run's id as the idempotency key so a run acted on twice starts one agent; a run whose agent a start already made settles as that start before any guard reads.
- Guards run first: a run past the daily cap or concurrency limit, or queued before its automation was paused, is skipped, never queued.
- One automation's runs are acted on one after another, so its concurrency guard holds across runs queued together.
- Refusals produce a failed run with one fixed word; three failed starts in a row pause the automation with
paused_reason: failuresand clearnext_run_at. Skipped and missed runs never count toward that. - A run still queued two hours after it fired, past the last sweep that would act on it, is settled as failed with reason
missed. - The run's model choice does not become the account's default, via a new
StartAsk.rememberChoice. - Each sweep claims due schedules, then acts on runs queued within the last hour: at most 12 per sweep, 4 at a time, oldest first, answering
{ fired, started, skipped, failed }. A deployment withoutCRON_SECRETor an eve origin fails each run asunavailable.