Wave Executor Skill

SkillAI & models

Use this skill when executing the agreed session plan in waves with role-based execution and parallel subagents. Handles inter-wave quality checks, plan adaptation, and progress tracking. Core orchestration engine for feature and deep sessions. Triggered by /go command.

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 Wave Executor Skill skill

What this skill tells your AI

The instructions your AI receives, as published by kanevry/session-orchestrator in skills/wave-executor/SKILL.md and read by ahel’s review.

Execution Model

You are the coordinator. You do NOT implement — you orchestrate. Your job:

  1. Dispatch subagents for each wave
  2. Wait for ALL agents in a wave to complete
  3. Review their outputs
  4. Adapt the plan if needed
  5. Dispatch the next wave
  6. Repeat until all waves complete

Design Philosophy

This harness exists to enable multi-agent coordination at scale — not by removing friction, but by making it visible, classifiable, and recoverable.

The wave-executor is process scaffolding around LLM agents. It handles task breakdown, scope enforcement, circuit breaker guards, and recovery patterns. Unlike direct chat with an agent, it trades flexibility for safety and repeatability across a bounded execution envelope.

Every harness creates friction. The goal is not minimum friction — it is useful friction that prevents higher-cost problems downstream.

Friction we accept:

  • Wave planning overhead and wave-scope.json pre-dispatch setup
  • Per-wave quality gates before proceeding
  • Worktree isolation costs for parallel agents
  • Turn-limit constraints that stop runaway agents early

Friction we prevent:

  • Agent scope violations (PreToolUse hooks block out-of-scope file edits)
  • Cascading failures (circuit breaker + spiral detection halt broken agents before they propagate damage)
  • Silent partial completion (STATUS line requirement forces explicit reporting)
  • Untracked carryover work (session-end plan verification catches unresolved tasks)

The harness does not hope agents self-correct. It detects stagnation patterns — pagination-spiral, turn-key-repetition, error-echo (read by the coordinator during post-wave review), plus psa007-git-write and status-partial (detected live by the transcript tailer, recorded with source: "tail") — classifies error-echo into the Error-Class Taxonomy defined in circuit-breaker.md, and re-scopes mechanically. Review logic lives in wave-loop.md § "Review Agent Outputs"; the tailer's start and its silence-is-not-success caveat live in the same file, step 2.0-bis.

Platform Note

State files live in the platform's native directory: .claude/ for Claude Code, .codex/ for Codex CLI, .cursor/ for Cursor IDE. All references to .claude/ below should use the platform's state directory. Shared metrics (sessions.jsonl, learnings.jsonl) live in .orchestrator/metrics/ — both platforms read and write there. See skills/_shared/platform-tools.md for tool mappings.

Phase 0: Bootstrap Gate

Read skills/_shared/bootstrap-gate.md and execute the gate check. If the gate is CLOSED, invoke skills/bootstrap/SKILL.md and wait for completion before proceeding. If the gate is OPEN, continue to the Pre-Execution Check.

Session-start only: This gate check runs ONCE at the start of /go execution — before the first wave. It does NOT run before each wave step. Repeating the check per wave would add latency with no safety benefit, since bootstrap.lock is immutable within a session.

Phase 0.5: Parallel-Aware Preamble

Skip silently when persistence: false in Session Config.

Before Phase 1, run the parallel-aware preamble per skills/_shared/parallel-aware-preamble.md. The preamble detects other active sessions in the worktree-family via findPeers(repoRoot, { mySessionId }), classifies the caller's mode via classifyMode(callerMode) against the exclusivity-matrix, and fires the appropriate AUQ on conflict.

Outcome handling:

  • PASS_THROUGH → continue to Phase 1
  • EXCLUSIVE_BLOCKED → exit Phase 0 cleanly per the AUQ outcome
  • PROMOTION_OFFER → user picks Worktree-Promotion (see parallel-aware-auq.md outcome-handling — calls enterWorktree()), in-place + Deviation, or Abbrechen

For session-end specifically: the preamble is DETECTION-ONLY. The lock-release path in later phases keeps its current behavior — releasing the OWN session's lock requires no matrix consultation.

Implementation reference: skills/_shared/parallel-aware-preamble.md § Implementation. AUQ reference: skills/_shared/parallel-aware-auq.md.

Pre-Execution Check

