Skip to content

Latest commit

 

History

History
390 lines (302 loc) · 14.7 KB

configuration.md

File metadata and controls

390 lines (302 loc) · 14.7 KB

Configuration

factory.config.ts is committed application configuration. The gateway and Workflows service both import it.

Edit it before the first deployment and whenever you change repositories, labels, model assignments, permissions, publishing limits, or Sandbox lifetimes.

Example

export default defineFactoryConfig({
  repos: {
    "acme/web": {
      baseBranch: "main",
      install: "npm ci",
      verify: ["npm run build", "npm test"],
      egress: ["registry.npmjs.org"],
    },
    "acme/api": {
      baseBranch: "main",
      install: "go mod download",
      verify: ["go test ./..."],
      egress: ["proxy.golang.org", "sum.golang.org"],
    },
  },
  labels: {
    triage: "factory:triage",
    specApproved: "factory:spec-approved",
    prReview: "factory:pr-review",
    autopilot: "factory:autopilot",
  },
  commandPrefix: "/factory",
  minimumPermission: "write",
  limits: {
    repairRounds: 2,
    maxChangedFiles: 200,
    maxFileBytes: 1_000_000,
  },
  sandbox: {
    timeoutSeconds: 4 * 60 * 60,
  },
});

Repositories

repos is keyed by exact GitHub owner/name. The factory ignores webhooks from any other repository, even if the App is installed there.

Field Required Meaning
baseBranch Yes Branch used for Triage, implementation, and snapshots
install No Dependency command run after clone credentials are removed
verify Yes Commands required before publishing and during PR review
egress No Extra HTTPS hosts this repository's Sandboxes may reach, or "allow-all"

Use deterministic install commands such as npm ci. Snapshot creation fails if installation changes a tracked file. Use verify: [] only when you intentionally want no command-based verification.

Sandbox egress

Sandboxes reach the network through an HTTPS allow-list that Render enforces. Every other destination fails as a reset connection. The factory builds each list:

Sandbox Allowed hosts
Task Sandbox github.com, every agent harness's model API, and the repository's egress
Snapshot builder github.com, deb.debian.org, and the repository's egress

List in egress every host the repository's install and verify commands download from. Task Sandboxes need the list too: they reinstall when the lockfiles change. Common hosts:

Ecosystem Hosts
npm registry.npmjs.org
Go modules proxy.golang.org, sum.golang.org
Go toolchain download go.dev, dl.google.com
PyPI pypi.org, files.pythonhosted.org

A rule matches one exact hostname. Use *.example.com to admit its subdomains; a wildcard may only be the leftmost label. Only HTTPS is admitted. An invalid host stops both services at startup. An install that fails under the allow-list names the allowed hosts in its error.

Set egress: "allow-all" to lift the restriction for one repository. Prefer listing hosts: the allow-list is what stops dependency scripts and prompt-injected agents from sending code or the model key to arbitrary destinations.

Render fixes a Sandbox's policy when it is created, so an egress change applies to the next Sandbox after a Workflows deploy. No snapshot refresh is needed.

After adding a repository:

  1. commit and deploy the configuration;
  2. install the GitHub App on the repository;
  3. run npm run setup:github; and
  4. push to the base branch, start a factory task, or refresh the snapshot directly.

Labels, permission, and limits

Setting Default Meaning
labels.triage factory:triage Starts or continues Triage
labels.specApproved factory:spec-approved Starts Spec-to-PR
labels.prReview factory:pr-review Starts PR Reviewer
labels.autopilot factory:autopilot Runs all three stages through a reviewed draft PR
commandPrefix /factory Prefix for commands such as /factory cancel
minimumPermission write Live repository permission required to start work
limits.repairRounds 2 Repair attempts after failed verification
limits.maxChangedFiles 200 Maximum files in a published change
limits.maxFileBytes 1000000 Maximum size of one changed file
sandbox.timeoutSeconds 14400 Provider-side Sandbox lifetime in seconds

minimumPermission accepts triage, write, maintain, or admin. The factory checks the triggering actor's current permission at the start of every GitHub task.

setup:github also creates factory:working. This label records an active run; it is not a trigger.

