# Workflow: compass:cook

You are the build orchestrator. Mission: execute a prepared plan wave by wave, spawning a fresh sub-agent per wave with **strictly scoped context**. After all waves, chain to `/compass:test` then `/compass:commit` with user confirmation at each step. Prevent context pollution; keep the main agent focused on orchestration, not implementation.

**Principles:** Sub-agents implement; main agent orchestrates. Fresh context per wave — no carry-over between waves. Retry 2x on failure, then ask the dev. Test and commit are separate steps chained after all waves — user confirms each.

**Purpose**: Turn `plan.json` into merged code, wave by wave.

**Output**:
- Implemented code on the session's feat branch
- Chain: `/compass:test` → `/compass:commit` (user confirms each)
- `state.json` waves array populated with wave_id, status, retry_count
- Session status: `build` → `complete`

**When to use**:
- After `/compass:prepare` — you have a reviewed `plan.json`
- Resuming a paused build (progress.json remembers last done wave)

---

Apply the UX rules from `core/shared/ux-rules.md`.

---

## Step 0 — Resolve active project

Apply `core/shared/resolve-project.md`. Extract `$CONFIG`, `$LANG`, `$SPEC_LANG`.

---

## Step 0a — Pipeline + project gate

Apply Step 0d. Dev sessions standalone.

---

## Step 0b — GitNexus check

Apply the shared snippet from `core/shared/gitnexus-check.md`. Sets `$GITNEXUS_STATUS` and `$GITNEXUS_REPO`. Include both in every Agent worker prompt (Step 5.B) so workers can call `gitnexus_impact()` before modifying symbols.

---

## Step 1 — Resolve target session

Same pattern as `/compass:prepare` Step 1:

```bash
if [ -n "$ARGUMENTS" ]; then
  SLUG="$ARGUMENTS"
  SESSION_DIR="$PROJECT_ROOT/.compass/.state/sessions/$SLUG"
else
  LATEST=$(compass-cli session latest "$PROJECT_ROOT" 2>/dev/null)
  SESSION_DIR=$(echo "$LATEST" | jq -r '.dir // empty')
  SLUG=$(basename "$SESSION_DIR" 2>/dev/null)
fi

STATE=$(compass-cli state get "$SESSION_DIR")
TYPE=$(echo "$STATE" | jq -r '.type')
STATUS=$(echo "$STATE" | jq -r '.status')

[ "$TYPE" != "dev" ] && fail "Not a dev session (type=$TYPE)"
[ ! -f "$SESSION_DIR/plan.json" ] && fail "No plan.json. Run /compass:prepare first."
[ "$STATUS" != "prepared" ] && [ "$STATUS" != "build" ] && fail "Status=$STATUS; need 'prepared' or 'build' to proceed."
```

---

## Step 2 — Git context

Apply `core/shared/git-context.md` Parts B (branch state) + D (stash recovery). Ensure currently on the session's feat branch. Handle dirty state via the dialog.

---

## Step 3 — Load plan + resume check

```bash
PLAN=$(cat "$SESSION_DIR/plan.json")
TOTAL_WAVES=$(echo "$PLAN" | jq '.waves | length')

# Resume detection
PROGRESS=$(compass-cli progress load "$SESSION_DIR" 2>/dev/null || echo '{}')
LAST_DONE=$(echo "$PROGRESS" | jq -r '.last_wave_done // 0')
START_WAVE=$((LAST_DONE + 1))

if [ "$START_WAVE" -gt 1 ]; then
  # AskUserQuestion: resume-from-N / restart-from-1 / abort
fi

if [ "$START_WAVE" -gt "$TOTAL_WAVES" ]; then
  echo "✓ All waves already complete for this session."
  exit 0
fi
```

