---
description: "When executing session plan waves via /go command — task sequencing, quality checks, progress tracking"
globs: ""
alwaysApply: false
---

# Wave Execution (Cursor Adaptation)

> **CRITICAL**: Cursor does NOT have an `Agent()` tool. All wave tasks execute **sequentially in the current Composer session**. You are both the coordinator AND the implementer. Agent dispatch (3-tier resolution: project > plugin > general-purpose) is NOT applicable — follow the role's agent-prompt patterns directly.

> **State directory**: All state files live in `.cursor/` (STATE.md, wave-scope.json).

## Pre-Execution

Before starting Wave 1:

1. `git status --short` — commit or stash any uncommitted changes
2. Capture baseline: `SESSION_START_REF=$(git rev-parse HEAD)` — keep for the whole session
3. Confirm the agreed plan is still valid
4. Read `persistence` from Session Config (default: `true`)

### Initialize STATE.md (if persistence enabled)

Create `.cursor/STATE.md` (`mkdir -p .cursor` if needed):

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

```markdown
## Current Wave

Wave 0 — Initializing

## Wave History

(none yet)

## Deviations

(none yet)
```

## Wave Loop

For each wave in the session plan:

### Step 1: Write Scope Manifest

Write `.cursor/wave-scope.json` before each wave. Set `enforcement: warn` unless `.cursor/hooks.json` is installed — then `preToolUse` can block via `cursor-hook-bridge.mjs`. `afterFileEdit` remains post-hoc (cannot unwrite):

```json
{
  "wave": 1,
  "role": "<role>",
  "enforcement": "warn",
  "allowedPaths": ["<union of all task file scopes for this wave>"],
  "blockedCommands": ["rm -rf", "git push --force", "DROP TABLE", "git reset --hard", "git checkout -- ."]
}
```

Role-specific scope rules:
- **Discovery**: `allowedPaths: []` — READ-ONLY, do not edit files
- **Quality Phase 1 (Simplification)**: production files changed this session only (no test files)
- **Quality Phase 2 (Tests)**: test file patterns only (`**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`)