If you rename a trigger label, deploy both services and rerun npm run setup:github. The command creates required labels but does not remove old ones.

Keep sandbox.timeoutSeconds at least as long as the longest Workflows task.

Agent harnesses and models

Each agent runs on a harness: an agent library and its configuration, built by an adapter in app/harness/. The built-in agents share two harnesses from app/agents/models.ts:

export const piSonnet = pi({ model: getBuiltinModel("anthropic", "claude-sonnet-5") });
export const piOpus = pi({ model: getBuiltinModel("anthropic", "claude-opus-5") });

Triage and PR Reviewer use piSonnet; the spec writer and implementer use piOpus. Each value is named for its library and model. To change a model, add or edit a value and rename it to match, or give one agent its own harness in its definition. Model IDs are type-checked against the library's catalog, so a typo fails npm run typecheck. The pi adapter also takes thinkingLevel (off, minimal, low, medium, or high; default medium).

Each harness declares the env var of its key, its credential. Setup and doctor derive the keys they manage from the agents' harnesses, so a harness that needs a new key needs no other configuration change. The pi adapter supports Anthropic (ANTHROPIC_API_KEY), Google (GEMINI_API_KEY), and OpenAI (OPENAI_API_KEY). After a harness change that needs a new key:

  1. add the key to local .env;
  2. run npm run setup:render to add it to the software-factory-workflows env group;
  3. deploy factory-workflows; and
  4. run npm run doctor -- --service workflows.

Each harness also declares its egress: the hosts its model requests go to. Every task Sandbox's allow-list includes the hosts of all registered agents. The pi adapter uses the host of the model's baseUrl, so switching providers needs no egress change. Pass egress to pi() when a custom models registry serves the model from another host, such as a proxy.

Tool support

Every harness gives the model the factory's own tools, so an agent's tools and the read-only shell policy behave the same on any harness.

Use a key dedicated to this deployment with a provider-enforced spend limit. The factory has no hard dollar budget.

What must be redeployed

Change Required action
Repository, label, permission, or publishing limit Deploy both services
Agent harness or model Deploy factory-workflows
Prompt, agent definition, or Workflows service code Deploy factory-workflows
Gateway routing or validation Deploy factory-gateway
Label, App permission, or subscribed event Run npm run setup:github; update the App when prompted
app/runner/ or app/harness/ sources Deploy factory-workflows; each Sandbox gets the new runner payload when it opens
sandbox/ Deploy factory-workflows, then refresh each repository snapshot
Repository install command or lockfiles Refresh the snapshot, or let the next task reinstall

Snapshots

Each configured repository has a dependency-installed Sandbox snapshot. A push to its base branch refreshes it. If no snapshot exists, the first task builds one.

A failed refresh does not delete the previous usable snapshot.

Refresh a snapshot

Start the refresh-snapshot task with the repository, its GitHub App installation ID, and the base branch's current commit:

git ls-remote https://github.com/<owner>/<name>.git refs/heads/<base-branch>

cat > input.json <<'JSON'
[{"repo":"<owner>/<name>","installationId":<id>,"sha":"<commit>"}]
JSON

render workflows tasks runs start \
  <workflow-slug>/refresh-snapshot \
  --input-file input.json

The installation ID is the number at the end of the installation settings URL:

https://github.com/settings/installations/<id>

Organization installations use the equivalent organization settings URL.

Follow and control runs

While a GitHub task runs, the issue or pull request has factory:working. The App-authored status comment shows progress, the run ID, and the final result. Its hidden marker records the current Sandbox for cancellation.

Open that run under factory-workflows in the Render Dashboard to inspect its input, logs, and result.

The service also writes structured events:

  • run.finished records the task, state, outcome, and duration.
  • agent.finished records the agent, model, turns, token usage, reported cost, and stop reason.

Search them with the Render CLI:

render logs -r <factory-workflows-id> --text run.finished
render logs -r <factory-workflows-id> --text agent.finished

To cancel a run, post this as the first line of an issue or pull request comment:

/factory cancel

The commenter needs minimumPermission. Cancellation stops the run named in the status comment and terminates its recorded Sandbox. It also clears a stale running status when the task has already stopped.

