Agent skill

Parallel Run Fan-Out

by yzhao062 in yzhao062/anywhere-agents

Fans a task out into independent units that run on a separate Gemini-based agent pool, while the current session only coordinates.

Apache-2.0Auto-check passedAgent Workflows

Install Parallel Run Fan-Out

skills CLI
$ npx skills add yzhao062/anywhere-agents --skill prun -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install yzhao062/anywhere-agents prun --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/yzhao062/anywhere-agents.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/prun .claude/skills/prun && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
prun
GitHub stars
249
Token cost
~9.8k tokens
SKILL.md length
5,622 words
Files
12 (incl. scripts)
Skills in repo
5
Repo updated
First seen
Licence
Apache-2.0

At a glance

Fans a task out into independent units that run on a separate Gemini-based agent pool, while the current session only coordinates.

  • Works in 6 steps: Gate: confirm the task splits into… → Decompose: write one prompt per unit.… → Assign: every unit goes to Agy. Gather… → …
  • Splitting a task into independent units that can run at once
  • SKILL.md covers Overview, Relationship to the native…, When to use and Executors, plus 11 more sections
  • Runs PowerShell, Shell and Python scripts from its folder; calls git, curl and codex

What it does

Every worker unit runs as an Agy process using Gemini through the Antigravity CLI on its own Google AI plan quota, deliberately kept off the Claude account the coordinating session itself needs; a Sonnet subagent or any other Claude-side worker is never a target, and neither is Codex, whose higher-cost quota stays reserved for a separate gatekeeper role. The coordinator decomposes the task, dispatches units to run unattended in a scratch directory or throwaway clone, gathers results as slower units finish, reviews their diffs, and performs the final integration itself.

It differs from the native Workflow tool, which fans a task out across Claude subagents under a deterministic script and spends the Anthropic plan's own usage instead. Unit count follows the actual dependency graph rather than a small fixed cap, and when the Gemini group's quota can't take the next batch, units are queued or deferred rather than silently rerouted onto the Claude account.

When your agent uses it

  • Splitting a task into independent units that can run at once
  • Running a large parallel batch without spending Claude-side quota
  • Running separate research questions or module changes concurrently

Example prompts

  • “Fan this refactor out across the independent modules using prun.”
  • “Run these five research questions in parallel without touching our Claude quota.”
  • “Check current Agy quota before dispatching the next batch.”

Requirements

  • Agy / Antigravity CLI with a Google AI plan
  • A Gemini-backed worker pool

Workflow steps

6 steps, taken from the first numbered list in SKILL.md.

  1. Gate: confirm the task splits into independent, checkable units. Else use a single worker.
  2. Decompose: write one prompt per unit. State the task; for a code-writing unit, that the
  3. Assign: every unit goes to Agy. Gather anything a unit needs from session-internal tools
  4. Dispatch in parallel: run scripts/dispatch-task-agy.py in the background for each
  5. Monitor (do not go idle): launch scripts/monitor.{sh,ps1} ... in the background
  6. Reconcile, then integrate: before integrating, reconcile the ledger: every dispatched unit

What it can do on your machine

Read from SKILL.md and the folder at commit 7ad8abc. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships 10 files in scripts/ (PowerShell, Shell and Python), which the agent can run.

    Shell commands in SKILL.md call:

    • git
    • curl
    • codex

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use git and curl, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Parallel Run Fan-Out loads about 9.8k tokens when it runs. Until then it costs about 152 tokens; SKILL.md has 5,622 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~152
When it runs · the whole SKILL.md, loaded when a task matches
~9.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.

SKILL.md

The full file from yzhao062/anywhere-agents at commit 7ad8abc, republished under its Apache-2.0 licence (© yzhao062). 5,622 words, ~9,833 tokens.

