Skip to content
Development
Command

/fanout

Spawn a wave of JAT agents partitioned by file so they can't conflict, then get harness-notified when the wave is done.

From plugin
jat
24836 skills36 commands
Install
$ npx -y skills add joewinke/jat --agent claude-code

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/fanout

Context preview

What this command does when you run it.

Spawn a wave of JAT agents partitioned by file so they can't conflict, then get harness-notified when the wave is done.

Command definition

fanout.md
argument-hint: <taskId> <taskId> ... (the parallel wave to spawn)
description: Spawn a wave of JAT agents partitioned by file so they can't conflict, then get harness-notified when the wave is done.

/jat:fanout — Spawn a conflict-free wave of JAT agents, tracked to completion

Fan a set of tasks out to **real JAT agents** (full identity, signals, leases, `/work` board presence, token tracking) **and** bridge them to the Claude Code harness so you get re-invoked the moment the wave finishes — instead of babysitting them with manual polls.

> **You are the ORCHESTRATOR.** This skill spawns *other* agents; it does not start > your own task. Run it from a session that is coordinating the work.

When to use

  • You have several independent tasks ready (`jt ready`) and want them worked in parallel.
  • A larger piece decomposes into file-disjoint chunks (e.g. one new component per agent).
  • Anytime you'd otherwise spawn agents and then repeatedly poll `jt show` by hand.

The one rule: partition by file

Two agents editing the same file in one shared checkout = merge hell (and silent clobbers — a sibling's merge can replace your pushed file). So **every agent in a wave must own a disjoint set of files.** The classic safe shape:

  • **UI work → one NEW component file per agent.** Tell each agent: *build only your

component, take data via props/stores, DO NOT edit the shared page.* Mounting the components into the shared page/route is a **separate serial step** done by ONE agent afterward (see Step 5).

  • **Non-UI work → name the exact files** each agent owns; refuse the wave if any two

footprints overlap.

Steps

1. Define the wave

For each task, decide its **footprint** (the globs it will touch) and model. Derive footprints from the task description, or set them. Example wave:

.7  → ide/src/lib/components/mobile/MobileAnswerSheet.svelte   (sonnet)
.9  → ide/src/lib/components/mobile/MobileVerbSheet.svelte     (sonnet)
.12 → ide/src/lib/components/mobile/MobileTerminalPeek.svelte  (sonnet)

2. Partition guard (MANDATORY)

Verify no two footprints overlap before spawning. If they do, either re-scope a task to a disjoint file or move it to a later serial phase. Check existing leases too:

sqlite3 -noheader ~/.config/jat/file-leases.db "SELECT task_id, agent_name, globs FROM file_leases;"

2.5. Link the wave under an epic (MANDATORY)

A wave of loose sessions is hard to read on `/tasks` — you can't tell which of N running agents belong together. Fix this by making the wave an **epic with the spawned tasks as its children**. The `/tasks` active-sessions view groups every session under its epic header automatically (`getSessionsByEpic` keys off exactly the epic→child dependency edge), and `/epics` then shows the whole wave as one pilotable unit with its dependency graph. You get the grouping for free — you just have to wire the edge.

**Promote your orchestrator task to an epic** (the task this `/jat:fanout` session is running under), or create a fresh epic to hold the wave:

# Promote the orchestrator/fanout task itself:
jt update <orchestratorTaskId> --type epic
#   (works on sqlite AND postgres projects — pg routes issue_type through the REST API)

# …or create a dedicated wave epic:
jt create "Wave: <what this batch does>" --type epic --priority 1   # capture its id → EPIC

Use that epic id as `EPIC` in Step 3. The children are wired at spawn time (the spawn API auto-links them when you pass `epicId` — see Step 3), so there's no separate `jt dep add` round-trip in the normal flow.

> **Direction matters:** the edge is **epic depends_on child** (epic is BLOCKED until > the wave finishes; each child stays READY to work). The spawn API and `jt dep add > EPIC CHILD` both write it in this direction. The epic naturally clears once every > child closes — a clean "wave done" signal.

2.6. Put YOURSELF in the picture (MANDATORY — the UX must express your role)

Wiring the children is not enough: **your own orchestrator session must also read as the wave's overseer on `/tasks`**, or the human sees N grouped workers plus one mystery "Independent" session that is secretly running the show. Two wires, both required, right after spawning (or immediately after arming the watchers in Step 4):

# (a) Group your session under the epic header — link YOUR task as a child too.
#     (Skip if you promoted your own task to be the epic in Step 2.5.)
#     Correct semantics: the wave isn't done until your serial integration/docs
#     step closes, so the epic SHOULD wait on you.
jt dep add <EPIC> <yourOrchestratorTaskId>

# (b) Render the conducting/overseer chip — emit a working signal that carries
#     the `orchestrating` payload (this is what the /tasks UI keys the cyan
#     "conducting ▸N" badge off; the jat-wave-complete ownership claim adds the
#     OVERSEER badge, but only this signal expresses WHAT you are conducting).
jat-signal working '{
  "taskId": "<yourOrchestratorTaskId>",
  "taskTitle": "<your task title>",
  "approach": "Overseeing wave epic <EPIC>: jat-wave-complete driving <N> agents; serial integration after.",
  "orchestrating": { "epicIds": ["<EPIC>"], "childTaskIds": ["<taskId>", "..."] }
}'

If your session was previously in `review` state, emitting `working` here is correct — you have re-entered active work (overseeing + the serial step). Re-emit `review` when the whole wave and your serial step are done.

> `jat-wave-complete` also emits this signal automatically on your behalf when it > claims the wave (it resolves the epic from the task set's dep edges) — but emit it > yourself anyway when you orchestrate without the completer daemon, e.g. hand-rolled > watch loops or critical-path mode before the first completer run.

3. Scope + lease + spawn — per task

For UI/component tasks, write the **component-only scope** into the task description so the spawned agent reads it at `/jat:start` (it scans `.descrip

Read more
Ships withjat

Agents ship, suggest, repeat. You supervise — or they run on their own. JAT is the complete, self-contained environment for agentic development. Task management, agent orchestration, code editor, git integration, terminal access—all unified in a single IDE.

Get the whole plugin