Three repositories, one workflow
You hand a ticket to one command. It reads the issue, explores the code, writes a plan for you to approve, implements it test-first, has three AI models review the result, commits, opens the pull request and reports back. Three repositories make that possible, and each does a completely different job. This page explains which does what, and how they fit together.
Think about how a crew works. There is a process that knows what order the work happens in, there is the craft knowledge of how things are built in this particular shop, and there are the actual tools and eyes on the job. These three repositories are exactly those three things.
multi-agent-pipeline
The part that knows the order and refuses to skip steps. It drives a ticket all the way to a merged pull request: analyse, plan, build, review, commit, report. It knows what needs your approval, what is a hard stop, and where to loop back and try again.
multi-agent-plugins
The answer to how we build things here, one answer per stack: iOS, Android, frontend, backend and shared. Folder shape, naming, which layer may know what, how tests are written. The pipeline consults this knowledge while it builds instead of carrying it inside itself.
multi-agent-toolkit-mcp
The server that lets the AI actually launch the app, tap a button, take a screenshot, run a build and audit a package. Without it the AI can only read code. With it, it can look at the result of its own work.
They change at different rates, and each is useful on its own. The process can stay still for months. A stack's conventions change whenever that stack evolves. The toolkit grows when a new device capability appears. If they were one repository, every small convention change would force a release of the whole pipeline. The toolkit in particular stands alone: it gives any AI editor control of a simulator, whether or not the pipeline is involved.
The three repositories at the top. The three command-line tools that run them in the middle. At the bottom, the systems a run talks to and the durable trace it leaves behind.
6 phases in order. Two of the arrows go backwards, and those two are the reason this is worth drawing rather than listing.
There are not many unrelated modes to memorise. There are two independent switches - where the work happens, and whether anything is confirmed - and the names are just their useful combinations.
local
This is a question too, asked at Phase 0 Step 5b. A separate checkout of the same repository leaves your own working copy untouched and lets two tasks run side by side. Local works in the checkout you are sitting in, which is simpler when you want to watch it happen, and costs the user-test phase - that gate checks the change out of a separate checkout, and there is none. The local entries answer it up front; autopilot resolves it to a separate checkout without asking, because an unattended commit in your own copy is the thing separate checkouts exist to prevent.
base branch
Phase 0 collects the candidate base branches with the evidence behind each one before it asks. An issue carrying a target version, or linking the issue that represents the release, already names the branch on a repo whose release branches encode the version - so that becomes a ranked row whose description says why, beside rows reading "the repository's default" or "you used this last time". Nothing is hardcoded: any issue field whose schema resolves to a version is read whatever the board calls it, and the branch naming convention is inferred from the branches that exist rather than read off a table. A failed fetch degrades loudly - the list falls back to local refs and the question says so, instead of presenting a weeks-old cache as the remote's answer.
maturity
Phase 0 scores how ready the issue is before anything is built, and until 17.6.0 it scored and stopped: a blocker ended the run, an unattended queue moved to the next item, and the issue stayed exactly as immature as it was found. It now asks. Interactively, at that step: open the issue and fix it, continue without it - and which gap you waved through is recorded, not just that you continued - or abort. Unattended, on the issue itself, if you turned that on: one comment naming what is missing, then it stops and waits. Never a status change, never an assignee, never a close. Edit the issue and the next scan re-checks it, and if the gap closed development starts on its own - an edit is a reason to look again, never proof, so the check re-runs against the new text and decides. The same question is never asked twice.
autopilot
Interactively it stops for the plan, the commit and the pull request. On autopilot it resolves those to sensible defaults, writes down what it chose, works in a separate checkout without asking, and turns on the gates that check its claims. It still stops for a review blocker it cannot fix, a build that will not go green, a circuit-breaker trip and the channel choice at reporting. Under the continuous-mode runner it ends at a draft pull request that a person reviews and merges.
One of those switches is decided purely by which entry you type: whether the run asks you anything. The other is a question the run asks itself at Phase 0, with the evidence it has already gathered in front of you.
You choose how much ceremony a task gets. The full pipeline asks at every step. Autopilot runs the same phases and resolves the defaults itself. Analysis runs 5 of the 6 phases and produces a document instead of code. Every mode runs its whole phase set: no answer during a run adds or removes a phase.
| Mode | Phases | Worktree | Asks you | Dev model |
|---|---|---|---|---|
/multi-agent | 0 1 2 3 4 5 | asked at Step 5b | at every step | Sonnet |
:autopilot | 0 1 2 3 4 5 | yes | only at reporting | Sonnet |
:analysis | 0 1 3 4 5 | no | at every step | Fable |
:resume | 0 3 4 5 | inherited from the run | at every step | none when the branch diff is the input |
A check that cannot fail is worse than no check, because the green tick gets read as coverage. So every gate declares which side of the line it is on, and several deliberately stay closed when the evidence is missing.
| Gate | Phase | Kind | What it prevents |
|---|---|---|---|
| Init exit gate | 0 | stops | A run entering analysis with no task type or design access on record |
| Design access | 0 | stops | UI derived from a text description instead of the design |
| Analysis validator | 1 | stops | A malformed analysis reaching the planner |
| Reuse discovery | 1 | stops | A second copy of something that already exists |
| Plan validator and approval | 1 | stops | An unexecutable or unapproved plan reaching the build |
| Autopilot safety classifier | 1 | pauses | Unattended execution of a plan that deletes or migrates data |
| Build queue lock | 2 | serializes | Two builds racing on the same output |
| Build, lint, tests, secrets | 2 | stops | Spending review budget on a change that does not compile |
| Evidence gate | 2, 4 | stops | A claim that the build passed with no log that shows it |
| Test integrity | 3 | raises blockers | Tests weakened or deleted until the suite turns green |
| Visual conformance | 3 | stops | A component drifting away from its design |
| Diff risk score | 3 | advisory | Nothing. It routes attention, it is not a gate. |
| Secret and attribution hooks | 4 | stops | Committing a token, crediting a bot, force-pushing a protected branch |
Each specialist is defined once and states a tier, not a model name. Every CLI maps that tier onto a model it can actually reach, so one definition works in three places.
| Specialist | Phase | Tier | What it does |
|---|---|---|---|
task-clarifier | 0 | haiku | Scores how ambiguous the request is |
explorer | 1 | sonnet | Scans the codebase in parallel |
ios-architect | 1 | fable | iOS structural judgement |
android-architect | 1 | fable | Android structural judgement |
backend-architect | 1 | fable | API and service judgement |
plan-critic | 1 | fable | One adversarial round on the plan under four lenses, with the quality gates on |
dev-critic | 2.5 | sonnet | Critiques the code before review sees it |
code-reviewer | 3 | fable | Member of the review panel |
security-auditor | 3 | opus | Security and store compliance |
bulk-reader | any | haiku | Summarises one large file so its full text never enters the caller's context |
Continuous mode lets one machine pick work up on its own. /multi-agent:autopilot-on selects the repositories it watches; labelled GitHub issues and assigned, labelled Jira items then join a queue. A launchd tick takes the head of the queue, runs the whole pipeline on it in a separate checkout, and ends at a draft pull request. A person reviews it and merges it. Nothing in the pipeline merges.
research
A run that parks on a maturity blocker or an open analysis question gets a research pass first. It reads the tracker thread, linked items and documents, similar closed items and the repository. A script closes only the gaps a quoted source or a resolving file:line backs, and the original check re-runs and decides. Whatever is still open parks the run with the candidates found.
draft PR
The session never pushes. Phase 4 writes a request, and the runner, outside the session, re-runs the stack's build and tests itself, re-checks the gate ledger, the secret scan and the outbound gate, pushes from a clean staging repository and opens the pull request as a draft. A check that fails leaves the item marked verification-failed with the verdict on disk.
operations
A second launchd agent keeps the Mac awake while the mode is on. Every credential a configured repository needs is probed before the schedule is offered and again on every tick. A circuit breaker stops launching after attempts that produced nothing, a cost ceiling caps spend over a rolling 24 hours, a parallel-agent cap limits sessions at once, and a daily cleanup report is written.
security
Under the unattended flag the Bash guard judges a command only when it reduces to simple commands, and refuses anything it cannot parse. On what it can parse: no push, PR, issue or tracker write, no Keychain read, no fetch outside the network allowlist, no package install. Ticket and PR text reaches the model inside untrusted-data delimiters. The boundary itself is a separate non-admin macOS user plus branch rulesets.
gates
Before a commit, a run's claims about the repository are checked: a localization key or endpoint it references exists, open analysis questions are answered or park the run, a triage citation quotes the line it cites, a pre-existing finding points at a line the diff did not touch, and a test pass shows a positive executed count plus a new test that fails without the change. Every verdict goes to a ledger, and the commit hook refuses a commit whose mandatory gates did not pass.
client
Commands and run questions are declared as data. /multi-agent:serve starts a localhost server over the same JSON the CLI prints, with a manifest, types and fixtures a client builds against. Four phone routes read redacted runs, answer a parked question with one of its offered options, and, only when switched on, queue a launch. Every request carries a signature from an enrolled device.
How you build a screen in an iOS project has nothing to do with how you add an endpoint in a backend. The pipeline does not carry those answers inside itself. It asks that stack's plugin. Six plugins carry 253 skills between them; each versions on its own.
| Plugin | Stack | Covers |
|---|---|---|
ai-ios-toolkit | iOS / SwiftUI | Architecture, code style, file placement, navigation, forms, previews, Figma to component, Code Connect, HIG and design review (Liquid Glass, SF Symbols, app icons, haptics, iPhone Duo), Apple's own Xcode skills first where Xcode provides them, build and test, a new project scaffold |
ai-android-toolkit | Android / Compose | Architecture, state, navigation, testing, Gradle, a new project scaffold |
ai-frontend-toolkit | React / TypeScript | Components, state, routing, styling, accessibility, a new project scaffold |
ai-backend-toolkit | Python / Node | Creating and evolving endpoints, auth, data, validation, security, the OpenAPI spec, a new service scaffold |
ai-common-toolkit | every stack | Accessibility audit, plain-language editing, security review, backlog, decision council |
ai-analyst-toolkit | every stack | Citable evidence (issues, releases, registries) kept apart from advisory signal (forums) |
index
One entry point that decides which skill should answer a given request.
reference
Knowledge to read, not steps to run: layers, naming, file placement, which unit may know what.
workflow
Create a screen, evolve a component, fix a bug, scaffold a new project, branch and open a PR. Imperative and ordered.
tools
Figma data, Code Connect mappings, component documentation, resource registries.
knowledge
Framework and API depth. Never hand-written here: it is generated from the pipeline, and the next build overwrites it.
A model being able to read code does not mean it knows whether what it wrote works. This server closes that gap: it can launch the app, tap it, take a screenshot, run a build, and audit the package before submission. All 197 tools over a standard MCP connection.
| Family | Count | What it does | Who uses it |
|---|---|---|---|
ios_* | 42 | Drives the simulator: screenshots, taps, typing, permissions, locale, location, push, appearance; plus a build wrapper and result drill-down | Device checks in review, design check, store readiness |
android_* | 33 | The same surface for the emulator: install, navigate, permissions, dark mode, font scale, logcat, APK audit | The same consumers, Android side |
web_* | 35 | A real browser page: navigate, click, type, read text, evaluate, screenshot, an accessibility snapshot whose refs the other tools accept, console and network capture, a bounded crawl | Frontend work and verification |
design_* | 7 | Opens the app in mock mode, enumerates every state, compares it to the Figma variant pixel by pixel | The design check command |
agent_* | 2 | Runs an array of steps in a single round trip, and queries a large tool result offloaded to a file | Multi-step manual test paths |
code_* | 8 | Language-server queries: definition, references, hover, workspace and document symbols, diagnostics | Navigating unfamiliar code in Dev and Review |
context_* | 4 | A local full-text index over repositories and documents: index, search, fetch | Analysis and research without pasting whole files |
research_* | 2 | Web search and cited answers through a configured provider | The research step in attended runs |
security_* | 2 | Offline CVSS 3.1 scoring and a lockfile dependency inventory | The security review command |
pass_* | 4 | Builds, signs, inspects and validates Apple Wallet passes, and reports the signing identity and its expiry | Wallet pass work |
media_* | 1 | Extracts scene-change frames from a video | Reading a screen recording attached to a bug |
The server runs independently of the pipeline. It connects to any MCP client, because it is a plain stdio MCP server. Some pipeline skills declare a minimum version, so when the server moves it has to ship: the sync step has its own gates, from a syntax check to package completeness to the advertised tool count matching the real one.
The same tree installs into three places, in three different shapes, because the three hosts disagree about how skills are discovered and how much of a prompt survives.
Codex collects the name and description of every skill it finds into one block, and silently drops the overflow. It does not warn. This was measured, not assumed.
The pipeline supports one operating system, and says so where a machine can act on it. Every credential read goes through the macOS Keychain, every iOS build through xcodebuild, every piece of visual evidence through simctl. A run on another platform would not shrink into a smaller run; it would fail partway through, after creating a worktree and a branch.
| Where | What it does |
|---|---|
package.json | Declares os: ["darwin"], so npm refuses a non-macOS install before a single file is written |
index.js · install | Refuse a non-darwin host with one line naming the requirement; uninstall, help and --version stay available |
doctor | Runs a host-platform check first, so a wrong host gets one honest line instead of a cascade of failures |
A task rarely arrives self-contained. Six typed fetchers pull in the surrounding evidence, and six channels write the result where people are already looking. Neither has an address or a secret hard-coded: addresses come from preferences and secrets from a keychain mapping.
| Source | What it brings in | If it fails |
|---|---|---|
| Confluence | Feature specification pages | The run continues |
| Swagger / OpenAPI | Endpoint contracts | The run continues |
| Crashlytics | The stack trace behind a reported crash | The run continues |
| Graylog | Service logs for a transaction id | The run continues, but not silently |
| Fortify | Security scan findings | A critical finding stops review |
| Figma | Design truth: screenshots, tokens, component mapping | If all three tiers fail, the run stops |
| Channel | What it writes | The rule it respects |
|---|---|---|
| Pull request | A description with fixed sections: summary, changes, architecture, verification, dependencies, related | A Bitbucket update must not clear the existing reviewers |
| Jira | One comment: summary, test scenarios, source references | It writes once, no matter how many PRs opened |
| Confluence | A page under a parent you choose | Design frames upload as attachments, never hotlinked |
| Wiki | A component page, per-platform sub-pages, screenshots, a status board | It only opens for component work |
| GitHub issue | A comment plus a progress-flag update in the issue body | The issue is never closed, and comment and flags are both or neither |
| PR review | One inline comment per finding, anchored to a line, plus a review state | One big lumped comment is forbidden |
Most of what follows exists because the opposite behaviour shipped once and cost something.
And passing means a log that shows it. A self-assertion does not count as a build.
Auto-close keywords are forbidden in commits, PR titles and bodies. Closing is a human decision.
The cleanup step has a safe skip path and never force-removes.
If a variant, spacing or string is missing, the run stops and asks for the analysis to be regenerated. Substituting something close has cost rebuild rounds.
Not in commits, not in PR bodies, not in code comments. The author is always the user's identity.
If a question is submitted blank it is asked again, never resolved to a default.
Every number describing how many commands exist is computed from the tree, and a gate requires the prose to agree.
The runner fails a suite that exits successfully having checked nothing. Three suites were doing exactly that.
On top of those sit the smoke suites, a separate parity gate per host, an install fingerprint that catches structural drift, and a registry where a command name may only disappear through a recorded rename.