The work graph that keeps a fleet of coding agents from colliding — and from silently breaking each other's decisions. The state is a git repository. There is no database.
Three surfaces, none of them a staq-built screen: your terminal & agents, a headless hub (an HTTP API over a git repo), and the git remote itself. Here's what's running on the box.
work.git directly over SSH. The bare repo is the single source of truth.Tasks, locks, decisions, contracts and the event outbox are all git refs & blobs in one bare repo. Nothing to provision, back up, or keep in sync.
The HTTP hub is a ~41 MB process with zero state of its own — it just runs git plumbing against work.git. Kill it and restart anywhere; no data is lost.
Claiming a task is a single atomic git update-ref with an expected old value. Git's ref store does the compare-and-swap — so two agents racing for the same task can never both win. No lock server, no quorum.
The mutex stops two agents editing the same task. The harder problem — and the thing nothing else does — is catching when an agent's decision on task A silently breaks task B. staq records what each task relies on (a contract) and what each decision touches, then intersects them.
block on an in_review task fails the gate (staq check exits non-zero).open used to be skipped and never revisited. Now, when B transitions to in_review, staq review replays the entire prior decision log against it (recheckTaskCoherence) — so a break that was invisible at decision time is surfaced at the moment it matters.
Agents coordinate through the hub's HTTP API and sync the graph over the git remote. Only /health is open; everything else needs the bearer token.
| Endpoint | What it does | |
|---|---|---|
GET /health | Liveness — the only unauthenticated route. | 200 |
GET /status | All tasks with lock state projected on. | 200 / 401 |
GET /ready | The pull queue: open, unblocked, unheld tasks with cost/lane/contended annotations. | 200 / 401 |
POST /claim | Server-side CAS claim of a task. | 200 / 409 |
POST /release | Release a held lock. | 200 / 403 / 404 |
# talk to the coordination API curl -H "Authorization: Bearer $STAQ_HUB_TOKEN" https://your-hub.example.com/ready # sync the work-graph (the distributed git-ref mutex lives here) git remote add hub ssh://staq@your-hub.example.com/srv/staq/work.git git push hub refs/work/*
Work originates in Linear; staq only coordinates. The one-way importer mirrors issues → tasks (blocked_by → deps) without ever writing back — paginated beyond 250 and reconciling closures against live state, never on a fragile time window.
Every route but /health requires Authorization: Bearer. The token is never logged, stored in a ref, or echoed in a response.
The hub binds 127.0.0.1 only; Caddy terminates TLS in front. It refuses to bind a public interface without a token set.
The staq user's login shell is git-shell — SSH allows git push/pull and nothing else. No interactive shell, no arbitrary commands.
State is the git repo. A bad release is a per-machine rollback; a dead hub loses zero data. Re-deploy is idempotent.