Skip to content

Software factory ​

The software factory turns a piece of work into a pull request without you sitting in the loop. You send it an open issue, a red CI run on the default branch, or a task you type. An agent fixes it in its own clone of the repository, the repository's own check command verifies the change, a second agent reviews it, and the factory pushes a branch and opens a pull request (a merge request on GitLab) as a dedicated factory account. Every change gets a risk tier, T0 to T3, that decides how much of your attention it needs. If CI fails it loops on the failures, up to a capped number of attempts, then stops and waits for you. You review and merge. The factory merges nothing on its own unless you turn on auto_merge_t0 for trivial changes.

It works on GitHub, Azure DevOps and GitLab. Pull request, merge request and repository names below follow the forge you use.

The factory is free on the Personal plan, with 2 agents running at once.

Early access

The factory ships behind the factory.core release flag, which is off by default. Turn it on per node in Settings > Labs. See Feature Flags.

Set up ​

Setup has three required parts. Do them once per repository the factory may change. To give agents a full machine of their own (root, Docker, services), also set up a factory machine.

1. Create the factory OS user ​

On the node that hosts the factory account, create an operating system user named clusto-factory. It is a regular account with a shell, no password, and no git or GitHub credentials. If the repository needs pnpm or node to build and test, make them available to that user.

The agent runs as this user, so it cannot read your own files or credentials.

2. Add a factory account ​

The steps below are for GitHub. For Azure DevOps or GitLab see the end of this section.

  1. Open Settings > GitHub and add a separate GitHub account for the factory. Do not reuse your own.
  2. Set its Purpose to Factory.
  3. Enter the Organization the factory works in. The field appears when the purpose is Factory and is required for a factory account. One factory account serves one organization.
  4. Give it a fine-grained token scoped only to the repositories the factory may change, with these permissions:
PermissionAccess
ContentsRead and write
Pull requestsRead and write
IssuesRead and write
ActionsRead and write
ChecksRead
Commit statusesRead
MetadataRead

Workflows stays off, so the factory can never change a repository's CI. Make the token's resource owner the same organization, so GitHub enforces the same boundary as Clusto does. Create token on GitHub, under the token field, opens GitHub's new-token page with all of this filled in, the organization as resource owner included; pick the repositories, generate it and paste it back.

Your personal accounts stay on the Browse purpose and are never used by the factory.

Organization binding ​

The factory only starts work in repositories owned by the factory account's organization. The owner is matched case-insensitively, and the node's daemon enforces it, so a repository from another owner is refused even if it is sent by other means. The Send to factory repository picker lists only that organization's repositories.

Azure DevOps and GitLab accounts ​

A factory account on another forge works the same way, bound to its own boundary instead of a GitHub organization:

ForgeRepository nameBound to
GitHubowner/repositorythe account's organization
Azure DevOpsorganization/project/repositorythe account's Organizations list
GitLabgroup/.../projectthe account's Group
  • Azure DevOps. Add the account in Settings > Azure DevOps with a personal access token, set its Purpose field to Factory, and list the organizations it works in. The list must not be empty for a factory account: repositories outside it are refused. The factory finds its pull request by source branch, shows the tier as a label, reads PR builds and their failed task logs, retries them, and completes the pull request with the account's default merge type. Work items are the issues you can send. See Azure DevOps.
  • GitLab. Add the account in Settings > GitLab, set its purpose to Factory and its Group to the full path it works in (acme or acme/platform; subgroups are included). A repository must sit inside that group. The factory opens merge requests, labels them with the tier, reads pipelines and failed job traces, retries them, and merges with the expected head commit. GitLab accounts sit behind the gitlab.accounts flag in Settings > Labs (see Feature Flags). See GitLab.

The token stays on the account's host node on every forge, and the factory machine and the agent never see it.

3. Add factory.toml to the repository ​

Add .clusto/factory/factory.toml to the repository's default branch. Only check is required; this is the smallest useful file:

toml
check = "pnpm check"

A complete example with every key and a recipe:

toml
setup = "pnpm install --frozen-lockfile"
check = "pnpm check"
max_check_attempts = 2
max_ci_fix_attempts = 3
max_usd = 5.0
model = "sonnet"
review = true
review_model = "opus"
max_open_prs = 3
max_diff_lines = 600
intake_label = "factory"
plan = false
auto_merge_t0 = false
deploy = "pnpm deploy:prod"
holdout_pass_rate = 1.0
instructions = "Follow the conventions in AGENTS.md. Never edit generated files."

