Skip to content

Repository files navigation

OpenFactory

Issue → agent → verified PR.

OpenFactory is a small, self-hosted software factory: an orchestrator that turns GitHub / Linear / Sentry issues into pull requests using coding agents (Claude Code or Codex) running inside ascii.dev Box sandboxes. Every issue gets its own disposable VM; every change is adversarially reviewed by a second agent in a second VM before a PR is opened. The orchestrator itself runs on a Box too — one factory box drives the whole fleet, and even intake triage is an agent on that box.

issue ──▶ triage ──▶ fork template box ──▶ plan ──▶ [human gate] ──▶ build + test
                                                                        │ stop (snapshot)
                    PR ◀── [human gate] ◀── verdict ◀── review in a fresh box
                    ▲                          │ fail
                    └── fix loop (resume) ◀────┘

Why Box makes this simple

  • Your template box: a box you set up over SSH — your stack, your logged-in devtools, your repo checkout. OpenFactory verifies it, snapshots it, and every run starts as a seconds-fast fork of that snapshot.
  • Verification in a fresh fork: the reviewer gets a pristine template fork plus the built branch as a git bundle — exact committed tree, zero setup, and it can't corrupt the builder's workspace. That same box pushes the PR, so a run needs two machines, not three.
  • Stop = pause: while a run waits for human approval, its box is stopped and costs nothing. resume brings the same filesystem back in seconds.
  • TTL as a dead-man's switch: every worker box gets a finite TTL that the orchestrator extends per phase. If the orchestrator dies, boxes self-stop — no runaway spend.
  • Snapshots as audit trail: finished runs keep their archived build and verify boxes. Wake one for an hour from the run page to poke at the exact tree, or read it cold with box snapshot tree/pull — no resume needed.
  • Events as transcripts: the run page replays what each agent actually did (prompts, responses, tool calls) from the platform's box-events API — for live and archived boxes, builder and reviewer.
  • Triage on the factory box: the intake gate is a tool-using agent prompted on the orchestrator's own box, with a local checkout of your repo to check signals against real code. It fails open on every error.

Quickstart

Requirements: the box CLI logged in, an API key, and a repo you want the factory to work on.

git clone https://github.com/kunalm2345/openfactory && cd openfactory
ASCII_API_KEY=box_… npm run bootstrap

The bootstrap script creates a factory box, installs OpenFactory as a systemd service, exposes the dashboard on a stable HTTPS URL, and prints the link (with a generated DASHBOARD_TOKEN). The dashboard's Welcome page walks you through the rest:

  1. GH_TOKEN — a fine-grained GitHub PAT (contents + pull requests: read/write) in the Box account environment on box.ascii.dev, so every box inherits it and gh just works.
  2. Agent credentials — connect Claude Code / Codex once in the ascii.dev dashboard (Agents tab).
  3. Template box — paste a box id (or create a plain box from Settings), SSH in, install your stack, clone your repo, then Finalize — that verifies the checkout and snapshots the box.

For local development: cp .env.example .env, set ASCII_API_KEY, and npm run dev — the dashboard is on http://localhost:3000.

Webhooks

Point your issue tracker at the running server:

Source URL Notes
GitHub POST /webhooks/github issues events; every opened issue on the configured repo is triaged. Set GITHUB_WEBHOOK_SECRET.
Linear POST /webhooks/linear Issue-created events. Set LINEAR_WEBHOOK_SECRET.
Sentry POST /webhooks/sentry Issue alerts become "investigate and fix" runs. Set SENTRY_CLIENT_SECRET.

The dashboard port is hosted publicly so webhooks can reach it — they verify their own signatures, and the UI itself is gated by DASHBOARD_TOKEN.

The run lifecycle

