
Agent Fanout
- 3 installs
- 1 repo stars
- Updated July 15, 2026
- gbasin/agent-fanout
Delegates implementation work to parallel headless agent CLIs (codex or omp) in persistent git worktrees, with Claude orchestrating plan, review, and merge.
About
An orchestration skill that decomposes work, launches cheaper headless agent CLIs across disjoint worktrees, then reviews and merges each diff. A developer uses it to parallelize implementation across subagents while keeping Claude as the reviewing orchestrator.
- Runner table for codex (sandboxed) and omp/Gemini Flash with launch flags and gotchas
- Persistent-worktree recipe, disjoint file ownership, and subagent visual QA via dev-browser
Agent Fanout by the numbers
- 3 all-time installs (skills.sh)
- Ranked #13,657 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 24, 2026 (Skillselion catalog sync)
npx skills add https://github.com/gbasin/agent-fanout --skill agent-fanoutAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 1 |
| Last updated | July 15, 2026 |
| Repository | gbasin/agent-fanout ↗ |
What it does
Delegates implementation work to parallel headless agent CLIs (codex or omp) in persistent git worktrees, with Claude orchestrating plan, review, and merge.
Files
Agent fan-out delegation
Claude is the orchestrator: decompose, write precise briefs, review every diff, merge, and own final verification. Headless agent CLIs implement. They are cheaper and faster, not smarter — never merge their work unreviewed.
Runners
Any agent CLI qualifies as a runner if it (a) runs headless and exits when the task is done (completion = process exit, which fires the Bash background notification automatically), (b) operates on the current working directory, and (c) follows a "do not commit" instruction. Pick per phase; mixed fleets are fine.
| Runner | Launch core | Best for | Sandbox |
|---|---|---|---|
| codex (default) | codex exec --sandbox workspace-write ... | substantial phases needing judgment | macOS seatbelt; reads everywhere, writes workspace+/tmp |
| omp (oh-my-pi) | omp -p --no-session --auto-approve --model gemini-3.5-flash ... | mechanical/template phases, high-volume cheap work | none — full user permissions |
Runner notes:
- codex: ALWAYS pass
--sandbox workspace-writeexplicitly — the global
~/.codex/config.toml may set danger-full-access, and a bare codex exec would run unsandboxed. Knobs: -m <model>, -c model_reasoning_effort="high".
- omp:
--auto-approvemeans it can touch anything the user can; the
worktree is discipline, not containment — keep briefs tightly scoped. Model is fuzzy-matched (--model gemini-3.5-flash, --model flash); --thinking low|medium|high trades speed for depth. Needs one-time auth (omp then /login, or a provider key like GEMINI_API_KEY in env) — headless runs fail fast with "Use /login, set an API key..." if missing. Briefs attach via @/path/to/brief.md; capture stdout as the result.
Hard-won architecture rules
1. Never combine the Agent tool's `isolation: worktree` with an async runner. If the launching subagent exits before the runner finishes, the harness reaps the "unchanged" worktree and orphans the still-running task in a deleted directory. Create persistent worktrees manually (below). 2. Launch runners directly via backgrounded Bash; completion = process exit. No status-polling watcher loops. (History: codex-companion background jobs key their state per-cwd, so status from any other directory says "No job found" — which grep watchers misread as "finished".) 3. Visual QA from inside a runner works, with preconditions. Pre-start the dev-browser daemon from the orchestrator before launching (avoids concurrent auto-start races; and under codex's seatbelt, daemon auto-start is impossible — Chrome dies at Mach-port registration, and the task may wedge for hours retrying fallbacks). codex additionally needs -c 'sandbox_workspace_write.network_access=true' or the connect to ~/.dev-browser/daemon.sock is blocked ("Daemon failed to start within 5 seconds"). Screenshot writes happen daemon-side into ~/.dev-browser/tmp/, readable by all runners — they can view the PNGs and check their own work.
Procedure
0. Plan phases
- If the work needs shared conventions (design system, schema, API shape), run
a foundation phase first as a single task, review and commit it, THEN fan out. Parallel phases inherit the committed foundation.
- Give parallel phases disjoint ownership: each owns specific files or
specific functions. Additions to shared files go in a clearly marked appendix block (# === <phase> additions ===) to keep merges trivial.
- Assign runners by difficulty: codex for phases needing judgment, omp+flash
for mechanical ones.
1. Pre-flight (orchestrator, main repo)
# avoid the gitlink trap: embedded worktrees must never reach `git add -A`
grep -qx '.claude/worktrees/' .git/info/exclude 2>/dev/null \
|| echo '.claude/worktrees/' >> .git/info/exclude
# if any phase needs visual QA: warm the daemon OUTSIDE any sandbox
dev-browser <<< 'console.log("daemon warm")'Start from a clean, committed state — runner diffs are reviewed against HEAD.
2. Persistent worktrees (one per parallel phase)
git worktree add .claude/worktrees/<phase> -b wt-<phase>3. Briefs
Write each brief to /tmp/<phase>-brief.md (avoids shell quoting issues). Template:
<one-paragraph goal and context>
You are working in <ABSOLUTE worktree path>. Run `pwd` first to confirm.
If this directory does not exist or is not a git worktree, STOP and report —
do not work anywhere else.
SCOPE — you own ONLY: <files / functions>. Additions to shared files go in an
appendix block marked `# === <phase> additions ===`. Touch nothing else.
Do NOT commit. The orchestrator reviews and merges your working-tree diff.
TEST PROCEDURE (run before reporting):
<exact build/test commands>
# visual check (daemon is already running; use a unique browser name):
cat > /tmp/<phase>-shot.js <<'JS'
const page = await browser.getPage("<phase>");
await page.setViewportSize({ width: 1440, height: 1000 });
await page.goto("file://<path under the worktree>");
await page.waitForTimeout(300);
console.log(await saveScreenshot(await page.screenshot({ fullPage: true }), "<phase>.png"));
JS
dev-browser --browser <phase> run /tmp/<phase>-shot.js
# then VIEW the saved png and verify: <concrete visual criteria>
REPORT: what changed, how you tested (including what the screenshot showed),
known gaps.4. Launch (one backgrounded Bash call per phase)
codex:
cd <abs worktree path> && codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write.network_access=true' \
-o /tmp/<phase>-result.md \
"$(cat /tmp/<phase>-brief.md)"omp (e.g. Gemini Flash):
cd <abs worktree path> && omp -p --no-session --auto-approve \
--model gemini-3.5-flash \
@/tmp/<phase>-brief.md > /tmp/<phase>-result.mdRun with run_in_background: true. The completion notification fires when the process exits — do not write watcher loops.
5. Monitor (only when prompted or suspicious)
Tail the Bash output file. If no new output for ~10 minutes, check git -C <worktree> diff --stat — runners sometimes wedge AFTER the work is done. If the diff looks complete, kill the process and salvage the tree; the work is rarely lost.
6. Review and merge (orchestrator)
git -C .claude/worktrees/<phase> diff > /tmp/<phase>.patch
# READ the patch, check the result file, then:
git apply --3way /tmp/<phase>.patchRe-run build/tests on main after each apply. Overlapping hunks across phases mean the ownership split failed — resolve manually, don't re-delegate.
7. Final QA — yours, not the runners'
Runner screenshots are a first-line filter. Before committing, re-verify the key surfaces yourself (desktop + mobile widths at minimum). Then commit and:
git worktree remove .claude/worktrees/<phase> --force
git branch -D wt-<phase>Failure-mode quick reference
| Symptom | Cause | Action |
|---|---|---|
| codex: "Daemon failed to start within 5 seconds" | daemon not pre-warmed, or network_access not set | warm daemon from orchestrator; relaunch with the -c flag |
| codex stuck retrying browser fallbacks (node_repl, NODE_PATH, --connect) | same as above | same; salvage any completed diff first |
| omp: "Use /login, set an API key environment variable..." | no stored credentials and no provider key in env | one-time omp + /login, or export the provider key; then relaunch |
| omp touched files outside its scope | no sandbox + loose brief | tighten SCOPE wording; review patch hunks before apply (you do this anyway) |
| worktree vanished mid-task | it was harness-managed (Agent isolation) | kill orphans, recreate persistent worktree, relaunch |
companion status: "No job found" | per-cwd state; checked from wrong dir | cd to the exact launch dir; never treat as completion |
| task "running" for an hour with frozen progress tail but full diff present | end-of-run wedge | kill, salvage tree, do verification yourself |
| stray gitlinks in a commit | .claude/worktrees/ not excluded | pre-flight exclude line; fix commit with git rm --cached |
MIT License
Copyright (c) 2026 Gary Basin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
agent-fanout
Delegate implementation work to parallel headless agent-CLI subagents — codex by default, or omp with a cheap model like Gemini Flash — in persistent git worktrees, with the orchestrating agent planning the phases, writing the briefs, reviewing every diff, merging, and owning final verification.
Install
npx skills add gbasin/agent-fanout --all -gWhat it does
The runners do the volume work — cheaper and faster, reviewed before anything lands. The skill encodes the working recipe for orchestrating them:
1. Foundation first — if the work needs shared conventions, run one task, review, commit, then fan out 2. Disjoint ownership — each parallel phase owns specific files or functions; shared-file additions go in marked appendix blocks so merges stay trivial 3. Persistent worktrees, launched directly — completion is process exit, so the background-task notification doubles as the "done" signal; no status-polling watcher loops 4. Patch-based review and merge — read every diff, git apply --3way, re-test after each apply 5. Final QA belongs to the orchestrator — runner screenshots are a first-line filter, not a sign-off
Mixed fleets are fine: codex for phases needing judgment, omp + Gemini Flash for mechanical ones. Any agent CLI that runs headless, exits on completion, and works against the current directory can slot in.
Why it's shaped this way
Distilled postmortem — a real orchestration session hit every failure mode at least once:
- Harness-managed worktree isolation reaped the worktree out from under a still-running async codex task, orphaning it in a deleted directory
- Job-status watchers parsed "no job found" (per-directory job state, checked from the wrong directory) as "finished"
- A codex task wedged for ~2 hours after completing its work, retrying browser fallbacks its sandbox could never satisfy
- Embedded worktree gitlinks snuck into a commit via
git add -A
The visual QA trick
Headless codex under the macOS seatbelt sandbox cannot launch a browser (Chrome dies at Mach-port registration). But it can drive one: pre-start the dev-browser daemon outside the sandbox and run
codex exec --sandbox workspace-write -c 'sandbox_workspace_write.network_access=true' ...The socket connect succeeds, screenshots are written daemon-side, and codex reads the PNGs back to visually verify its own work. Both halves are required — without the network flag the connect is blocked even with the daemon running. Unsandboxed runners (omp) can drive the daemon directly, but pre-starting it still avoids auto-start races between parallel tasks.
Requirements
- At least one runner CLI with auth configured: Codex CLI (
codex exec) and/or omp (omp /loginonce, or a provider key such asGEMINI_API_KEY) - Optional, for visual QA: dev-browser
License
MIT