Download SKILL.mdSave it as .claude/skills/prun/SKILL.md (or your agent's skills folder). This skill also uses 11 other files; get the full folder from GitHub.
name
prun
description
Parallel delegation fan-out on Agy. The coordinating session decomposes and integrates while task units run in parallel as Agy processes (Gemini through the Antigravity CLI), never on the coordinator and never on Claude-side workers such as Sonnet subagents. Each unit runs unattended in a scratch dir or throwaway clone and gets follow-up turns while slower units finish. Codex is not a prun executor either. Unit count follows the dependency graph rather than a small fixed cap. Units may read or write code; workers never commit or push, and the session plus the user are the final integration gate.

prun (parallel run)

Overview

prun fans a task out into independent units that run in parallel while the current session only coordinates. Every worker is an Agy process running Gemini through the Antigravity CLI, on the Google AI plan authenticated in agy. The coordinator decomposes the task, dispatches the units, gathers their results, reviews their diffs, and integrates. It never runs a unit itself.

No Claude-side workers. A Sonnet subagent, a Workflow agent, or any other Agent-tool worker draws on the same Claude account as the coordinating session, so a fan-out of them spends that account's quota at the rate of the fan-out. That is the pool the coordinating session itself needs, and it drained fast once prun routed units to Sonnet. Codex is not a prun executor either; its higher-cost quota is reserved for the default /vet gatekeeper role. Exact plan buckets can change, so inspect current Agy quota before a large batch.

Relationship to the native Workflow tool

The native Workflow tool fans a task out across Claude subagents under a deterministic script, with structured output, judge panels, and resume. A Workflow run counts against the Anthropic plan's usage and rate limits, and its agents use the session model unless the script routes a stage to a different Claude model.

prun is the fan-out that stays off that account. Its units run on Agy and use the Google AI plan; the coordinating session spends only the small Anthropic amount it needs to decompose, dispatch, read results, and integrate. prun therefore never starts a Workflow or a Claude subagent, not as a unit, a fallback, or a second panel. When the user explicitly asks for a Claude panel, that is a Workflow run the user asked for, and it happens outside prun. A cross-vendor read on staged work is what /vet is for.

When the Agy Gemini group cannot accommodate the next batch, queue or defer units instead of moving them onto the Claude account. Read the meter with agent-quota, including snapshot age and reset times, and do not silently shrink a genuinely parallel task to an arbitrary two or three workers. The dispatcher's own quota route, described under dispatch-task usage, already stops a unit from launching into an empty group.

When to use

Use prun when the task splits into independent units that can run at once (different modules, separate research questions, parallel analyses). Units may be heterogeneous, and there can be many of them: a dozen or twenty in parallel is normal when the task warrants it.

Do not use prun when the task is one sequential unit, or units depend on each other's output, or a unit's result cannot be checked without redoing it.

Executors

ExecutorQuotaNotes
Agy (agy)Google AI plan authenticated in AntigravityThe only worker. The newest Gemini Flash High the account lists, at high effort; fast, separately funded, and dispatched with full unattended tool permission inside a scratch dir or throwaway clone.
Claude session (this session)Current Claude account; check Settings > Usage for the applicable limits or creditsCoordinator and integrator only, on whatever model is selected. Never a unit.

Rules:

  • Every unit runs on Agy. Research, verification, extraction, cross-checks, and code-writing units in a throwaway clone all go through dispatch-task-agy. The dispatcher gives a unit the same unattended capability as the /vet Agy reviewer, so it can verify numbers, run experiments, and fetch the web. Agy defaults to the newest gemini-*-flash-high the account lists (gemini-3.8-flash-high as of 2026-09) at the CLI's maximum high effort.
  • Never a Claude-side worker. Do not spawn an Agent-tool subagent (Sonnet or any other model) or a Workflow agent for a unit, including as a fallback when the Agy pool is short. Those workers spend the coordinating session's own Claude account. When Agy cannot take a batch, queue it or tell the user.
  • Codex is excluded from prun. Its quota is intentionally reserved for the /vet reviewer role. Do not route a prun unit to codex exec, even if a legacy dispatcher remains on disk for compatibility with old state directories.
  • Session-internal tools stay outside the fan-out. An Agy process cannot use the coordinator's MCP, email connectors, or Artifact tool. Gather what a unit needs from those tools in the coordinating session before dispatch, and put it in the unit prompt; leave a small action that needs them to the coordinator as one inline step. A task whose substantive work needs those tools throughout is not a prun task.
  • Keep the Agy pool busy with follow-up turns. Units return at different times. When one returns while others are still running, dispatch a follow-up unit rather than idling, provided the follow-up discharges real work: an acceptance criterion the result left open, a claim it made without evidence, a source it cited but did not fetch, a check it proposed but did not run, or the next independent unit in the queue. A slower sibling is not by itself a reason to invent work. --continue-from <state-dir> resumes the same conversation, so the follow-up keeps the earlier context; a fresh prompt with a fresh result path is the alternative. Record each follow-up in the ledger like any other unit.
  • The Claude session stays the coordinator, never a unit. Independent substantive work belongs in Agy workers.

Why Agy alone. Its pool is separate from the Claude plan, it is fast, and it adds an independent model family without spending the higher-cost Codex pool used by /vet. The earlier split put Sonnet beside Agy on the grounds that the two draw on separate pools. They do, but Sonnet's pool is the coordinator's own Claude account, so every Sonnet worker spent the quota the coordinating session runs on, and a wide fan-out consumed it quickly. The coordinator still reviews every result and every diff. Check current quota before a large batch, but do not convert changing meter readings into an arbitrary low worker cap.

Concurrency

The orchestrator decides the unit count autonomously. Partition the task by dependency structure (split only along genuinely independent boundaries) and balanced workload (roughly equal-sized units, each worth a full worker run). High autonomy is the intent: do not target a fixed number, and do not cap artificially. A dozen-plus in parallel is fine when the task genuinely decomposes that way.

Two soft bounds, not hard rules: local CPU/RAM (enough concurrent workers eventually contend and the excess queues) and the headroom of the Agy pool. agent-quota reads the current snapshot of both Agy groups. The usual real ceiling is integration bandwidth, since the orchestrator must read and reconcile every result, so prefer fewer well-scoped units over many tiny ones. Over-splitting into trivial units wastes worker startup and tends to produce thin results. Dispatch in batches that fit the runtime's concurrent-worker limit and the available quota, and leave the rest queued; a runtime's in-flight limit is separate from how many units a run may have in total.

What a unit may do, and the one rule

A unit may read or write code, run commands, and fetch the web, with full access. The single hard rule: a worker never commits, pushes, or runs destructive git (commit, push, branch/tag mutation, reset --hard, clean). Everything else is allowed. The final gate is the Claude session integrating the results and the user deciding; workers never touch the real repo history.

This is enforced structurally, not by trust:

  • Read-only / research units run from a per-unit scratch cwd, so accidental writes stay out of the repo. dispatch-task-agy.py does this by default.
  • Code-writing units run inside a throwaway local clone of the repo with its remote removed:
    git clone --local -c core.longpaths=true <repo> <clone-dir>   # longpaths: Windows MAX_PATH safety
    git -C <clone-dir> remote remove origin
    The worker edits freely in the clone. An accidental git push has no remote to reach (GitHub / Overleaf stay untouched); an accidental git commit only lands in the throwaway clone. The coordinator reads git -C <clone-dir> diff, integrates the wanted changes into the real tree, and the user approves the actual commit. That is the only gate.

The worker's environment is scrubbed. dispatch-task-agy.py builds the child environment with worker_env(), which drops every name containing KEY, TOKEN, SECRET, PASSWORD, PASSWD, CREDENTIAL, APIKEY, or AUTH, and every name starting AWS_. ANTIGRAVITY_* is kept, because the CLI's own session plumbing lives there. The names withheld from a run are listed in <state-dir>/env-withheld; values are never written anywhere. A unit that genuinely needs one variable through gets it with PRUN_KEEP_ENV=NAME1,NAME2.

This exists because the dispatcher previously passed os.environ.copy() straight through. A worker only has to list its environment once, in an env command or a traceback, for a live key to enter a third-party model's context. On 2026-09-20 a scan found two live credentials in 90 files across 18 Agy conversations over three days. A worker never calls a model, so it never needed them.

Beyond that: the user writes the prompts, the clone has no path to the real remotes, and the Claude session plus the user are the integration gate. Scrubbing removes the credential class of accident; it is not a sandbox, and a worker can still read any file the user can.

Flow

  1. Gate: confirm the task splits into independent, checkable units. Else use a single worker.
  2. Decompose: write one prompt per unit. State the task; for a code-writing unit, that the working dir is a throwaway clone to edit freely but not commit or push; that the unit writes a result summary to its result file (a fresh path, in one write).
  3. Assign: every unit goes to Agy. Gather anything a unit needs from session-internal tools first and write it into that unit's prompt. Pick read-only (scratch) or code-writing (clone) mode, and record the mode in the ledger.
  4. Dispatch in parallel: run <python> scripts/dispatch-task-agy.py in the background for each unit. With no --mode it runs accept-edits with --dangerously-skip-permissions in a scratch directory it creates. A caller-supplied workspace, meaning PRUN_SCRATCH_CWD (a throwaway clone for a code-writing unit) or --add-dir (a clone or snapshot the unit should see), requires an explicit --mode accept-edits or --mode plan, so the write-capable mode is a named choice for any directory the dispatcher did not create. --continue-from <state-dir> resumes an earlier unit's conversation for a follow-up turn.
  5. Monitor (do not go idle): launch scripts/monitor.{sh,ps1} <state-dir> ... in the background (run_in_background=true) and wait on its completion. It wakes you on the first actionable event: all done, any unit stalled (tail no-growth for PRUN_STALL_THRESHOLD, default 10 min), or any unit failed (FALLBACK result or dead dispatch), printing a per-unit digest. On a stall, surface it to the user with a likely cause (capacity or concurrency pressure; suggest lowering the worker count or re-dispatching) rather than waiting silently; act, then re-launch the monitor on the still-running units until all are done. monitor only observes. The Agy dispatcher relies on the CLI's bounded --print-timeout; it does not scan for or terminate unrelated agent processes. (gather.{sh,ps1} remains for the plain wait-for-all case.)
  6. Reconcile, then integrate: before integrating, reconcile the ledger: every dispatched unit must have a non-empty result. If any is missing or empty, do not integrate the partial set; recover the worker's output from its <state-dir>/tail (dispatch-task-agy also salvages the tail into the result file automatically under a FALLBACK header). If no usable result can be recovered, re-dispatch that unit or flag the user. Then the coordinator reads each result plus each clone's git diff, merges the wanted changes into the real tree, runs verification, and asks the user before any commit.

Resolve scripts via this order, first hit wins: skills/prun/scripts/, then .claude/skills/prun/scripts/, then .agent-config/repo/skills/prun/scripts/.

dispatch-task usage (Agy)

<python> scripts/dispatch-task-agy.py --prompt-file <prompt> --result-file <fresh abs result> --unit-id <id>
  • Emits exactly one stdout line STATE-DIR <abs-path>; Agy stream events and stderr land in the state directory, the conversation id from Agy's init event is recorded to <state-dir>/conversation-id, and the final response is published atomically to the result path.
  • Defaults to the newest gemini-*-flash-high at high effort. The constants DEFAULT_MODEL (gemini-3.8-flash-high) and SECOND_MODEL (claude-sonnet-5-5-high) are family templates: Gemini Flash and Claude Sonnet. The preflight's agy models listing supplies the newest version in each family, so a new Flash or Sonnet release runs without an edit. The effort tier may change too: Agy 1.2.16 retired claude-sonnet-4-6 for slugs that carry the tier, and the old template would still have reached claude-sonnet-5-5-high. The tier is chosen first (the template's own, then high), and the newest version within it second. An older high therefore beats a newer medium. An unset ANTIGRAVITY_DISPATCH_EFFORT follows the chosen slug's tier, because Agy rejects a conflicting --effort. The switch shows as a MODEL-RESOLVE from=... to=... line on stderr and in <state-dir>/quota-note, and <state-dir>/model names the model that ran. Quota routing picks the group first, and floating never moves a unit to the other group. One exception keeps a batch alive after a larger rename. When Agy lists nothing in the Sonnet family, a unit routed there runs on the Gemini default, recorded as MODEL-FALLBACK ... reason=template-unlisted. The Gemini group's quota is checked again first, so a fallback into an empty group exits 75 without launching. A model named in ANTIGRAVITY_DISPATCH_MODEL runs verbatim, and ANTIGRAVITY_PREFLIGHT=off skips the listing, so the templates run as written. ANTIGRAVITY_DISPATCH_EFFORT overrides the effort. The dispatcher passes --effort for the Gemini models only. Agy rejected the flag for the older Claude models. Since 1.2.16 it rejects any effort that conflicts with the tier in a Claude slug, so the flag is omitted for the second group.
  • Agy Ultra exposes a second quota group for Claude and GPT-OSS models (Claude Opus 5.5 and Sonnet 5.5 at low, medium, and high, and gpt-oss-120b-medium, as of 2026-10), metered apart from the Gemini group. A unit that names no model goes to whichever group has the freer meter, with the newest Claude Sonnet as the second group's model. A unit is shallow work that either group handles, so the meter decides rather than the model family. The worker is the Agy CLI either way, so a Claude model here spends Agy quota and never the Claude account the coordinator runs on. This is a routing policy the user set on 2026-09-15, after a 198-unit batch spent 77 points of the Gemini five-hour meter in an hour while the second group sat untouched. An agent still does not reach for that group on its own outside this rule: one that did spent 646 generations of it in a day. ANTIGRAVITY_DISPATCH_MODEL disables headroom balancing for the run. The exhaustion rules below still apply to the model it names, including the fallback from an exhausted Claude and GPT group to Gemini.
  • The dispatcher checks group quota before launching. The two groups are metered separately, and one dispatch names one model, so a batch aimed at an empty group fails once per unit: on 2026-09-11 four units of a seven-unit fan-out died in a row, each carrying Individual quota reached ... Resets in 34m. Before launching, the dispatcher reads the snapshot agent-quota maintains and decides:
    • No model was named: each unit starts from the Gemini default. When both groups are reported, it moves to the Sonnet model if the second group's lowest remaining fraction is at least 15 points higher, or if Gemini is empty and the second group has quota left. A move on headroom also needs both metered windows of the destination present in the snapshot, since a group entry is its emptiest bucket and an unreported window may be the empty one. An empty own group moves the unit without that evidence, because the alternative is not running at all. Units decide independently, so successive readings can switch the group a batch is using. The line MODEL-BALANCE from=... to=... reason=freer-meter own=... other=... goes to stderr and to <state-dir>/quota-note.
    • A named model in the Claude and GPT group, with that group empty and Gemini not: dispatch the Gemini default instead, and record the swap. The line MODEL-FALLBACK from=... to=... reason=claude-and-gpt-quota-exhausted resets=... goes to stderr and to <state-dir>/quota-note. <state-dir>/model always names the model that actually ran, so the ledger's executor column is not the model the caller asked for when the two differ.
    • A named Gemini model whose group is empty: exit 75 without launching, and say so. A model someone chose is not escalated into the metered group on its own; the message names ANTIGRAVITY_DISPATCH_MODEL for the operator who wants that.
    • Both groups are empty: exit 75 with both reset times.
    • A group the snapshot does not report is unknown rather than empty, and an unreadable snapshot skips the check entirely. The gate stops a dispatch only into a group it read as empty. PRUN_AGY_QUOTA_GATE=off disables it. A run that fails at the backend forces a snapshot refresh before exiting, past the readout's own five-minute TTL, because the meter it just hit is newer evidence than the snapshot. Later units then route on what it recorded. This is not a guarantee: a refresh that cannot run, a meter that is unavailable, and units already in flight can still produce repeated quota errors.
  • --mode defaults to accept-edits with --dangerously-skip-permissions, the same unattended capability the implement-review Gemini reviewer already runs with, so a unit can verify numbers, run experiments, and fetch the web without a permission prompt. The default applies only to the scratch directory the dispatcher creates. When the caller supplies a workspace through PRUN_SCRATCH_CWD or --add-dir, the dispatcher refuses to launch until --mode is given, because the write-capable mode could otherwise reach a directory it did not create. Safety stays structural either way: point those at a throwaway clone with no remote or a read-only snapshot, never the real tree. --mode plan is the strictly read-only opt-in; it keeps request-review permissions and never gets the skip flag. In headless use a tool request that needs an approval nobody can give (run_command, read_url, browser tools) is denied, and the process can still exit 0 with a normal-looking result that reports it could not verify. A normal result file therefore does not prove those checks ran; read its Verification and Open items fields. If the worker has not written a non-empty result file, a missing, empty, or shorter-than-20-byte final response produces FALLBACK, and so does a final result event whose status is not SUCCESS. A standing permissions.allow rule in Agy's own settings.json (~/.gemini/antigravity-cli/settings.json, entries such as read_url(*) or command(*)) is the alternative for a plan-mode unit.
  • --add-dir PATH (repeatable) adds a directory outside the unit's working directory to its workspace without copying a repository into the scratch area, in either mode; it requires an explicit --mode. Point it at a clone or a read-only snapshot, never the real tree, since accept-edits can write there. The dispatcher resolves each path to absolute and refuses to launch if it is empty or not an existing directory.
  • --continue-from STATE_DIR resumes the conversation recorded at <STATE_DIR>/conversation-id for a follow-up dispatch that should keep the earlier turn's context instead of re-embedding the prior result in a new prompt. It still needs its own fresh --result-file; an empty STATE_DIR argument, or one whose conversation id file is missing or empty, is a pre-launch error.
  • Requires a fresh result path and refuses to overwrite an existing result. The final response is published to that path, unless the worker already wrote a non-empty result file there itself: then the worker's file is kept and the final response lands beside it as <result>.response.<ext>, so a one-line closing reply never replaces a full result. If no non-empty worker result exists, a failed preflight, launch, worker run, or timeout, or an unusable final response, produces an atomic FALLBACK result with captured tails.
  • Both signals decide the outcome. A non-zero process exit fails the unit, and after an exit of 0 the final result event's status is consulted, because Agy exits 0 when it stops on a quota limit and that ERROR event still carries the opening narration in response. Publishing that response would hand the coordinator work that never happened. Any status other than SUCCESS fails the unit and carries the event's error text into the FALLBACK result. The one exception is a status that is missing or blank, which counts as success so that an older Agy keeps working.
  • A failed run whose worker had already written its own result keeps that file, because the worker may have finished before the backend stopped. The partial response lands beside it as <result>.response.<ext>, and the dispatcher exits non-zero with the backend error on stderr. Both monitors classify a stable worker-written result as done without reading the backend status. Before integrating such a unit, wait for the dispatcher to finish and check its exit code. When that code is unavailable, read the result event's status and error in <state-dir>/tail and the captured dispatch diagnostics. The sibling response is supporting context: a successful run writes one too, and it records no status.
  • ANTIGRAVITY_DISPATCH_TIMEOUT_SECONDS defaults to 2700 and is passed to Agy's bounded --print-timeout. The dispatcher never enumerates or terminates another agent process.
  • The dispatcher omits Agy's --sandbox flag by default. On Windows that sandbox starts an elevated admin broker and raises a UAC prompt for every unit that runs a command; a declined prompt fails the command. PRUN_AGY_SANDBOX controls whether the flag is added; it does not disable a sandbox enabled in Agy's own settings (enableTerminalSandbox). Accepted values are 1/true/yes/on to add the flag and 0/false/no/off, empty, or unset to omit it. Values ignore case and surrounding whitespace; anything else exits 2 before state creation or launch. Scratch directories and throwaway clones reduce accidental changes to the working repository. They do not enforce filesystem or network isolation; the worker must follow the prompt's ban on commit, push, and destructive git.
  • The Codex worker scripts that prun used before 2026-09-13 are archived in legacy/prun-codex-worker/ in the source repositories and are no longer shipped. They remain available if pricing makes a Codex worker the cheaper pool again. The report-state and snapshot-tail launchers recover old unit state through prun_state.py without them.