[recipe.feature]
description = "A new feature: plan first, review with a stronger model"
plan = true
review_model = "opus"
max_usd = 12.0
max_diff_lines = 900
instructions = "Add or update documentation for every user-facing change."
labels = ["feature", "enhancement"]

[recipe.cleanup]
description = "Small refactors and chores"
plan = false
review = false
max_usd = 2.0
labels = ["chore"]
KeyDefaultMeaning
check(required)The command that verifies a change, for example pnpm check. Exit 0 passes.
setuppnpm install --frozen-lockfileRuns once in the clone before the agent starts.
max_check_attempts2How many times the agent may retry after the check command fails.
max_ci_fix_attempts3How many times the agent may push a fix after CI fails on the pull request.
max_usd5.0Agent spend per item before it stops and waits for you. Must be above 0.
modelthe agent CLI's defaultThe author's model.
reviewtrueA clean-context reviewer reads every change before the pull request opens.
review_modelthe agent CLI's defaultThe reviewer's model. A different model than the author's catches more.
max_open_prs3Open factory pull requests this repository may have at once; further items wait in the queue. At least 1.
max_diff_lines600Changed lines above which the change is sent back once to shrink, then to you. At least 1.
intake_labelunsetOpen issues carrying this label start items on their own (GitHub only). Unset, you start every item.
planfalseThe agent writes a plan first. A T2 or T3 item waits for you to approve it.
auto_merge_t0falseT0 items merge themselves once CI is green. See Auto-merge.
deployunsetA command that runs when you press Deploy on a merged item. See Auto-merge.
holdout_pass_rate1.0Share of holdout scenarios that must pass, from 0 to 1.
instructionsunsetExtra instructions added to every brief of this repository.

A key outside this list is an error, so a typo fails loudly instead of being ignored.

Recipes ​

A [recipe.<name>] table is a profile for one kind of work. The name is 1 to 40 lowercase letters, digits and -. Every key is optional and overrides the base value for items that use the recipe:

KeyMeaning
descriptionShown in the Send work dialog.
plan, reviewTurn the plan step or the reviewer on or off.
model, review_modelModels for this kind of work.
max_usd, max_check_attempts, max_ci_fix_attempts, max_diff_linesBudgets and the size limit.
instructionsAdded after the base instructions in every brief.
labelsIssue labels that select this recipe when intake starts an item. The first matching recipe wins.

Name a recipe in the optional Recipe field of the Send work dialog, or let intake pick one from the issue's labels. With no recipe the base values apply.

The file is read only from the default branch, so an agent's own branch can never change its rules.

4. Optional: add risk.toml ​

.clusto/factory/risk.toml tells the factory which parts of your repository are riskier than they look. It is also read only from the default branch. See Risk tiers.

Factory machine ​

By default the agent runs as the clusto-factory user on the node itself. For work that needs more, such as installing packages, running Docker or keeping services running between items, give the factory its own machine: an Incus system container on the node.

Map the OS user to the container in the node's config:

toml
[sandboxes]
clusto-factory = "clusto-factory"

The key is the OS user and the value is the Incus container name. Leave the section out and nothing changes. Edits apply on config hot reload to processes started after the change; running processes are not moved.

When a user has a sandbox, every process Clusto runs as that user runs inside the container instead of on the host, as the same user (same uid and gid, with the home directory mounted at the same path): the factory's setup and check commands, the agent chat, git, and terminal sessions. Inside the container the agents can use sudo, run Docker, install packages and keep services running. Root in the container is not root on the host.

  • Shell on the factory machine. Open a terminal session on that node with run as clusto-factory and you get a shell inside the container. Use it to look around, install tools or sign the agent CLI in. Signing in happens inside the container as that user, and the login stays in that home.
  • The token stays out of reach. The node's daemon and the factory's GitHub token stay on the host. The container cannot read them, so an agent cannot push to your default branch or merge its own pull requests.
  • Internet only. The container's network reaches the internet. Private address ranges, the host's daemon port and the tailnet are blocked.

Requirements:

  • a Linux node, with the daemon running as root,
  • Incus installed on the node, with a container prepared for the user,
  • the user must exist on the host and in the container with matching user and group ids.

To move the factory to another machine, move the factory account's host node (see GitHub), then prepare the container on that node and add the [sandboxes] entry there. The GitHub token is entered again on the new host. See the configuration reference for the key.

Send work ​

Open the Factory view in the navigation and press Send work. Pick one of:

  • an open issue (GitHub issues, Azure DevOps work items or GitLab issues),
  • a red CI run on the default branch,
  • a task you type.

Choose a recipe in the optional Recipe field to use one of the repository's [recipe.<name>] profiles.

You can also send an issue from the GitHub view: on an issue page, Send to factory appears when a factory account can reach the repository.

