Immutable. This exact content is served forever at /api/v1/blob/20309cb5c882a059.
---
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`