Worktree per ticket
Main is never touched by an agent.
label-driven autonomy
A label-driven autonomous ticket pipeline for any GitHub repository.
npx skills add mikeroysoft/factory
uv tool install git+https://github.com/mikeroySoft/factory # stable
uv tool install git+https://github.com/mikeroySoft/factory@main # latest
uv tool install git+https://github.com/mikeroySoft/factory@v0.3.0 # pinned
$ factory dispatch
claimed ready-for-agent → agent/<n>
attempt worker → factory gate
gate PASS
pr-opened agent/<n>
review APPROVE
merged main
pipeline
GitHub labels, comments, and PRs plus a gitignored .factory/ directory. The dispatcher is stateless and safe to re-run.
control surfaces
Runs on your machine, on a systemd timer, with the agent CLIs you already have. No SaaS, no runner fleet.
Main is never touched by an agent.
Any [[gate.check]] commands, exclusive lock for GPU or serial checks, and per-check timeouts.
Path:line citations and a hard VERDICT.
Gate, reviewer, GitHub CI, and freshness. Fail-closed.
ready-for-human with the reason, log path, and handoff note.
State is GitHub plus a gitignored .factory/ directory. Safe to re-run.
how a ticket moves
Issues labelled ready-for-agent move through a worktree, your deterministic checks, a second model, GitHub CI, and freshness against main.
Deterministic lint rejects bodies under 80 chars or without acceptance criteria; the model then picks one of four labels. wontfix is only ever proposed.
The dispatcher re-reads the issue, assigns itself, takes a per-ticket flock, and creates .factory/wt-<n> on agent/<n>.
The worker gets the issue, comments, standing instructions, and on retries the previous gate report or reviewer findings.
Conflict markers, your [[gate.check]] list in order, then a leak-scan of added lines. Exclusive checks serialise on a host-wide lock.
The reviewer sees the diff, issue, and gate report. REVISE returns to the worker; APPROVE adds factory-approved.
One PR per pass, requiring gate PASS, factory-approved, green GitHub checks, and a head containing the current main tip.
Unresolved work gets ready-for-human, loses its assignee, and gets a comment with the reason and worker log path. The worktree stays for forensics.
quick start
Linux, Python >= 3.11, git, and gh. No Python dependencies.
cd your-repo
factory init # .factory.toml, .gitignore, issue template, labels
$EDITOR .factory.toml # put your real test/lint commands in [[gate.check]]
factory doctor # tools, auth, remotes, model endpoint
factory install --dashboard # systemd user timer every 10 min + dashboard on :8765
factory triageLabel needs-triage issues via the local model.
factory dispatchOne stateless pass: sync → merge stage → claim → work → gate → PR → review.
factory gateRun the deterministic gate in the current worktree and write a Markdown report.
factory statsTicket table: attempts, review rounds, hours to merge; --json.
factory learnDistill lessons from finished tickets into .factory-lessons.md.
factory dashboardLocal ops UI.
factory doctor / init / installOnboarding.
configuration
Bring your own reviewer model and, optionally, a local OpenAI-compatible model for triage.
[workers] # ticket label -> argv
default = ["omp", "-p", "--cwd", "{cwd}", "@{prompt}"]
chore = ["droid", "exec", "-f", "{prompt}", "--auto", "medium", "--cwd", "{cwd}"]
[review]
command = ["codex", "exec", "{prompt}"]
[[gate.check]]
name = "tests"
run = ["cargo", "test", "--workspace"]
exclusive = true
[triage]
url = "http://127.0.0.1:11434/v1/chat/completions"
model = "qwen3:30b"