---
title: Stations reference
description: Model, tools, sandbox, and output contract for each of Foreman's five subagents, including the full outputSchema of every station.
type: reference
summary: Per-station reference for the Classifier, Analyst, Implementer, Reviewer, and Researcher.
related:
  - /docs/pipeline
  - /docs/tools
  - /docs/configuration
---

# Stations reference



Foreman declares five subagents under `agent/subagents/`.

Four of them are stations, running in fixed order on every work item. The fifth, the Researcher, runs out of band when an item depends on a fact the repository does not hold.

For the narrative version of how these fit together, see [the pipeline](/docs/pipeline).

## At a glance

| Subagent    | Model                       | Authored tools                   | Sandbox                 |
| ----------- | --------------------------- | -------------------------------- | ----------------------- |
| Classifier  | `openai/gpt-5.6-terra-fast` | none                             | default, empty          |
| Analyst     | `openai/gpt-5.6-terra-fast` | none                             | own, with repo checkout |
| Implementer | `anthropic/claude-fable-5`  | `checkout_branch`, `push_branch` | own, with repo checkout |
| Reviewer    | `openai/gpt-5.6-terra-fast` | `checkout_branch`                | own, with repo checkout |
| Researcher  | `openai/gpt-5.6-terra-fast` | none                             | default, empty          |

Model ids come from the `MODELS` map in `agent/lib/models.ts`. The Implementer and Reviewer run on different vendors deliberately, so the review does not share the Implementer's idiom or blind spots.

Authored tools are the ones written specifically for that station, in its own `tools/` directory. Every station also gets eve's default tools on top of those, so the column shows what each station adds rather than everything it can call.

Each of the three checkouts is a separate Vercel Sandbox with its own clone of `FACTORY_REPO`, declared by an identical `sandbox.ts` in that station's directory. None of them is more separate than the others.

The Reviewer's isolation gets the most attention only because it is the one doing load-bearing work: it fetches the pushed branch into a clean checkout, so it reviews what was actually pushed rather than the Implementer's working tree.

## What every station gets by default

A station inherits nothing from the orchestrator, but anything it leaves unspecified falls back to an eve default rather than to nothing. Two consequences are worth knowing before you read the per-station sections.

Every eve agent gets a sandbox whether or not it asks for one. The Classifier and Researcher never set one up, so they run in an empty `/workspace` with no repository in it. The other three set up their own and clone `FACTORY_REPO` into it.

Every station also gets eve's default tools: `bash`, `read_file`, `write_file`, `glob`, and `grep` against its own sandbox, plus `web_fetch`, `web_search` where the model provider supports it, and `todo`. Four built-ins are missing. `load_skill` and `connection_search` show up only for agents that have skills or connections, and stations have neither. `ask_question` needs a session that can pause for a person, which task mode cannot do. And the root-only `agent` tool never reaches a station at all.

So what actually limits a station is its empty or full sandbox and what its instructions permit.

## Classifier

Pure triage on a fast, cheap model. The Classifier works from the message alone: the work item plus whatever thread context the orchestrator packs in.

It has only eve's default tools and never checks out the repository, so there is nothing in its `/workspace` to read. Its instructions do the rest of the narrowing, confining it to triaging the text it was handed rather than going looking for more.

Its instructions tell it to be decisive. When information is thin but the intent is clear, it classifies with its best judgment and notes assumptions in the summary rather than blocking. It sets `needs_clarification` only when proceeding would risk building the wrong thing entirely.

<TypeTable
  type={{
  type: {
    type: "'bug' | 'feature' | 'refactor' | 'question' | 'chore' | 'security'",
    description: "The kind of work item.",
    required: true,
  },
  priority: {
    type: "'critical' | 'high' | 'medium' | 'low'",
    required: true,
  },
  complexity: {
    type: "'trivial' | 'small' | 'medium' | 'large'",
    required: true,
  },
  affected_area: {
    type: "string",
    description: "Best guess at the component or layer, or 'unknown'.",
    required: true,
  },
  actionable: {
    type: "boolean",
    description: "Whether there is enough information to act.",
    required: true,
  },
  needs_clarification: {
    type: "boolean",
    description: "True when proceeding would risk building the wrong thing entirely. Stops the pipeline.",
    required: true,
  },
  questions: {
    type: "string[]",
    description: "The specific clarifying questions. Empty unless needs_clarification is true.",
    required: true,
  },
  summary: {
    type: "string",
    description: "One-sentence restatement of the work item.",
    required: true,
  },
}}
/>

## Analyst

The Analyst plans and never writes the implementation. It works from a live checkout of `FACTORY_REPO` at `/workspace/repo`, so the plan names real files and the repository's actual conventions instead of guesses.

Its instructions require it to read files before naming them, discover the package manager and the lint, typecheck, and test commands, and trace the code path the item touches. It modifies nothing.

<TypeTable
  type={{
  problem_statement: {
    type: "string",
    description: "The request restated as a precise engineering problem.",
    required: true,
  },
  approach: {
    type: "string",
    description: "The chosen strategy, plus the main rejected alternative and why.",
    required: true,
  },
  plan: {
    type: "string[]",
    description: "Ordered, concrete, independently verifiable steps. At least one.",
    required: true,
  },
  affected_surface: {
    type: "string[]",
    description: "Files, modules, and interfaces the change touches, with public contracts called out.",
    required: true,
  },
  risks: {
    type: "string[]",
    description: "What could break, and how the plan mitigates each.",
    required: true,
  },
  acceptance_criteria: {
    type: "string[]",
    description: "Objective, testable criteria the Reviewer later judges verbatim. At least one.",
    required: true,
  },
  test_strategy: {
    type: "string",
    description: "Grounded in the repository's real test setup.",
    required: true,
  },
  assumptions: {
    type: "string[]",
    required: true,
  },
  open_questions: {
    type: "string[]",
    required: true,
  },
}}
/>

