Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ __pycache__/
autodev*.yaml
!autodev.yaml.example
mia_data.yaml
mia.yaml
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ Agents are untrusted code driven by text from issues, so they never run on the h
| path | |
|---|---|
| `autodev/` | autodev: workflows, activities, CLI; GitHub client and csub job runner shared with mia-data |
| `mia_data/` | mia-data: workflows, activities, harness job specs, CLI (`pixi run md`) |
| `mia_data/` | mia-data: workflows, activities, harness job specs, CLI (`pixi run mia exec mia-data ...`) |
| `mia_ops/` | `pixi run mia`: starts, stops and inspects every service in `mia.yaml` (Temporal, autodev, snippets, mia-data), and `mia exec <service> <cmd>` runs an app command with that service's config |
| `docs/` | setup guides, the [mia-data workflow overview](docs/mia-data-workflow.md) and the [development guide](docs/development.md) |
| `notes/` | design notes, runbook, specs |
| `scripts/` | `autodev-up.sh`, `mia-data-up.sh` |
| `tests/` | unit tests (`pixi run test`); no GitHub, LSF or Temporal server needed. `pixi run lint` runs ruff, `pixi run typecheck` runs mypy |
2 changes: 1 addition & 1 deletion autodev.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ github:
base_branch: main
auth: token # token: $GITHUB_TOKEN (run as yourself) | app: GitHub App (service account)
token_env: GITHUB_TOKEN
token_file: null # auth: token, when $GITHUB_TOKEN is unset; scripts/autodev-up.sh needs it
token_file: null # auth: token, when $GITHUB_TOKEN is unset; `mia up` needs it
# (the sandbox masks .env): gh auth token > ~/.config/autodev/token; chmod 600 it
app_id: null # auth: app
installation_id: null # auth: app
Expand Down
34 changes: 4 additions & 30 deletions autodev/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""The `autodev` command line: health checks, label setup, the worker, the poll schedule, and deployment
management (up/down/ps/logs, from ops.py). Heavy imports happen inside the commands that need them so
`--help` and the ops commands stay fast.
"""The `autodev` command line: health checks, label setup, the worker and the poll schedule. Heavy imports
happen inside the commands that need them so `--help` stays fast. Starting and stopping deployments is
`pixi run mia` (mia_ops).
"""

from __future__ import annotations
Expand All @@ -17,23 +17,13 @@
from typing import TYPE_CHECKING, Any
from urllib.parse import urlsplit

from . import ops
from .config import PROJECT_DIR, STAGES, load_config
from .config import STAGES, load_config

if TYPE_CHECKING:
from temporalio.client import Client

from .config import Config, Deployment

OPS = {
"up": ops.cmd_up,
"down": ops.cmd_down,
"restart": ops.cmd_restart,
"ps": ops.cmd_ps,
"attach": ops.cmd_attach,
"logs": ops.cmd_logs,
}

log = logging.getLogger("autodev")


Expand Down Expand Up @@ -250,24 +240,8 @@ def main(argv: list[str] | None = None) -> int:
sub.add_parser("worker", help="run the Temporal worker")
schedule = sub.add_parser("schedule", help="manage the poll Schedule")
schedule.add_argument("action", choices=["apply"])
sub.add_parser("up", help="start this deployment (tmux: Temporal, broker, worker)")
sub.add_parser("down", help="stop this deployment's tmux session")
sub.add_parser("restart", help="down, then up")
sub.add_parser("ps", help="show whether this deployment is running")
sub.add_parser("attach", help="attach to the deployment's tmux session")
logs = sub.add_parser("logs", help="tail a log (default: worker)")
logs.add_argument("name", nargs="?", default="worker", choices=["worker", "temporal", "broker"])
logs.add_argument("-n", "--lines", type=int, default=50)
logs.add_argument(
"-f", "--follow", action="store_true", help="keep following the log (default: print the last lines and exit)"
)
args = parser.parse_args(argv)
cfg = load_config(args.config)
if args.cmd in OPS:
args.config_path = Path(
args.config or os.environ.get("AUTODEV_CONFIG") or PROJECT_DIR / "autodev.yaml"
).resolve()
return OPS[args.cmd](cfg, args)
_setup_logging(cfg, args.cmd if args.cmd == "worker" else None)
cmd = {"check": cmd_check, "init-labels": cmd_init_labels, "worker": cmd_worker, "schedule": cmd_schedule}[args.cmd]
return asyncio.run(cmd(cfg, args))
Expand Down
98 changes: 0 additions & 98 deletions autodev/ops.py

This file was deleted.

52 changes: 29 additions & 23 deletions docs/autodev-local-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,24 @@ testing autodev. For the shared service account, see "Run it as the group servic

## What you're starting

`scripts/autodev-up.sh` starts three things in one tmux session named after your `deployment`:
`pixi run mia up` reads `mia.yaml` (copy `mia.yaml.example`) and starts, in tmux:

| tmux window | runs | where |
|---|---|---|
| `temporal` | Temporal dev server (gRPC `localhost:7233`, UI `:8233`) | host |
| `broker` | `csub-broker`, applying your `~/.config/csub/broker.toml` policy | host |
| `worker` | `autodev worker`, wrapped in `agentic-sandbox` | sandbox |
| tmux session | window | runs | where |
|---|---|---|---|
| `temporal` | `temporal` | Temporal dev server (gRPC `localhost:7233`, UI `:8233`); shared by every service | host |
| `<service>` | `broker` | `csub-broker`, applying your `~/.config/csub/broker.toml` policy | host |
| `<service>` | `worker` | `autodev worker`, wrapped in `agentic-sandbox` | sandbox |

The worker polls GitHub, drives the workflows, and asks the broker to submit Claude Code jobs to LSF.
For a personal instance `mia.yaml` lists one service, your `autodev.yaml`. The worker polls GitHub, drives the
workflows, and asks the broker to submit Claude Code jobs to LSF.

## Prerequisites

Run these from a Janelia cluster submit host.

1. **pixi** on your PATH, and **tmux**.
2. **agentic-sandbox** checked out next to this repo (`../agentic-sandbox`), or set `$SANDBOX` to its `scripts`
directory. It needs rootless podman or bwrap set up, per its README.
2. **agentic-sandbox** checked out next to this repo (`../agentic-sandbox`), or set `sandbox_dir` in `mia.yaml` to
its `scripts` directory. It needs rootless podman or bwrap set up, per its README.
3. **csub with Claude support.** The pixi env pins csub to `krokicki/csub` branch `deploy-all`, so `pixi install`
provides it, including `csub-broker`.
4. **Broker policy** in `~/.config/csub/broker.toml`:
Expand Down Expand Up @@ -57,8 +58,8 @@ Edit `autodev.yaml`:
- `github.repo`: the repo to watch.
- `paths.work_root`: agent workspaces, which must be inside your broker's `allowed_roots`.
- `paths.state_dir`: the worker's logs and state. Keep it apart from `work_root`. `work_root` is bound read-write
into the sandbox, so it must not be an ancestor of `state_dir`, and `<state_dir>-host` (the Temporal binary and
database) must stay outside every writable bind.
into the sandbox, so it must not be an ancestor of `state_dir`, and `<state_dir>-host` (the broker log) and
`temporal.state_dir` from `mia.yaml` (the Temporal binary and database) must stay outside every writable bind.

### 3. GitHub token

Expand All @@ -75,18 +76,19 @@ from this file because the sandbox masks `.env`. Host-side commands such as `aut
### 4. Check, create labels, start

```bash
pixi run autodev check # the `temporal` row is BAD until step 5 starts the dev server; fix any other BAD first
pixi run autodev init-labels # creates the autodev-<you>:* labels (idempotent)
scripts/autodev-up.sh # tmux session autodev-<you>; applies the poll schedule
pixi run autodev check # now every row should say ok
cp mia.yaml.example mia.yaml # keep only the `autodev` service, pointing at your autodev.yaml; keep temporal.managed: true
pixi run mia exec autodev check # the `temporal` row is BAD until `mia up` starts the dev server; fix any other BAD first
pixi run mia exec autodev init-labels # creates the autodev-<you>:* labels (idempotent)
pixi run mia up # tmux sessions temporal and autodev; applies the poll schedule
pixi run mia exec autodev check # now every row should say ok
```

### 5. Try it

Open a small issue in the repo (for example "add a `--version` flag"), add the label `autodev-<you>:go`, and watch:

```bash
tmux attach -t autodev-<you> # windows: temporal, broker, worker
pixi run mia attach autodev # windows: broker, worker (Temporal: mia attach temporal)
```

The Temporal UI is at `http://localhost:8233`. From your laptop use
Expand All @@ -100,15 +102,19 @@ instead. Only `/approve` comments posted after the bot's latest round summary co
## Day to day

```bash
pixi run ad ps # status
pixi run ad logs [worker|temporal|broker] [-n N] [-f]
pixi run ad attach
pixi run ad restart # after changing code or config
pixi run ad down
pixi run mia ps # every service, or: mia ps autodev
pixi run mia logs autodev [worker|broker] [-n N] [-f]
pixi run mia logs temporal
pixi run mia attach autodev
pixi run mia restart autodev # after changing code or config; leaves Temporal and other services alone
pixi run mia restart # everything, Temporal included
pixi run mia down # everything, Temporal last
pixi run mia exec autodev check # any app command, with this service's config
pixi run test # unit tests; no GitHub, LSF or Temporal server needed
```

Logs: worker logs are in `<state_dir>/logs`. The Temporal and broker logs are in `<state_dir>-host/logs`.
Logs: worker logs are in `<state_dir>/logs`, the broker log in `<state_dir>-host/logs`, and Temporal's in
`temporal.state_dir/logs` from `mia.yaml` (`mia logs temporal`).

## Gotchas

