Implementation · Changes 1–3

Factory Build Guide

How to stand the factory up for real. Every file named here is in this repo: a Claude Code plugin, 12 role charters, a runner for the OCI box, and a project template you copy into each monorepo.

The core idea

A role is eight things, not a prompt

A persona that only lives in a prompt drifts and can't be held to account. Each role in the kit is a folder: roles/<role>/ROLE.md for people and models to read, and role.json for the runner to enforce.

1 · Charter

Job description

Mission, what it owns, output contract, definition of done, hard limits, when to escalate. ROLE.md

2 · Context

What it knows

PROJECT.md (thesis, users, north star), AGENTS.md (repo map), plus live inputs pulled each run.

3 · Tools

What it can use

An allowlist of tools and MCP servers in role.json. Everything else is refused (permissionMode: dontAsk).

4 · Access

What it can touch

Its own secrets file and narrowly scoped tokens. Read-only by default. Builders can't push to main or edit tests.

5 · Trigger

When it runs

A schedule (systemd timer), an event (issue label, PR, alert webhook), or a session (inside a builder run).

6 · Output

Where work lands

Structured GitHub issues, labels, PR comments or files. Another role or script must be able to consume it.

7 · Model

Who does it

Opus for judgment, Sonnet for volume, a different vendor (GPT-6) for QA so the checker isn't the builder's twin.

8 · Scorecard

How it's judged

Two or three numbers per role, computed weekly from GitHub and the run log. The Coach reviews them.

+ · Memory

What it remembers

Its own past issues (it reads its last four runs), plus memory: project for session agents. No hidden state.

Where each role runs

Three runtimes, one queue

GitHub Issues and labels are the queue and the state machine. That keeps every hand-off visible, auditable and in one trust boundary, with no custom database.

RoleRuntimeTriggerModelTools and access
In-session · Claude Code plugin factory
BuilderContainer per task, claude -p --agent factory:builderDispatcher sees readySonnet; Opus for stop-lane tasksOwn DB clone, Grafana read-only, branch push only, done-gate hook
Design reviewerSubagent inside the builder sessionDone-gate, when UI changedOpusRead, Bash (Playwright, axe); no edits
Event · GitHub Actions in each project repo
Spec Architectclaude-code-action@v1You label a bet approvedOpusRead repo, create issues
QA Leadcodex exec in CIIssue labelled spec; PR ready or updatedGPT-6 AstraWrites only tests/acceptance/<n>; reads private scenarios repo; runs the app and a browser
Verdictscripts/verdict.ts (code, not AI)After QAnoneLabels, auto-merge
Scheduled · runner on the OCI box (Agent SDK, API key)
Chief of Staffrun-roleDaily 07:30SonnetGitHub read-only, Sentry, run log; push via ntfy
Market Scoutrun-roleMon 05:00OpusWeb, Exa, DataForSEO; checks every link
Product Strategistrun-roleMon 06:30OpusPostHog read-only, issues
Outcome Analystrun-roleDaily 06:00SonnetPostHog read-only, issue comments
Janitorrun-role + Sentry webhookNightly 02:00, on alertSonnetSentry, Grafana read-only, create issues
Growth Leadrun-roleTue 06:00OpusPostHog, DataForSEO; no spend, no posting
Support & Voicerun-role via inbox webhookNew messageSonnetInbox MCP, create issues
Designerrun-roleapproved + UIOpusFigma MCP, writes design/ only
Coachrun-roleSun 20:00OpusScorecards, transcripts; opens PRs on the factory repo only
The pipeline

One task, end to end

opportunity→bet→approved · you→spec→QA writes tests→ready→building→QA reviews→verdict→merged · canary

Before code. QA turns each acceptance criterion into failing tests on branch task/<n>. Builders inherit that branch and can't edit those files; the verdict also rejects any later change to them.

During code. The done-gate keeps the builder working until lint, types, dead code, duplication, tests, simplify, review, verify and design review all pass, with evidence written down.

After code. QA runs the real app, hidden scenarios and attacks. verdict.ts decides: AUTO_MERGE, FIX (back to the builder with findings, max 3 tries) or NEEDS_HUMAN.

The manager

Feedback runs on three clocks

"A manager who assesses work and gives feedback" is three loops at different speeds. Only the slowest needs a manager agent; the fast ones are mechanical.

Minutes · per task
  1. QA BLOCKs with reproducible findings on the PR.
  2. Dispatcher re-queues the task; the next attempt reads the findings first.
  3. Three failures, and it lands in your inbox as needs-human.

Mechanism: labels + dispatch.ts

Weekly · per role
  1. scorecard.ts computes each role's numbers from GitHub and runs.jsonl.
  2. The Coach reads scorecards plus best, worst and random transcripts.
  3. It opens one PR on this repo: lint rules first, then missing inputs, then prompt edits.
  4. You approve. Next week's scorecard shows whether it worked.

Plugin changes are tested with claude plugin eval (with vs without, in fresh sandboxes) before merging.

Every miss · you
  1. Your weekly audit finds a bad shipped change.
  2. Label the issue audit-miss with one line on what went wrong.
  3. The Coach must turn it into a permanent check: a hidden scenario, a lint rule or a policy path.

The same mistake twice is a Coach failure, and it's on the Coach's scorecard.

Visibility: set CLAUDE_CODE_ENABLE_TELEMETRY=1 and the OTEL_* exporter vars in the runner's environment (not a repo's settings, where they're ignored). Cost, tokens and tool decisions per session then flow into your existing Grafana.

Tools

What to use for each need