Before starting the first wave (Discovery role):

  1. git status --short — ensure clean working directory (commit or stash if needed)

  2. Verify no parallel session conflicts (unexpected modified files)

  3. Confirm the agreed plan is still valid (no new critical issues since planning)

  4. Verify jq is installed — run command -v jq. If not found, warn the user: "⚠ jq is not installed. Scope and command enforcement hooks will be DISABLED. Install jq (brew install jq / apt install jq) to enable security enforcement." Do NOT proceed with waves until user acknowledges.

  5. Read Session Config: Parse Session Config per skills/_shared/config-reading.md. Store result as $CONFIG. Extract these fields:

    • persistence (default: true), enforcement (default: warn), isolation (default: auto)
    • agents-per-wave (default: 6), max-turns (default: auto), pencil (default: null)

    Execution Config shortcut: If the session-plan output contains an ### Execution Config section, its execution-level fields (waves, agents-per-wave, isolation, enforcement, max-turns) take precedence over $CONFIG. Session-level fields (persistence, pencil) always come from $CONFIG. If the Execution Config section is missing, use $CONFIG alone.

  6. Initialize session metrics (if persistence enabled): Prepare a metrics tracking object for this session:

    • session_id: <branch>-<YYYY-MM-DD>-<HHmm> (HHmm from started_at — ensures uniqueness across multiple sessions per day)
    • session_type: from Session Config
    • started_at: ISO 8601 timestamp
    • waves: empty array (populated after each wave) This object lives in memory during execution — it is written to disk by session-end.

Pre-Execution: User Instructions

If the user provided additional instructions with /go (e.g., /go focus on API endpoints), apply them as a priority modifier:

  1. Incorporate into agent prompts: Add a "Priority Focus:" section to each agent's prompt that includes the user's instructions verbatim
  2. Do NOT override the plan: User instructions adjust emphasis within the existing plan, they do not replace it. If the instructions conflict with the plan, note the conflict and follow the plan.

Example: If user said /go focus on API endpoints, each agent prompt includes:

**Priority Focus (from user):** focus on API endpoints

Pre-Wave 1a: Capture Session Start Ref

Before dispatching Wave 1, capture the current commit as the session baseline:

SESSION_START_REF=$(git rev-parse HEAD)

Store this value for use throughout the session — it is needed by the simplification pass (Quality wave) and session-reviewer dispatch to determine which files changed during this session. Include it in the coordinator's context, NOT in individual agent prompts.

Pre-Wave 1b: Initialize STATE.md

Skip this section entirely if persistence: false.

Before dispatching Wave 1, write <state-dir>/STATE.md with YAML frontmatter and Markdown body:

---
schema-version: 1
session-type: feature|deep|housekeeping
branch: <current branch>
issues: [<issue numbers from plan>]
started_at: <ISO 8601 timestamp with timezone>
status: active
current-wave: 0
total-waves: <from session plan>
---
## Current Wave

Wave 0 — Initializing

## Wave History

(none yet)

## Deviations

(none yet)

Create the <state-dir> directory if needed (mkdir -p <state-dir>) before writing. This file is the persistent state record — other skills and resumed sessions read it.