Resume dialog:
```json
{"questions": [{"question": "Session has $LAST_DONE of $TOTAL_WAVES waves done. How to proceed?", "header": "Resume", "multiSelect": false, "options": [
  {"label": "Resume from wave $START_WAVE", "description": "Continue where the last run left off"},
  {"label": "Restart from wave 1", "description": "Redo everything — existing commits untouched but new commits will appear"},
  {"label": "Abort", "description": "Stop — decide manually"}
]}]}
```

---

## Step 4 — Update session status

```bash
compass-cli state update "$SESSION_DIR" '{"status":"build","updated_at":"'$(date -u +%FT%TZ)'"}'
```

---

## Step 5 — Wave execution loop

**Status display**: Before starting the loop, print the full task list with initial status. Update after each wave completes:

```
⏳ Executing...

  Wave 1:
    ⬜ DEV-01 — Define types                 [pending]
    ⬜ DEV-02 — Implement handler            [pending]

  Wave 2:
    ⬜ DEV-03 — Write tests                  [pending]

  Progress: 0/<TOTAL_TASKS>  |  Waves: 0/<TOTAL_WAVES>
```

States: `⬜ pending` · `🔄 running` · `✅ completed` · `❌ failed` · `🚫 blocked`

After each wave completes, re-print the status block with updated states and `Progress: N/<TOTAL>`.

Apply `core/shared/wave-execution.md` per wave:

```bash
for WAVE_ID in $(seq $START_WAVE $TOTAL_WAVES); do
  WAVE=$(echo "$PLAN" | jq ".waves[] | select(.wave_id == $WAVE_ID)")
  
  # Mark wave tasks as running in status display
  echo ""
  echo "▶ Wave $WAVE_ID of $TOTAL_WAVES"
  echo "  Title: $(echo "$WAVE" | jq -r '.title')"
  TASK_IDS=$(echo "$WAVE" | jq -r '.tasks[].task_id // .tasks[].id')
  for TID in $TASK_IDS; do echo "  🔄 $TID — running"; done

  # --- Step 5.A: Load pre-built context packs ---
  # Context packs were generated by /compass:prepare Step 8b.
  # Load from session dir; fall back to on-the-fly generation if missing.
  CONTEXT_BUNDLE=""
  for TASK_ID in $TASK_IDS; do
    PACK_FILE="$SESSION_DIR/$TASK_ID.context.json"
    if [ -f "$PACK_FILE" ]; then
      PACK=$(cat "$PACK_FILE")
    else
      PACK=$(compass-cli context pack "$SESSION_DIR" "$TASK_ID" 2>/dev/null || echo "")
    fi
    CONTEXT_BUNDLE="$CONTEXT_BUNDLE\n\n--- Task $TASK_ID context ---\n$PACK"
  done

  # --- Step 5.A2: Load worker rules (base + tech-stack + framework + test-framework addons) ---
  # Base rules (project override takes priority)
  if [ -f "$PROJECT_ROOT/.compass/worker-rules.md" ]; then
    WORKER_RULES=$(cat "$PROJECT_ROOT/.compass/worker-rules.md")
  else
    WORKER_RULES=$(cat "$HOME/.compass/core/worker-rules/base.md")
  fi

  # Dedup tracker — avoid loading the same addon twice if listed in multiple fields
  LOADED_ADDONS=""
  _load_addon() {
    local name="$1"
    [ -z "$name" ] && return
    echo " $LOADED_ADDONS " | grep -qw "$name" && return
    # Project override wins over global
    local addon="$PROJECT_ROOT/.compass/worker-rules/addons/$name.md"
    [ ! -f "$addon" ] && addon="$HOME/.compass/core/worker-rules/addons/$name.md"
    [ ! -f "$addon" ] && return
    WORKER_RULES="$WORKER_RULES"$'\n---\n'"$(sed '1,/^---$/d' "$addon")"
    LOADED_ADDONS="$LOADED_ADDONS $name"
  }

  TECH_STACK=$(echo "$CONFIG" | jq -r '.tech_stack // [] | .[]?' 2>/dev/null)
  FRAMEWORKS=$(echo "$CONFIG" | jq -r '.frameworks // [] | .[]?' 2>/dev/null)
  TEST_FRAMEWORKS=$(echo "$CONFIG" | jq -r '.test_frameworks // [] | .[]?' 2>/dev/null)

  for NAME in $TECH_STACK $FRAMEWORKS $TEST_FRAMEWORKS; do
    _load_addon "$NAME"
  done

  # --- Step 5.B: Build sub-agent prompt ---
  FILES_AFFECTED=$(echo "$WAVE" | jq -r '[.tasks[].files_affected[]] | unique | .[]' | tr '\n' ' ')
  
  # Extract relevant DESIGN-SPEC sections based on wave task covers[]
  WAVE_REQS=$(echo "$WAVE" | jq -r '[.tasks[].covers[]] | unique | join("|")')
  DESIGN_SECTIONS=$(awk -v reqs="$WAVE_REQS" '
    /^## / { sec=$0 }
    $0 ~ reqs { print_sec = 1 }
    print_sec { buf = buf "\n" $0 }
    END { print buf }
  ' "$SESSION_DIR/DESIGN-SPEC.md")
  
  TEST_SECTIONS=$(awk -v reqs="$WAVE_REQS" '
    /^### Test:/ || /^## / { sec=$0 }
    $0 ~ reqs { print_sec = 1; print sec }
    print_sec { print }
  ' "$SESSION_DIR/TEST-SPEC.md")
  
  PROMPT=$(cat <<END
# Wave $WAVE_ID — $(echo "$WAVE" | jq -r '.title')

You are implementing a wave of code changes. Read the context below, then implement ONLY the listed tasks.

## GitNexus
GitNexus: $GITNEXUS_STATUS
GitNexus Repo: $GITNEXUS_REPO
If GitNexus is GITNEXUS_AVAILABLE, run gitnexus_impact({target: "symbolName", direction: "upstream", repo: "$GITNEXUS_REPO"}) before modifying any function/class/method. If risk is HIGH or CRITICAL, report to orchestrator before proceeding.

## Strict scope rules
- Files you may modify: $FILES_AFFECTED
- Do NOT touch files outside this list, even if you see issues elsewhere
- Do NOT refactor unrelated code
- Do NOT run 'git commit' — the orchestrator handles commits
- Do NOT add features beyond the task list

## Context (decisions locked earlier)
$(cat "$SESSION_DIR/CONTEXT.md")

## Design Spec — sections relevant to this wave
$DESIGN_SECTIONS

## Test Spec — tests for this wave
$TEST_SECTIONS

## Wave tasks
$(echo "$WAVE" | jq -r '.tasks[] | "### \(.task_id) — \(.name)\n- Files affected: \(.files_affected | join(", "))\n- Briefing: \(.briefing_notes)\n- Acceptance commands:\n\(.acceptance.criteria | map("    " + .) | join("\n"))\n"')

## Worker Rules
$WORKER_RULES

## Execution steps
1. Read each file in files_affected to understand current state
2. Implement each task (edit/create files in files_affected)
3. Run ALL acceptance commands for each task
4. If any test fails, read the error, make a targeted fix, re-run. Up to 2 local retries.
5. Report back as a JSON object:
   {
     "status": "success" | "needs_human" | "partial",
     "files_changed": [{"path": "...", "change_summary": "..."}],
     "tests_run": [{"command": "...", "exit_code": N, "output_excerpt": "..."}],
     "retries_used": 0 | 1 | 2,
     "notes": "any ambiguity or decisions you made"
   }
END
)

  # --- Step 5.C marker — actual Agent dispatch is a prose instruction, not bash ---
  # See the prose block immediately below "### Step 5.C — Dispatch (MANDATORY Agent tool calls)"

  # --- Step 5.D: Parse response, handle failure ---
  # Parse RESPONSE JSON block
  # If status=needs_human → AskUserQuestion retry-with-guidance / skip / abort
  # If status=partial → AskUserQuestion accept / retry-full / abort

  # --- Step 5.E: Update status + persist wave progress ---
  # Mark wave tasks as completed in status display
  for TID in $TASK_IDS; do echo "  ✅ $TID — completed"; done
  echo "  Progress: <completed>/<TOTAL_TASKS>  |  Waves: $WAVE_ID/$TOTAL_WAVES"
  compass-cli state update "$SESSION_DIR" "$(cat <<JSON
{
  "waves": [{
    "wave_id": $WAVE_ID,
    "status": "done",
    "retry_count": $RETRIES,
    "completed_at": "$(date -u +%FT%TZ)"
  }]
}
JSON
)"
  compass-cli progress save "$SESSION_DIR" "wave-$WAVE_ID-done" "{\"last_wave_done\": $WAVE_ID}"

  # --- Step 5.F: Review gate ---
  # AskUserQuestion:
  #   Continue / Retry wave / Pause / Abort
done
```