State What happens
TRIAGING A tool-using agent on the factory box decides whether the webhook signal deserves a run (duplicates, noise). Skipped signals land in SKIPPED with the reasoning; every failure path admits the run.
QUEUED Admission control: concurrency cap, Box plan limits, creation rate.
PROVISIONING Fork the template box; cut openfactory/<run>-<slug> from the base branch.
PLANNING Agent explores the repo, writes PLAN.md + PLAN_META.json.
AWAITING_PLAN_APPROVAL Box stopped (zero cost) until a human approves. Configurable: auto / manual / manual-for-large.
BUILDING Agent implements the plan, runs tests, commits. Never pushes.
SNAPSHOTTING Box stops; its final snapshot is the exact built tree. The branch is captured as a git bundle, and the full diff is pulled for the run page.
VERIFYING A verify box is forked from the pristine template and receives the bundle. A second agent (a different model family by default) reviews the diff, reruns tests, writes REVIEW.json. The box is then stopped and kept — it pushes the PR next, and its transcript stays readable.
FIXING On a failing verdict, the issue box resumes, gets the findings, and fixes them (bounded rounds), then re-verifies.
CREATING_PR The verify box resumes, resets the branch to the exact reviewed commit, and runs git push + gh pr create with plan and verdict in the body. (If that box is gone, a throwaway fork of the template does it instead.)
PR_OPEN The PR is the run's live artifact: the engine polls GitHub for mergeability and flips the run when the PR merges or closes. Merge from the dashboard, or Fix & merge when it conflicts with the base branch.
MERGING Fix & merge: the build box resumes, origin/<base> is merged into the branch (an agent resolves conflicts if needed), pushed, and squash-merged.
MERGED / FAILED / REJECTED / CANCELLED / SKIPPED The build and verify boxes stay archived as the run's audit trail (stopped, so free — wake either from the run page to inspect it). A PR closed without merging rejects its run.

Everything is driven by a compare-and-swap state machine over SQLite — the process can be killed at any point and resumes safely. A reconciler loop mirrors real box states, extends TTLs that would expire mid-phase, and reaps orphaned boxes. See docs/design.md for the full state machines, event catalog, and failure-handling rationale.

The template box is yours

OpenFactory never installs anything into your template box. You bring one (paste an id) or create a plain box from Settings, then set it up over SSH: install your stack, log into your devtools, clone the repo (gh repo clone). Env vars and secrets live in the ascii.dev dashboard; agent credentials under its Agents tab. Finalize runs mechanical checks (checkout exists, git can reach origin) and snapshots the box.

When a PR merges — from the factory or anywhere else — the template refreshes itself at the next quiet moment: resume → git pull --ff-only → snapshot. If a pull can't fast-forward, the job fails with the raw git output and you fix it the same way you built the box: over SSH.

Configuration

Env vars carry secrets only (ASCII_API_KEY, GH_TOKEN, DASHBOARD_TOKEN, webhook secrets); everything behavioral lives in the dashboard Settings page (stored in SQLite): the repository, a model + reasoning-effort choice per stage (triage / plan / build / review — the provider is derived from the model), the plan approval gate, review strictness (any / major / none), max fix iterations, concurrency, and all six prompt templates.

The hosted Settings page also shows the running commit. Update from main fetches and fast-forwards the factory checkout, verifies the build, and lets systemd restart the orchestrator. Local service files are preserved; a diverged branch or overlapping local edit fails loudly for manual repair.

Notes & caveats

  • Issue text is untrusted input to an agent that holds your GitHub token (boxes get the account env so gh works). Every opened issue is triaged, so don't point the factory at repos where strangers can open issues, and leave PR review as the human boundary. GitHub issues from repos other than the configured one are rejected at intake.
  • The triage agent runs on the factory box with tools, and it reads untrusted issue text (its prompt marks the body untrusted and forbids acting on it, but that is a prompt, not a sandbox). If your intake sources are public, weigh that trade.
  • One run per issue at a time; duplicate webhook deliveries are deduped.
  • Approvals never time out: a plan can wait for days — its boxes are stopped, so waiting is free.
  • The PR body carries the plan and verdict.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages