User guide: contents

The label pipeline: one label → plan + code → one pull request

The built-in flows epic-questions, issue-gitflow and release-daily form a pipeline that you drive with one label. You only step in for three things: answering questions (asked all at once, up front), approving risky plans, and merging the pull request. (Label names below are the ones from the example configuration; yours are whatever you set in the watcher.)

flowchart LR
    G["<b>Factory_go</b><br/>you add it (to one issue<br/>or a whole batch)"] --> Q["questions check<br/>for all new issues"]
    Q -->|questions| NI["Factory_needs_info<br/>you reply, or /defaults"]
    NI --> W
    Q -->|none| W["Factory_working<br/>Opus plans → Codex checks<br/>→ risk score"]
    W -->|"risk > 75, or label<br/>Factory_review_plan"| A["Factory_waiting<br/>you reply /approve or /reject"]
    A -->|/approve| C
    A -->|/reject + what to change| W
    W -->|risk ≤ 75| C["Sonnet codes, tests,<br/>2 Codex reviews, docs"]
    C -->|"merged into develop<br/>(or the rolling PR)"| D["Factory_done"]
    C -->|3 failed fix rounds or an error| E["Factory_ERROR<br/>reason on the issue"]
    E -->|you remove the label| W
    D --> M(["daily release PR develop → main:<br/>you merge it → issues close"])

The only label you set#

LabelAdd it when…What happens
Factory_gothe issue (or a batch of issues) should be builtNew issues are first checked together for questions only you can answer. Then each issue is planned right before it is coded, in one run: Opus plans, Codex checks the plan against the code, both give a risk score, the plan is posted on the issue — and coding starts straight away unless the plan is risky.
Factory_review_plan (optional)you always want to approve this issue's plan yourselfThe plan waits for your /approve, whatever its risk score

The risk score#

Every plan gets a score from 0 to 100 from Opus, and Codex gives its own; the higher one counts.

ScoreTypical change
0–25local, well covered by tests, easy to undo
26–50several modules, or behaviour users see
51–75persistence or migrations, concurrency, public APIs or file formats, hard to test
76–100security or trust (signing, secrets, auth), installing/updating/deleting software or user data, irreversible steps, privacy — or assumptions the planner couldn't verify

Above 75 a human decides: the plan is posted with the score and the reason, and the run waits (label Factory_waiting). The comment starts with **What you need to do:** Reply /approve or /reject. and ends with: "The plan is risky and waits for your decision — reply /approve to start coding (optionally with notes), or /reject followed by what to change — it then plans again." Your feedback goes into the new plan. Only people with write access to the repository can approve. (The threshold is the risk_threshold variable, default 75.)

Issues that are too big#

When the planner finds an issue too big for one change, it proposes a split into smaller issues that can each be built and tested on their own, and scores how risky it is to split without you looking (0–100: a mechanical split along existing boundaries is low; deferring or reinterpreting scope, or anything that needs your decision, is high).

  • Split risk 50 or lower: the Foundry creates the new issues by itself — with the original's labels, Factory_go, and a Depends on section with the real issue numbers so they are built in order — comments the list on the original and closes it. The new issues then go through the questions check and are built like any other.
  • Above 50, or the issue has Factory_review_plan: the split is posted on the issue and waits (Factory_waiting). The comment starts with **What you need to do:** Reply /approve or /reject. and ends with: "The issue is split into smaller ones and waits for your decision — reply /approve and the Foundry creates these issues and closes this one, or /reject followed by what to change."
  • You already agreed to a split in a comment ("split it"): it creates the issues without asking.

The threshold is the auto_split_max_risk variable (default 50).

Stories that fix failing tests#

A story can be written to repair tests that already fail on the branch. Without this, the run stops at baseline_failed before it changes anything, because the tests must pass first.

Name the tests. Under a heading ## Failing tests to fix in the issue body, list one test per line, as the JUnit XML reports print it: Class.method.

## Failing tests to fix

- `com.example.BarTest.parsesEmptyInput`
- `com.example.BazTest`
  • A line with only a class name covers every test of that class. Otherwise a name must match exactly, so a part of a name never matches.
  • A list item marker and backticks are fine. Code fences and blank lines are skipped. The list ends at the next heading.
  • The names come from JUnit XML reports. If the test command writes none (TEST-*.xml or junit*.xml in the repository), the run stops at baseline_failed and says so: turn on a JUnit XML reporter in the test command.

What happens when the tests fail before any change (the tests are run once more first, as before):

  • Every failing test is named: the run goes on. The run log says BASELINE: going on, and the report on the issue has a Tests before the change section with the failing tests.
  • A failing test is not named: the run stops at baseline_failed. The failure summary says that the tests failed before any change, lists the failing tests that are not named, and the log shows the rest. Add them to the heading and resume the run; a resume reads the issue again.
  • No test is named: the run stops and lists the failing tests.
  • The tests pass before the change: the run goes on as always. If the issue names tests, the report says that they already pass.