NeedUseWhy, and the alternative
Personas, gates, skillsClaude Code plugin from a marketplace (this repo)One version for every repo and container; update once
Unattended ClaudeClaude Agent SDK + API keySubscriptions are for your attended sessions
Second-vendor QACodex CLI codex exec + OpenAI APIDifferent model family catches more of the builder's blind spots
Queue and stateGitHub Issues + labelsOne trust boundary. Linear if you want its triage UI
Event runtimeGitHub Actions, anthropics/claude-code-action@v1Already wired to PRs and issues
Scheduled runtimesystemd timers on the OCI boxBoring and reliable. Restate if you outgrow it
Builder isolationDocker per task + Postgres 18 template cloneFly Sprites or Daytona if you move execution off ARM
Product dataPostHog MCP; Sentry MCP; Grafana MCP read-onlyAmplitude MCP if you're on Amplitude
Market researchExa MCP, DataForSEO MCP, WebFetch for link checksAhrefs or Semrush MCP
DesignFigma MCP or Claude Design for drafts; Playwright + axe for reviewStitch MCP
Hidden scenariosPrivate repo factory-scenarios, Playwright journeysOnly QA's CI token can read it
Push to phonentfy (private topic)Telegram bot, Pushover
SecretsPer-role env files on the box, chmod 6001Password service accounts or Infisical when you scale
ObservabilityClaude Code OpenTelemetry → your GrafanaLangfuse for transcript search
Rollout

Step by step

Pilot on one repo for the first two weeks. Copy to the others when the scorecard is stable.

Day 1
Change 1

Install the gates in one repo

  • Register the marketplace and plugin for the repo, then commit the settings file it writes.
  • Merge project-template/scripts.example.json into package.json; copy .factory/gate.json.
  • Run /run-skill-generator once so /verify can start the app with its DB and env.
claude plugin marketplace add pungoyal/agent-factory --scope project
claude   # then:  /plugin install factory@agent-factory
pnpm add -D github:pungoyal/agent-factory#path:packages/config knip jscpd dependency-cruiser

a session in auto mode cannot finish without passing checks and writing evidence. You stop typing follow-up prompts.

Day 2
Change 1

Shared rules, baseline cleanup

  • Point eslint.config.js, tsconfig, knip, jscpd and dependency-cruiser at @pungoyal/factory-config.
  • Give one agent the task "make the repo pass the new rules with no behaviour change". Expect a large but mechanical PR.

the repo is green under the shared rules; any later rule change lands everywhere.

Day 3
Change 2

Close the holes

  • Branch protection on main: require the qa-review checks and code-owner review; no direct pushes.
  • Create two GitHub Apps: factory-builder (contents and PRs, branches only) and factory-qa. Remove personal tokens from the box.
  • Move prod, Pulumi and cloud keys into CI with OIDC. Grafana service account: Viewer.
  • Copy .claude/settings.json deny rules and CODEOWNERS. Test that a deny rule actually blocks in auto mode.
  • Postgres 18 on XFS or ZFS; run runner/bin/setup-db-clones.sh; build a scrubbed golden DB per project.

no credential an agent can reach can deploy, delete data or push to main.

Week 2
Change 3

QA Lead and the verdict, in shadow

  • Copy qa-tests.yml, qa-review.yml, spec.yml and scripts/verdict.ts (with its test) into the pilot repo.
  • Repo secrets: ANTHROPIC_API_KEY, OPENAI_API_KEY, FACTORY_QA_APP_KEY, SCENARIOS_READ_TOKEN. Variable FACTORY_SHADOW_MODE=true.
  • Create private factory-scenarios and write 10 journeys for the pilot (this one is yours).

every PR gets a QA verdict and a merge decision you can compare with your own.

Week 2
Change 3

Dispatcher and builders

docker build -t factory-builder:latest runner/builder
cp runner/factory.config.example.json runner/factory.config.json   # edit
install -m 600 secrets/*.env /var/lib/factory/secrets/
systemctl enable --now factory-dispatch
  • Label a bet approved and watch it flow: spec issues, QA tests, a build, a QA verdict.
  • Keep Remote Control for the sessions you drive yourself.

one approved bet reaches a merge-ready PR with no prompt from you.

Week 3
Change 3

Chief of Staff and Janitor

  • Enable timers: factory-role@chief-of-staff:portfolio, factory-role@janitor:<project>.
  • Point a Sentry alert webhook at the Janitor. Subscribe your phone to the ntfy topic.

your day starts with one digest, and production errors become fix tasks without you.

Weeks 3–4
Change 3

Product team on your lead project

  • Wire PostHog (read-only personal key), Exa and DataForSEO into the roles' secrets.
  • Enable Market Scout, Product Strategist, Outcome Analyst, Designer. Write PROJECT.md first.
  • Hold your first 90-minute bet review from the Chief of Staff's bet pack.

you approve bets, not tasks.

Week 4+
Change 3

Coach, then autonomy

  • Run scorecard.ts weekly before the Coach; review its first PR line by line.
  • When shadow verdicts match your calls ≥90% for a week, set FACTORY_SHADOW_MODE=false.
  • Add Growth Lead and Support & Voice on revenue projects, with GROWTH.md and SUPPORT.md written first.

reversible changes ship on their own, and every miss turns into a check.

Only you

What you write by hand

These are the artifacts agents can't be trusted to write, because they define what "right" means.

PROJECT.md per project

Thesis, users, north star, guardrails, budget cap. The Strategist optimises for whatever this says.

Hidden scenarios

10–20 journeys per lead project that must never break. QA runs them; builders never see them.

GROWTH.md and SUPPORT.md

Brand voice, allowed channels, spend cap, what support may promise. The limits for the roles that face the public.

One read of each charter

Edit the twelve ROLE.md files once to your taste. After that, the Coach proposes and you approve.

Before you trust it

Check these against your versions

Repo map