To restart work, remove the trigger label and apply it again.

factory:autopilot grants upfront approval to draft a spec, implement it, open a draft pull request, and post a COMMENT review. It never approves or merges the pull request, and it does not apply the three manual transition labels. Triage may still clear a stale spec-approval label when it revises the spec. The trigger label is removed when a run starts. If Triage asks questions, the factory adds it back so a human reply can start the continuation. Use /factory cancel to stop an active root workflow.

Check a deployment

doctor is read-only, does not print secret values, and exits nonzero when a required check fails or cannot be verified. This includes unreachable task APIs, missing task registration, and installation permissions or events that have not been accepted. The gateway intentionally skips GitHub checks because it has no App credentials.

Webhook delivery is verified only when the latest attempt went to the configured gateway and received a 2xx response. If no such evidence is available, confirm Webhook > Active in the App's settings, save, and redeliver a ping from Advanced before rerunning doctor. GitHub's API does not report the Active setting, so a successful delivery proves delivery only at the reported time; it cannot guarantee that the hook has not since been disabled.

From a local checkout, this checks the services and credentials represented by local .env:

npm run doctor

It cannot inspect values stored only in Render. Check the deployed gateway from its Render Shell:

npm run doctor -- --service gateway

Workflows services have no Shell. Put the deployed Workflows values and exact RENDER_WORKFLOW_SLUG in local .env, then run:

npm run doctor -- --service workflows

The setup runbook lists the required values and common failures.

Environment variables

Local variables are listed in .env.example. The Blueprint supplies RENDER_WORKFLOW_SLUG to the gateway from the Workflows service. Every other deployed value comes from an environment group.

Gateway

Variable Purpose
GITHUB_WEBHOOK_SECRET Verify GitHub webhook signatures
RENDER_API_KEY Start Workflows tasks
RENDER_WORKFLOW_SLUG Address the deployed Workflows service
PORT HTTP port; defaults to 3000

Workflows

Variable Purpose
RENDER_API_KEY Manage Sandboxes, snapshots, and task cancellation
RENDER_WORKSPACE_ID Workspace that owns Sandboxes and snapshots
GITHUB_APP_ID Authenticate the GitHub App
GITHUB_APP_PRIVATE_KEY Mint installation tokens
ANTHROPIC_API_KEY Call the default models; each agent's harness declares the key it needs
GITHUB_API_URL Optional GitHub API base URL

GITHUB_API_URL is not in the Blueprint. GitHub Enterprise Server users must set it in local .env for setup commands and add it to the software-factory-workflows env group in the Render Dashboard.

Never commit .env or provider keys.

Environment groups

Secrets never pass through render.yaml or the Blueprint form. Each service links Render environment groups instead:

Group Linked to Variables
software-factory-shared both services RENDER_API_KEY
software-factory-gateway factory-gateway GITHUB_WEBHOOK_SECRET
software-factory-workflows factory-workflows RENDER_WORKSPACE_ID, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, ANTHROPIC_API_KEY

The gateway links no group that holds GitHub App credentials or a model key. ENV_GROUPS in scripts/lib/requirements.ts defines this layout for setup and doctor.

A Blueprint can't define a group that holds secrets it doesn't commit, so the groups must exist in the workspace before the Blueprint is applied. Create or update them from local .env:

npm run setup:render -- --workspace <tea-...>

The command runs the pre-deployment checks before writing any group: required values, pinned Node version, workspace access, repository Issues availability, and configured base branches must pass. Run npm run setup:preflight -- --workspace <tea-...> to check without updating groups. Model and Sandbox execution remain unverified until the approved smoke test.

It updates only values that changed, leaves other variables in each group alone, and never prints a secret. A second group with the same name makes it stop, because a Blueprint links groups by name.

To rotate a secret, change it in .env, rerun npm run setup:render, and redeploy the services that link the group.

Create the groups in the Dashboard

To keep the Render API key out of local .env, create the groups by hand before applying the Blueprint. In the workspace, open Environment Groups and create each group in the table with the exact name and variables shown. Paste values from .env or from their source without showing them in chat. Workflows doctor still reads these values locally, so export them in the shell where it runs.