The merge rule. A run that went on past a red baseline is checked again after its change, on test_develop and test_main (and on the hotfix path of issue-gitflow, which also starts from a baseline commit that passed or was named). The check passes only when:

  • every named test passes, and
  • every test that passed before the change still passes, and
  • every test that failed before the change is still reported and passes. A test that is deleted, renamed or skipped counts as a failure.

If it does not pass, the run stops at the test step, and the log lists what is not yet right.

Not covered. A test that fails by chance is still run only once more, not fixed by this rule. The baseline on main for a release is not changed. You cannot switch the baseline check off for a repository with this.

Labels the Foundry sets#

LabelMeansWhat you do
Factory_needs_infoWaiting for you — questions: asked up front for the whole batch, or by the plannerReply on the issue — or just /defaults to accept the recommendations. It continues by itself.
Factory_workingWorking, or paused — usage limit: planning and coding are running or pausedWait; follow it on the Runs page
Factory_waitingWaiting for you — risky plan / split: it waits for your decision/approve or /reject + feedback on the issue
Factory_doneDone: implemented, tested, reviewed and merged into develop (gitflow) or in the rolling pull requestNothing — merge the release pull request (or the rolling one) when you like
Factory_ERRORIt failed; the comment on the issue starts with what you need to do, then says what happened, why, the kind of problem, what was already tried and your options, with the failing outputFix the cause if needed, then remove the label to start over, or resume the run on its page to continue at the failed step. If the Foundry itself failed, the comment says so and names the fix

Issues with an excluded label (e.g. geni) are never picked up, whatever other labels they have.

How do I…?#

I want to…Do this
Build an issue, or a whole epicAdd Factory_go to each issue (select them all in GitHub's issue list → Labels). Give stories a Depends on section so they are built in order.
Answer the questionsReply on the issue, or /defaults — or in the app, on Home
Check a plan before it is codedAdd Factory_review_plan before (or together with) Factory_go
Approve / reject a risky plan/approve (+ notes), or /reject + what to change — or in the app, on Home
Retry after an errorRemove Factory_ERROR to start over, or resume the run on its page to continue at the failed step
Stop the Foundry from touching an issueRemove Factory_go, or add an excluded label
Get the work into mainMerge the release pull request develop → main (gitflow), or the rolling Foundry pull request

Why is nothing happening?#

A line can say "waiting — a bug story goes first": a story with a bug label is repaired before anything new is built. There is nothing to do; the story goes on by itself after that.

Look at the Dashboard: the Waiting card lists every labelled issue that isn't being worked on right now (the same list is on each watcher's card on the Watchers page). Every line starts with a badge for who has the next move (You, Foundry, Another story, A time limit, Something is wrong), then the status name with its ? (press it for what it means and what happens next), then the issue, what to do, why, "Continues: …" when it continues by itself (it can say how long the run it waits for still needs, for example "after #88 (about 20 min left)", and the Runs list shows it too), and a link to the place to do it (GitHub links open in a new tab). Lines where you have the next move come first, above the rest. The state of the watcher itself (active, disabled or watcher error) has a ? too. Problems of the Foundry itself (a restart wait, a usage limit, a watcher error) are in the health line on every page. Runs that started and then paused are listed too: a usage limit, the daily budget, a code area that another run uses, or an interruption. The same sentences are in the failure comment on the issue and in notifications, and they also end the Foundry's comments that ask you something (questions, a risky plan, a split, the push approval). The comment has the fixed sentence; the Dashboard may add the number of questions or what is being approved. Every comment that needs you starts with one bold line, **What you need to do:** …, so you see at once what to do. For a risky plan it looks like this:

**What you need to do:** Reply /approve or /reject.

🤖 **Spaghetti Code Foundry plan**
…
_The plan is risky and waits for your decision — …_

A comment that only reports something says so too. In flows built from the push-plan, push-result and triage blocks the plan comment starts with **Nothing needed from you**. The result comment starts with **What you need to do:** Review and merge the pull request. when there is a pull request, else with **What you need to do:** Open a pull request from the branch. The comment that lists the new issues after a split starts with **Nothing needed from you**. In github-auto it starts with **What you need to do:** Start the new issues when you want them built. when the new issues are not picked up by themselves (auto_subtasks is no).

The label-driven flows do the same:

  • The plan from issue-plan starts with **What you need to do:** Add the code label to start coding.
  • The result of issue-code-daily, issue-deliver and issue-gitflow starts with **Nothing needed from you** — it goes to main with the release pull request.
  • The result of a hotfix in issue-gitflow starts with **Nothing needed from you** — the fix is on main and in develop. or, when develop could not take the fix yet, with **What you need to do:** Merge main into develop, because the fix is not there yet.
  • The reply of pr-feedback after review comments starts with **What you need to do:** Look at the changes.
  • The daily report (daily-pr) and the release check (release-daily) start with **What you need to do:** Merge the release pull request when you like. when the checks pass, and with **Nothing needed from you** — it stays a draft until the checks pass. when they fail.