**`allowedPaths` is COMPUTED, never hand-transcribed (#1020).** Hand-transcribing the union produced 5 scope divergences in one session. Three steps, in this order:

1. Write each task's file scope verbatim as a JSON array of strings to `.cursor/filescopes/wave-<N>/<task-id>.json` — that path IS `$TASK_FILESCOPE_JSON`, reused verbatim by the subset assertion below; never a `$TMPDIR` temp file, which no later step can address. Expect a `bash-write-verify … OUTSIDE the wave's allowedPaths` notice here: `.cursor/filescopes/` is control state like `.cursor/wave-scope.json`, and this step runs before the union exists. Your OWN planned direct edits go into `coordinator.json` in the same directory and take part in both steps below (2 of those 5 divergences were coordinator-direct edits, for which no task scope file exists by construction).
2. Build the sidecar — an ARRAY of `{id, files}` records, never an object map, so a duplicated id stays visible — and assert disjointness **before** the union, or the union launders a double assignment into the artefact meant to prevent it: `node "$CURSOR_RULES_DIR/../scripts/validate-wave-scope.mjs" --assert-disjoint "$WAVE_SCOPES_SIDECAR" < .cursor/wave-scope.json`. Exit 1 = two tasks were handed the same file; fix the plan, never widen the union.
3. `--union <sidecar>` is a QUERY MODE that still needs a schema-valid manifest on stdin: write the skeleton with `"allowedPaths": []`, run `node "$CURSOR_RULES_DIR/../scripts/validate-wave-scope.mjs" --union "$WAVE_SCOPES_SIDECAR" < .cursor/wave-scope.json`, and insert the printed array as `allowedPaths`. It prints that array **instead of** the manifest echo — one JSON document per run. It already applies test-sibling expansion, so do not run the helper by hand as well.

After the final wave, delete `.cursor/wave-scope.json` **and** `.cursor/filescopes/`.

**Test-sibling expansion (#970):** a scope entry naming a production file but not its test sibling makes the task's own regression test unwritable — the guard then enforces exactly the inconsistency the quality gate exists to catch. Step 3 above expands it for you via `expandTestSiblings(unionFileScopes(scopes), { role })`; do not hand-derive the paths.

It emits a **glob** from the production basename (`foo.mjs` → `tests/**/foo*.test.mjs`), skips absolute entries, and is a strict no-op on Discovery's `[]`. **Which roles it fires for is decided inside the helper** — `TEST_SIBLING_EXPANSION_ROLES` in `scripts/lib/scope-gate.mjs` (currently Impl-Core / Impl-Polish; Quality Phase 1 is off, Phase 2 inert). Pass the role; do not pre-filter by role here. An absent or unrecognised role does **not** expand (fail-closed). The disjointness check (step 2) runs on the declared scopes, before the union expands anything; expansion happens before the file is written, in one pass. Full rule, the measured hit-rate and its ~15% residual, and the per-repo configurability: `skills/wave-executor/wave-loop.md` § Scope Manifest #3.

Validate with: `node "$CURSOR_RULES_DIR/../scripts/validate-wave-scope.mjs" < .cursor/wave-scope.json` — fix JSON if it exits 1. Before each task batch, also assert the task's file scope — `$TASK_FILESCOPE_JSON`, i.e. the step-1 file itself — against the manifest, carrying the mechanical half of the rule above (gated on the manifest's `role`, so it self-skips where expansion does not fire). This assertion is unchanged by #1020 and keeps running: it checks scope ⊆ union, a different property than disjointness — a file claimed twice is a subset twice over, so a double assignment is structurally invisible to it.

```bash
node "$CURSOR_RULES_DIR/../scripts/validate-wave-scope.mjs" \
  --assert-subset "$TASK_FILESCOPE_JSON" --expand-test-siblings \
  < .cursor/wave-scope.json
```

### Step 2: Execute Tasks Sequentially

For each task in the wave:
1. Read the task description and acceptance criteria
2. Implement the task fully (you are the implementer)
3. Report status after each task:
   ```
   Task "[description]": done|partial|failed — [1-line summary]
   ```
4. If stuck, note the blocker and move to the next task

**DO NOT commit during wave execution** — `/close` handles commits.

### Step 3: Run Incremental Quality Checks

After ALL tasks in the wave complete:

| Role | Quality check |
|------|--------------|
| Discovery | None (read-only) |
| Impl-Core | typecheck + test changed files |
| Impl-Polish | typecheck + test changed files + integration verification |
| Quality | Full Gate: typecheck + test + lint — all must pass |
| Finalization | `git status` — verify clean state |

### Step 4: Quality Wave — Simplification Pass

At the START of the Quality wave, before running tests:

1. `git diff --name-only $SESSION_START_REF..HEAD` — list files changed this session
2. Filter to production files (exclude `*.test.*`, `*.spec.*`, `__tests__/`). If none, skip.
3. Review each production file for AI-generated patterns and apply targeted cleanup:
   - Remove unnecessary try-catch around non-throwing operations
   - Delete over-documentation (params that restate the name, returns that say "the result")
   - Replace re-implemented stdlib functions with standard alternatives
   - Simplify redundant boolean logic (if/else returning true/false, double negation)
   - **Do NOT change functionality**
4. After simplification, run Full Gate quality checks (typecheck + test + lint)

**Note:** Session-reviewer cannot be dispatched as a subagent on Cursor. Quality review is deferred to session-end Phase 1.8. In-wave quality relies on incremental checks + this simplification pass.

### Step 5: Update STATE.md (if persistence enabled)

After each wave:

1. **Frontmatter**: update `current-wave` to the completed wave number
2. **`## Current Wave`**: replace with next wave info
3. **`## Wave History`**: append:
   ```
   ### Wave N — <Role>
   - Task "[description]": done|partial|failed — [files changed] — [1-line note]
   ```
4. **`## Deviations`**: if plan was adapted, append:
   ```
   - [<ISO timestamp>] Wave N: <what changed and why>
   ```

### Step 6: Capture Wave Metrics (if persistence enabled)

Record after each wave:
- `wave_number`, `role`, `agent_count` (always 1 on Cursor), `files_changed`
- Per-task results: `{description, status: done|partial|failed}`
- `quality_check`: pass/fail/skipped

This data is written to `.orchestrator/metrics/sessions.jsonl` by session-end.

### Step 7: Report Wave Status

```
## Wave [N] ([Role]) Complete

- Task 1: [done/partial/failed] — [1-line summary]
- Task 2: [done/partial/failed] — [1-line summary]
- Tests: [passing/failing] | TypeScript: [0 errors / N errors]
- Adaptations for Wave [N+1] ([NextRole]): [none / list changes]
```

### Step 8: Adapt Plan (if needed)

- **On track**: proceed to next wave as planned
- **Minor issues**: add fix tasks to the next wave
- **Major blocker**: STOP — inform the user, propose revised plan
- **Scope change**: document WHY, adjust remaining waves, inform user

## Agent-Mapping Config (Reference Only)

If Session Config includes `agent-mapping`, use it to determine which role's prompt patterns to follow for each task type (e.g., `db-specialist` patterns for DB tasks). On Cursor this is reference only — no actual agent dispatch occurs.

## Circuit Breaker

**If the same error appears 3+ times:**

1. STOP immediately
2. Report:
   ```
   CIRCUIT BREAKER: Stuck on [error description].
   Attempted [N] times. Same error persists.

   Options:
   1. Narrow scope and retry with a different approach
   2. Skip this task and continue with remaining waves
   3. Abort session
   ```
3. Wait for user decision

**Spiral indicators**: same file edited 3+ times without progress, same error repeating, reverting own changes.

## Session Type Behavior

| Type | Behavior |
|------|----------|
| Housekeeping | Serial tasks, no wave-scope.json, Baseline QC at end, single commit via /close |
| Feature | Full 5-role wave execution, incremental QC |
| Deep | Full 5-role wave execution, extra emphasis on Discovery and Quality roles |

## Completion

After the final (Finalization) wave:
1. Delete `.cursor/wave-scope.json`
2. Report final status to the user
3. Suggest running `/close` — do NOT auto-commit

## Error Recovery

| Situation | Action |
|-----------|--------|
| Tests fail after wave | Diagnose and fix, don't skip |
| TypeScript errors introduced | Track count, fix by Quality wave |
| New critical issue discovered | Inform user, add to remaining waves if in scope |
| Edits to wrong files | Revert via git, redo with correct scope |
| Stuck in loop | Trigger circuit breaker (see above) |

## Anti-Patterns

- **NEVER** skip inter-wave review — quality degrades exponentially
- **NEVER** commit during wave execution — `/close` handles that
- **NEVER** continue to next wave with unresolved failures
- **NEVER** execute without reporting progress to the user
- **NEVER** modify files outside the wave's `allowedPaths`