Show full SKILL.md (1,998 more words)Show less

gather usage

scripts/gather.sh <result-file-1> <result-file-2> ...
  • Prints GATHER-START count=N timeout=Ss, then DONE <abs-path> per file as it lands; exits 0 when all land, exits 2 with TIMEOUT remaining=<k>.
  • A file is "landed" when it exists, is non-empty, and has been quiet for the stable window (default 10s); no startup-snapshot race.
  • Use a fresh result path per unit per run (delete any stale file before dispatch). Have each unit write its result in one operation.

monitor usage

scripts/monitor.sh <state-dir-1> <state-dir-2> ...
  • Takes the STATE-DIR paths from each dispatch (not result files); reads each unit's tail (growth), result-file (done/fail), and dispatch-pid (liveness).
  • Prints MONITOR-START units=N stall-threshold=Ts timeout=Ss, then on the first actionable event MONITOR-EVENT <all-done|stall|fail|timeout> and one UNIT <name> <status> line per unit (done / failed(fallback) / failed(dispatch-dead) / stalled(Ns) / growing).
  • Exit: 0 all done, 3 attention needed (a stall or fail), 2 hard timeout.
  • Env: PRUN_STALL_THRESHOLD (default 600, ten minutes; raise it for long code-writing units), PRUN_MONITOR_POLL (default 15), PRUN_MONITOR_TIMEOUT (default 3600), PRUN_MONITOR_STABLE_WINDOW (default 10).
  • Run it in the background; after handling a stall or fail, re-launch on the still-running units so a resolved unit is not re-flagged.