The empty line and the heading follow, as before.

In issue-gitflow, a plan that starts coding by itself starts with **Nothing needed from you** — it is being worked on. The server gives them for every run and issue at GET /api/next (and as next on each run). The usual reasons:

  • It needs you: a question (Factory_needs_info), a risky plan (Factory_waiting) or an error (Factory_ERROR).
  • It waits for another issue. If the issue has a Depends on (or Blocked by) line or section, it starts only when those issues are done — closed, or Factory_done (in the Foundry pull request). You can name them as #72 or by title (Story 4 — Download the update safely). So you can put Factory_go on a whole chain at once; each story is planned and coded on top of the code of the one before it.
  • Another issue is being built. One issue at a time per repository; the rest start after it.
  • New issues are being checked for questions (a few minutes, once per batch).
  • The usage limit or the daily budget is reached. Runs pause and continue by themselves later; the label stays Factory_working.
  • The Foundry isn't running. Watchers only run while scf ui / scf serve runs.
  • It has an excluded label such as geni.
  • The story is parked. Its trigger label was removed while its run waited for your answers, or the run was cancelled while stopped. Nothing is asked of you and nothing runs: the overview says "parked — not tracked by a watcher". Add the trigger label again to start the story over (see How issue watchers use labels).
  • The issue was closed on GitHub, but its run is still working. The line says so and links to the run page. Cancel the run there if the work is no longer wanted; if the issue was closed by the run itself (report, split, merge) you see nothing. A run that waits for you or is stopped is not cancelled when its issue is closed: it only leaves your lists. If you reopen the issue, the watcher puts the status label back and the same run is listed again; no second run starts. Runs that an older version already cancelled stay cancelled.
  • The watcher has not checked for a long time (more than three times its interval). The line says since when ("has not checked since 11:20"). Press Check now on the Watchers page; the line is gone after the next check.

The pipeline can deliver in two ways; the watcher's flow decides which.

Gitflow — flow issue-gitflow, with release-daily once a day

