Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .claude/.claude-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,43 @@ Not distributed by this plugin (repo-scaffolded, project-specific):
- `.claude/workflows/*.js` — deterministic fan-out workflows, not plugin-portable.
- `.claude/self/*` — this repo's OWN self-hosting adapter (gates, checks), not for downstream
projects; downstream adopters get the placeholder `.claude/gates.json` instead.

## Enabling in a consuming project

`marketplace.json` lives alongside `plugin.json`, at `.claude/.claude-plugin/marketplace.json` —
so its "marketplace root" is `.claude/`, the same directory the plugin itself is rooted at. It
lists a single plugin entry, `orchestrator`, with `"source": "./"` (relative to that marketplace
root, i.e. the plugin payload is the marketplace root itself).

A consuming project registers this repo as a marketplace source and enables the plugin from it in
its own `.claude/settings.json`:

```json
{
"extraKnownMarketplaces": {
"ai-project-orchestrator": {
"source": {
"source": "github",
"repo": "robercano/ai-project-orchestrator"
}
}
},
"enabledPlugins": {
"orchestrator@ai-project-orchestrator": true
}
}
```

`extraKnownMarketplaces` registers `robercano/ai-project-orchestrator` (a GitHub repo) as a
marketplace named `ai-project-orchestrator`; `enabledPlugins` then enables the `orchestrator`
plugin from it, addressed as `<plugin-name>@<marketplace-name>`.

