Sentry · urgentry · Prometheus → decisions → pull requests

Errors in.
Fixes out.

aitomaton watches the errors of everything running on your Docker Swarm. It reads each one as a single event, as part of a burst, and as a long-term pattern, for one service or across a whole architecture. Then it decides what to do. When the answer is "this is a bug in our code", a coding agent gets a ready-to-push clone and a full brief, and a pull request comes back.

How it works

One small service with no dependencies beyond Python's standard library. Webhooks or polling bring errors in, and every tick turns them into findings, decisions and work.

Ingest

Sentry / urgentry webhooks and API polling, Alertmanager alerts. Everything becomes per-minute occurrences.

Discover

Swarm service labels say where each service's source is, what it depends on, and what aitomaton may do.

Analyse

Single errors, bursts and trends per issue, per service and per architecture, including cascades.

Decide

Rules, then laya, JEV and an LLM, each asked only when the last is unsure. Capped by policy.

Act

A stairway space with a prepared clone; the agent fixes, tests and pushes, and aitomaton opens the PR.

Three horizons, three scopes

One exception, a storm of them, and a slow creep over two weeks call for different responses. So does a failure in one app versus the whole stack failing because its database did.

issue
service
architecture
single
new, regression, level, in-app stack frames
failed swarm tasks (OOMKilled…)
n/a
burst
last 5 min vs the 24 h baseline rate
all of the service's issues together
cascades: services failing together, root from depends
long term
14-day trend: rising, chronic
memory / CPU context from Prometheus
events and issues per architecture

When the front end times out because the database is down, only the database is a root cause. Downstream services are reported, never "fixed".

Cheap decisions first

Every tier answers the same three things: an action (ignore, watch, report, fix), a confidence, and the probability that it is a code defect. The next tier is asked only when the stakes are real and the last answer is unsure or disagrees. A tier that is down hands over to the next.

always

rules

Deterministic severity and code-defect scores from the analysis, memory pressure and cascade position.

free · µs
system 1

laya

Two calibrated yes/no reads in one forward pass: urgent? code defect? 0.96 vs 0.11 for a KeyError vs a full disk.

CPU · seconds
router

JEV

TypeSafe's jev-router on OpenRouter picks the model and reasoning effort per request.

OpenRouter · env
system 2

LLM

Any OpenAI-compatible model, from OpenRouter or local through Ollama or vLLM. It sees what the earlier tiers said.

any provider

Guard rails no model can override: no repo means no fix; an unlikely code defect means no fix; the service's policy caps the action.

The swarm is the configuration

There's no registry of apps to keep in sync. Put a few labels on a service and aitomaton knows its repo, its Sentry project, what it depends on, which agent to use, and how far it may go.

  • Reads holodocker's labels (holodocker.repo, …repo_ref, …repo_path)
  • Never fixes third-party images: an image's source label is only a hint until you opt in
  • Credentials by name: the label names a token held in env or a Docker secret; tokens can also be per repo owner or global
  • Per-service agent and model: Claude Code, opencode, crush or openclaude, with any OpenRouter model
# any service in a stack
deploy:
  labels:
    aitomaton.repo: https://github.com/acme/shop-api
    aitomaton.ref: main
    aitomaton.policy: pr          # observe|report|pr|push
    aitomaton.sentry.project: shop-api
    aitomaton.depends: db
    aitomaton.test: pytest -q
    aitomaton.agent: opencode
    aitomaton.model: qwen/qwen3-coder
    aitomaton.git.credential: acme  # a name, not a token
    aitomaton.notes: "never touch migrations/"
observe

Analyse and decide, record only.

report

Open a GitHub issue with the full analysis.

pr

Fix in a stairway space, deliver a pull request.

push

Push to the branch when the tests pass, open a PR otherwise.

A space to fix it in

A fix becomes a stairway experiment: a folder on your server with a coding agent in it, visible in claude.ai/code so you can watch or take over.

  • Prepared clone. aitomaton clones the repo with the service's own token and checks out aitomaton/fix-…. The agent only commits and pushes.
  • A complete brief. The stack trace, breadcrumbs, release, analysis and metrics, plus how to deliver under the policy.
  • A result protocol. The agent ends with AITOMATON-RESULT; aitomaton opens the PR and deletes the credential.
  • Bounded. Dry-run by default, plus limits on concurrent and daily fixes, and a cooldown per issue.
dry-runqueuedrunningwaiting doneno-fixfailed
## the brief, abridged
You are aitomaton's fixer. A production error in
service `billing_api` was judged a code defect.

- GitHub: https://github.com/acme/billing (main)
- Already cloned in ./repo on aitomaton/fix-5120,
  with push access configured.

4. Commit and `git push -u origin aitomaton/fix-5120`.
   Report status "branch": aitomaton opens the PR.

### KeyError: 'vat_rate'
billing/invoice.py:88 in total
    rate = country['vat_rate']

AITOMATON-RESULT: {"status": "branch",
  "title": "Default VAT for countries without a rate", …}

One JSON, one page

/status.json lists works in progress, findings, every decision with each tier's reasoning, and the issues with their analysis. The status page renders it. The demo below runs the real engine on a synthetic swarm: a database takes down its stack, a new KeyError bursts, a worker slowly degrades.

aitomaton · demo/status.json

Quick start

Requirements: a swarm manager, a Sentry or urgentry, and optionally stairway, laya, an OpenRouter key and GitHub tokens. Start in dry-run, read its decisions for a few days, then let it act.

  • Webhook: POST /hooks/sentry (HMAC-verified)
  • Alerts: POST /hooks/alertmanager?token=…
  • Agent callbacks: POST /hooks/stairway
  • Output: GET /status.json and GET /
git clone https://github.com/sirmmo/aitomaton
cd aitomaton
docker build -t aitomaton:0.1 .
docker build -t aitomaton-laya:0.1 laya

cp .env.example .env
# SENTRY_URL / SENTRY_TOKEN / SENTRY_ORG
# STAIRWAY_URL / STAIRWAY_TOKEN
# JEV_MODEL=typesafe/jev-router, LLM_API_KEY
# AITOMATON_GIT_TOKEN_<OWNER>=…
# AITOMATON_DISPATCH=0  ← dry run first

docker stack deploy -c stack.yml aitomaton

Documentation

All of it lives in the repository.