flowchart LR
    M[main] -->|"created from main (once)"| D[develop]
    D --> F1["feature/86-…"] -->|"tests + reviews pass →<br/>Foundry merges"| D
    D --> F2["feature/88-…"] -->|merged| D
    D -->|"17:00: release PR, you merge"| M
  • Every issue gets its own branch, feature/<issue>-<title>, from develop. When its tests and both Codex reviews pass, the Foundry merges it into develop itself, runs the tests on the merged develop, pushes, deletes the merged feature branch (delete_merged_branches: no keeps it) and closes the issue — done means merged into develop (close_when_merged: no leaves it open until the release reaches main). If develop moved meanwhile, it merges again; conflicts are resolved by an agent (keeping both changes), then tested again. The changelog never conflicts: both sides' entries are kept (union_merge_files).
  • Several issues are coded at the same time when they change different parts of the code: each plan names its code areas (AREAS:), and a run waits only while another run holds an overlapping area — without holding a slot: it steps aside and continues as soon as that run is done. Docs, the changelog and whole test folders are never locked (they merge safely). Issues in a Depends on chain are still built one after the other.
  • No waiting when nobody needs to act: when a run ends, the Foundry checks that repository at once, so the next story, a resume or a retry starts right away instead of at the next interval.
  • No more rounds than the work needs. Opus plans at high effort. Only a plan with a risk score above 75 — one a person approves anyway — is rewritten by Opus (at its highest effort) after Codex's review (revise_above_risk); for every other plan Codex's notes go straight to the coder. Each code change gets one Codex review. Set review_twice_above_risk to a number (for example 50) to get a second review after a [high] finding or for stories riskier than that; the default is off.
  • No test run twice on the same code. The tests before a change and the tests after a merge are skipped when exactly that code already passed them in another run — which is the normal case for the tests before a change: the story starts from the develop the previous story just tested. The log says "not run again". Set reuse_test_results: no to always run them.
  • One full test run per story. Inside a story (after coding, after the review fixes) the watcher's quick_test_cmd runs when it is set — for example only the tests the change touches (npx vitest run --changed --passWithNoTests). The full test_cmd runs before the change and on the merge with develop, so nothing reaches develop without the full tests.
  • The merge is tested ahead of its turn. A finished story tests its merge with develop before it takes its turn at develop (steps pretest_merge and pretest_tests), so several stories test side by side. If develop did not move meanwhile, the turn needs no test run and the story is pushed at once.
  • A few test runs at a time. At most test_slots test commands (default 2) work on the machine at once, over all runs and repositories; the others wait their turn. Many test runs at once make each of them slow and let timing-sensitive tests fail. 0 switches the limit off. Test runs work at a lower priority (nice 10; FACTORY_TEST_NICE changes it, 0 keeps the normal priority), so the server, its pages and the agents stay responsive while tests work.
  • Tests an agent starts itself count too. In an agent step, npm/pnpm/yarn/bun test scripts, npx vitest (and jest, mocha, playwright …), pytest, python -m pytest, gradle/mvn test, check, build or verify, and go test/cargo test go through the same test slots and run at the same low priority (the small commands in tools/agent-bin come first on the agent's PATH). The wrappers of Gradle and Maven, ./gradlew and ./mvnw (also as sh ./gradlew or with an absolute path), count too when they run a test goal: test, check, build, verify, integrationTest, connectedCheck, install, or a task such as :app:test. A Gradle run in a slot gets a low-priority Gradle daemon. Other goals, such as ./gradlew assemble, run unchanged. An agent waits at most 5 minutes for a slot (FACTORY_AGENT_TEST_WAIT_SEC), then its tests run anyway. Every other command runs unchanged. Not covered: other test scripts started by path, such as ./scripts/test.sh, and steps of a user's run in the OS sandbox.
  • Small sessions for easy steps. Writing the docs and resolving a merge conflict each start a fresh session with only what they need, instead of continuing the whole coding conversation. If the tests fail after a resolved conflict, the coding session takes over.
  • Every day at 17:00 (release-daily) the Foundry runs the full tests and build on develop and opens (or updates) one pull request develop → main that lists and closes the day's issues — a draft while the checks fail. You merge it once a day. When the tests pass on a clean checkout, the run also records that pass in the data folder, so that self-update can skip its own test run on the same code.
  • develop is created from main the first time, and kept up to date with main (for example after a hotfix) before new work starts. develop must not be in Protected branches (Settings), since the Foundry pushes to it; main stays protected (the one exception is the hotfix path below, and only when you switch it on).
  • Hotfix — bug stories go to main at once. An issue with a label in hotfix_labels (default bug, any case) is built as a hotfix when an admin has switched on Hotfixes in Settings → Safety (off by default). Without the setting, or in a changed copy of the flow, it is built as a feature and the report says why in one line. The plan, plan review, size and risk checks, code areas, build, tests, reviews and docs are the same. What differs:
    • The branch is hotfix/<issue>-<title> (hotfix_prefix), made from the main commit the tests passed on before the change.
    • After the reviews the Foundry merges it into main (no fast-forward), runs the tests on the merge result and pushes main only when they pass. If main moved meanwhile, it merges and tests again. The issue is closed as soon as the fix is on main; it does not wait for the release. No pull request, no tag.
    • Then main goes into develop (conflicts are resolved by an agent and tested, like for a feature), so the release has no conflicts from hotfixes. The merged hotfix/… branch is deleted (delete_merged_branches: no keeps it; a branch that got new commits after the merge is kept).
    • A hotfix is never split by itself, and a plan above the risk threshold still waits for you.
    • The comment on the issue says: fixed on main (with the commit), merged into develop, and whether the running Foundry already has the fix (only when the issue belongs to the Foundry's own repository).
flowchart LR
    M[main] --> H["hotfix/42-…"]
    H -->|"tests + reviews pass →<br/>merge, test, push"| M
    M -->|"merge, test, push"| D[develop]

If develop cannot take the fix (conflicts the agent cannot resolve, or red tests), the fix is still on main; the comment starts with Merge main into develop and says why. The Foundry tries again when it starts the next story (without conflicts only). To be sure, merge main into develop yourself — until then the daily release pull request can show conflicts.

  • Size limit: a plan over 15 files or about 800 lines of production code (tests and docs don't count) is split into smaller issues instead — automatically when the split risk is low (max_files, max_code_lines).

The human-in-the-loop pipeline#

For work where you want to see and approve every plan before any code is written: issue-plan (label Factory_ready) plans the issue and posts the plan; you read it and add Factory_code; issue-code-daily codes it on the day's branch; daily-pr opens the day's pull request to main at 17:00, and no new coding starts while it is open.

Which flows ship#

Two delivery pipelines, what supports them, and the standalone refinement flow: epic-questions, issue-gitflow and release-daily (gitflow), issue-plan, issue-code-daily and daily-pr (human in the loop), and refine-brief (the architect's context brief, see Refinement; it can't be deleted, because Refinement uses it) and refine-round (the architect's question round, same section). Build anything else yourself in the editor, with ✨ Draft flow with Claude, or with any AI assistant (Let any AI write a flow). A flow that a watcher uses — enabled or disabled, or as its questions check — or that another flow runs as a step can't be deleted; the Foundry says which watchers or flows use it.

Improve this page on GitHub