WAVES — Workers · Aggregate · Verify · Extend (Cursor)

SkillProductivity

WAVES — Workers · Aggregate · Verify · Extend — wave-based orchestration for Cursor. Decompose a big goal into independent slices, fan them out to isolated parallel subagents via parallel Task tool calls as a bounded "wave", verify each structured handoff, then synthesize, and extend into another wave only when warranted. Invoke explicitly with /waves; bounded by design to avoid runaway token loops. For big research, analysis, audits, and codebase or data exploration where one linear pass is slow. Formerly parallel-orchestrate; also fan out, parallelize, orchestrate subagents, multi-agent.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the WAVES — Workers · Aggregate · Verify · Extend (Cursor) skill

What this skill tells your AI

The instructions your AI receives, as published by rayfernando1337/rayfernando-skills in plugins/waves/skills/waves/SKILL.md and read by ahel’s review.

Run wave-based orchestration inside one local Cursor session. A wave is a bounded round of isolated agents working in parallel, then a round that verifies what came back, then a deliberate decision to build on it — not an open-ended loop. You are the orchestrator: you discover, decompose the goal into independent slices, fan them out to parallel workers (multiple Task tool calls in one message, backgrounded where the surface supports it), read each worker's structured handoff, verify it, and synthesize one deliverable. Workers are isolated and return exactly one handoff.

The shape of every wave — WAVE:

  • W — Workers. Fan out isolated workers across disjoint slices (the bounded parallel round).
  • A — Aggregate. Wait for all of them and merge their structured handoffs at the synthesize barrier.
  • V — Verify. The moat: check the evidence behind each handoff before you trust it.
  • E — Extend. Decide — deliberately — whether to launch another wave, or stop.

A loop doesn't know when to stop; a wave does, because verification is the stop function. (Invoked explicitly with /waves: a run spawns more agents than usual, so it's opt-in, not auto-triggered.)

Waves runs in place of cloud orchestration. It adopts the principles the Cursor team proved out in their cloud orchestrate plugin — planners plan, workers hand off up, no cross-talk — but runs them on local subagents with zero setup: no separate cloud agents, no API keys, no runtime. Local subagent runs are the whole story here.

When to use

  • A large goal that splits into independent slices (research areas, data chunks, files/modules, audit dimensions).
  • The work is mostly read / research / analysis — the safest thing to parallelize locally (see "Parallel writes" for why).
  • A single linear pass would be slow and you want real speedup from concurrency.

