AGCoord
Local CI and merge queue for coding agents that share one machine.
When several coding agents share one workstation, nothing coordinates them. They compete for
the machine, so test runs time out or get OOM-killed for reasons that have nothing to do with
the code. They merge on stale evidence, so main breaks from changes that were never tested
together. And they leave no shared record of what ran, on which head, with what result.
AGCoord is the missing layer. One detached broker per OS user owns a queue for every repository and worktree on the machine:
- Resource-aware admission. A job declares the CPU, memory, and scratch it needs. It starts when the machine has room, and on a supported host cgroup v2 holds it to what it declared.
- Atomic landing.
agc land 123 -- ./scripts/test.shbrings the current target into the pull-request branch, runs your gate once, and merges the pull request in the same durable step. A red gate publishes nothing. A moved target never reuses a green result. - One record. Every job has a stable ID, a combined log, and a row you can list, follow, cancel, or watch in a terminal UI from any shell.
It works with any agent that can run a shell command, such as Claude Code, Codex, or Aider, and with people. It complements hosted CI rather than replacing it. The core is forge-neutral; GitHub support is an optional adapter. Why AGCoord exists makes the case in depth.
What you need
- Linux on x86_64, and Python 3.10 or newer for the
agcoordclient. - For the two-minute try-out below: nothing else. The released broker runs as your own user with admission-only accounting.
- For enforced limits: an Ubuntu 24.04-class host (unified cgroup v2 mounted read-write with
nsdelegate, AppArmor ABI 4,kernel.apparmor_restrict_unprivileged_userns=1, systemd 254 or newer) and one privileged install step. The broker itself runs as an unprivileged user service; no root daemon is installed. - For landing: a GitHub pull request. Checks and full gates need no forge at all.
AGCoord is alpha software that moves quickly. A client commands only a broker of its own minor release line, and the changelog records every user-facing change.
Try it in two minutes, without root
The client talks to a broker executable that must match its version exactly, and it ships
the SHA-256 of that executable. One command fetches the release broker, verifies it against
that pin, places it under ~/.local/libexec/agcoord, and configures an unmanaged spool:
python -m pip install agcoord # in a virtual environment, or: pipx install agcoord
agc host install --user
Now submit work from inside any Git checkout:
cd ~/src/your-repo
agc run --label "unit tests" --resource cpu=2 -- python -m pytest -q
agc list
agc tui
The first client starts the broker on demand, and closing the terminal does not cancel the
job. The quickstart continues from here: watching two oversized jobs
queue behind each other, following logs, moving to the enforced host, and landing a pull
request. After pip install --upgrade agcoord, run agc host install --user again; a client
refuses a user-owned broker that is not the one it was released with.
Turn on enforcement
On a supported Ubuntu host, one privileged step installs the pinned broker as a root-owned
file, enables a systemd user service and an enforcing AppArmor profile, and proves a one-CPU
limit before reporting success. It needs an empty default spool: if you ran the try-out on
this machine, agc drain it and move ~/.local/state/agcoord aside first.
python -m pip install --upgrade agcoord
agc host install --download
--download fetches the release bundle that matches the installed client, verifies its
checksums and the broker against the digest the client ships with, and refuses anything else.
A fresh install records the machine's available CPU count as both cpu and jobs capacity
with a required cgroup-v2 binding, so --resource cpu=N becomes a real cpu.max limit.
Memory, tmpfs, persistent scratch, process, and block-I/O bindings live in the same
config.json. The native host runbook has the full host contract,
offline installation from a bundle, upgrades, recovery, and rollback.
Tell your agents
Agents follow the tools they are given. Paste this into your repository's CLAUDE.md or
AGENTS.md, and every agent on the machine coordinates through the same queue:
Run every check through the local coordinator and declare what it uses:
agc run --label "<what>" --resource cpu=2 -- <command>
Land a pull request only through one gate-and-publish request; never merge directly:
agc land <pr> --resource cpu=4 -- <full test command>
Never run agc from inside an admitted job. A stale-main or head-changed refusal means:
update the branch, push, and submit a new land request.
The agent guide has the long form, what every refusal and handback means and what to do next, and notes for sandboxed shells. This repository's own AGENTS.md is a worked example.
Run work
agc run --label "unit tests" --resource cpu=2 -- python -m pytest -q # an ordinary check
agc full --label "release gate" --resource cpu=4 -- ./scripts/test.sh # a clean exact-head receipt
agc land 123 --label "land PR 123" --resource cpu=4 -- ./scripts/test.sh # gate and publish, one row
agc list # every job on the machine
agc show check-0123456789ab # one durable row, as JSON
agc log land-0123456789ab --follow # one combined log
agc cancel check-0123456789ab # process-group cancellation
agc tui # live view across repositories
Every job implicitly holds one jobs slot. Repeatable --resource NAME=UNITS options add
named, configured resources; an unknown or impossible request fails instead of waiting
forever. Without a binding, a name is admission accounting only. With a binding in
config.json, the broker applies and measures it, and every run reports what was requested,
applied, and observed. Scratch is opt-in: a job that declares neither a tmpfs nor a
project-quota policy receives no temporary directory from AGCoord, and inherited TMPDIR,
TMP, and TEMP values are removed. The
resource contract covers bindings,
delegated cgroups,
child CPU leases for tools that
fan out inside one job, and the optional
pytest-xdist adapter.
full validates an exact clean head and keeps a durable receipt; it is ordinary lane work,
not a barrier. land is the only barrier. Push the branch, open the pull request, and submit
from a clean checkout of that head: the row excludes other lands in its repository and jobs
from its own worktree, holds its lane and resources from preflight through publication, and
never rebases or rewrites commits. If the target advanced, the default GitHub adapter first
merges it into the request branch and pushes with an exact lease; --no-target-sync refuses
instead. A red gate records gate-failed and publishes nothing. stale-main,
head-changed, pr-not-ready, publish-failed, merge-error, and avoided-commit are the
other handbacks, and none of them moves the target. Cancellation is refused only once a land
is publishing. The atomic landing contract is the
authority.
Maintenance stays in the same tool. agc drain --reason ... refuses new submissions while
admitted work finishes, and agc resume <drain-id> reopens the queue. agc avoid <sha>
stores a commit that no landing on this machine may publish again after a target rewrite.
agc clear removes terminal history while the queue is idle.
State lives in ${XDG_STATE_HOME:-~/.local/state}/agcoord; AGCOORD_STATE_DIR or
--state-dir selects a different spool for an unmanaged coordinator. One config.json there
holds capacities, bindings, cgroup_root, database_timeout, and native_broker; with
no file at all, capacity defaults to two job slots. A spool left below protocol 5 by a release
before 0.6.0 is refused with instructions; see
migrating a pre-native spool.
How it compares
| If you use | What it coordinates | Machine resources | Merge gating |
|---|---|---|---|
| GitHub merge queue, Mergify | Pull requests, on hosted CI | No | Yes, in CI |
| pueue, task-spooler, nq | Shell commands on one machine | Parallel count only | No |
| Claude Squad, Conductor, Vibe Kanban | Agent sessions and worktrees | No | Manual |
| Gas Town | An agent workforce with an LLM-run merge queue | Session count cap | Yes |
| Container limits | One container | Static per container | No |
| AGCoord | Jobs from any agent, tool, or person | Declared and enforced | Yes, atomic with the gate |
AGCoord sits under the session managers and beside the hosted queues. Use one or the other per branch: a branch that requires a hosted merge queue rejects the direct ref update that atomic publication performs for anyone without bypass rights. The comparison covers each of these by situation, with Gas Town in depth.
Documentation
- agcoord.readthedocs.io: these pages as a site, with search and a version switcher.
- Quickstart: the no-root try-out and the enforced host, step by step.
- Coordinator contract: scheduling, lanes, resources, landing, recovery, CLI, and TUI.
- Native host runbook and native broker architecture.
- Documentation index for everything else, including the release and conformance contracts.
Project status
AGCoord is distributed as the agcoord project on PyPI, with import package agcoord and
the agc console command; python -m agcoord remains an equivalent module entry point.
Build and install development checkouts in an isolated environment rather than copying
modules into another project. Contributors follow AGENTS.md and the
contributor workflow. The gourd mascot and its asset notes live under
docs/assets.
Release files for agcoord 0.7.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agcoord-0.7.1.tar.gz | 1.7 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agcoord-0.7.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.7 MB
Release files / agcoord-0.7.1.tar.gz
| Download URL | agcoord-0.7.1.tar.gz |
|---|---|
| Size | 1.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3c73cd6595fe19f4a6ac61d88a3f7ac1169a79f34ee1bae894ec2cb98bcedfa9
|
|
BLAKE2b-256 checksum How to use checksums |
db9115d568089753f038e1dc431d487f0cea787f212feab5b270cba63058e53a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.11
|
Release files / agcoord-0.7.1-py3-none-any.whl
| Download URL | agcoord-0.7.1-py3-none-any.whl |
|---|---|
| Size | 88.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df665e176a5d14083863760ee431f8b60d399784cbbf46e65412408cd440bc10
|
|
BLAKE2b-256 checksum How to use checksums |
90054e46fad981d8f00f186bb4f88cd8b6c880db93b3090b377456b26e1083bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.11
|