**Known limitation:** Claude Code's documented `"source": "github"` marketplace source resolves
`marketplace.json` at the repo **root** (`.claude-plugin/marketplace.json`), with no documented
field to point it at a subdirectory. This repo's `marketplace.json` instead lives at
`.claude/.claude-plugin/marketplace.json`, matching the plugin-root-is-`.claude/` layout described
above. Until Claude Code supports a subdirectory marketplace source (or this repo additionally
publishes a repo-root alias), the snippet above is the intended shape but may require consumers to
add the marketplace from a local clone instead (e.g. `/plugin marketplace add
<path-to-clone>/.claude`) rather than the bare GitHub shorthand. Revisit this note if/when
subdirectory marketplace sources land upstream.
19 changes: 19 additions & 0 deletions .claude/.claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "ai-project-orchestrator",
"owner": {
"name": "Roberto Cano"
},
"description": "Multi-agent orchestration harness: fan out sub-tasks to isolated worktree implementers, gate them, and route results through reviewers.",
"version": "0.1.0",
"plugins": [
{
"name": "orchestrator",
"source": "./",
"description": "Multi-agent orchestration harness: fan out sub-tasks to isolated worktree implementers, gate them, and route results through reviewers.",
"version": "0.1.0",
"author": {
"name": "Roberto Cano"
}
}
]
}
2 changes: 1 addition & 1 deletion .claude/self/checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ json_parse() { node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'u

do_build() {
local rc=0
for f in .claude/gates.json .claude/self/gates.json .claude/settings.json .claude/.claude-plugin/plugin.json .claude/hooks/hooks.json; do
for f in .claude/gates.json .claude/self/gates.json .claude/settings.json .claude/.claude-plugin/plugin.json .claude/.claude-plugin/marketplace.json .claude/hooks/hooks.json; do
if [ ! -f "$f" ]; then echo "build: missing $f"; rc=1; continue; fi
if ! json_parse "$f" 2>/dev/null; then echo "build: invalid JSON — $f"; rc=1; fi
done
Expand Down
174 changes: 111 additions & 63 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,96 @@
# Getting Started

From a fresh repo created off this template to a working orchestrator, in 6 steps. Budget ~20 minutes.
From an existing project to a working orchestrator, installed as a **Claude Code plugin**, in 6 steps.
Budget ~20 minutes.

> **Prefer to be guided?** Run **`/setup-orchestrator`** in Claude Code. It interviews you (project basics,
> module boundaries, gate commands, review/merge config), then does the steps below for you — writes
> `gates.json` + `CLAUDE.md`, fixes `.gitignore`, creates the `module:*` labels, verifies the bot, checks the
> CI gates, and offers to arm the PR loop and (last) `/harden`. This page is the manual reference behind that
> command — read it to understand what the command is doing, or to configure by hand.
> **Prefer to be guided?** Once the plugin is enabled (Step 1), run **`/orchestrator:setup`** in Claude Code.
> It interviews you (project basics, module boundaries, gate commands, review/merge config), then scaffolds
> the files a plugin can't carry into your repo — `.claude/gates.json` + `CLAUDE.md` + the fan-out workflow +
> the CI gate workflow — fixes `.gitignore`, creates the `module:*` labels, verifies the bot, checks the CI
> gates, and offers to arm the PR loop and (last) `/orchestrator:harden`. This page is the manual reference
> behind that command — read it to understand what it's doing, or to configure by hand.

## Prerequisites
- Claude Code installed and authenticated (`claude` runs).
- `node` and `git` on PATH (the gate script uses `node` to read `gates.json`).
- Your project's actual build/test tooling installed (so the gate commands work).

## Step 1 — Create your repo
On GitHub, **Use this template → Create a new repository**. Clone it and open it in Claude Code.
(The template ships agents and config under `.claude/`; Claude Code picks them up automatically.)

## Step 2 — Fill in `CLAUDE.md`
This is project-wide context every agent reads. Either edit it by hand, or paste the **"Draft CLAUDE.md"**
prompt from [`PROMPTS.md`](PROMPTS.md) into Claude Code and let it propose a draft from your codebase, then
trim. Keep it lean — project-wide only.

## Step 3 — Fill in `.claude/gates.json` (the adapter — the important one)
This is the *only* file that makes the generic agents work on YOUR stack. Set:
- **`project`** — name, language, package manager.
- **`modules`** — the map of independent areas + their paths. This is what the orchestrator uses to give each
worker a non-overlapping boundary. Get this right and clashes mostly disappear.
- **`gates`** — the exact shell commands for `build`, `lint`, `typecheck`, `test`, `test_affected`,
`coverage`, `e2e`, `security`. Leave any you don't have as `""` (it's skipped, not failed).
- **`coverage_threshold`**, **`review.lenses`**, **`budget`** (model routing + `max_parallel_workers`),
**`merge.policy`**.

Use the **"Fill gates.json"** prompt in [`PROMPTS.md`](PROMPTS.md) to have Claude infer most of this from your
`package.json` / `Makefile` / CI config, then verify each command runs.

**Sanity-check the gates manually:**
## Step 1 — Install the `orchestrator` plugin
This template ships as a Claude Code plugin named `orchestrator` (plugin root `.claude/`), with a
`marketplace.json` alongside it. Two ways to add it — lead with the one that works today:

**The reliable method today — a local clone.** Claude Code's `/plugin marketplace add` accepts a plain
filesystem path, and this repo's marketplace root is `.claude/`:
```bash
git clone https://github.com/robercano/ai-project-orchestrator.git ../ai-project-orchestrator
```
Then, in Claude Code, inside **your own project**:
```
/plugin marketplace add ../ai-project-orchestrator/.claude
/plugin install orchestrator@ai-project-orchestrator
```
(`/plugin` alone opens an interactive picker if you'd rather browse marketplaces/plugins than type the
commands above.)

**The target flow — a GitHub source (has a known gap today).** The eventual "no local clone" install is to
declare the marketplace straight from GitHub in your project's `.claude/settings.json`:
```json
{
"extraKnownMarketplaces": {
"ai-project-orchestrator": {
"source": { "source": "github", "repo": "robercano/ai-project-orchestrator" }
}
},
"enabledPlugins": { "orchestrator@ai-project-orchestrator": true }
}
```
> **Known limitation.** Claude Code's `"source": "github"` marketplace source resolves `marketplace.json` at
> the repo **root** (`.claude-plugin/marketplace.json`). This repo's manifest instead lives at
> `.claude/.claude-plugin/marketplace.json`, because the plugin root is `.claude/`, not the repo root — so the
> bare GitHub shorthand above may not resolve for you yet. Use the local-clone method until this repo ships a
> dedicated, standalone marketplace repo at its root (a deferred follow-up — it can't be created from inside
> this repo). See `.claude/.claude-plugin/README.md` (once cloned) for the up-to-date detail on this gap.

## Step 2 — Onboard: run `/orchestrator:setup`
With the plugin enabled, run:
```
/orchestrator:setup
```
It interviews you, then writes the files a plugin **cannot** carry into your repo — agents, commands, hooks,
and scripts ship *with* the plugin, so there's nothing to copy or wire by hand for those. What the interview
collects and scaffolds:

- **`.claude/gates.json`** (the adapter — the *only* file that makes the generic agents work on YOUR stack). Set:
- **`project`** — name, language, package manager.
- **`modules`** — the map of independent areas + their paths. This is what the orchestrator uses to give each
worker a non-overlapping boundary. Get this right and clashes mostly disappear.
- **`gates`** — the exact shell commands for `build`, `lint`, `typecheck`, `test`, `test_affected`,
`coverage`, `e2e`, `security`. Leave any you don't have as `""` (it's skipped, not failed).
- **`coverage_threshold`**, **`review.lenses`**, **`budget`** (model routing + `max_parallel_workers`),
**`merge.policy`**.
- **`CLAUDE.md`** — project-wide context every agent reads (what the project is, stack & layout, conventions,
definition of done, merge policy). Kept lean by design.
- **`.claude/workflows/feature-fanout.js`** — the deterministic fan-out workflow (managed; re-stamped on
updates, see [`USAGE.md` → "Updating the plugin"](USAGE.md#updating-the-plugin)).
- **`.github/workflows/gates.yml`** + **`.github/actions/setup/action.yml`** — the CI gate workflow (Step 4
below).
- **`module:*` GitHub labels**, a **bot-account check** (`GH_BOT_TOKEN`), and offers to arm **`/orchestrator:pr-loop`**
and, last, **`/orchestrator:harden`**.

**Sanity-check the gates once it's written** (ask Claude Code to run these, so `${CLAUDE_PLUGIN_ROOT}`
resolves correctly whether the plugin is installed or you're dogfooding this repo directly):
```bash
bash .claude/scripts/gate.sh build
bash .claude/scripts/gate.sh lint
bash .claude/scripts/gate.sh test
bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/gate.sh build
bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/gate.sh lint
bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/gate.sh test
```
Each should run the right command (or say "not configured — skipping").

> **Bootstrap first.** On a brand-new repo there's usually no build system yet, so *real* gate commands will
> **fail** (not skip) — e.g. `pnpm -r build` with no workspace, `forge test` with no `foundry.toml`. Pick one:
> **Bootstrap first.** On a brand-new project there's usually no build system yet, so *real* gate commands
> will **fail** (not skip) — e.g. `pnpm -r build` with no workspace, `forge test` with no `foundry.toml`. Pick
> one:
> - **Scaffold a minimal buildable skeleton first** (workspace manifest + empty buildable packages/stubs that
> build and pass a trivial test), *then* wire the real gate commands. This makes the pilot run (Step 6) work
> build and pass a trivial test), *then* wire the real gate commands. This makes the pilot run (Step 3) work
> immediately and gives parallel workers a green baseline to branch from. Recommended.
> - **Or keep the gates empty** (`""` = skipped) until your first task is an explicit "bootstrap the workspace"
> ticket, and only fill in real gate commands once that lands.
Expand All @@ -55,8 +99,9 @@ Each should run the right command (or say "not configured — skipping").
> `Stop` hook and every agent's "done".

### Choosing `test_affected` per stack
`test_affected` runs on the `Stop` hook after every change, so it should be *fast* — ideally only the tests
touched by the diff. But "test only what changed" isn't free in every stack. Sensible options:
`test_affected` runs on the `Stop` hook (shipped by the plugin's `hooks/hooks.json`, no `settings.json` edits
needed) after every change, so it should be *fast* — ideally only the tests touched by the diff. But "test
only what changed" isn't free in every stack. Sensible options:

| Stack | Cheap `test_affected` | Notes |
|---|---|---|
Expand All @@ -73,21 +118,12 @@ reach for affected-filtering when the full run is too slow to gate on every `Sto
gotcha: any filter that diffs against `origin/main` needs that ref present in the worktree, so `git fetch
origin main` first (or fall back to the full suite) — the per-worktree setup hook in #9 is the place for that.

## Step 4 — Review the agents (usually no change needed)
Skim `.claude/agents/*.md`. They're generic and read `gates.json`, so they typically need no edits. Adjust
`model:` per agent if your routing differs, or add project review skills (e.g. a security/audit skill) and
reference them in `gates.json` → `review.skills`. Run `/agents` in Claude Code to confirm they're detected.

## Step 5 — Decide on hooks
`.claude/settings.json` wires two gates as hooks:
- **PostToolUse (Edit|Write)** → `gate.sh lint` after every edit.
- **Stop** → `gate.sh test_affected` when an agent tries to finish — a **red test blocks completion**, forcing
iteration.
Agents (`/agents` in Claude Code lists orchestrator, implementer, reviewer, test-runner) and hooks ship
generic and read `gates.json`, so they typically need no edits. Adjust `model:` per agent if your routing
differs, or add project review skills (e.g. a security/audit skill) and reference them in `gates.json` →
`review.skills`.

These are inert until you configure the matching commands in `gates.json`. Disable/adjust if you don't want a
hard test gate yet. (The `update-config` skill can help edit settings safely.)

## Step 6 — Pilot run
## Step 3 — Pilot run
Don't unleash the whole army first. Run ONE real task end-to-end:

```
Expand All @@ -98,9 +134,9 @@ Scope it, show me the plan, and wait for my approval before writing code.
Approve the plan, let one implementer run in its worktree, watch the reviewers gate it, review the PR. Then
read [`USAGE.md`](USAGE.md) to scale up, and [`TOKEN_BUDGET.md`](TOKEN_BUDGET.md) before you go parallel.

## Step 7 — Enforce gates in CI (server-side)
## Step 4 — Enforce gates in CI (server-side)
The hooks and `gate.sh` enforce gates *locally*, and the orchestrator runs them before opening a PR — but
nothing stops a human (or a bot) merging a PR whose gates never ran. The template ships
nothing stops a human (or a bot) merging a PR whose gates never ran. `/orchestrator:setup` scaffolds
[`.github/workflows/gates.yml`](../.github/workflows/gates.yml) + a `.github/actions/setup` composite action
that run **the same `gate.sh` gates** on every `pull_request`, reading commands from `gates.json`. It's
adapter-driven — you configure `gates.json`, not the YAML.
Expand Down Expand Up @@ -134,28 +170,40 @@ A copy-pasteable checklist for wiring a new project into the autonomous loop. Se
[`USAGE.md` → "Autonomous loop & the issue queue"](USAGE.md#autonomous-loop--the-issue-queue) for the mental
model (the `module:*` opt-in queue + the owner-approval merge gate) that this checklist wires up.

1. **`.claude/gates.json`** — the only per-project file that must be filled: `project.{name,language,packageManager}`;
1. **Install the plugin and run `/orchestrator:setup`** (Steps 1–2 above) — it writes everything below except
the two GitHub-side actions (4, 6).
2. **`.claude/gates.json`** — the only per-project file that must be filled: `project.{name,language,packageManager}`;
`modules[]` (one entry per independently-ownable area, each with a non-overlapping `path` — these become
both the worker boundaries and the `module:<name>` labels the loop understands; include non-code areas
like `docs` if you want them automatable); `gates.*` (real shell commands, `""` = skip); `coverage_threshold`;
`review.{lenses,consensus}`; `budget.*` (model routing, `max_parallel_workers`); `merge.{policy,baseBranch}`.
2. **`CLAUDE.md`** — project context, conventions, definition of done, merge policy.
3. **Bot machine account** — create it, add as a write collaborator, put `GH_BOT_TOKEN` in `.env` (gitignored).
3. **`CLAUDE.md`** — project context, conventions, definition of done, merge policy.
4. **Bot machine account** — create it, add as a write collaborator, put `GH_BOT_TOKEN` in `.env` (gitignored).
All agent/loop `gh` calls run as the bot via `bot-gh.sh`; only `git` commits/pushes stay on the owner's
auth, so the owner can approve bot PRs. Setup notes live at the top of `.claude/scripts/bot-gh.sh`.
4. **Create the `module:*` labels** matching your `modules[]` names — see the bootstrap note at the top of
5. **Create the `module:*` labels** matching your `modules[]` names — see the bootstrap note at the top of
`.claude/scripts/seed-issues.sh`. Without the label, ADVANCE can never queue the issue.
5. **Server-side gates** — confirm `.github/workflows/gates.yml` runs your gate commands (Step 7 above), and
6. **Server-side gates** — confirm `.github/workflows/gates.yml` runs your gate commands (Step 4 above), and
set branch protection / required status checks on `merge.baseBranch` if your plan supports it.
6. **Arm the loop** — run **`/pr-loop`**. It self-adjusts cadence (FAST when there's ≥1 open PR or ≥1 open
`module:*` issue, else IDLE) but the cron is session-scoped, so re-run it at the start of each session.
7. *(optional)* **Hardening** — `/harden` for the bypass + strict-sandbox profile, see
7. **Arm the loop** — run **`/orchestrator:pr-loop`**. It self-adjusts cadence (FAST when there's ≥1 open PR or
≥1 open `module:*` issue, else IDLE) but the cron is session-scoped, so re-run it at the start of each session.
8. *(optional)* **Hardening** — `/orchestrator:harden` for the bypass + strict-sandbox profile, see
[`HARDENING.md`](HARDENING.md).

## Step 5 — Keep the plugin up to date
When a new version of `orchestrator` ships, update it and re-stamp the files it scaffolded into your repo —
see [`USAGE.md` → "Updating the plugin"](USAGE.md#updating-the-plugin).

## Verification checklist
- [ ] The `orchestrator` plugin shows as enabled (`/plugin`).
- [ ] `CLAUDE.md` describes the project and lists modules.
- [ ] `.claude/gates.json` has real commands; `gate.sh build|lint|test` behave correctly.
- [ ] `/agents` lists orchestrator, implementer, reviewer, test-runner.
- [ ] `/agents` lists orchestrator, implementer, reviewer, test-runner; `/orchestrator:*` commands resolve.
- [ ] A pilot task produced a branch/PR that passed gates + review.
- [ ] CI gates run on PRs and are set as **required** status checks on the base branch (Step 7).
- [ ] CI gates run on PRs and are set as **required** status checks on the base branch (Step 4).
- [ ] You've checked spend with `/cost` or `npx ccusage`.

## Migrating an existing hand-copied install
Already have `.claude/` copied wholesale into a repo from before this was packaged as a plugin? See
[`MIGRATION.md`](MIGRATION.md) — it covers what to delete (now carried by the plugin), what to keep
(your adapter/`CLAUDE.md`/workflow/CI files), and how to switch to the plugin install above.
Loading
Loading