Introduction
Documentation: https://yorch.github.io/auto-swe/
A durable, governed multi-agent workflow platform for engineering and non-engineering teams. Workflows are versioned JSON DAGs executed on Temporal; agents run inside isolated Docker workspaces, with human approval gates, security scanning, and cost control applied to every run.
Its flagship use case — and what the default template ships for — is ticket in, reviewed and tested pull request out. That flow is an ordinary workflow template built from the same node types any team can author, not privileged runtime code, so it is a starting point rather than the boundary.
Teams drive all of it from the Slack channel teammate: a channel-resident assistant with its own memory and persona that answers questions with repo and run context, and also starts, watches, and gates the workflows themselves.
The flagship flow
Section titled “The flagship flow”- Submit a work request — POST a ticket ID (Jira, Linear, GitHub Issues, etc.) and description to the API
- Agent implements — An LLM agent clones the target repo into an ephemeral Docker sandbox, writes code and tests, and iterates until tests go green (TDD loop, max 5 iterations)
- Review network runs — Three parallel review agents (Security Auditor, Domain Logic, Performance) inspect the diff and can request fixes
- PR opens — A pull request is created on GitHub with the implementation
- CI self-heals — If pipeline checks fail, the agent wakes up, reads the logs, and pushes a fix
- Human merges — The workflow waits (up to 7 days) for a human to merge the PR, then commits learnings to semantic memory (pgvector) for future runs. A repository can opt into native auto-merge for low-risk changes (
docs/pr-evaluation.md); it is off by default
All execution is durable via Temporal.io — workflows survive crashes, restarts, and API rate limits.
Other automations — dependency sweeps, release-note generation, scheduled audits, PRD decomposition — are the same engine with a different DAG, authored on the visual template editor or in natural language. See docs/product-overview.md for the full capability map and docs/channel-assistant.md for the Slack teammate.
Architecture
Section titled “Architecture”CLI / API │ POST /api/v1/work-requests ▼Fastify Gateway ──────────────────▶ Temporal Server │ │ │ PostgreSQL 18 + pgvector │ Task Queue │ (state, memory, tokens) ▼ │ Temporal Worker │ │ │ │ RunnableWorkflow (generic interpreter over a JSON WorkflowSpec) │ │ walks step / set / cond / signal / terminate / fanOut nodes │ │ └── dispatches to activities below │ │ │ ├── validateContext (Context Validator agent) │ ├── planDecomposition (split work request into Subtask[]) │ ├── executeImplementation (Implementer agent + DinD TDD loop; │ │ takes optional Subtask for per-branch fan-out) │ ├── runLint / Typecheck / Tests / Build / VulnScan / PerfBench (quality gates) │ ├── executeGateFixImplementation (fix-loop for failed gates) │ ├── runReviewNetwork (Security / Domain / Performance agents) │ ├── executeReviewFixImplementation │ ├── mergeBranches (fast-forward subtask branches into the feature branch) │ ├── createOrUpdatePullRequest (GitHub API) │ ├── fetchCILogs + executeCIFixImplementation (self-healing on CI failures) │ ├── ⏳ await humanMergeSignal │ └── commitToMemory (pgvector lesson embedding) │ └── POST /api/v1/webhooks/git (GitHub merge webhook → humanMergeSignal) POST /api/v1/webhooks/ci (CI check_run webhook → ciPipelineSignal)For multi-repo epics, EpicOrchestratorWorkflow decomposes the request into per-repo child RunnableWorkflows using the Planner agent and runs them with dependency-graph scheduling. Each child runs the team’s configured workflow spec (default: ticket-to-merge@v1). See docs/architecture.md for the spec schema, node types, and step catalog.
Tech stack
Section titled “Tech stack”| Layer | Technology |
|---|---|
| HTTP API | Fastify 5.12 + Zod 4 validation |
| Orchestration | Temporal 1.31 (server + admin-tools + ui) + @temporalio/* SDK 1.24 |
| Agents | Mastra 1.73 + Vercel AI SDK 7 (default claude-opus-5-5) |
| Database | PostgreSQL 18 + pgvector (Prisma 7.10) |
| Embeddings | OpenAI text-embedding-3-large (1536d) |
| Workspace isolation | Docker-in-Docker |
| Observability | OpenTelemetry → Grafana LGTM (grafana/otel-lgtm:0.34.0) |
| Web dashboard | Next.js 16 + React 19 + Tailwind CSS 4 + TanStack Query 5 |
| Language | TypeScript 7 (strict mode, Yarn 4.18 monorepo) |
| Tests / Lint+Format | Vitest 4 / Biome 2.5 |
Prerequisites
Section titled “Prerequisites”- Node.js ≥ 26.0.0
- Yarn 4 via corepack (
npm install -g corepack && corepack enable) — Node 26 no longer bundles corepack - Docker + Docker Compose
Local development
Section titled “Local development”# 1. Install dependenciesnpm install -g corepack && corepack enable && yarn install
# 2. Configure environmentyarn env:setup init# Creates .env from .env.example and generates the required secrets —# CONFIG_ENCRYPTION_KEY, BETTER_AUTH_SECRET, JWT_SECRET, SEED_ADMIN_PASSWORD# (and GARAGE_RPC_SECRET for the bundled object store). After pulling a change to# .env.example, `yarn env:setup sync` carries your values over; `yarn env:setup check`# validates the file.# Optionally GITHUB_TOKEN / GITHUB_WEBHOOK_SECRET as bootstrap fallbacks.# LLM provider keys and model picks are NOT env vars — add them via the# dashboard at /models after starting the gateway+web. See# docs/model-configuration.md for the bootstrap flow.
# 3. Start infrastructure (Postgres, Temporal + UI, Garage)# Garage starts only when COMPOSE_PROFILES includes objectstore (set by default in .env.example).yarn docker:infra:up
# 4. Set up the database# db:deploy (`prisma migrate deploy`) applies the committed migrations verbatim.# Not db:migrate: `migrate dev` reads the hand-written HNSW/CHECK DDL as drift.yarn db:deploy && yarn db:generate && yarn db:seed
# 5. Start the gateway and dashboard (two terminals)yarn dev:gateway # Terminal 1 → http://localhost:8080yarn dev:web # Terminal 2 → http://localhost:3000
# 6. Sign in as admin@auto-swe.local and add model credentials at# /models (anthropic for the agents, openai for embeddings).# The worker refuses to start until they exist.
# 7. Start the workeryarn dev:worker # Terminal 3The full walkthrough, through connecting a repository and starting a first run, is in
docs/quickstart.md.
Temporal Web UI is available at http://localhost:8233 (local dev only: it sits behind the temporal-ui Compose profile, which yarn docker:* and just enable and production does not).
For production deployment, see docs/deployment.md — end-to-end runbook covering env vars, DB + Temporal setup, image build, service layout, smoke test, day-2 ops, backup, and a hardening checklist. Upgrading a deployment that ran earlier code: read docs/upgrade-notes.md for fixed defects that need you to rotate a credential or clean up logs.
Start a workflow
Section titled “Start a workflow”Four equivalent entry points — pick the one that fits the workflow:
-
Web dashboard (recommended) — open
http://localhost:3000, sign in, and click + New request. Pick a workflow template (for example the default,ticket-to-merge), fill the inputs it asks for, and run it. The run appears in Active runs immediately. -
Slack — ask the channel teammate in a channel it is installed in, or use the slash command. It also posts run progress and HITL prompts back into the channel. See
docs/slack-app-setup.md. -
CLI (
auto-swe) — exportAUTO_SWE_TOKEN(mint one at Settings → API tokens) and run the CLI’sworkflowssubcommands. Seepackages/cli/README.md. -
Raw HTTP — useful for scripting / CI. Use a PAT (mint one at Settings → API tokens) as the bearer:
Terminal window TOKEN=<your-PAT-from-Settings → API tokens>curl -X POST http://localhost:8080/api/v1/work-requests \-H "Authorization: Bearer $TOKEN" \-H 'Content-Type: application/json' \-d '{"externalTicketId": "JIRA-42","description": "Add a GET /health endpoint that returns { status: ok }","repoIds": ["<repo-uuid>"],"budgetTier": "STANDARD"}'
budgetTier controls the LLM token budget: STANDARD (2M/500K tokens), LARGE (8M/2M), EPIC (20M/5M). The workflow is terminated with BUDGET_EXCEEDED if the limit is breached.
For multi-repo changes, use Epics (the Epics type under Requests in the dashboard, or POST /api/v1/epics) — the Planner agent decomposes the brief into per-repo child runs.
Monitor
Section titled “Monitor”# Dashboard — KPIs, recent activity, "needs attention" queueopen http://localhost:3000
# Full run history with filtersopen http://localhost:3000/runs
# Temporal Web UI — real-time workflow state, history, signalsopen http://localhost:8233Or via the API:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/workflowscurl -H "Authorization: Bearer $TOKEN" 'http://localhost:8080/api/v1/workflow-runs?status=RUNNING'Environment variables
Section titled “Environment variables”| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
TEMPORAL_ADDRESS |
Yes | Temporal server address (default: localhost:7233) |
CONFIG_ENCRYPTION_KEY |
Yes | AES-256-GCM key (base64-encoded 32 bytes) for encrypting provider_credentials.api_key_ciphertext. Generate with openssl rand -base64 32. |
GITHUB_TOKEN |
Bootstrap⁵ | GitHub PAT with repo scope — env var is a bootstrap fallback; the DB value set at /admin/integrations → GitHub takes precedence |
GITHUB_WEBHOOK_SECRET |
Bootstrap⁵ | Secret for verifying GitHub webhook signatures — same DB-primary rule as GITHUB_TOKEN |
JWT_SECRET |
Yes¹ | Secret for HS256 JWTs (used when JWT_PRIVATE_KEY_PATH is unset — default for Docker Compose) |
JWT_PRIVATE_KEY_PATH |
Optional¹ | Path to RSA private key. Setting this switches JWT signing to RS256 |
JWT_PUBLIC_KEY_PATH |
Optional¹ | Path to RSA public key. Required when using RS256 |
BETTER_AUTH_URL |
Prod² | Gateway base URL used as the OAuth callback origin |
BETTER_AUTH_SECRET |
Yes² | ≥32-char secret for signing better-auth session cookies; yarn db:seed needs it too |
SEED_ADMIN_PASSWORD |
Seed | Password for the seeded admin user; yarn db:seed refuses to run without it |
SEED_ADMIN_EMAIL |
Optional | Override for the seeded admin email (default: admin@auto-swe.local) |
PORT |
Optional | Gateway HTTP port (default: 8080) |
CORS_ORIGIN |
Optional | Comma-separated browser origins; first entry is the better-auth client origin |
PUBLIC_URL |
Optional | Public base URL of the gateway used for the Slack OAuth callback |
DEFAULT_TEAM_SLUG |
Optional | Slug new better-auth sign-ups join automatically (default: default) |
NEXT_PUBLIC_API_URL |
Web | Gateway URL the web bundle calls (default: http://localhost:8080) |
NEXT_PUBLIC_APP_VERSION |
Web | Version label shown in the login page footer |
NEXT_PUBLIC_SUPPORT_URL / NEXT_PUBLIC_SUPPORT_EMAIL / NEXT_PUBLIC_SUPPORT_SLACK |
Web (optional) | Support contact shown on the sign-in, error and not-found screens and in the sidebar; omit all to hide it (see docs/configuration.md §8) |
RESEND_API_KEY |
Optional³ | Resend HTTP API key for magic-link email delivery |
SMTP_HOST / SMTP_PORT |
Optional³ | SMTP transport for magic-link email (alternative to Resend) |
SMTP_USER / SMTP_PASS |
Optional³ | SMTP auth credentials (omit for unauthenticated relays) |
AUTH_FROM_EMAIL |
Optional³ | From address for magic-link / verification emails |
GITHUB_CLIENT_ID |
Optional⁴ | GitHub OAuth client id (enables “Continue with GitHub”) |
GITHUB_CLIENT_SECRET |
Optional⁴ | GitHub OAuth client secret |
GOOGLE_CLIENT_ID |
Optional⁴ | Google OAuth client id (enables “Continue with Google”) |
GOOGLE_CLIENT_SECRET |
Optional⁴ | Google OAuth client secret |
SLACK_CLIENT_ID |
Optional | Slack OAuth app credentials |
SLACK_CLIENT_SECRET |
Optional | Slack OAuth app credentials |
SLACK_SIGNING_SECRET |
Optional | For verifying Slack interactive webhook signatures |
SLACK_BOT_TOKEN |
Optional | Bot token (xoxb-…) for posting run notifications |
BRANCH_PREFIX |
Optional | Git branch prefix (default: auto) |
GITHUB_URL |
Optional | Override for GitHub Enterprise Server |
GITHUB_API_URL |
Optional | Override for GitHub Enterprise Server API |
OTEL_EXPORTER_OTLP_ENDPOINT |
Optional | OTLP/gRPC endpoint (:4317) for traces, metrics + logs (set when running yarn docker:app:up) |
ARTIFACT_S3_* |
Optional | S3-compatible artifact store; falls back to Postgres-inline when unset. yarn docker:infra:up ships a Garage container at localhost:9000 that provisions its key and bucket on first boot, or point it at AWS S3 / R2 / B2 instead (see .env.example) |
¹ JWT auth has two modes: HS256 (default — set JWT_SECRET) or RS256 (set JWT_PRIVATE_KEY_PATH + JWT_PUBLIC_KEY_PATH).
² Required everywhere except yarn dev:gateway, which sets NODE_ENV=development and may fall back to a dev-only placeholder. The seed’s better-auth step and every Docker image run without that, and refuse the placeholder.
³ Magic-link email transport. Choose one: SMTP (SMTP_* + AUTH_FROM_EMAIL) or Resend (RESEND_API_KEY + AUTH_FROM_EMAIL). Without either, links print to gateway stdout (dev only).
⁴ OAuth providers — the matching login button is hidden when its env vars are unset. See docs/oauth-setup.md for the full setup.
⁵ GitHub credentials are DB-primary: configure them at /admin/integrations → GitHub after first boot (resolveGitHubConfig() falls back to the env vars only when no DB row exists). One of the two must be configured somewhere for the worker/webhooks to function; GitHub App auth is also available (see docs/github-app-setup.md).
Provider API keys (Anthropic, OpenAI, Google, OpenAI-compatible) and per-role model selection are NOT env vars — they live in the database and are managed at /models. See docs/model-configuration.md for the bootstrap flow and day-2 operations.
See .env.example for the full annotated template, including LLM provider selection, per-model price overrides, and embedding configuration.
Commands
Section titled “Commands”yarn build # Build all packagesyarn test # Run all tests (Vitest)yarn lint # Lint + format check (Biome)yarn lint:fix # Auto-fix safe lint issues + formatyarn docs:check # Fail on stale doc claims or broken doc linksyarn migrations:check # Apply migrations to a throwaway Postgres (needs Docker) and diff against schema.prismayarn db:deploy # Apply the committed Prisma migrationsyarn db:migrate # Create a new migration from a schema.prisma change (dev only)yarn db:generate # Regenerate Prisma clientyarn db:seed # Seed admin user + sample repositoryyarn dev:gateway # Gateway in watch modeyarn dev:worker # Worker in watch modeyarn dev:web # Next.js dashboard (port 3000)yarn docker:infra:up # Start infra services (postgres + temporal + temporal-ui + garage). Garage starts only when COMPOSE_PROFILES includes objectstore. Observability (Grafana/OTel) starts with yarn docker:app:up.yarn docker:infra:down # Stop infra servicesyarn docker:app:up # Start everything (infra + app)yarn docker:app:down # Stop everythingyarn docker:app:logs # Tail logs (infra + app)yarn docker:app:build # Rebuild app imagesWith just installed, just infra-up, just dev-up, just prod-up (and the matching -down/-logs/-ps recipes; just --list) wrap the same compose stacks.
Project structure
Section titled “Project structure”packages/├── shared/ # Prisma schema, DB client, shared types, workflow engine (spec + interpreter)├── gateway/ # Fastify 5.x HTTP API (auth, RBAC, routes, webhooks)├── worker/ # Temporal worker, Mastra agents, activities├── web/ # Next.js 16 web dashboard (App Router, React Flow template editor)├── cli/ # auto-swe binary — thin REST client over the gateway (+ local bundle authoring)└── sdk/ # @auto-swe/sdk — bundle authoring SDK (defineAgent/Skill/Template, signBundle)See AGENTS.md for full conventions, critical implementation notes, and design decisions.
Key design decisions
Section titled “Key design decisions”| Decision | Rationale |
|---|---|
| Docker, or Kubernetes pods | Docker needs no cluster; WORKSPACE_BACKEND=kubernetes runs agent workspaces as pods |
| Temporal.io for orchestration | Durable execution — workflows survive crashes and wait days for human signals |
| Human-governed merges | By default nothing merges a PR; the one opt-in path is per-repository native auto-merge, see docs/pr-evaluation.md |
| PAT or GitHub App | PAT for simplicity; GitHub App (short-lived installation tokens) for production — see docs/github-app-setup.md |
| pgvector for agent memory | Semantic similarity search surfaces relevant past lessons into agent context |
| Mastra + Vercel AI SDK | Mastra agents bind models through the AI SDK provider adapters; getModel(key, ctx) picks the provider from DB config, so agent code never imports a provider directly |
© 2026 Jorge Barnaby
Source-available under FSL-1.1-MIT