Skip to content

The pipeline

Open the diagram full size

scripts/factory.ps1 runs the whole loop. It loads four modules from factory2/lib/ and keeps every run in its own work folder named issue-N-<time> under .work/. Each stage writes one line to the console, to factory.log and to the run’s event files.

The script reads the issue with gh. If the issue is not open it prints that and exits with code 2 without touching anything.

Before it goes on, it looks for leftovers from an earlier run on the same issue. If a claim file for that issue belongs to a process that is still alive, the run refuses to start. Otherwise it closes every open pull request whose branch starts with factory/N-, deletes those branches and says so in the log. See Concurrency.

The plugin repository is cloned with --depth 1 into the run’s work folder. The commit that main points to becomes the base. Line ending conversion is switched off so the diff stays exact.

A Sonnet agent with read-only tools reads the code the issue touches and returns structured output defined by factory2/prompts/plan.schema.json. The plan holds

  • the numbered steps, each naming a file and a symbol
  • the files the implementer may edit or create, tests included
  • whether a writer could notice the change (user_visible) and, if not, why
  • whether the change involves AI, so that the acceptance check starts Flare with AI on
  • a release note for writers, or the reason there is none

Paths outside the repository, paths with .. and paths the agent wrapper cannot turn into an edit rule are dropped. The factory plans again only when the implementer says it cannot build the plan, when diagnosis says the approach cannot work, or when a failure stalls. See Failure handling.

Once the plan exists, the run writes its claim and waits for any earlier run that plans to change one of the same files.

The cycle has no limit. Each pass does the following.

  1. Implement. A Sonnet agent edits only the planned files. It cannot run commands. On the first pass it follows implement.md. On every later pass it gets retry.md with the last failure and the current diff. It ends with BUILT or CANNOT_BUILD, and the second sends the run back to planning.
  2. Scoped tests. The patch is written, a private copy of the code graph is taken, and the gate builds the solution and runs only the unit tests and integration tests the change can reach. See Testing design.
  3. Commit and push. On the first pass the run creates the branch factory/N-<slug>. It stages only the planned files, adds the release note file under changes/ when the plan has one, commits and pushes. Later pushes use --force-with-lease against the last commit this run pushed. The first push opens the pull request with Fixes #N.
  4. Acceptance in real Flare. If a writer could notice the change, or the change touches the E2E harness or the Flare hook, the run takes a place in the VM line. At the front it replays its change on the latest main and an agent uses the change in Flare. See The VM layer.
  5. Required checks. The run polls the pull request every 30 seconds until the plugin’s build and feedback-worker checks have passed. A failing check goes to diagnosis and back to implement.
  6. One last look at main. If main moved while the run waited and what landed meets what this change reaches, the change is replayed on the new main and checked again.

Any layer that fails goes through the same path. The failure is diagnosed, handed to the next implement pass and counted for stall detection.

The pull request is merged with a merge commit, the branch is deleted and the merge is pinned to the commit the run last pushed (--match-head-commit). If GitHub says the pull request is not mergeable yet because checks are still settling, the run waits 30 seconds and tries again, up to 20 times.

  • A comment on the issue links the merged pull request and lists anything the acceptance agent could not verify.
  • Problems the acceptance agent saw that are not part of the issue become bug issues labelled bug and from:factory, once per title.
  • The run writes run.done, removes its claim and prints the time, the cycle count and the Claude usage.
Question Answered by Where
Which files may change The plan Invoke-PlanStep in Build.psm1
Which tests run The test plan and the code graph Get-TestPlan and Get-AffectedTest in Gate.psm1
Whether Flare is used user_visible in the plan, or a harness change Test-NeedsAcceptance in factory.ps1
Whether main moved in a way that matters The code graph’s reach of the change Sync-WithMain in factory.ps1
Why a try failed A read-only diagnosis agent Get-Diagnosis in factory.ps1