### Step 5.C — Dispatch (MANDATORY Agent tool calls)

**This is not a bash command.** Stop writing bash. For each task in the current wave, invoke the `Agent` tool exactly once per task:

```
Agent(
  description: "Implement wave $WAVE_ID — task <task.id>",
  subagent_type: "general-purpose",
  prompt: <contents of $PROMPT built in Step 5.B for this task>
)
```

Rules:
- If the wave has multiple tasks → batch ALL Agent calls in a **single assistant message** so they dispatch concurrently.
- Do NOT simulate by reading files manually in the orchestrator context.
- Do NOT bundle all tasks into one Agent call.
- Wait for every sub-agent to return before moving to Step 5.D.

### Step 5.F — Wave review gate

**Mandatory**: After each wave's bash block executes (status updated, progress saved), the orchestrator invokes an AskUserQuestion BEFORE starting the next wave. Do NOT batch waves silently — the dev needs a control point between waves.

Skip this gate only when `--auto` mode is active (set via `/compass:cook --auto` or inherited from upstream auto-chain). In auto mode, treat "Continue" as the implicit answer and print `⚡ Auto: Continue to next wave` instead of asking.

Print the wave summary first (1-2 lines):
```
✓ Wave $WAVE_ID done — N tasks completed, M files changed, R retries used
```

Then:

```json
{"questions": [{"question": "Wave $WAVE_ID done. Continue?", "header": "Wave $WAVE_ID", "multiSelect": false, "options": [
  {"label": "Continue to next wave", "description": "Wave $((WAVE_ID+1)) will start"},
  {"label": "Retry this wave", "description": "Show wave diff first, then confirm rollback (git reset --hard HEAD~1)"},
  {"label": "Pause build", "description": "Save state and stop — resume later with /compass:cook"},
  {"label": "Abort build", "description": "Stop completely — commits stay, branch stays, status=paused"}
]}]}
```

**Branch**:
- **Continue** → proceed to next wave
- **Retry this wave** → **DO NOT reset immediately**. First preview + confirm:

  ```bash
  # Show what will be rolled back
  echo "📊 Wave $WAVE_ID commit that will be DESTROYED:"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  git -C "$PROJECT_ROOT" log -1 --stat HEAD
  echo ""
  echo "💥 git reset --hard HEAD~1 will:"
  echo "  - Move HEAD back 1 commit"
  echo "  - Discard all file changes in that commit (unrecoverable unless reflog used)"
  echo "  - Remove wave $WAVE_ID from state, requeue for retry"
  ```

  Then AskUserQuestion:

  ```json
  {"questions": [{"question": "Confirm destructive rollback of wave $WAVE_ID?", "header": "Confirm reset", "multiSelect": false, "options": [
    {"label": "Yes — reset and retry", "description": "Run git reset --hard HEAD~1 and re-spawn the wave sub-agents"},
    {"label": "Keep commit, retry on top", "description": "Leave wave $WAVE_ID commit in place and run sub-agents again (layered fix)"},
    {"label": "Cancel retry", "description": "Go back to the wave gate"}
  ]}]}
  ```

  - "Yes — reset" → `git -C "$PROJECT_ROOT" reset --hard HEAD~1`, remove wave from state, loop back to same wave
  - "Keep commit, retry on top" → do NOT reset, just re-dispatch sub-agents; new edits land on top of existing wave commit
  - "Cancel retry" → back to Step 5.F gate