report-state usage

scripts/report-state.sh   [--root DIR] [--json] [--summary] [--sort path|tail-bytes-desc]
                          [--min-tail-bytes N] [--include-legacy-pid]
scripts\report-state.ps1  (same flags)

Read-only. It inspects prun-task-* directories left behind by earlier runs and writes nothing at all, which tests/test_prun_report.py checks by hashing the tree before and after a run. Reach for it when a fan-out was interrupted and you need to know which unit output survived. --root repeats, and defaults to the system temp directory.

Every unit carries two independent fields instead of one verdict. A single label such as "salvageable" would read as permission to act, and this command cannot support that reading without the process identity it deliberately does not record.

result_path_stateMeaning
resolvedthe unit recorded a result path and it could be read
absent-entryno result-file entry was written
invalid-entrythe entry was empty, or a relative path escaping its unit
unreadablethe entry exists but could not be read
resultMeaning
presentthe result file exists and holds bytes
emptythe result file exists and is zero bytes
missingthe recorded path does not exist
unknownnothing is claimed: either the path never resolved, or it resolved and the target could not be observed

result is unknown for every result_path_state other than resolved, and resolved may also carry it. Only FileNotFoundError proves a target is gone; a denial or an I/O error yields resolved/unknown plus an entry in that unit's errors, so a failed observation is never reported as an outcome. No other pairing can be emitted, and test_no_illegal_pair_can_be_emitted checks that against the table the module exports.

