Software Factory

Team per repo and per request

Which agent and model plays each Software Factory role — the workspace team, `.ghosty/factory.md` in the repo and "@build con opus" in the message.

Updated 2026-10-07

Ask your coding agent

Read https://www.ghosty.studio/en/docs/factory/team-per-repo.md and create this repo's `.ghosty/factory.md` with the conventions you see in the code.

Paste it in Claude Code, Cursor or Codex with the ghosty-agent skill installed.

Who plays each role is decided at three levels. The most specific wins:

message > the repo's .ghosty/factory.md > workspace team

Workspace team

In /factory → "Workspace team" (owner only): which Studio agent is @plan, @build and @check by default, plus the optional @eval. Every repo that doesn't say otherwise uses it.

From the CLI: ghosty agents teams on <id> <workspace> --factory plan,build,check gives those roles to an agent (each role keeps its name and avatar). ghosty agents teams ls <id> shows which roles it holds in each workspace.

If nobody asks for a model, @plan and @check run on Opus 5.5 when their agent is a Claude one; @build uses its agent's model. On another engine, each role uses its agent's model.

.ghosty/factory.md in the repo

A file in the repo with frontmatter per role and, below it, the conventions all three roles must know:

markdown
---plan: { agent: Planner }build: { agent: Builder, model: opus }check: { agent: Reviewer, model: flash }---Conventions of this repo that @plan, @build and @check must know:- Tests run with `pnpm test` and use Vitest.- Already-applied migrations in `prisma/migrations/` are never touched.
## @checkA change to `.github/workflows/` or `prisma/` without a test is a finding.
  • agent: name or id of a Studio agent in your workspace.
  • model: alias (opus, sonnet, fable, pro, flash, sol, terra, luna) or full id. It must belong to that agent's engine: changing engine means changing agent.
  • A role that isn't listed uses the workspace's. The body travels with every factory turn and overrides the generic rules.
  • A ## @plan, ## @build or ## @check section goes only to that role: that's where this repo's review rules go, for example. It ends at the next ## .
  • It's read with a 5-minute cache; in /factory, "Team in this repo" → ↻ refreshes it and shows where each role comes from ("from the repo" or "from the workspace").

Test environment: setup:

If the repo's tests need a database, Redis or variables, declare it in the same frontmatter. @build and @check get the exact call that prepares their work box before every run:

markdown
---build: { agent: Constructor }setup:  services: [postgres, redis]  db: fruteria  script: .ghosty/setup.sh  env:    JWT_SECRET: test-actions-jwt-secret---
  • services: postgres and redis (installed and stopped in the dev box). Anything else is ignored.
  • db: name of the database and its user (the password is the same name). It's dropped and recreated on every run, so the suite always starts with an empty database. The connection arrives as DATABASE_URL; Redis as REDIS_URL.
  • env: test variables, the same ones your CI uses. Never real secrets: the repo's secrets stay in the vault.
  • script: runs from the repo root with that env loaded (migrations, seeds).
  • If the repo doesn't have it and the request touches tests with a database, @plan proposes it from the services: in .github/workflows/.

A box per run

Each run gets its own computer (4 GB) from the moment you sign the plan until it's merged or cancelled: @build builds there, @check tests there (on a clean clone of the PR) and the preview runs there. Idle, it sleeps; when the run closes, it's deleted. Your factory plan has execution slots (F1: 2, F2: 4, F3: 6) shared by runs and by previews of PRs without a run. A new run displaces a loose preview; a new loose preview replaces the oldest one; if every slot is a run in progress, the run or preview waits ("Waiting for a slot") and starts on its own as soon as one frees up.

In the message

text
@build con opus@plan en agenda@plan en blissito/agenda con sonnet

The keywords stay in Spanish (con = with, en = in):

  • "con <model>" changes that role's model in that thread: later hand-offs of the same request inherit it.
  • "en <repo>" picks the repo when the room has several.
  • A model from another engine or an agent that doesn't exist won't run: the thread says why.

Each reply's footer shows the model that ran that turn, not just the configured one.