- **Pause build** → status=paused in state, print resume hint, stop
- **Abort build** → status=paused, stop

---

## Step 6 — Chain: test → commit

After all waves done, chain to test and commit with user confirmation at each step.

### 6a. Print cook summary

```bash
TOTAL_RETRIES=$(echo "$STATE" | jq '[.waves[].retry_count] | add // 0')
```

Print: `✓ All <TOTAL_WAVES> waves dispatched. Retries: <TOTAL_RETRIES>.`

### 6b. Chain to compass:test

```json
{"questions": [{"question": "Run tests?", "header": "Test", "multiSelect": false, "options": [
  {"label": "Run compass:test (Recommended)", "description": "Verify all acceptance criteria from TEST-SPEC + detect test suite"},
  {"label": "Skip tests", "description": "I'll test manually later"}
]}]}
```

If "Run compass:test" → invoke `/compass:test` workflow inline (read and execute `~/.compass/core/workflows/test.md`). Wait for results.

### 6c. Chain to compass:commit

After test completes (or was skipped):

```json
{"questions": [{"question": "Commit changes?", "header": "Commit", "multiSelect": false, "options": [
  {"label": "Run compass:commit (Recommended)", "description": "Auto-generate conventional commit message from all changes"},
  {"label": "Skip commit", "description": "I'll commit manually later"}
]}]}
```

If "Run compass:commit" → invoke `/compass:commit` workflow inline (read and execute `~/.compass/core/workflows/commit.md`).

### 6d. Final status

```bash
compass-cli state update "$SESSION_DIR" '{"status":"complete","updated_at":"'$(date -u +%FT%TZ)'"}'
```

Print:
```
✓ Cook complete
  Session:  <slug>
  Waves:    <TOTAL_WAVES>
  Tests:    passed / skipped
  Commit:   <SHA> / skipped
  Branch:   <FEAT_BRANCH>

  Ship when ready:
    git push -u origin <FEAT_BRANCH>
    gh pr create
```

Stop. Do NOT auto-push or auto-create PR.

---

## Edge cases

| Situation | Handling |
|---|---|
| No prepared session | Step 1 fails, suggests `/compass:prepare` first |
| All waves already done | Step 3 exits early with `✓ All waves complete` |
| Git dirty on non-session branch | Step 2 git-context dialog handles |
| Sub-agent times out | After spawn, treat same as "needs_human" — prompt dev |
| Sub-agent reports success but tests fail | compass:test chain catches after all waves |
| Retry count reaches 3 | Prompt dev (retry-with-guidance / skip / abort) |
| Dev picks "skip wave" | Wave marked status=skipped, continue to next. Note in state.json. |
| Commit fails (hook rejects, etc.) | compass:commit handles — print error, dev fixes hook issue |
| Branch diverged during build (external commits) | Detect via `git rev-parse` mismatch vs state.json.git.session_start_sha — warn, ask dev |
| `compass-cli context pack` fails for a task | Skip context pack, fall back to raw file read in prompt. Log warning. |
| Sub-agent unavailable (no Agent tool) | Fail fast with error: "Build requires Claude Code Agent tool. Run in Claude Code or OpenCode." |
| Resume after interrupt | Step 3 progress load handles automatically |

---

## Final — Hand-off

Step 6 handled it. Stop cleanly.