Remaining JSON fields:

FieldMeaning
schema_version1; bump on any field change
rootsabsolute directories inspected
unit_countunits inspected, counted before any display filter
discovery_errorsroots or matching entries that could not be listed or stated
unitabsolute path of the unit directory
tail_bytessize of the unit's tail, 0 when absent, or null when it could not be stated or is not a regular file
result_targetthe resolved result path, or null
errorsper-unit observation failures; see the table below
legacy_pid_unverifiedshown only under --include-legacy-pid
safetythe sentence below, present on every run

Each errors entry is {"stage": <where>, "error": <value>}. The value is an exception class name, or one of two names for a condition that raises nothing: NotARegularFile when the path exists but is a directory, FIFO, or device, and EntryTooLarge when a result-file or dispatch-pid entry exceeds 64 KiB. That size limit reports rather than truncates. A truncated entry can strip down to a real path and be mistaken for a complete one. Consumers branch on stage:

stageWhat could not be observed
result-entrythe unit's result-file exists but could not be read
result-targetthe recorded path could not be stated, or is not a regular file
resultclassification raised unexpectedly; the unit is still reported
tailthe unit's tail could not be stated, or is not a regular file
legacy-piddispatch-pid exists but could not be read, under --include-legacy-pid

Discovery failures sit apart from any unit, in a top-level discovery_errors array whose entries carry stage (root or unit-entry), the offending root or unit, and error. They are separate because a root that cannot be listed produces no unit to attach a failure to, and used to read as an empty corpus. Any entry in either place sets exit 1.

