Docs menu

Configure Agents

Agents

An agent is a folder that a process step names. The folder holds a manifest, two prompts and, if you want them, skills. Factorial reads the folder at the commit that the ticket pinned.

The agent folder

Each agent lives in .factorial/agents/<name>/. The folder name must equal the name in the manifest. It must also equal the agent: value of the process step that uses it. A human gate needs no agent.

.factorial/
  agents/
    plan-agent/
      agent.yaml          the manifest
      system-prompt.md    who the agent is, and what it must deliver
      instructions.md     the workspace, the delivery and the revision flow
      skills/             skills only this agent uses
        speckit-specify/SKILL.md
      mcp/                MCP servers only this agent uses
  skills/                 skills that any agent can share
    project-constitution/SKILL.md
  mcp/                    MCP servers that any agent can share
    product-docs.yaml

Every path that the manifest declares must exist. Sync checks both prompts, each skill and each MCP server file. A step whose agent folder is missing, or whose manifest does not parse, fails when it starts. The sync answer lists such agents as agent_warnings. See Snapshot the config.

The agent file

The keys of an agent file use snake_case. A key that the table does not list is an error. The only vendor-specific part is runtime.kind. The process chooses the runtime that runs the agent.

KeyRequiredWhat it holds
specVersionyesfactorial/v1. The value adlc/v1 still parses. This key is camelCase, like the same key in the other config files.
kindyesAgent.
nameyesThe agent name. It equals the folder name and the agent: value of the step.
descriptionnoOne line for people who read the file.
promptsyesTwo paths, system and instructions, relative to the agent folder. See Prompts.
skillsnoSkills by name, in two lists, local and shared. See Skills.
mcpnoMCP servers by name, in the same two lists. Without the key, the agent has no MCP server. See Select it for an agent.
modelyesA concrete model id, for example claude-haiku-4-5-20251001. Factorial refuses a size name such as sonnet. It also refuses an id that ends in -latest and an id with no digit. The model API rejects them, and the agent would fail on its first turn.
limitsyesYou must give max_duration. Write a number and a unit, s, m or h, for example 30m or 1h. max_turns is optional. Write a whole number of 1 or more.
secretsnoA list of the keys that the agent needs. Each item has a key and an optional description. See Secrets.
permissionsnoThe GitHub permissions that the agent asks for. See Permissions.
runtimeyeskind is claude or codex. It must match the family of the runtime that runs the agent. Sync refuses a mismatch.

This is the manifest of the plan agent in the sample lane:

specVersion: factorial/v1
kind: Agent
name: plan-agent
description: Turns a raw request or ticket into a reviewable contract: spec, plan, task list

prompts:
  system: system-prompt.md        # the role and the output
  instructions: instructions.md   # the workspace, the delivery and the revision flow

skills:
  local:                          # from skills/ in this agent folder
    - speckit-specify
    - speckit-clarify
    - speckit-tasks
  shared:                         # from skills/ in .factorial/
    - project-constitution

mcp:
  shared:
    - product-docs                # needs an allowlist entry, see MCP servers

model: claude-haiku-4-5-20251001  # a concrete id, never a size name

limits:
  max_duration: 1h                # a run gets a one-hour token, so deliver inside the hour
  max_turns: 40

secrets:
  - key: ANTHROPIC_API_KEY
    description: Anthropic API key for the model
  - key: GH_TOKEN
    description: GitHub App token, made for each run

runtime:
  kind: claude

Secrets

The secrets list names the keys that Factorial must supply to the agent. It never holds a value. You store the values in the Secrets section of the project. A declared key with no stored value blocks activation, and the answer names the key. Give the agents their credentials shows how to store them.

Factorial makes GH_TOKEN for each run, so you store no value for it. When the process names a runtime, the model key comes from that runtime alone, and a declaration of it in the agent file does nothing. The list is a statement of what the agent needs. It does not limit what a run can read: Security explains where the boundary is.

Permissions