The `acceptance_criteria` field is the load-bearing output. It travels unchanged from the Analyst to the Reviewer, which is what makes the review a judgment against a fixed contract rather than a vibe check.

## Implementer

The Implementer executes the approved plan in its own checkout, on the strongest coding model in the factory. On a fresh run it branches from the default branch as `factory/<type>-<short-slug>`, for example `factory/bug-dedupe-reset-emails`. On a revision run it fetches the existing branch with `checkout_branch` and pushes to the same branch.

It writes complete, runnable code with no placeholders or stubbed logic, keeps the change minimal, and verifies with the repository's own checks, recording exactly what it ran and what each command produced.

<TypeTable
  type={{
  branch: {
    type: "string",
    description: "The feature branch it pushed.",
    required: true,
  },
  base: {
    type: "string",
    description: "What the branch was cut from. The Reviewer diffs against this.",
    required: true,
  },
  pushed: {
    type: "boolean",
    description: "Whether the push happened. When false, the reason appears in known_limitations.",
    required: true,
  },
  change_summary: {
    type: "Array<{ path: string; change: string }>",
    description: "Per-file summary of what changed.",
    required: true,
  },
  verification: {
    type: "Array<{ command: string; result: string }>",
    description: "Each command it ran and the exact result.",
    required: true,
  },
  deviations: {
    type: "string[]",
    description: "Departures from the plan, each with a reason.",
    required: true,
  },
  known_limitations: {
    type: "string[]",
    description: "Anything the change does not cover.",
    required: true,
  },
}}
/>

<Callout title="The Implementer cannot ask questions">
  Task mode means no station can pause for input. When the plan leaves something genuinely open, the Implementer makes the narrowest reasonable choice and records it in `deviations`. When no reasonable choice exists, it stops, sets `pushed` to false, and explains in `known_limitations`.
</Callout>

## Reviewer

The Reviewer is the quality gate, built to be independent. It runs on a different model vendor than the Implementer, in its own separate checkout, and it never sees the Implementer's reasoning or working state.

It fetches the branch with `checkout_branch` and reads the real diff with `git diff <base>...<branch>`, never judging from the change summary alone. Summaries describe intent; diffs describe reality. Where a claim is cheap to check, it re-runs the fastest of the Implementer's verification commands.

Its review order is fixed: correctness, then each acceptance criterion individually with evidence, then safety, scope, verification adequacy, and quality.

<TypeTable
  type={{
  verdict: {
    type: "'approve' | 'request_changes' | 'reject'",
    description: "approve ships as-is. request_changes means fixable problems. reject means the approach itself is wrong and iteration will not fix it.",
    required: true,
  },
  criteria_results: {
    type: "Array<{ criterion: string; pass: boolean; evidence: string }>",
    description: "Every acceptance criterion from the analysis, judged one by one with evidence.",
    required: true,
  },
  blocking_findings: {
    type: "string[]",
    description: "Specific, actionable problems. Each one blocks approval.",
    required: true,
  },
  suggestions: {
    type: "string[]",
    description: "Advisory notes that do not block.",
    required: true,
  },
  summary: {
    type: "string",
    required: true,
  },
}}
/>

## Researcher

The Researcher runs before the Analyst when a work item turns on a fact the repository and its trackers do not hold, such as an upstream bug, a library version, or a claim to verify.

Like the Classifier, it has only eve's default tools and never checks out the repository. Those defaults are enough on their own, since `web_search` and `web_fetch` cover web research with no extra wiring.

Every claim it returns carries at least one real, fetched source URL. Anything it could not verify goes in `gaps` rather than being reported as a finding.

<TypeTable
  type={{
  summary: {
    type: "string",
    description: "One to three sentences.",
    required: true,
  },
  findings: {
    type: "Array<{ claim: string; sources: Array<{ url: string; title: string }>; confidence: 'high' | 'medium' | 'low'; notes: string }>",
    description: "Each finding needs at least one source. Only cited findings feed the Analyst's message.",
    required: true,
  },
  gaps: {
    type: "string[]",
    description: "What it could not verify, surfaced honestly instead of papered over.",
    required: true,
  },
}}
/>

## Why no station holds a gated tool

Declaring an `outputSchema` puts a subagent in task mode: it must return structured output, and it cannot stop for approval or input. A tool that returned `user-approval` inside a task-mode child would strand the run with nobody able to clear the card.

So station side effects are inert by construction rather than gated by approval. The Implementer's `push_branch` is the only one, and three things keep it safe:

* `validateBranch` refuses `main`, `master`, and anything that is not a plain branch name.
* The push targets the literal repository URL, never the model-writable `origin` remote.
* The installation token is injected at the sandbox firewall, so it never enters the sandbox.

Validated feature branchs on their own ship nothing.

## Next steps

<Cards>
  <Card href="/docs/pipeline" title="The pipeline" description="The narrative version: how these five fit together on a real work item." />

  <Card href="/docs/tools" title="Tool surface" description="Every tool Foreman can reach and the gate in front of it." />

  <Card href="/docs/customization" title="Customizing" description="Change a station's model, procedure, or output contract." />
</Cards>


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)