--summary adds two byte counters that never overlap. missing_or_empty_result covers units whose result path resolved to a file that is missing or empty. unresolved covers units whose result was never classified while their tail still holds bytes. Each counter names what was observed rather than what may be done about it, because neither a missing target nor an empty one proves that no other copy exists or that a live producer will not fill it. Both appear because the second group is easy to lose: across a live corpus of 220 units the first counter read 24.3 MiB while another 0.4 MiB sat in a unit nothing had classified.

Under --json, those counters arrive in a summary object:

Summary fieldMeaning
unitsunits inspected, matching unit_count
by_resultcount per result value
by_path_statecount per result_path_state value
missing_or_empty_result_units / missing_or_empty_result_bytesresolved path, result file missing or empty, tail holds bytes
unresolved_units / unresolved_bytesresult never classified, tail holds bytes
--min-tail-bytes hides small units from the listing and moves no unit between classes; unit_count
still counts them. --include-legacy-pid stays off by default. A recorded PID may be stale, or
reused by an unrelated process, so it can never show that a worker is alive.

Exit codes: 0 every root was listed and every unit inspected cleanly, 1 at least one entry was recorded in a unit's errors or in discovery_errors while everything readable was still reported, 2 a usage error. An unreadable root is never reported as an empty one.

snapshot-tail usage

scripts/snapshot-tail.sh   --unit DIR [--dest DIR | --output FILE] [--json]
scripts\snapshot-tail.ps1  (same flags)

Copies one unit's tail into a ZIP holding exactly two members, tail.bin and manifest.json, both stored without compression. Only a regular file, or a symlink to one, may be snapshotted; a directory, FIFO, or device exits 4 and publishes nothing. Without that rule a device such as /dev/null reported zero bytes and published an empty archive as a complete capture, and a FIFO with no writer blocked the open indefinitely. The copy is byte-for-byte, so a tail carrying NUL or CR arrives unchanged. Given neither --dest nor --output, the archive lands in a per-user state directory: %LOCALAPPDATA%\anywhere-agents\prun\snapshots on Windows, and $XDG_STATE_HOME/anywhere-agents/prun/snapshots elsewhere, falling back to ~/.local/state when that variable is unset.

On POSIX the command creates the directory mode 0700 and the archive mode 0600. A snapshot extends the lifetime of prompts and tool output, so a directory that already exists and is group- or world-accessible is refused, with the chmod that fixes it named in the message.

Publication goes through os.link. That is the one portable operation which is both atomic and refuses to replace: os.replace overwrites, os.rename differs by platform, and checking first races. An existing destination therefore exits 3 and leaves the file byte-identical. Six concurrent attempts on one name produce exactly one winner. Any other link failure exits 6 rather than falling back to an operation that could overwrite.

Manifest fieldMeaning
schema_version1
captured_atUTC timestamp of the capture
source_pathabsolute path of the tail that was read
source_size_at_opensize taken from fstat on the already-open handle
bytes_copiedbytes actually written
sha256digest of the copied bytes, re-verified after the archive closes
source_may_be_livealways true
capture_outcomecomplete_bounded_read when the two counts agree, short_read otherwise
noterecords that equal counts do not prove the source held still

The read is bounded by source_size_at_open, and it is best-effort. Equal counts do not establish that the source held still, because bytes can arrive from different generations of a growing file and still total the same number. Read complete_bounded_read as "the reader returned source_size_at_open bytes before EOF", never as "the source was unchanged" or "this is a consistent point-in-time copy". A truncate-and-regrow sequence can also total exactly that many bytes.

JSON output adds published, the final path, and warning, which is null on a clean run. A warning appears when the archive is linked into place but the temporary file could not be removed. The snapshot is valid in that case, so the command still exits 0.