The optional permissions key asks for GitHub permissions for the token of the run. It maps a scope to read or write. The scopes are contents, issues, pull_requests, metadata, checks and actions. Another scope, workflows included, is an error. Without the key, the agent gets the default profile of the App.

permissions:
  contents: write
  pull_requests: write
  issues: read

A manifest only asks. The operator gives the grant through the permission_ceiling setting of the project. Factorial refuses a request that is above the ceiling and names each scope that is too high. See permission_ceiling.

Prompts

An agent has two prompts. Both are markdown files beside agent.yaml. A prompt is never inline YAML.

prompts.system
Who the agent is, and what it must deliver. Write the role and the output here.
prompts.instructions
The working context: where the agent stands, how it delivers its branch and pull request, and what it does when a reviewer asks for changes.

Keep the prompts small. Factorial already does the bookkeeping. It admits the ticket, sets the labels and assigns people. It moves the ticket to the next step, merges at a gate and checks every postcondition. A prompt that describes any of this duplicates the orchestrator and goes out of date when the config changes. Describe the work and nothing else.

An agent does not read .factorial/ from its checkout. The checkout is the workspace.base branch, where the folder is old or missing. Each run gets the pinned config commit in the variable FACTORIAL_CONFIG_SHA and in its prompt. A prompt that must read the process file uses this command:

git show "$FACTORIAL_CONFIG_SHA":.factorial/processes/process-simple.yaml

Do not put {slug} in workspace.base of the step. When Factorial cannot work out the slug, the base becomes a branch that does not exist. The ticket then blocks on the first try. Use a base that always exists, and let the instructions name the real branch point. The adapter also writes base into the prompt as the branch of the checkout, so write those instructions so that the agent cannot miss them.

Skills

A skill is a folder that holds a SKILL.md file. The file starts with YAML front matter that has a name and a description. The text after it tells the agent how to do one kind of task.

---
name: speckit-tasks
description: Decompose an approved plan into an ordered task list. Use as phase 3 of planning, after specify and clarify.
---

# Tasks

Phase 3 of planning. Produce tasks.md.

The agent file declares a skill by name, in one of two lists:

skills.local
The skill is agents/<name>/skills/<skill>/SKILL.md. Only this agent uses it.
skills.shared
The skill is .factorial/skills/<skill>/SKILL.md. Any agent can declare it.

A skill name must be unique across both lists of an agent, because the agent finds a skill by name. A declared skill with no SKILL.md is an error.

What an agent must never do

Every prompt keeps one short rule. The agent never merges a pull request, never approves its own pull request, never adds a label and never pushes to main. Do not remove it.

The rule is in the prompt because the agent and Factorial share one GitHub permission profile. Two things enforce the merge: the branch protection that you set and the gate. It cannot enforce the other three rules. The sentence in the prompt is the control. Security explains this accepted cost.

The agent must also report what happened, with no gloss. Factorial does not read the report as proof. It checks the postconditions of the step against GitHub: the branch, the file, the pull request, the CI result. When they do not hold, the step fails, whatever the report says, and Factorial retries it or blocks the ticket. An honest report that says what was not done helps the next try and the person who reads the ticket.

Start from the catalog

You do not write the agent folders by hand. The process builder holds a catalog of agents: plan-agent, build-agent and test-agent. When you press Commit and sync, the commit writes a folder for each catalog agent that your process names. It skips an agent that the repository already has. It never rewrites an agent folder or a shared skill that the repository holds, so you can tune the prompts after the first commit.

Most catalog agents have variants for the shape of the lane. The stacked variant opens the implementation pull request against the spec branch. The base-target variant opens every pull request against the base branch. Only the variant that matches the branches of your lane writes files. The shared project-constitution skill has the same variants, and its naming table uses the branches of your lane.

The explorer in the process builder lists every file that the commit would write. You can read and edit each file before you tick it. An agent that the catalog does not know, or whose files you leave unticked, gets a agent.yaml stub that holds only comments. Fill in the stub, because a step whose agent is still a stub fails when it starts. Fill .factorial/ gives the steps of the builder, and A stale edit blocks the commit explains the stale badge on a file that you edited.