Master-orchestrator workflow for herdr, native harnesses (claude/codex/pi lanes). Fire when inside a herdr pane (HERDR_ENV=1) and the captain hands over a spec, feature, or read-only evaluation to orchestrate across mixed harnesses. All-pi runs go to herdr-orchestrate-pi instead.
Installs into .claude/skills of the current project.
Are you the author of Herdr Orchestrate?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/alexhooi-herdr-orchestrate)
---
name: herdr-orchestrate
description: "Master-orchestrator workflow for herdr, native harnesses (claude/codex/pi lanes). Fire when inside a herdr pane (HERDR_ENV=1) and the captain hands over a spec, feature, or read-only evaluation to orchestrate across mixed harnesses. All-pi runs go to herdr-orchestrate-pi instead."
---
# Herdr Orchestration v2
Precondition: `test "${HERDR_ENV:-}" = 1` — otherwise say so and stop.
All lane plumbing is one tool: `<this skill dir>/bin/herd` (on PATH or by absolute path) — every command below is `herd <verb>`. Run it from the project directory (or set `HERD_PROJECT`); it keeps a ledger in `<project>/.herd/ledger.json`.
Preflight once per session:
- `herdr integration status --outdated-only` — update anything listed; outdated integrations re-raise trust dialogs on spawn.
- **Find the project first.** If the cwd holds no `.herd/` and no spec and the brief carries no absolute path, locate the project (mdfind/spec search; disambiguate siblings by ledger lane prefixes and freshness), then `export HERD_PROJECT=<abs>` and run every `herd` command from there. Never spawn from `$HOME` — herd refuses to create a ledger there.
- `herd status` — an existing ledger means you are **resuming**: adopt it (see Resume), don't spawn duplicates.
## Layers
Captain ↔ **root orchestrator** (product shape, taste, spec-writing; where the captain lives) → **this session = ops** (route, watch, review-route, land, sweep) → **one owner lane** per spec (decides whether and how to split, spawns peers, owns integration + acceptance) → peers. Ops never designs, slices, implements or reviews; root never touches lanes. The captain zooms into ops only by choice.
## Roles
| lane name | kind | takes |
| --- | --- | --- |
| (this session, ops) | claude | routing, triage, status. NEVER implements, reviews, slices or designs seams |
| `impl-fable[-<name>]` | claude (Fable, high) | default owner: holds the whole spec — architecture, integration, acceptance — spawns Astra/Opus/kimi peers for what it won't hold. Fable typing well-specified code itself is a routing smell |
| `impl-astra[-<name>]` | pi (`-- --model openai-codex/gpt-6-astra:high`) | owner for well-specified specs, or peer for scoped chunks |
| `impl-opus[-<name>]` | claude (`--model claude-opus-5-5 --effort high`) or pi (`-- --model claude-bridge/claude-opus-5-5:high`) | Astra-equivalent: owner or peer, same as Astra; pick over Astra when codex sandbox/receipts are in the way, or to spread quota |
| `frontend-kimi` | pi (`-- --model moonshotai/kimi-k3:high`) | design code / frontend ONLY, any platform (web, SwiftUI/native) — never backend or review; assets via the Higgsfield MCP directly |
| `review-astra` | pi (`-- --model openai-codex/gpt-6-astra:high`) | reviews fable-implemented work (cross-model) |
| `review-fable` | claude (Fable 5.1, `--model claude-fable-5-1`) | THE code reviewer — reviews astra/opus/kimi-implemented work |
| `review-opus` | claude (`--model claude-opus-5-5 --effort high`) | Astra-equivalent reviewer: reviews fable-implemented work when no Astra lane is available, or stands in for review-fable on a Fable-limit stall |
| `review-ui` | pi (`-- --model openai-codex/gpt-6-astra:high`) | reviews frontend-kimi work by DRIVING it — real browser or simulator, never a text-only diff — Astra is the specialist UI reviewer |
| `scout-*` sub-lanes | claude (`--model claude-sonnet-5`) or pi (`claude-bridge/claude-sonnet-5:medium`) | search, recon, read-only fan-out (sonnet floor, never haiku) |
**Delegation = herd panes.** pi lanes run without an in-harness subagent tool (drop the pi-subagents extension); a lane that needs help spawns a herd sub-lane and delegates as much as it wants — every helper is an observable pane. Claude lanes may still use Task for read-only recon at sonnet, nothing heavier. Lane-to-lane `herd send` is legitimate — peers settle interfaces with each other; ops hears only deadlocks. Sub-lanes an owner spawns are namespaced under it (`impl-fable-ui-kimi-1`) and are that lane's to watch, review-route, and tear down — the orchestrator sees only the parent's report. Sub-lane `--cwd` is the project root or a worktree, never a subdirectory (nested `.herd/` = forked ledger, unledgered lane; spawn warns). Codex/sandboxed lanes never own build receipts (`xcodebuild`, SwiftPM manifest resolution write to `~/Library/Caches` — seatbelt blocks it): the orchestrator runs the receipt itself and reviewer briefs pre-declare it as orchestrator-verified.
## Spawning
`herd spawn` (syntax in the lifecycle block below) bakes in the verified per-kind launch flags (single source: `KIND_ARGS` in `bin/herd`) — approvals and sandboxing are set at launch so routine dialogs are prevented, and codex gets `--no-alt-screen` (without it completed responses are unrecoverable from scrollback). Workers are visible interactive panes the captain can watch and interrupt. Extra native args go after `--`. `--profile <name>` (pi lanes) runs the lane under `~/.pi/agent-<name>` — shared auth/models by symlink, own settings/extensions (e.g. a lean profile for models that choke on heavy extensions).
Pi lanes with a `--model` override get verification built into spawn: `"model_verified": true|false` in its JSON against the pane breadcrumb. Send work only on `true`; on `false`, fix in place (`herdr agent prompt <lane> "/model <provider/model>"`), re-check. For claude/codex lanes, confirm the model in the pane banner after any restart — herdr can restore a default model; fix in place rather than respawning.
Spawn lazily on first task; `herd spawn` is idempotent — a live same-kind lane is adopted (also the resume path). Each lane gets its own tab; `--worktree` gives it a managed worktree on branch `lane/<name>` instead. `HERD_WORKSPACE=<id>` targets new tab lanes at a specific herdr workspace.
## Lane lifecycle (the whole loop)
```text
herd spawn <lane> --kind <claude|codex|pi> [--worktree] [--cwd DIR] [--profile NAME] [--no-nudge] [-- extra-args]
herd send <lane> --file prompt.md [--state implementing] # or inline text / stdin
herd watch <lane> [--text] --timeout 1200 # implementing lane
herd watch --any <lane> <lane> ... [--text] --timeout 600 # first lane wins; reviews ~600s
herd send <lane> --review --state review --file review-prompt.md
herd send <lane> --refocus # replay the lane's recorded brief (task→slice→spec intent) after a compaction or drift; keeps the outstanding report token
herd triage <project>/.herd/findings-<lane>-N.json --backlog <backlog-file> [--promote ID[,ID...]]
herd land <lane> # honors ship_mode; conflict -> handback
herd close <lane> [--integrated] # closes tab / removes worktree; --integrated: dirty worktree whose files the parent already integrated
```
**spawn** pre-seeds the CLI's own trust store for the lane's cwd (codex `~/.codex/config.toml`, claude `~/.claude.json`; pi via `--approve`) so folder-trust dialogs never appear; anything pretrust can't handle falls to the exit-3 path. It also gitignores `.herd/` in the project root.
**send** appends a unique per-turn `REPORT-END-<hex>` token and verifies delivery. herd answers no dialog, ever: a blocked lane makes send exit 3 with the pane excerpt — answer it yourself (`herdr agent send-keys <lane> ...`), then resend. Concurrent sends to different lanes are safe.
**watch** blocks until the lane's token appears as a lone line AND the agent has settled — run it in the BACKGROUND, one watch per lane; a foreground watch freezes your whole turn. Tokens are single-use: a re-watch with nothing pending waits instead of matching stale pane output; `--resume` (resumed sessions) turns an already-consumed token into success reason `already-reported`; a newer send supersedes an attached watch with a fast `superseded` failure; a deliberately closed lane returns reason `closed`, not a crash. Escalation exits: 2 agent gone, 3 dialog, 4 timeout — all self-notify, so a stalled lane is never silent; on a timeout that is not `advanced:true` (below), escalate rather than looping. On exit 3, answer the dialog yourself (`herdr agent send-keys`), confirm the pane moved, re-attach — only questions you cannot answer go to the captain: zero captain involvement is the bar, zero orchestrator involvement is not. **Watch the lane, never the artifact**: an output-file wait has no blocked-escape and turns a stuck worker into silence — the only legal waits are `herd watch` and its exit codes. `watch` also reads the token from the lane's pi session jsonl when compaction redrew the pane over the report (pi-blackhole compacts at agent_end, right after the report turn — the report surfaces in the watch tail, no wait), and nudges an idle lane ONCE for its report when the token is truly missing (non-coercive: "if still working, do not reply"; after `--nudge-after` seconds of quiet, default 600; per token, ledger-recorded) — spawn orchestrator / pseudo-orchestrator lanes with `herd spawn --no-nudge` (or watch `--no-nudge`): they idle on their own sub-lanes by design — and accepts a review lane's findings file as completion (reason `findings-file`). Exit 4 carries `agent_status` and `advanced`: `advanced:true` = the lane is still moving — re-arm the watch, don't escalate. Size fix-round timeouts to the handback: 8+ findings or an engine rewrite → `--timeout 3600`.
Every worker prompt carries:
- the whole spec with product-level acceptance (owner), or the chunk the owner handed off (peer) — never a method; lanes delegate per the rule above (sub-lanes: Astra or Opus 5.5 for well-specified code, kimi for UI, sonnet scouts for recon) and own their lifecycle;
- "For exploration/search subagents use `model: sonnet` — the floor tier; keep your own tier for reasoning and synthesis."
(Report-footer and sentinel are herd's job — don't add your own.)
**On any pane weirdness** — an unexpected watch/send exit, an empty read, a mid-run tool update, a spawn startup timeout, a missing toast, a codex sandbox denial — read `LORE.md` in this skill dir before diagnosing.
## Routing
**Flat by default: the spec goes whole to ONE owner lane.** The owner (Fable, Astra or Opus — never kimi) decides whether to split at all, spawns peers for what it won't hold (kimi for UI, Astra/Opus for scoped code, sonnet scouts), and owns integration and product-level acceptance. Ops slices only when the captain/root hands over specs already split. Peers negotiate interfaces with each other directly over `herd send` — the lanes that will live with a seam settle it, record the decision in their briefs, and move on; ops arbitrates only a reported deadlock, never pre-authors contracts. Atomizing into tickets produces modules that pass in isolation and no product; the owner's brief is the whole spec.
Acceptance is product-level: "the user can do X", never "module Y's tests pass". **Run the final thing yourself** before calling anything done — drive the real UI hands-on with realistic data volumes, and walk anything web-facing at mobile/tablet/desktop widths (390/768/1440). Lane test suites catch what they were written to catch — a 41-assertion real-browser run once still shipped a mobile layout broken at every width.
**Read-only / evaluation mode** (audit, "what's missing", UX review): route analysis slices per the table, prefix every prompt "READ-ONLY — do not edit, write, or create files", skip the review matrix, synthesize the lanes into one Artifact — findings ranked, evidence as file:line. Teardown still applies.
## Shipping modes and collisions
`herd set ship_mode scratch|merge|pr` (per project, in the ledger; default scratch).
- **scratch** — lanes work directly in the project tree; review the git diff before the captain declares done. Scratch lanes get disjoint files. Commits land on the project's default branch, no push — that is the explicit go that satisfies "never commit to the default branch without owner go" in global AGENTS.md/CLAUDE.md.
- **merge** — implementers spawn `--worktree`, commit on `lane/<name>`. When review clears: `herd set <lane> state=reviewed`, then `herd land <lane>` merges (`--no-ff`). Land enforces the gate itself (refuses unreviewed or dirty states with exit 4 — for land/close, 4 means refused precondition, not timeout) and exits 3 on conflict with the files: hand the owning lane "merge main into your branch, resolve, keep both behaviors, re-run your checks", scoped re-review, land again. Conflicts are the owning lane's, never the captain's.
- **pr** — like merge, but `herd land` refuses to land locally; push and PR are the lane's manual steps (`git push -u origin lane/<name>`, then `gh pr create` per project docs). Herd doesn't enforce the review gate here — the PR review is the gate.
## Review matrix and bug loop
UI slices are driven for real before review — browser for web, simulator for native (an AR/camera slice also needs a physical-device pass).
- Owner reports done (its peers are its own to gate) → ONE review pass over the combined diff: Fable owner → review-astra (review-opus if no Astra lane); Astra owner → review-fable (Fable 5.1); Opus owner → review-astra (cross-vendor) or review-fable. **Never review a peer's chunk alone** — the whole spec at once is what gives the reviewer blast-radius judgment for cuts. Cross-model: pick the reviewer opposite the model that wrote most of the batch. Reviewers are tab lanes, no `--worktree` — the branch diff is visible from the project repo. Adversarial: refute-first, actionable findings only. Reviewer independence: a reviewer never also gets its sibling implementer's work in the same task.
- frontend-kimi (usually the owner's peer) done → **reviewed before the captain ever sees it** by a `review-ui` lane (Astra) armed with the spec — it DRIVES the real UI (browser for web, simulator for native): flows, validation, empty/error states, every viewport width, realistic data; findings arrive via `herd send --review`. The orchestrator reviews personally only when no Astra lane is available (it holds the product context). When the pass is clean, `herd set frontend-kimi state=reviewed` (landing needs it), then present what shipped (screenshot/URL/diff) for taste-level judgment; the captain is never the one to report "text box overflows on mobile." Other lanes don't gate on it.
- `herd send --review` makes findings arrive as data in `.herd/findings-<lane>-N.json` (herd gives the reviewer the format) — no finding is transcribed by hand.
- `herd triage <findings.json> --backlog <file> [--promote ID[,ID...]]`: disastrous/architectural/blocking findings and explicitly promoted IDs print for handback (send to the implementing lane verbatim, scoped re-review after the fix); the rest append to the backlog (project tracker or your own todo file) without interrupting anyone.
**Deslop is part of every review, not a separate pass.** The cut lens rides alongside the bug lens: one-caller helpers with no depth, defensive paths for impossible conditions, speculative flags/seams, comment bloat, implementation-pinning or duplicate tests, slow tests (worst `--durations`), stray files. **Tests pin product behavior through its interface** — a module-shaped or mock-exercising test is slop: cut it or rewrite it against the product surface. Keep-flags are mandatory: flag load-bearing code that merely looks like ceremony, with the reason — cutting it is the costly failure. Slop findings skip the backlog: safe cuts ride the handback and land with the slice; only risky cuts are backlogged. A whole-repo audit lane is an occasional tool for accreted fat, not a per-slice step.
A slice is done when review passes or all remaining findings are backlogged — and the orchestrator has verified the product.
## Captain contact points
Exactly two kinds: decisions only they can make (unknown dialogs, real worker questions, scope calls), and completion. Both get `herd notify "<title>" --body "<one line>" [--sound done|request]` — toast, falling back to a macOS notification — AND the same message in-channel: notify accompanies, never replaces.
## Status and resume
`herd status`: lane, kind, state (queued / implementing / review / fixing / user-review / done / backlogged-findings), liveness, task. Keep current with `herd set <lane> state=<s> task=<one-liner>`.
A fresh session resumes from the ledger: re-adopt live lanes with `herd spawn <lane> --kind <kind>` (idempotent), confirm each lane's model banner, re-attach a background `herd watch <lane> --resume` for every lane in implementing/review/fixing (tokens persist in the ledger, and consumed tokens return `already-reported`), and pick up the review obligations the states imply. Never re-send a slice a live lane already has.
## Teardown
**Etiquette — every lane cleans up what it opened before it reports done**, and the orchestrator verifies before `herd close`: browser tabs/windows/instances it launched (never the captain's own Chrome), booted simulators it booted, dev servers, log tails, recordings, device-mirroring sessions, background jobs, scratch files outside `.herd/`. The captain must never come back to ten Chrome instances and a running sim. Gotcha: `osascript … tell application "X"` LAUNCHES X if it isn't running — `pgrep -x` first. **Machine-clean sweep is the orchestrator's last act before the final report** — lanes cleaning up is necessary, not sufficient; you own the end state. Run it and put the result in the report:
```bash
xcrun simctl list devices booted | grep -c Booted # 0, or shut down what a lane booted (`xcrun simctl shutdown <udid>`)
pgrep -fl 'Simulator.app|xcodebuildmcp|mirroir|sim-use|peekaboo|recordVideo|chrome-devtools|--remote-debugging-port' | grep -v pgrep # empty
lsof -nP -iTCP -sTCP:LISTEN | grep -v -e herdr -e rapportd | awk 'NR>1{print $1,$9}' # no dev servers you or a lane started
herd status # every lane closed
```
Kill/close what the run started (never the captain's own Chrome, sims or servers — compare against what was running when you began). A run is not done while any of it is still up.
`herd close <lane>` as work completes: implementer when its slice landed, reviewer when no review is pending, frontend-kimi once committed and presented. Close only settled agents; read a blocked worker's dialog first. Close refuses tabs it didn't create; worktree lanes lose the worktree (branch stays until landed). On spec completion, update your project docs.