Exit codes: 0 published, 3 the destination already existed, 4 the tail could not be opened or is not a regular file, 5 archive validation failed, 6 publication failed. Every failure other than 3 leaves no file at the final name.

The safety sentence

Snapshotting a tail is the only safe operation offered here. This output does not establish that deleting, overwriting, or promoting any unit is safe.

report-state prints those words on every run, in both text and JSON. snapshot-tail does not repeat them, so apply them yourself after a successful capture: holding a snapshot does not make the unit disposable. Deciding that a unit is finished needs process identity, which this slice records nowhere. See anywhere-agents#29 Part B.

Return contract (every unit writes this)

# <unit-id> result
Conclusion: <one line>
Files: <files created/modified in the clone, or "none (read-only)">
Open items: <blockers or follow-ups, or "none">
Verification: <what was run/checked/searched, or "none">

<body: the findings, survey, analysis, or change summary>

Ledger

Keep a simple run ledger (a file in a scratch area) recording each unit: id, executor, mode, prompt file, clone-dir, result file, status (dispatched / done / failed), start/end, and the unit's state-dir. Take the executor column from <state-dir>/model, which names the model that actually ran, so a quota fallback shows in the ledger. Use the ledger to report progress and to relaunch only units whose result is missing or fails validation.

Where a unit's own files go: four kinds of file belong under an agent-io directory inside the scratch area. They are the per-unit prompt, the result file, the shared-context file every worker reads, and the run ledger. The directory name tells the writing-style hook to skip them, because none of that text is the coordinator's prose to rewrite. A unit prompt is an instruction to a worker, and a result file holds what the worker sent back. Anything the fan-out produces for a human reader stays outside agent-io.

Web access

Agy runs on the user's local machine, so its requests leave from the user's local network rather than the cloud fetcher's egress IP, often a residential IP. That can reach some pages a cloud fetcher gets 403 on, though a hardened site can still block on bot score, fingerprint, or rate. It also surfaces pages a cloud fetch would miss. The dispatcher's default mode grants the web and the shell unattended, so a worker can fetch through read_url or a local-shell curl. It does not ask for Agy's own --sandbox: on Windows that sandbox starts an elevated admin broker (agy --exebox-admin-broker), which raises a UAC prompt for every unit that runs a command, and a declined prompt fails the command. Set PRUN_AGY_SANDBOX=1 to add the flag where the broker is acceptable. Only --mode plan withholds the web and the shell: it runs Agy in request-review mode, and a headless run denies the permission prompt. The process can still exit 0 with a result that says the fetch did not happen, so read a plan-mode result's Verification and Open items fields before trusting it.

Web units, all on Agy:

  • Discover a page when the URL is unknown: give the unit the question and let it search; ask it to list the candidate URLs it considered, so a thin search shows up in the result.
  • Fetch a known URL: the unit fetches unattended through read_url or curl in the default mode.
  • A page that blocks the fetch: have the unit retry through curl from the local network, and record which path failed and the HTTP status each returned.
  • A high-stakes fact that might be stale or blocked: dispatch a second unit that verifies the claim from an independent source, and have the coordinator compare the two results.

An Agy web-fetch unit can use curl in the default mode (--mode plan denies it). Report the HTTP status per URL so a cloud-vs-local block shows up in the result. In Windows PowerShell, name the binary curl.exe, since a bare curl can resolve to the Invoke-WebRequest alias instead:

bash
curl -sSL -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36" -o <body-file> -w "%{http_code} %{url_effective}\n" <URL>
powershell
curl.exe -sSL -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36" -o <body-file> -w "%{http_code} %{url_effective}\n" <URL>

© yzhao062, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 11 other files (scripts) in skills/prun of yzhao062/anywhere-agents.

  • SKILL.md
  • agents/openai.yaml
  • scripts/dispatch-task-agy.py
  • scripts/gather.ps1
  • scripts/gather.sh
  • scripts/monitor.ps1
  • scripts/monitor.sh
  • scripts/prun_state.py
  • scripts/report-state.ps1
  • scripts/report-state.sh
  • scripts/snapshot-tail.ps1
  • scripts/snapshot-tail.sh

Open the folder on GitHubat commit 7ad8abc

Compare with similar skills

Parallel Run Fan-Out next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Parallel Run Fan-Out compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Parallel Run Fan-Out this skillyzhao062/anywhere-agents249—~9.8kAutomated safety check: PassApache-2.0
UltracodePabloNAX/ultracode-skill187—~3.7kAutomated safety check: PassMIT
Paseo Advisor Second Opiniongetpaseo/paseo20k1 repos~756Automated safety check: PassCustom licence
O2 Review Loopopenobserve/openobserve22k—~3.7kAutomated safety check: PassAGPL-3.0
Kimi Code DelegationCherryHQ/cherry-studio52k1 repos~504Automated safety check: PassAGPL-3.0
Harness Agent Team Designerrevfactory/harness9.1k—~4.5kAutomated safety check: PassApache-2.0