The board ​

The Factory view has four tabs: Board, Inbox, Holdouts and Metrics. The Board has these lanes:

LaneMeaning
Waiting on youA plan to approve, a pull request ready for review, or the factory stopped and needs a decision.
Agent workingAn agent is planning, writing, reviewing or fixing code.
CI and holdoutsA pull request is open and checks or holdout scenarios are running.
QueuedWaiting for a free agent slot. At most 2 agents run at once.
MergedYou merged the pull request.
StoppedAbandoned, or out of attempts or budget.

Open an item to see its state, why it is waiting, the pull request link, the agent's chat, the check and CI fix attempts, review rounds and spend.

Item actions:

  • Take over chat - stop the factory driving the item so you can type in its chat yourself. The item moves to Waiting on you.
  • Retry - start again with fresh attempts and a fresh budget. On an item you took over, this hands it back to the factory.
  • Abandon - stop the item. An open pull request stays open.
  • Address review - send the new review comments on the pull request to the agent so it pushes a follow-up.

Risk tiers ​

Every change gets one of four tiers. The tier decides how much of your attention it needs, and it is shown as a badge on the item, as a label on the pull request (factory:t0 to factory:t3) and as a Risk tier line at the top of the pull request description, with the reasons and the reviewer's explain-back card below it.

TierNameWhat it means
T0Auto-mergeNo behavior change, such as documentation or lock files only. May merge itself once the repository allows it.
T1Owner skimThe default: a small tested change. AI review plus your skim.
T2Line reviewRead the diff line by line.
T3Designed firstYou design first; the factory builds only what you approved.

