# Compact Loop — Delegate to /apex

> **Loaded by:** step-04 section 5 (after first full iteration)
> **Purpose:** Execute ALL remaining tasks autonomously by delegating to /apex.
> **EXECUTION GUARANTEE:** This loop runs until ALL tasks are done, max iterations, or dead-end.
> **ABSOLUTE RULES:**
> - NEVER stop the loop
> - NEVER wait for user input
> - NEVER ask for confirmation
> - NEVER re-read step files
> - NEVER pause between iterations
> - NEVER generate code directly — delegate to /apex
> - Execute A → B → C → D → back to step-04 section 1 → repeat

---

## Loop Entry

Display compact progress:
```
[{iteration}/{max}] {completed}/{total} done | Finding eligible tasks...
```

---

## A. Find Eligible Tasks

```javascript
const prd = readJSON('.ralph/prd.json');
const MAX_RETRIES = 3;

// RETRY: Reset failed tasks to pending if retries remain (max 3 attempts per task)
for (const task of prd.tasks) {
  if (task.status !== 'failed') continue;
  const retryCount = task._retryCount || 0;
  if (retryCount < MAX_RETRIES) {
    task.status = 'pending';
    task._retryCount = retryCount + 1;
    task.error = null;
    console.log(`RETRY [${task.id}] attempt ${retryCount + 1}/${MAX_RETRIES}: ${task.description.substring(0, 60)}`);
  }
}

// Unblock tasks whose dependencies are no longer failed (due to retries above)
for (const task of prd.tasks) {
  if (task.status !== 'blocked') continue;
  const stillBlocked = task.dependencies.some(depId => {
    const dep = prd.tasks.find(t => t.id === depId);
    return dep && (dep.status === 'failed' || dep.status === 'blocked');
  });
  if (!stillBlocked) {
    task.status = 'pending';
    task.error = null;
    console.log(`UNBLOCKED [${task.id}]: dependencies resolved`);
  }
}

// Block pending tasks whose dependencies are still failed/blocked
for (const task of prd.tasks) {
  if (task.status !== 'pending') continue;
  const depsBlocked = task.dependencies.some(depId => {
    const dep = prd.tasks.find(t => t.id === depId);
    return dep && (dep.status === 'failed' || dep.status === 'blocked');
  });
  if (depsBlocked) { task.status = 'blocked'; task.error = 'Blocked by failed dependency'; }
}

writeJSON('.ralph/prd.json', prd);

// Find ALL eligible tasks (dependencies met)
const eligible = prd.tasks.filter(task => {
  if (task.status !== 'pending') return false;
  return task.dependencies.every(depId => {
    const dep = prd.tasks.find(t => t.id === depId);
    return dep && dep.status === 'completed';
  });
});

if (eligible.length === 0) {
  // Dead-end or all done — return to step-04 sections 2-4
  goto CHECK_COMPLETION;
}

// BATCH: group by category, take first group (max 5)
const firstCategory = eligible[0].category;
const batch = eligible.filter(t => t.category === firstCategory).slice(0, 5);
```

Display:
```
Batch: {batch.length} [{firstCategory}] → {batch.map(t => `[${t.id}] ${t.description.substring(0, 60)}`).join(' | ')}
```

---

## B. Delegate Batch to /apex

> **Ralph NEVER generates code.** All code generation, MCP calls, POST-CHECKs,
> and build verification are handled by apex.

### B1. Mark Batch In-Progress

```javascript
for (const task of batch) {
  task.status = 'in_progress';
  task.started_at = new Date().toISOString();
}
writeJSON('.ralph/prd.json', prd);
```

### B2. Invoke /apex

**INVOKE `/apex -d .ralph/prd.json`**

Apex handles everything for the current module:
- Reads PRD, extracts context (context_code, app_name, module_code, entities, sections)
- Executes ALL layers: domain → infrastructure → migration → application → api → seed data → frontend → tests
- Runs full POST-CHECKs (43 checks from `references/post-checks.md`)
- Commits per layer (atomic commits)
- Validates with MCP (`validate_conventions`)
- Updates task statuses in the PRD file directly

> **FLAGS:** `-d` implies `-a` (auto, no user confirmation) and `-e` (economy, no nested teams).

### B3. Verify Post-Apex Results

```javascript
// Re-read PRD after apex execution
const updatedPrd = readJSON('.ralph/prd.json');
const batchIds = batch.map(t => t.id);

const completed = updatedPrd.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed').length;
const failed = updatedPrd.tasks.filter(t => batchIds.includes(t.id) && t.status === 'failed').length;
const pending = updatedPrd.tasks.filter(t => batchIds.includes(t.id) && t.status === 'pending').length;

if (failed > 0) {
  console.log(`WARNING: ${failed} tasks failed during apex execution`);
  // Failed tasks will be retried in next loop iteration if retries remain
}

if (pending > 0) {
  console.log(`INFO: ${pending} tasks still pending — apex may not have reached them`);
}

console.log(`Apex completed: ${completed}/${batchIds.length} tasks`);
```

---

## C. Commit PRD State

### C1. Update Progress File (MANDATORY)

```javascript
const prdCheck = readJSON('.ralph/prd.json');
const totalCompleted = prdCheck.tasks.filter(t => t.status === 'completed').length;
const total = prdCheck.tasks.length;

appendFile('.ralph/progress.txt',
  `Iteration ${prdCheck.config.current_iteration}: Delegated to /apex — ` +
  `${completed}/${batchIds.length} batch tasks completed. ${totalCompleted}/${total} overall.\n`
);
```

### C2. Increment Iteration

```javascript
prdCheck.config.current_iteration++;
prdCheck.updated_at = new Date().toISOString();
writeJSON('.ralph/prd.json', prdCheck);
```

### C3. Git Commit (PRD state only — apex already committed code)

```bash
git add .ralph/prd.json .ralph/progress.txt
[ -f .ralph/modules-queue.json ] && git add .ralph/modules-queue.json

git commit -m "$(cat <<'EOF'
chore(ralph): update PRD state — iteration {iteration}

Tasks: {completed}/{batch_size} completed via /apex
Overall: {totalCompleted}/{total}

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"
```

### C4. PRD Sync Verification (HARD CHECK)

```javascript
const prdVerify = readJSON('.ralph/prd.json');
const actuallyCompleted = prdVerify.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed');

if (actuallyCompleted.length !== completed) {
  console.error('PRD SYNC ERROR: Mismatch between apex results and prd.json');
  // Force re-read — apex may have written after our read
  // DO NOT overwrite — apex is the source of truth for task statuses
}
```

---

## D. Loop Back (MANDATORY — NO PAUSE)

**IMMEDIATELY return to step-04 section 1 (Read Current State).**

DO NOT stop. DO NOT wait for user. DO NOT ask "should I continue?". DO NOT re-read step files.
DO NOT display a summary and pause. DO NOT output partial results and wait.
**Go directly to step-04 section 1. NOW.**

The ONLY exit conditions (enforced by step-04 sections 2-4):
- ALL tasks complete (all modules if multi-module) → step-05
- Max iterations reached → step-05
- Dead-end (all remaining blocked/failed) → step-05
- User Ctrl+C (external interrupt)
