Project guide for AI coding agents working in this repository.
The generic core of a PR preview platform and AI agent orchestration system. It receives GitHub webhooks, deploys PR previews via Docker Compose behind Traefik, integrates bidirectionally with Slack, and runs an LLM-powered agent that executes GitHub/Slack actions from natural language.
This repo contains the Go application, Docker Compose files, and a reusable Terraform module. Deployment-specific configuration (secrets, infrastructure state, operational tasks) lives in separate deployment repos created with mise run create-deployment <name>.
All automation goes through mise tasks. The Go app runs inside a Docker dev container — app-exec wraps commands to run inside it.
| Task | What it does |
|---|---|
mise run app-tests |
Unit tests with -race (go test -race ./internal/...) |
mise run app-build |
Compile Go binary to orchestrator-app/bin/server |
mise run app-quality |
go vet + gofmt check |
mise run app-dev |
Start hot-reload dev server (air) inside the dev container |
mise run app-setup |
Full bootstrap: build images, start compose, deps, tests, dev server |
mise run app-compose -- <args> |
Proxy to docker compose using docker-compose.dev.yml |
mise run app-exec <cmd> |
Run a command inside the app container (e.g. mise run app-exec go test ./internal/agent/...) |
mise run create-deployment <name> |
Scaffold a new deployment repo at ../productbuilding-deployment-<name>/ |
To run a single test or package:
mise run app-exec go test -race -run TestName ./internal/agent/domain/Infrastructure and operational tasks (infra-plan, deploy, ssh, switch-llm-provider, etc.) live in deployment repos, not here.
This repo is the generic core. Each deployment has its own repo (created via create-deployment) containing:
- Terraform root module (calls
infrastructure-mgmt/modules/orchestrator/from this repo via relative path) - Secrets (age-encrypted AWS creds, GitHub PATs, per-target configs)
- Operational mise tasks (deploy, ssh, infra-plan, instance management)
The Go app (orchestrator-app/) is organized into independent verticals, each with domain/, web/, infra/, and facade/ sub-packages:
- agent — LLM agent loop: router classifies intent → specialist executes (issue_creator, delegator, commenter, researcher, closer, event_narrator). Multi-provider backend (Anthropic, OpenAI-compatible) with optional fallback. Tools wrap GitHub and Slack actions via adapter pattern. Prompts are in
specialist_prompts.go. Scope boundary: the system's job ends at producing good PRs — it does NOT merge. When users approve, the delegator posts an approval summary; merging is a developer responsibility. - preview — Preview lifecycle: clone repo, run Docker Compose, track status in SQLite, health-check, report back on PR.
- github — Webhook receiver: parses/validates incoming PR/issue events, triggers preview or agent flows.
- slack — Slack Events API handler, @mention routing to agent, notification debouncing, thread tracking. See
internal/slack/domain/NOTIFIER.mdfor the two-lane event buffer design. - dashboard — Simple web dashboard.
- platform — Cross-cutting: config (env vars via
caarlos0/env), SQLite database + migrations, logging (slog), HTTP server with graceful shutdown, target registry. - featurecontext — Cross-cutting context assembly: gathers issue state, PR details, CI check runs, and preview status into a single
FeatureSnapshot. Used by the Slack notifier (to enrich notifications) and the Slack handler (to inject feature state into agent context). Adapters inadapters.gobridge the GitHub client and preview repository to consumer-side interfaces.
cmd/server/main.go constructs the full dependency graph explicitly — no DI framework. Config -> DB -> migrations -> registry -> feature assembler -> infra implementations -> domain services -> HTTP handlers -> server.
- OpenTofu: Reusable module at
infrastructure-mgmt/modules/orchestrator/(EC2, Traefik, Route53, IAM, per-target resources). Deployment repos call this module with their specific values. - Docker Compose: Production stack has Traefik reverse proxy + orchestrator on shared
preview-netnetwork. Dev stack mounts source and exposes port 8091. Deployment-specific values (domain, ACME email) are read from environment variables.
SQLite with migrations in orchestrator-app/migrations/ (embedded via embed.FS). Three tables: previews (PR preview state), slack_threads (GitHub<->Slack mapping), agent_conversations (conversation context).
- Adapter pattern: GitHub/Slack clients wrapped for agent tool consumption (
github_adapter.go,slack_adapter.go). Thefeaturecontextpackage uses the same pattern —GitHubIssueAdapter,GitHubPRAdapter, etc. bridge the GitHub client to consumer-defined interfaces. - Interface-based testing: Every external dependency is behind a domain interface. Tests inject mock implementations — no real Docker, GitHub API, Slack API, or network calls needed. See
TESTING.mdfor patterns and conventions. - Functional options:
ServiceOptionon preview service (e.g.WithSlackThreadChecker),HealthCheckerOptionon health checker (e.g.WithPollInterval,WithHTTPClient),AgentOptionon agent configuration. Follow this pattern when adding optional dependencies. - baseURL injection: Both the GitHub client and Slack client have a
baseURLfield that defaults to the production API URL but can be overridden for httptest servers in tests. All HTTP methods must usec.apiURL()(GitHub) orc.baseURL(Slack) — never hardcode API URLs. - Reconciliation on startup: Self-healing after re-provisioning.
Every external system integration is fully testable through deterministic mocks. No test in this codebase requires Docker, network access, or real API calls.
See TESTING.md for the complete guide: mock conventions, httptest patterns, how to write integration tests for the preview service, and how to add tests for new features.
All three checks must pass before committing:
mise run app-tests # Unit tests with race detector
mise run app-quality # go vet + gofmt
mise run app-build # Compile check