autodev turns a labelled GitHub issue into a PR written by Claude Code in a sandboxed LSF job, revises it on request, and merges it after a human approves. Design: agentic-dev-infrastructure.md.
| you do | autodev does |
|---|---|
add autodev:go to an issue (needs write access) |
runs an agent round, opens PR autodev/issue-N, labels the issue autodev:review |
review with Request changes, or comment /revise <what> (write access) |
runs another round on the same branch (up to max_revisions) |
approve the PR (or comment /approve <sha> under a personal token, after the latest round's summary) |
squash-merges that exact commit, closes the issue |
| close the PR | stops; re-add autodev:go to start over (closing the issue does not stop a running workflow) |
sees autodev:needs-human |
read the failure comment, fix, re-add autodev:go (an open PR is resumed) |
Trust boundaries:
- Only people with write access can trigger (
:go), approve, or request changes, and only their comments reach the agent's prompt. The issue body is included as-is, so edits to it after labelling are a known ceiling. - Agent commits touching
.github/are refused. - Permanent failures stop the round immediately; transient GitHub/csub/git errors retry for up to 6 h.
- Agent summaries and log tails are posted with secret-shaped strings (
sk-ant-…,ghp_…, JWTs, the worker's own GitHub token) replaced by[redacted]; that is a pattern filter, not a guarantee. - The Temporal dev server's gRPC port and web UI are unauthenticated: anyone with a login on the host can start, signal or terminate workflows. Run autodev on a host you don't share with untrusted users.
- Prerequisites P1-P4 in the plan.
csub is pinned to
krokicki/csubbranchdeploy-all(has theclauderequest field);pixi installgets it. cp autodev.yaml.example autodev.yamland setdeployment: autodev-<you>(keeps your labels, branches and workflows apart from the service and from colleagues),paths.work_rootunder your csuballowed_roots. Keepwork_rootandpaths.state_dirapart:work_rootmust not be an ancestor ofstate_dir, becausework_rootis bound read-write into the sandbox, and<state_dir>-host(the broker log) andtemporal.state_dirfrommia.yaml(the Temporal binary and database) must stay outside every writable bind.gh auth token > ~/.config/autodev/token && chmod 600 ~/.config/autodev/token(or a fine-grained PAT with Contents, Pull requests and Issues read/write, and Checks and Commit statuses read), and setgithub.token_file: ~/.config/autodev/token. The sandboxed worker reads the token from this file (the sandbox masks.env); host-side commands likeautodev checkalso accept.env/$GITHUB_TOKEN.pixi run mia exec autodev check: thetemporalrow isBADuntil step 5 has started the dev server; fix any otherBADrow first.pixi run mia exec autodev init-labels, thenpixi run mia up. It starts, in tmux, a Temporal dev server (sessiontemporal) and the pixi env'scsub-broker(reading your~/.config/csub/broker.tomlpolicy) on the host, and the worker inside the sandbox. Re-runpixi run mia exec autodev check: now every rowok.- Open a small issue ("add a
--versionflag"), addautodev-<you>:go, watchpixi run mia attach <service>.
Manage the services listed in mia.yaml: pixi run mia ps, mia logs <service> [worker|broker] (-n N, -f to
follow), mia logs temporal, mia attach <service>, mia down [<service>...], mia restart [<service>...],
mia up [<service>...]. Without a service name each command acts on everything, Temporal first on the way up and
last on the way down.
Files: the Temporal CLI binary, temporal.db and its log live in temporal.state_dir from mia.yaml (default
~/.local/state/temporal); the broker log is in <state_dir>-host/logs, which the sandbox never binds; worker logs
are in <state_dir>/logs.
Upgrading a host from the old up-scripts: the dev server's database used to be <autodev state_dir>-host/temporal.db.
To keep in-flight workflows, stop the old tmux sessions (tmux kill-session -t <deployment>) and copy that file into
temporal.state_dir before the first mia up, or point temporal.state_dir at the old -host directory. A fresh
database is fine on a dev host: the poll schedule is re-applied and open issues are picked up on the next poll.
Under a personal token you are both the bot and the reviewer, so GitHub won't let you approve your own PR:
comment /approve <first 7+ chars of the head sha> instead. Only /approve comments posted after the bot's latest
round summary count, so a short prefix approved earlier cannot approve a later head.
- Service account on the cluster with rootless podman / bwrap set up (agentic-sandbox README), a csub
broker installed for it (csub
deploy/INSTALL.md), and an LSF billing group in its broker policy. - Broker policy (
~svc/.config/csub/broker.toml):[agents] claude = ["api.anthropic.com", "claude.ai", "platform.claude.com"];allowed_hosts⊇agent.allow_hosts;allowed_rootscontainspaths.work_root; the CPU[queues.<name>]tables andcpu_short_queue/cpu_default_queueare named after queuesbqueueslists (at Janeliashortandlocal; a made-up name fails every submit withNo such queue). Config rejects GitHub hosts inagent.allow_hosts, andagent.extra_mountsmust be absolute and end in:ro. - Claude for the service account:
claude auth loginas that account (a team seat).agent.api_key_envis refused bymia upfor now. - GitHub App owned by the org, installed on the repo, permissions: Contents RW, Pull requests RW, Issues
RW, Checks R, Commit statuses R, Metadata R. Not Workflows, not Administration. Private key at
~svc/.config/autodev/app.pem(mode 600);github.auth: app,app_id,installation_id,private_key_path. Step by step:docs/github-app.md. - Branch protection / ruleset on
main: 1 approving review required, stale approvals dismissed on push, the App not in the bypass list. This is the real gate; autodev's own approval check is a pre-check. Add.github/CODEOWNERS(inside.github/, so agents cannot edit it) coveringautodev/,mia_ops/and.github/, so changes to the gates need a named human. - As the service account:
pixi run mia exec autodev init-labels,pixi run mia up, thenpixi run mia exec autodev check.
- No cancel command:
temporal workflow terminate -w <id>stops a workflow but leaves its job;csub kill <job>. - The agent's tests need tools inside the job (e.g. pixi on
path_prepend+extra_mounts, conda/pypi inallow_hosts); nothing installs them for you. - CI failures after approval only escalate; there is no automatic fix-CI round.
- Claude sessions are not resumed across rounds; each round starts fresh from the PR diff + feedback.
- Temporal is a dev server under the running account; a shared production server is a later step.
autodev/workflows.pyandautodev/decide.pyare workflow code: changing their decisions or command order breaks in-flight workflows on replay.test_replay_recorded_historyreplays a recorded run (tests/fixtures/autodev_issue_history.json) and fails on such a change. If the change is intentional, guard it withworkflow.patched()(there is no Worker Versioning yet) so runs in flight keep their old path, then re-record:AUTODEV_RECORD_HISTORY=1 pixi run pytest tests/test_autodev_workflows.py -k recorded_history.[bot]logins are treated as having no permission (never looked up), so no bot can trigger, approve or revise.- The live end-to-end run (issue, PR,
/revise,/approve) has not been done yet.
Prerequisites P1-P4 (plan): P1 repos mia-workflow-data and mia-snippets
with the GitHub App installed; P2 shared dirs for state_dir, work_root, storage_root (all under paths) visible from compute
nodes; P3 csub broker policy ([broker] in broker.toml: env_allow includes "MIA_HARNESS_*";
allowed_roots covers harness.dir, work_root and storage_root, none of them under $HOME, which csub never
mounts; [agents], data hosts, redirect probe; CPU queue tables named after real bqueues queues; mia exec mia-data check
names what is missing); P4 see the plan.
cp mia_data.yaml.example mia_data.yaml, fill in paths,harness.dirand agithub.token_file(or app key).paths.work_rootandpaths.storage_roottake a path or{path, url}; with aurl(the folder in Fileglancer) the issue comments link the round's workspace and the dataset's crops folder. The data repo needs at least one commit on its default branch: thegalleriesbranch is created through the trees API, which answers 409 on an empty repo.pixi run mia exec mia-data checkverifies repos, Temporal, csub, dirs and the harness pin (and thatcd harness && pixi installwas done inharness.dir). Run on the host,mia exec mia-data checkandmia exec mia-data test-prneed the mia-data broker:CSUB_TRANSPORT=unix CSUB_SOCKET=/scratch/$USER/mia-data/csub.sock pixi run mia exec mia-data check.pixi run mia upstarts Temporal (unlessmia.yamlsaystemporal.managed: false, in which case it only checks that the configured cluster answers), then each service: it ensures the service's namespace, starts its broker and sandboxed worker, and applies the poll schedule. Re-running it while a session exists only re-applies that service's schedule, so a run that failed partway is repaired by running it again. Usestemporalfrom PATH; a managed server downloads one intotemporal.state_dir/binotherwise. Manage withmia ps,mia logs mia-data [worker|broker](-n N,-f),mia attach mia-data,mia down mia-data,mia restart mia-data(needed after a config change; wait for running rounds to finish first).mia downwith no service stops everything, Temporal last; naming a service never touches Temporal.pixi run mia exec mia-data init-labelscreates thedata:*labels;pixi run mia exec mia-data seedseeds dataset issues from the catalog.pixi run mia exec mia-data test-pr <pr> --binding <file> --record <id>(all required) runs the crop check on a snippet PR's head with that binding and catalog record, and reports on the PR.- Labels on a dataset issue:
data:gostarts a run;data:approveapproves the snippet PR / crops for publish.