When to skip

  • Small or linear tasks (just do them — fan-out overhead isn't worth it).
  • Work needing tight back-and-forth or shared mutable state between steps.
  • Parallel edits to the same files — local workers share one filesystem.

Core principles

Adapted from orchestrate. These keep the run converging without coordination.

  1. Orchestrator plans and synthesizes; it does not do the heavy lifting. Discovering, decomposing, reading handoffs, and writing the final deliverable are your job. The bulk reading/research/analysis is delegated to workers.
  2. Workers are isolated. A subagent has no access to the user's message, your prior steps, or sibling workers. Every worker prompt must be fully self-contained: goal context, its exact slice, where to look, what to return.
  3. One worker, one slice, one handoff. The worker's final message is the only thing you read back. Define its exact shape (see references/handoff-format.md).
  4. Parallelism is for reading, not writing. Local workers share the workspace; concurrent writes to overlapping paths corrupt each other.
  5. Continuous motion. A handoff can reveal new work. Spawn a second wave (driven by a handoff gap or a new user request). Stop only when every slice is terminal and the synthesis is complete.
  6. Verify before you trust. A worker's Status: success is a claim, not evidence. Check each handoff against something re-openable before folding it into the synthesis. See "Verification" below and references/verification.md.
  7. Decomposition is entropy reduction. A vague goal is high-entropy — many plausible plans still fit it. Your first job is to shrink that space (dig locally, then pull from attached resources, then ask the user only if it pays) before you slice it; slicing a high-entropy goal yields overlapping, mis-sized slices. See "Entropy-first decomposition."

Entropy-first decomposition

Before you fan out, treat the goal as an entropy-reduction problem: shrink how many plausible interpretations and plans still fit what you know. A vague, high-entropy request ("build a Flappy Bird game", "make my app faster") doesn't slice cleanly yet — reduce the uncertainty first, then decompose the low-entropy version. Name what's uncertain, because the two kinds resolve differently:

  • Specification uncertainty — what the user wants (ambiguous goal, missing acceptance criteria, unstated constraints). Resolve by stating an explicit assumption and proceeding — or, only when a wrong guess is expensive, by asking.
  • Environment / knowledge uncertainty — facts you don't have yet but can get (repo shape, schema, API behavior, current docs, data size). Resolve by gathering, not by asking.

Spend the cheapest action that buys the most certainty first — an information-gain ladder — and aim each probe at the unknown whose answer eliminates the most plans: the highest-information question is the one that splits the surviving interpretations roughly in half, not the one easiest to look up.

  1. Dig locally first (cheap). Tool calls in the main session (list, read the schema/README, grep, sample data). This is Step 0, framed as entropy reduction; it often collapses most of the uncertainty for free.
  2. Then pull from attached resources. If the environment doesn't hold the answer, spawn a small scouting wave of research workers to fetch it (web, Exa/Ref MCP, docs) — route these read-heavy slices to the cheap, fast model (see "Picking the model per slice").
  3. Ask the user last, and only when it pays. Ask only when residual specification uncertainty is high and the question's expected information gain clearly beats its cost. Most requests carry enough to proceed on a stated assumption; over-asking is its own failure mode.

Then cascade: one high-level request becomes a decomposition wave (understand → locate unknowns → draft the plan) → verify → an execution wave that builds the ordered subtasks, with more scouting sub-waves wherever entropy stays high. Order the plan least-to-most — do the first-order subtasks first and let each verified result lower the uncertainty for the next. Keep the living plan in TodoWrite, and stop reducing when entropy is low enough to act: the verification gate doubles as "is the uncertainty low enough to commit?" One caution: a plan-then-execute pass fixes missing steps, not a misread goal — only the specification check above catches wrong framing, which is why it comes before planning. (Worked example + wave shape + paper grounding: references/examples.md.)

The loop

Track it with TodoWrite so the waves stay visible.

Step 0 — Discover first (serial, in the main session)

Do not fan out blind. Spend a few cheap tool calls in the main session to learn the shape of the problem: list the directory, read the schema, sample the data, confirm coverage/size. This is what tells you the natural decomposition (how many chunks, which workstreams). Skipping discovery produces overlapping or mis-sized slices.

Step 0.5 — Stage the data (when it's remote or messy)

explore workers are read-only by design, and read-only mode blocks all MCP tools (Cursor staff-confirmed), so they can't reach databases or MCP-backed sources. If the source is remote or wrapped in noise, the orchestrator must stage clean inputs before fanning out:

  • Pull remote → local. SSH/rsync/export the relevant data to a local scratch dir so read-only workers can read it (e.g. query a remote SQLite read-only, export the rows, rsync the markdown).
  • Clean + normalize once, centrally. Strip wrappers, boilerplate, and binary blobs (base64, logs); fix timestamps. Doing this once beats making every worker re-derive it (and keeps noise out of their context).
  • Pre-chunk for the workers. Split into the exact per-worker files/ranges so each prompt can point at one path.
  • One scratch dir per run. Keep staged inputs, worker artifacts, and between-wave syntheses in one place (e.g. .waves/<run>/ with staging/, handoffs/, synthesis-wave-N.md) so prompts cite paths instead of pasting content and later waves re-read files, not chat history.
  • Verify before you spawn. Print counts and per-slice bounds; confirm the partition sums to the total (e.g. 8 chunks × ~388 = 3,097) so no slice is a silent blind spot. Fix anomalies (bad sort, dups) centrally, then re-check. (Details: references/verification.md §1.)

In practice this serial prep is often the largest phase; the parallel fan-out is fast once inputs are clean.

Step 0.7 — Triage: size the run, then classify each slice

Size the run first, out loud. Weigh breadth (how many independent slices), depth (how much reasoning each needs), ambiguity (how well-formed the goal is — see "Entropy-first decomposition"), and stakes (how costly a wrong answer is — this sets verification tiers), then state the chosen shape in one line before spawning — e.g. Run shape: one wave, 4 workers (3 research + 1 data chunk); second wave only if handoffs expose gaps. On the fence between two shapes, pick the smaller and say so. And if triage says no wave is needed, do the task inline and say that — never present inline work as wave coverage.

Then classify each slice on three axes — this is the classify-and-act pattern, routing the right work to the right handler:

  • Worker type — read-only (explore) / web-research (generalPurpose) / shell / competing-attempt (best-of-n-runner) / specialized review (bugbot, security-review). (See the table under "Choosing subagent_type".)
  • Dependencies — which slices (if any) this one needs verified output from. Most slices should have none; a real dependency edge is what separates waves.
  • Verification tier — how much checking the slice's stakes justify: auto-accept (low-stakes, corroborated) → single verifier (medium) → multi-model panel (high-stakes) → debate (contested, no ground truth). Spend the verification budget where a wrong claim is expensive, not uniformly.

Record the triage as a wave manifest — one row per slice, written before you spawn (in TodoWrite or .waves/<run>/manifest.md):

slicescopeworker typemodeldepends_onverification tier
1msgs 1–500explore(default fast)auto-accept
2voice-stack researchgeneralPurpose(default)single verifier
3voice build spikegeneralPurpose(default)2single verifier

depends_on defines the wave boundaries: a wave is every not-yet-run slice whose dependencies are all met — and a dependency is met only when its handoff has been verified (Step 3), not merely returned. Launch wave 1 (no dependencies) in parallel; launch each dependent slice with the distilled, verified findings (or their .waves/<run>/ path) folded into its self-contained prompt — and unrelated slices stay parallel. The manifest is also your completion gate: N rows spawned means N handoffs collected and checked off before synthesis — a wave with a missing handoff has a silent hole in it (Step 3).

Step 1 — Decompose into independent slices

Split along whichever axis makes slices independent:

  • Data chunks — partition large data and give each worker a disjoint range (e.g. messages 1–500, 501–1000, …).
  • Workstreams — separate research/analysis areas (e.g. "research voice stack", "research the Notion SDK", "audit auth code").
  • Files / modules — disjoint, non-overlapping path sets.

Each slice needs: a one-line scope, what to look at, and a defined output. For a big wave (roughly 5+ workers), state the decomposition plan to the user before spawning so they can redirect cheaply. If you have many slices, fan out in waves (launch a batch, let it complete, launch the next) rather than all at once, so you stay within practical concurrency limits.

Step 2 — Fan out in parallel

Send one message with multiple Task tool calls — one per slice whose dependencies are met (handoffs verified, not just returned) — that is what makes them run concurrently (this is the officially documented parallelism mechanism). Pick subagent_type per slice (table below). Give each a 3-5 word description and a self-contained prompt ending with the required handoff format.

Backgrounding is surface-dependent: the documented switch is the is_background: true frontmatter field on custom subagents; a per-call run_in_background: true parameter exists on some surfaces but is undocumented (and absent on others, e.g. cloud agents) — pass it when the schema exposes it, and don't rely on it elsewhere. Background workers include their final message in the completion notification. (/multitask is a separate user-facing Agents Window command, not this skill's mechanism.)

When workers run in the background, end your turn. You are notified as each completes — do not AwaitShell, poll, or read output files in a loop. The Task call itself confirms the launch. When the surface runs Task calls synchronously, the batch still executes concurrently and returns together.

Step 3 — Collect and synthesize

Completion gate first: check off every handoff against the wave manifest — N spawned means N accounted for. A worker that never returns, errors out, or comes back partial/blocked is a hole in the wave, and synthesizing around it silently drops a slice. Worker failure ladder: (1) re-task once, narrower — resume the same worker (each Task returns an agent ID that resumes with context preserved; completed subagents persist checkpoints, so a resume restores prior context even after the worker finished) when the slice just needs continuation, or re-spawn fresh with a narrower scope and a note about what came back. Resume only for continuation of the same slice — a resumed worker carries its old slice's context, which contaminates an unrelated assignment; (2) if it fails again, do that slice yourself in the main session; (3) if it stays blocked, carry the slice into the synthesis explicitly as not-covered — never average over a missing slice as if coverage were complete.

As handoffs arrive, read each one: note Status, extract Key findings, and mine Open questions / Suggested follow-ups — each bullet may become a second-wave task. Reconcile conflicts across workers.

Don't trust a handoff because it says success. Verify each finding's evidence (cited file:line / URL / metric resolves and says what's claimed), recount headline numbers from the source, and route low-confidence, conflicting, or citation-heavy claims to a verifier (Step 3.5). See "Verification" below. A wave's handoffs count as verified only when these checks pass and every claim whose manifest tier demands a verifier has its verdict back — cheap checks alone don't clear a single verifier or higher tier.

Only then compress at the barrier: write the distilled synthesis to .waves/<run>/synthesis-wave-N.md and work from that file — next-wave prompts cite paths into the scratch dir, never re-paste raw handoffs. This file is what dependent slices and later waves consume, so nothing unverified enters it as a finding: a claim still awaiting its verdict is carried only as an explicit pending-verification line.

Pin the constraints through the compression. The wave manifest, the stop conditions/budget, and any safety or scope rules are carried verbatim into every synthesis file and every between-wave summary — never paraphrased or summarized away. Compaction silently drops in-context constraints (measured: violation rates rise from 0% to 30–59% after compaction; pinning restores 0% — arXiv 2606.22528), and a run whose stop conditions got compressed out is a run that loops or quits at random.

Step 3.5 — Verifier pass (when the tier demands it)

Before writing the wave synthesis, spawn dedicated verifier workers for every claim whose manifest tier is single verifier or higher — and for anything that arrived contested, surprising, single-sourced, or low-confidence. Give each verifier the claim + its cited sources, no generator reasoning, no authorship labels (see "Verification"). Verifiers can run while you draft around them, but their verdicts gate the wave synthesis itself, not just the final deliverable: until its verdict returns, a claim may sit in synthesis-wave-N.md only as an explicit pending-verification line — never as a settled finding, and never in a dependent slice's prompt (Step 0.7's met-only-when-verified rule).

Step 4 — Second waves (continuous motion)

If handoffs exposed gaps or follow-ups — or verified handoffs just unblocked dependent manifest slices — spawn another parallel wave the same way. Repeat until no slice is pending and nothing new surfaced. Stopping early while genuine follow-ups remain is the failure mode this skill guards against; the stop function is the manifest plus the stated budget (see "Bounded waves"), never "we've already done a wave or two."

Skipping a follow-up wave is legitimate in exactly three cases — name which one applies when you decide: the remaining open items are primary-source-verified (a verifier can't improve on the evidence), time-gated (unresolvable until an external event, carry them as explicit open items), or genuinely contested (independent quality sources disagree; more sampling won't settle taste — record the disagreement instead).

Step 5 — Deliver

Synthesize all handoffs into the single artifact the user asked for (roadmap, report, summary, plan). Cite which worker produced which finding when it helps, and carry each claim's confidence through (verified / single-sourced / unverified) — never launder a low into a confident sentence.

Then write any code/files yourself, or spawn a dedicated implementation wave (mind "Parallel writes"). Verify the deliverable, not just the handoffs: re-run/curl/validate served artifacts, regression-check sibling routes, and re-read the critical files you wrote (see references/verification.md §6).

Bounded waves — size, budget, and the stop function

A wave is bounded on purpose — but bounded by completion and budget, not by a wave count. "Loop-until-done" unbounded burns tokens for little gain: candidate generation is cheap, selection plateaus, and extra rounds are non-monotonic — more iterations can lower quality, not just cost. Equally real is the opposite failure: stopping while the manifest still has open slices. Bounded waves keep the exploration, drop the runaway, and never abandon un-terminal work.

  • Width: N = 3–8 workers per wave. Size N so you can fully verify all N. Go wider only when a cheap automatic check (tests, schema, exec) gates the results. (Grounding: homogeneous-agent teams plateau around N≈4–8 — added workers contribute redundant evidence, and diversity, not head count, is what escapes the ceiling — arXiv 2606.02646, 2602.03794. Practically, Cursor staff confirm no fixed subagent cap but that ~40 concurrent workers can overwhelm the extension host: batch into waves.)
  • Depth: the manifest is the stop function. Keep extending while any manifest slice is non-terminal and the last wave added verified progress. Stop only on one of three conditions: completion (every slice terminal and the synthesis done), stagnation (a wave surfaces nothing new and its outputs near-duplicate the last, or quality dropped), or budget exhaustion. State the budget up front in the run-shape line — a worker or token budget, not a wave count (e.g. budget: ~20 workers). Do not stop because a round number of waves has passed; a realistic run is often 12 + 3 + 1 workers across three waves, and a decomposition cascade on a vague goal legitimately runs more. (Grounding: verification-driven replan loops that stop on completeness thresholds, diminishing returns, and token budgets — not fixed iteration caps — arXiv 2603.11445; convergence-based stopping beats a fixed max_iterations at parity quality, arXiv 2606.27009.)
  • Scouting is cheap — don't let it eat the budget. Entropy-reduction waves (scouting, decomposition) run on cheap models and count separately from the execution budget. Never end a run "out of waves" when the caps were consumed by discovery before execution started.
  • Budget split: ~60% generation / 40% verification. Selection is the scarce resource; spend there.
  • Match width to difficulty: easy → 1 + a light refine; medium → 3–5; hard/open-ended → 5–8 for approach diversity; hardest/novel → don't loop, escalate the model.
  • Anti-poisoning handoff: carry only a distilled, verified handoff (the winner + a short critique) into the next wave — never raw transcripts or losing candidates. Long, irrelevant context measurably degrades reasoning.

Loop-until-done is justified only when ALL hold: a cheap, reliable ~ground-truth verifier exists; the signal is crisp and actionable (a failing test, not "try harder"); each iteration shows measurable progress; the work is easy–medium difficulty; and it stays hard-capped. That fits code-with-tests and exec-feedback pipelines; it misfits open-ended research/writing/design (verify in bounded waves instead).

Verification

The orchestrator's highest-leverage job. You can't make a worker smarter at inference time, but verifying a handoff is far cheaper than producing it, and in a multi-wave run one unchecked bad handoff compounds into the synthesis.

  • Gate before spawn — counts, coverage, partition-sums (Step 0.5).
  • Cheap checks every handoff — evidence present + resolves, scope match, contradiction skim, citations actually support the claim.
  • Self-checks in the prompt — cite-or-drop, confidence tags, "read COMPLETELY", live sources, flag-unverified. (Don't rely on freeform "double-check yourself"; give an oracle or a separate verifier.)
  • Dedicated verifier worker for high-stakes / contested / citation-heavy claims — give it the claim + sources but not the generator's reasoning nor any authorship label (judges favor output marked as their own; blind them), and have it reason against a rubric/reference before its verdict (reference-guided + CoT is the cheapest reliable judge upgrade). Never show the generator the verifier's rubric (anti-gaming). For the highest-stakes calls, a multi-model panel + synthesis checks harder still (see "Multi-model fan-out").
  • Measure & cross-check — re-run the oracle, recount from source, require ≥2 independent sources that actually entail the claim (a citation being present ≠ the claim being supported).
  • Escalate low-confidence / conflicting findings (re-task with a tighter prompt → dedicated verifier → ask the user, who may choose a stronger model) instead of folding them in.

Strongest on objective, checkable work (counts, code, facts-with-sources); on taste/judgment, verify the sub-claims, don't fake a grade. Keep claims honest: isolation reduces error propagation / path dependency, but don't claim a quantified "prevents poisoning" — there's no isolation-only ablation. Full playbook: references/verification.md.

Choosing subagent_type

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
129
Forks
19
Last commit
Jul 2026
Advanced
Catalog kind
skill
Gateway key
waves
Source
github.com/rayfernando1337/rayfernando-skills