Similar skills

  • Ultracode

    PabloNAX/ultracode-skill

    Run a lightweight Ultracode workflow for serious coding tasks: plan, split, delegate when useful and allowed by the host, integrate, and verify.

    187 GitHub stars~3.7k tokensUpdated 3 mo ago
    Agent WorkflowsAuto-check passed
  • Launches one separate agent through Paseo to give a second opinion on the current task, with a self-contained briefing and no permission to edit files.

    20k GitHub starsUsed in 1 repo~756 tokens
    Agent WorkflowsAuto-check passed
  • O2 Review Loop

    openobserve/openobserve

    Splits a change into planner, coder and independent reviewer roles: you confirm a spec, a subagent implements it, and a separate reviewer checks each round's local WIP commit.

    22k GitHub stars~3.7k tokensUpdated today
    Agent WorkflowsAuto-check passed
  • Kimi Code Delegation

    CherryHQ/cherry-studio

    Delegates one bounded repository task to Kimi Code in non-interactive prompt mode and reads back the final result from its JSON event stream.

    52k GitHub starsUsed in 1 repo~504 tokens
    Agent WorkflowsAuto-check passed
  • Harness Agent Team Designer

    revfactory/harness

    Designs a project-specific agent harness: defines specialist agents, writes the skills they follow, picks an execution mode and model for each, and keeps the setup maintained.

    9.1k GitHub stars~4.5k tokensUpdated 10 days ago
    Agent WorkflowsAuto-check passed
  • ClawTeam Multi-Agent Swarm

    win4r/ClawTeam-OpenClaw

    Launches a swarm of specialist Hermes agents in git-worktree-isolated tmux windows with a kanban board and file-based inboxes, using built-in templates like hedge-fund and code-review.

    1.5k GitHub starsUsed in 1 repo~2.9k tokens
    Agent WorkflowsAuto-check passed

More from yzhao062/anywhere-agents

  • Context-Aware Skill Router

    yzhao062/anywhere-agents

    Reads the working directory, file types and prompt to pick the right domain skill automatically, and decides when a task needs a brainstorm-plan-execute-verify workflow instead of a direct dispatch.

    249 GitHub stars~2.7k tokensUpdated yesterday
    Auto-check passed
  • Editable Figure Designer

    yzhao062/anywhere-agents

    Designs editable PowerPoint overview figures, especially Figure 1 for papers and proposals, from source material and the author's own visual preferences.

    249 GitHub stars~7.2k tokensUpdated yesterday
    Auto-check passed
  • Readme Polish

    yzhao062/anywhere-agents

    Audit a GitHub README and rewrite it using modern 2025-2026 patterns — centered header, badges, hero image, GitHub alert callouts, emoji-prefixed features, expandable details, Mermaid diagrams…

    249 GitHub stars~3.7k tokensUpdated yesterday
    Auto-check passed
  • CI Mockup Figure

    yzhao062/anywhere-agents

    Create paper and proposal figures guided by confirmed visual preferences and efficient use of page space.

    249 GitHub stars~7.5k tokensUpdated yesterday
    Auto-check passed

Works with

Categories

Questions about Parallel Run Fan-Out

What does Parallel Run Fan-Out do?

Fans a task out into independent units that run on a separate Gemini-based agent pool, while the current session only coordinates. Every worker unit runs as an Agy process using Gemini through the Antigravity CLI on its own Google AI plan quota, deliberately kept off the Claude account the coordinating session itself needs; a Sonnet subagent or any other Claude-side worker is never a target, and neither is Codex, whose higher-cost quota stays reserved for a separate gatekeeper role. The coordinator decomposes the task, dispatches units to run unattended in a scratch directory or throwaway clone, gathers results as slower units finish, reviews their diffs, and performs the final integration itself.

When should I use Parallel Run Fan-Out?

Parallel Run Fan-Out fits situations like: splitting a task into independent units that can run at once; running a large parallel batch without spending Claude-side quota; running separate research questions or module changes concurrently.

How do I install Parallel Run Fan-Out in Claude Code?

Run `npx skills add yzhao062/anywhere-agents --skill prun -a claude-code`. Or copy the skill folder (skills/prun in yzhao062/anywhere-agents) into .claude/skills/prun in your project. Claude Code loads it when a task matches its description.

How do I install Parallel Run Fan-Out in Codex?

Run `npx skills add yzhao062/anywhere-agents --skill prun -a codex`. Or copy the skill folder (skills/prun in yzhao062/anywhere-agents) into .agents/skills/prun in your project. Codex loads it when a task matches its description.

Can I use Parallel Run Fan-Out in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add yzhao062/anywhere-agents --skill prun -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/prun, .gemini/skills/prun, .github/skills/prun and .opencode/skills/prun in your project.

What does Parallel Run Fan-Out need to run?

Going by SKILL.md and its folder, Parallel Run Fan-Out needs PowerShell, a shell and Python for the scripts in its folder and the command-line tools its instructions call (git, curl and codex). Our summary lists: Agy / Antigravity CLI with a Google AI plan; A Gemini-backed worker pool.

Does Parallel Run Fan-Out access the network?

SKILL.md contains no URLs. Its commands use git and curl, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Parallel Run Fan-Out safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.

What licence does Parallel Run Fan-Out use?

Parallel Run Fan-Out is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Parallel Run Fan-Out use?

About 9.8k tokens (SKILL.md is roughly 39k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Parallel Run Fan-Out?

Skills that share tags, products or a category with Parallel Run Fan-Out: Ultracode (PabloNAX/ultracode-skill, 187 stars), Paseo Advisor Second Opinion (getpaseo/paseo, 20k stars), O2 Review Loop (openobserve/openobserve, 22k stars) and Kimi Code Delegation (CherryHQ/cherry-studio, 52k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Parallel Run Fan-Out?

yzhao062 (a GitHub user) maintains it in yzhao062/anywhere-agents, which has 249 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on October 7, 2026.

Source: yzhao062/anywhere-agents on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.