Pre-Wave 1b Extension: Docs Tasks Persistence (A3 / #230)

After writing the base STATE.md frontmatter above, conditionally persist the docs tasks block emitted by session-plan:

Condition: BOTH of the following must be true:

  1. The session plan contains a ### Docs Tasks (machine-readable) section with a YAML code block.
  2. $CONFIG."docs-orchestrator".enabled is true.

If either condition is false → omit the docs-tasks field entirely. Do NOT write an empty key (docs-tasks: []). Absence means "no docs tasks planned this session" — downstream consumers (session-end Phase 3.2) treat absence the same as an empty list.

When the condition is met, parse the YAML block from the session plan's ### Docs Tasks (machine-readable) section and append the following field to the STATE.md YAML frontmatter (alongside the base fields above):

docs-tasks:
  - id: <task id from plan>
    audience: <user|dev|vault>
    target-pattern: <glob pattern from plan>
    rationale: <rationale string from plan>
    wave: <wave number the task is assigned to>
    status: planned

Each entry's status is initialized to planned. session-end Phase 3.2 (Docs Verify) writes the terminal value per task: ok (diff is substantive), partial (diff region contains <!-- REVIEW: source needed --> markers), or gap (no matching diff). wave-executor does NOT perform intermediate status updates — planned remains until session-end runs.

Schema note: schema-version: 1 now includes the optional docs-tasks array. The field is backwards-compatible — its absence is a valid schema-version-1 STATE.md meaning "no docs tasks planned". Readers MUST treat a missing docs-tasks key identically to docs-tasks: [].

Ownership clarification: session-plan does NOT write STATE.md directly. The wave-executor owns ALL STATE.md writes — initialization here (Pre-Wave 1b) is the canonical write point for docs-tasks. session-plan only emits the source ### Docs Tasks (machine-readable) block for the coordinator to consume. See skills/_shared/state-ownership.md for the full ownership matrix.

Consumer cross-reference: session-end reads STATE.md frontmatter's docs-tasks field (if present) during Phase 3.2 Docs Verify — see skills/session-end/SKILL.md. The field is also readable by the docs-writer agent if it needs to know which tasks were planned for the current session.

Ownership: STATE.md is owned by the wave-executor. Only the wave-executor writes to it (initialization + post-wave updates). session-end reads it for metrics extraction and sets status: completed. session-start reads it only for continuity checks (Phase 0.5). No other skill should write to STATE.md.

Wave Execution Loop

Read and follow wave-loop.md in this skill directory for the complete wave execution loop, including agent dispatch, output review, plan adaptation, progress updates, and scope manifest creation.

Since #1157 that file is a 39-line INDEX and the loop body lives in three files under references/. Its own table is the routing table — read it there, not here: it carries a Read WHEN column stating at which moment each file is due, which is the half a copy loses. Two of the three steps are marked MANDATORY-BEFORE-DISPATCH; skipping either dispatches the wave unguarded and the failure is SILENT — no error, no ledger entry, indistinguishable from a clean run.

Turn budget, maxTurns, and stagnation recovery are unmoved: circuit-breaker.md. Every wave-loop.md § … citation elsewhere in this file resolves into one of the three sub-files.

Mission-Status Updates (#340)

The coordinator (you) is responsible for updating per-task mission status in STATE.md as tasks progress through the wave. Use setMissionStatus(stateContent, taskId, status) from scripts/lib/state-md.mjs and write the result back to STATE.md immediately.

taskId grammar (enforced). setMissionStatus refuses any taskId outside [a-z][a-z0-9]*(?:-[a-z0-9]+)*-\d+ — lowercase segments joined by single hyphens, ending in a bare digit run. Accepted: m-1, docs-2, w2-1, w2-a-10. Refused (refused: 'id-grammar'): w2-a10 (digits fused onto a letter segment), w3-p2 (no trailing bare-digit segment), W3-I1 (uppercase), Docs_2 (underscore). A refused write returns { written: false, reason: 'id-grammar' } from setMissionStatusOnDisk and logs a stderr WARN naming the rejected id — nothing is written to STATE.md on refusal, so mint ids matching this grammar from the start rather than relying on the refusal to catch a typo.

Per-task transition rules (coordinator fires these, NOT wave-loop.md):

TransitionWhen to fire
brainstormedvalidatedUser runs /go to approve the wave plan (all items simultaneously)
validatedin-devAgent for that wave-plan item is dispatched via Agent() tool
in-devtestingQuality wave begins and this item's implementation wave completed without failure
testingcompletedQuality-Lite gate passes (green) for this task's wave — coordinator confirms item done
Any → brainstormedItem is discarded, re-planned, or rolled back

Important scoping notes:

  • These transitions are coordinator-level orchestration decisions, not part of wave-loop.md dispatch/review logic. Do NOT modify wave-loop.md to add mission-status calls.
  • wave-loop.md is NOT modified by #340 — the transitions listed above are called by the coordinator after observing the wave-loop outcomes.
  • Only update items whose id appears in the ### Wave-Plan Mission Status (machine-readable) block emitted by session-plan. Invent no new IDs.
  • When STATE.md does not yet have a ## Mission Status body section, setMissionStatus creates it automatically (see scripts/lib/state-md.mjs).
  • readMissionStatus(stateContent, taskId) from the same module returns the current status string for a task (or null if not found), useful for guard-checking before transitions.

Backward compat: STATE.md files without a ## Mission Status section are valid — absence means no status tracking was started. The helpers are no-throw on bad input.

Circuit Breaker & Worktree Isolation

Reference: See circuit-breaker.md in this skill directory for MaxTurns enforcement, spiral detection, recovery protocol, and worktree isolation configuration. Apply those rules during every wave dispatch and post-wave review.

Coordinator CWD Discipline (#219)

Claude Code's Agent tool with isolation: "worktree" changes process.cwd() into the agent's worktree and does not restore it on agent return. Without discipline, the coordinator's subsequent Edit/Write/Bash calls silently route to a worktree branch — producing data loss when the worktree is later pruned.

Rules for the coordinator (this is YOU during wave execution):

  1. After every Agent() dispatch (before reading its output), call restoreCoordinatorCwd() from scripts/lib/worktree.mjs. wave-loop.md § 2 makes this explicit.
  2. Prefer absolute file paths for Read/Edit/Write tool calls. A drifted CWD turns relative paths into silent cross-tree writes.
  3. Before any Bash git command, either cd inside a subshell (cd /path && cmd) or rely on git -C /path <cmd>. Do not assume CWD.
  4. Verify at checkpoints — when in doubt, run git rev-parse --show-toplevel to confirm which tree is currently active.
  5. Never cd into a worktree in the coordinator's top-level shell. If you need to inspect a worktree, use git -C <wt-path> ... or spawn a subshell.

Coordinator User Interaction

Every mid-wave user decision — pause/continue, scope changes, plan revisions, routing between alternate tracks, confirming a risky recovery step, picking between recommendations — MUST go through the AskUserQuestion tool. Inline markdown-list "choose 1/2/3" questions in chat prose are forbidden: the user reliably misses them in the dense wave-execution stream. See .claude/rules/ask-via-tool.md for the full rule (AUQ-001 through AUQ-005).

Mechanics:

  • AskUserQuestion is a deferred tool in Claude Code. On the first coordinator decision point in a session, call ToolSearch with "select:AskUserQuestion" once to load its schema, then call the tool. Do not skip the question to avoid the load.
  • Option 1 always carries (Recommended) in the label. Each option carries a one-line description stating the trade-off.
  • AskUserQuestion is not available inside dispatched subagents. If an agent surfaces a decision back to you, ask the user via AskUserQuestion from the coordinator turn — do not let the agent emit a prose question.

Applies to every interaction point in wave-loop.md that currently says "inform the user", "propose revised plan", "ask the user whether to…", or "report specific mismatches to user" when a choice is implied.

Agent Prompt Best Practices

Each agent prompt MUST include:

  1. Clear scope boundary: "You are working on [X]. Do NOT modify files outside [paths]."
  2. Full context: file paths, current code structure, issue description. If a bite-sized executable plan exists at docs/plans/<feature>.md for the wave's tasks (see skills/write-executable-plan/SKILL.md), include the path in each agent's prompt and instruct the agent to follow the plan's 5-step structure verbatim.
  3. Acceptance criteria: measurable definition of done
  4. Rule references: the wave's applicable rules are injected automatically as the <APPLICABLE-RULES> block produced by scripts/print-applicable-rules.mjs (see wave-loop.md § "Pre-Dispatch: Glob-Scoped Rule Injection (#336/#694)"). The block is computed once per wave from the wave's allowedPaths and prepended to every agent prompt — do not hand-copy rule paths into the prompt. Past learnings arrive separately as the <LEARNINGS-INDEX> block from scripts/print-learnings-index.mjs (see wave-loop.md § "Pre-Dispatch: Learnings-Index Injection (#1014)"), computed per agent from its own file scope rather than once per wave.
  5. Testing expectation (need-gated): "Before writing any test, name the concrete bug a NEW test would catch that the existing suite does not. No nameable bug → write NO test and report no-tests-needed: <reason> — that is a SUCCESS outcome, not a gap. With a nameable bug: exactly one test for it. Running existing tests is always mandatory."
  6. Commit instruction: "Do NOT commit. The coordinator handles commits. Never git stash, git add, git checkout -- or git reset either (PSA-007) — to compare against the pre-change state, read git show HEAD:<path> (or git show <sha>:<path>); it never touches the shared index." Measured 2026-09-02: two agents in one wave reached for git stash to build a baseline; both recovered, both were the same shape.
  7. Turn limit: Include the maxTurns instruction from circuit-breaker.md
  8. Verification before completion: Before claiming any task done, run the verification command and quote the evidence inline. See .claude/rules/verification-before-completion.md.

Each agent prompt MUST NOT include:

  • References to other agents' tasks (isolation)
  • Vague instructions like "improve" or "optimize" without specifics
  • Assumptions about code state — provide the actual state

Agent Memory-Proposal Capability (#501)

Wave-executor agents may propose memory entries (learnings) mid-session via the memory.propose CLI. The coordinator surfaces proposals at session-end Phase 3.6.3 (skills/session-end/SKILL.md) for AUQ-confirm before promoting them to learnings.jsonl with _provenance: agent-proposed@<wave-id>. Conservative safety model: max memory.proposals.quota-per-wave (default 5) per wave, memory.proposals.confidence-floor (default 0.5).

Agent prompt boilerplate — when dispatching an Impl-Core / Impl-Polish / Quality agent in a session where memory.proposals.enabled: true (default), include this block in the agent's prompt so the capability is discoverable:

## Memory Proposal Capability (optional)

During this wave, you may propose a learning to the session's memory via the CLI:

  SO_WAVE_AGENT=1 node scripts/memory-propose.mjs \
      --type <one of: workflow-pattern|anti-pattern|recurring-issue|fragile-file|effective-sizing|proven-pattern|mode-selector-accuracy|hardware-pattern|autopilot-effectiveness|domain-regression|convention|architecture-pattern|design-pattern> \
      --subject "one-line title (max 100 chars, no newlines)" \
      --insight "your discovery paragraph (max 2000 chars)" \
      --evidence "concrete proof: code citation / log excerpt / commit ref (max 5000 chars)" \
      --confidence <0.5 to 1.0> \
      --file-paths "scripts/lib/a.mjs,scripts/lib/b.mjs"

MUST prefix with `SO_WAVE_AGENT=1` — without it the CLI returns exit 3 `rejected-wrong-context`. The env-var is the per-process guard that distinguishes wave-executor agents from coordinator-context invocations.

`--file-paths` is optional but strongly encouraged: repo-relative path(s) this learning applies to (repeatable and/or comma-separated, deduped; rejects absolute paths, `..` segments, embedded newlines, entries over 256 chars, and more than 20 entries). Without `--file-paths` this learning can never become `/reconcile`-eligible — the reconciliation engine can only convert a learning into a conditional `.claude/rules/*.md` rule when it carries a non-empty scope (issue #900).

Exit code 0 = queued (the coordinator will present at session-end via AskUserQuestion); 1 = quota-exceeded; 2 = rejected-low-confidence (below floor 0.5); 3 = rejected-wrong-context (STATE.md not active OR SO_WAVE_AGENT != "1"); 4 = error (arg validation or internal).

Use ONLY when you find a recurring pattern, anti-pattern, or constraint worth carrying into future sessions. The coordinator confirms each proposal before it lands in learnings.jsonl. Do NOT over-propose — quota is bounded per wave.

Analyzer-only learning types, including `autonomy-verdict`, are intentionally not valid here; those are emitted by `/evolve` after their analyzer-specific evidence gates pass.

Skip injection when:

  • memory.proposals.enabled: false in Session Config, OR
  • Discovery / Finalization waves (Discovery is read-only; Finalization is coordinator-direct)

Audit trail: the hooks/pre-bash-memory-propose-audit.mjs hook logs every CLI invocation to .orchestrator/metrics/events.jsonl with the value of --insight / --subject / --evidence redacted (privacy-by-default).

Cross-reference: PRD F2.1 / issue #501 / docs/memory-proposal-flow.md (coordinator-side AUQ rendering reference doc) / scripts/lib/memory-proposals/{schema,store,collector,sink}.mjs (the modules).

Session Type Behavior

Housekeeping Sessions

Housekeeping sessions use a simplified single-wave execution model instead of the multi-wave role-based dispatch:

  1. Initialize STATE.md as normal (session-type: housekeeping, total-waves: 1)
  2. Do NOT create wave-scope.json — scope enforcement is not needed for low-risk housekeeping tasks
  3. Dispatch tasks serially with 1-2 agents per task
  4. Run Baseline quality checks after all tasks complete (not between tasks)
  5. Skip session-reviewer dispatch — housekeeping changes are low-risk
  6. Do NOT update STATE.md to status: completed — that write is reserved for session-end per state-ownership contract (skills/_shared/state-ownership.md). Leave status: active.
  7. Proceed directly to session-end (/close)

Focus: git cleanup, SSOT refresh, CI fixes, branch merges, documentation. End with a single commit summarizing all housekeeping work.

Feature Sessions

  • Full wave execution (5 roles mapped to configured wave count)
  • 4-6 agents per wave (read from Session Config)
  • Balance between implementation speed and quality

Deep Sessions

  • Full wave execution (5 roles mapped to configured wave count)
  • Up to 10-18 agents per wave (read from Session Config)
  • Extra emphasis on Discovery role and Quality role
  • May include security audits, performance profiling, architecture refactoring

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
50
Forks
7
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
wave-executor
Source
github.com/kanevry/session-orchestrator