Expand All @@ -119,6 +125,6 @@ Logs: worker logs are in `<state_dir>/logs`. The Temporal and broker logs are in
hosts need to be in `agent.allow_hosts`. Nothing installs them for you.
- **The Temporal dev server is unauthenticated.** Anyone with a login on the host can signal or terminate
workflows, so don't run this on a shared host.
- **`agent.api_key_env` is refused** by `autodev-up.sh` for now. Use a Claude login.
- **`agent.api_key_env` is refused** by `mia up` for now. Use a Claude login.
- **No cancel command.** `temporal workflow terminate -w <id>` stops a workflow but leaves its job running. Also
run `csub kill <job>`.
4 changes: 2 additions & 2 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ Configuration for all four is in `pyproject.toml`. Keep them clean rather than s

| path | |
|---|---|
| `autodev/` | the issue → PR loop. Also home to the pieces mia-data reuses: the GitHub client (`github.py`), auth (`auth.py`), the csub job runner (`jobs.py`), git operations (`gitops.py`), the Temporal connection and logging (`cli.py`), and the up/down/ps/logs commands (`ops.py`) |
| `autodev/` | the issue → PR loop. Also home to the pieces mia-data reuses: the GitHub client (`github.py`), auth (`auth.py`), the csub job runner (`jobs.py`), git operations (`gitops.py`), and the Temporal connection and logging (`cli.py`) |
| `mia_data/` | the dataset → crops loop. Imports from `autodev`, never the other way round |
| `tests/` | one `test_<package>_<module>.py` per module; `autodev_helpers.py` and `mia_helpers.py` build configs and fake repos; `fixtures/` holds recorded workflow histories and sample catalog data |
| `docs/` | setup guides, the mia-data workflow overview, this page |
| `notes/` | design: `mia-crops-spec.md` (the mia-data spec), `autodev-runbook.md` (operations and known gaps), `agentic-dev-infrastructure.md` (why), `specs/` and `plans/` (dated, historical; they describe the code as it was when written) |
| `scripts/` | the up-scripts that start a deployment under agentic-sandbox |
| `mia_ops/` | `pixi run mia`: the services manifest (`mia.yaml`), the Temporal and app-service recipes under tmux and agentic-sandbox, and `mia exec` for app commands |

Within each package the split is the same: `workflows.py` (deterministic Temporal code, no I/O), `activities.py`
(everything that touches GitHub, the filesystem or the cluster), `models.py` (the dataclasses that cross between
Expand Down
12 changes: 6 additions & 6 deletions docs/github-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@ github:
private_key_path: ~/.config/autodev/app.pem # step 2
```

`token_env` and `token_file` are ignored when `auth: app`. `scripts/autodev-up.sh` binds the key read-only into
`token_env` and `token_file` are ignored when `auth: app`. `mia up` binds the key read-only into
the worker's sandbox. The worker signs a short-lived JWT with the key and exchanges it for an installation token,
which it renews about once an hour.

## 5. Check it

```bash
pixi run autodev check
pixi run mia exec autodev check
```

The `github identity` row should show `<app-slug>[bot]` and the `github repo` row should be `ok`. If the labels
don't exist yet for this repo, run `pixi run autodev init-labels`.
don't exist yet for this repo, run `pixi run mia exec autodev init-labels`.

## 6. Switch over

Expand All @@ -103,7 +103,7 @@ already handled, and repost its comments. Switch when nothing is in flight:

1. Finish or close every issue labelled `<deployment>:working` or `<deployment>:review`, e.g.
`gh issue list -R JaneliaSciComp/mia-workflow-prototype -l autodev:review`.
2. `pixi run ad restart`.
2. `pixi run mia restart autodev` (or the service name the App runs under).

At startup the worker looks up the bot user's ID and sets the commit author on every agent workspace to
`<app-slug>[bot] <ID+<app-slug>[bot]@users.noreply.github.com>`, which is how GitHub links commits to the bot.
Expand All @@ -120,12 +120,12 @@ Now that the bot is a separate account, protect `main`. Under **Settings → Rul
autodev only merges after an approval from someone with write access, and GitHub enforces the same rule. Reviewers
now click **Approve**. `/approve <sha>` comments still work, but they don't satisfy branch protection.

Add `.github/CODEOWNERS` covering `autodev/`, `scripts/` and `.github/` so changes to the gates need a named human
Add `.github/CODEOWNERS` covering `autodev/`, `mia_ops/` and `.github/` so changes to the gates need a named human
(see the runbook).

## Maintenance and troubleshooting

- **Rotate the key**: generate a new one on the App's page, replace `app.pem`, `pixi run ad restart`, then delete
- **Rotate the key**: generate a new one on the App's page, replace `app.pem`, `pixi run mia restart autodev`, then delete
the old key on GitHub.
- **Adding permissions later**: after you change the App's permissions, an org owner has to accept the new
permissions on the installation page before they take effect.
Expand Down
Loading
Loading