The factory takes the highest tier any signal reaches:

  • Built-in T3 paths: CI and deploy definitions (.github/workflows/**, .github/actions/**, .gitlab-ci.yml, azure-pipelines.yml, .azure-pipelines/**), Dockerfile, any migrations folder and the factory's own .clusto/** files.
  • Your risk map: .clusto/factory/risk.toml, below.
  • Diff signals: a changed dependency manifest, more than 400 changed lines or 15 files (T2); SQL schema changes and cryptography (T3); new environment or secret references, new routes or endpoints and new network calls (T2); code changed without a test change (one tier up); and more assertions removed than added in tests (T2).
  • The agent and the reviewer each rate the change too. They can only raise a tier, never lower it.

A change that touches only documentation or lock files starts at T0; anything else starts at T1.

risk.toml ​

Add .clusto/factory/risk.toml to the default branch to mark paths that matter in your repository. Each [[rule]] names glob patterns, the tier a match forces and an optional reason shown on the pull request:

toml
[[rule]]
paths = ["src/billing/**", "src/auth/**"]
tier = "t3"
why = "money and identity"

[[rule]]
paths = ["src/api/**"]
tier = "t2"

tier is t0 to t3. Each rule needs at least one path. why defaults to the first matching path. The map can only raise a tier.

Item flow ​

An item moves through these states. Each shows on its card in the Factory view.

StateWhat happens
QueuedWaiting for a free agent slot, or for an open pull request slot (max_open_prs).
Setting upThe clone is made and setup runs.
PlanningWith plan = true, the agent writes a plan before any code.
Plan to approveThe agent rates its own plan. A plan it rates T2 or T3 waits for you; any other goes ahead on its own.
Coding, Checking, Fixing checkThe agent works, your check runs, and failures go back to the agent up to max_check_attempts.
AI reviewA clean-context reviewer reads the task and the diff. A P1 finding goes back to the author within the check budget. The reviewer must not touch the clone; if it does, the item stops.
Opening PRThe branch is pushed and the pull request opens with the tier label.
CI running, Fixing CICI runs. Failed jobs are re-run once to rule out a flake before a fix attempt counts. Real failures go back to the agent up to max_ci_fix_attempts.
Holdouts runningWith holdout scenarios set, they run after green CI.
Ready to reviewGreen. You review and merge.
Addressing reviewThe agent answers your review comments.
Needs youStopped: attempts, budget or size limit used up, or a stalled turn.

Other guards:

  • Size gate. A change above max_diff_lines goes back to the agent once to shrink. If it is still too large it goes to you.
  • Stalled turns. An agent turn that has not finished in 60 minutes counts as stuck and the item goes to you.
  • Open pull request cap. Items above max_open_prs for a repository wait in the queue, so the factory never produces more than you can review.
  • Label intake. With intake_label set, open issues carrying that label start items on their own. An issue the factory has ever worked on is not started again. GitHub only; on Azure DevOps and GitLab you send work yourself.

The Inbox ​

The Inbox tab collects everything that waits on you, most urgent first, and is built to work from a phone. The tab shows a count.

  • Plans to approve. Open the item, read the agent's plan and press Approve plan. The optional Notes for the agent field changes the plan before it codes. The agent writes no code until you approve.
  • Ready to merge. T3 and T2 first. Each entry shows the reviewer's explain-back card (a walkthrough and questions meant to test your understanding) and the holdout result. Merge asks you to confirm, naming the pull request and its tier. The factory account merges it; a merge cannot be undone from Clusto.
  • Stopped for you. The reason is shown. Retry starts again with fresh attempts and a fresh budget; Abandon stops the item and leaves any open pull request open.
  • Sampling audits. One in 20 merged T0 and T1 items is picked for a second look, because those were merged without a line review. Read the change and press Audit clean or Audit finding. The results feed the Audit finding rate on the Metrics tab.

Holdout scenarios ​

A holdout scenario is an acceptance check you write for a repository that the agent never sees. After CI is green, the factory runs every scenario against the change. A miss goes back to the agent by scenario name only, so it can fix the behavior without being able to read or game the test.

Open the Holdouts tab, pick a repository and press Add scenario. A scenario is a name (up to 120 characters) and a shell script (up to 64 KiB) that exits 0 when the change behaves correctly. It runs as root in a fresh clone of the change after the repository's setup, with a 10 minute limit per scenario.

  • Scripts are stored on the host node only, never replicated to other nodes and never quoted into an agent's brief, a log or a notification.
  • Each run happens in a throwaway copy of the factory machine's clean snapshot, with the agent's home folder removed. The agent's own container is not touched and shares nothing with it. This means holdouts need a factory machine: a node with a [sandboxes] entry for clusto-factory and a clean snapshot of that container.
  • holdout_pass_rate sets how many scenarios must pass (default all of them). With no scenarios, the step is skipped.

Auto-merge and deploy ​

  • auto_merge_t0 is off by default. When on, a T0 item merges itself once CI is green and any holdouts pass. Turn it on only after the sampling audit has shown a low finding rate on your repository.
  • Deploy. A merged item shows Deploy when the repository's deploy command is set. Pressing it asks for confirmation, then runs the command on the host as the agent user in a fresh clone of the default branch. Deploying is outward-facing, so it is always started by you and never by the factory.

Factory chats ​

Each item's agent runs in a chat. The item pane in the Factory view shows it in place: pick the Chat tab under the item's actions to read and reply without leaving the board or the Inbox. Changes shows the pull request's description, checks and diff in the same spot, and Details the item's facts and verification. The tab you pick stays picked as you move between items.

Factory chats stay out of the Chats list so they do not crowd your own conversations. Use Show factory chats at the bottom of the Chats tree (or the command palette) to list them on this device. Shown factory chats have a factory icon.

While the factory drives an item, its chat is read-only: a banner above the composer says what the factory is doing, and the node refuses a message typed into the chat from any client. Take over in the banner hands you the chat; Hand back returns it to the factory. Stopping a running response stays available at any time.

Metrics ​

The top of the view shows:

  • Merged - how many factory pull requests you merged.
  • Cost per merged PR - agent spend divided by merged pull requests.
  • Rework - the share of merged changes whose files changed again on the default branch within 21 days.
  • Agents running - agents working now, out of the 2 allowed.

The Metrics tab goes further: open pull requests, items waiting on you, CI fixes per merge, abandoned items, merged items by tier, the sampling audit finding rate, and spend per repository and per week. Watch the audit finding rate before you enable auto_merge_t0.

Spend is the figure the agent CLI reports. On a subscription plan it is notional, not money charged to you.

Safety ​

  • The agent runs as the unprivileged clusto-factory user with no MCP servers, optionally inside a factory machine container.
  • The factory works only in repositories inside the factory account's organization, Azure DevOps organizations or GitLab group.
  • The agent never sees the forge token. Only the daemon pushes branches and opens pull requests.
  • The risk map, tiers and the reviewer can only raise a change's tier, never lower it, and a reviewer that touches the clone stops the item.
  • Holdout scenarios stay on the host and never reach the agent.
  • Deploy is started only by you; auto-merge is off until you turn it on.
  • Forge text such as issue bodies, logs and review comments is passed to the agent as quoted data, not as instructions.
  • The check command and limits come from the default branch only.
  • Every change reaches your default branch only through a pull request that you merge.

Notifications ​

The factory raises notifications when an item needs you, when a plan waits for your approval and when a pull request is ready. Like the GitHub alerts, you can mute them per device.

Clusto - remote AI coding agents over WebSocket.