---
name: step-01-task
description: Load or create tasks from prd.json (v3 unified or v2 legacy)
next_step: steps/step-02-execute.md
---

# Step 1: Load Task

> **MODULE TRANSITION CHECK:** Before "Only Read Once" rule, check for module transition:

```javascript
if (fileExists('.ralph/module-changed.json')) {
  const t = readJSON('.ralph/module-changed.json');
  console.log(`Module transition: ${t.fromModule} → ${t.toModule}`);
  deleteFile('.ralph/module-changed.json');
  // PROCEED (exception to "Only Read Once" rule)
}
```

> **CONTEXT OPTIMIZATION:** Read ONCE per module (first iteration).
> Iterations > 1 within same module → use COMPACT LOOP in step-04.
> Re-read ONLY on module transition (module-changed.json detected above).

## YOUR TASK:

Load task from prd.json or create initial task breakdown with categories, dependencies, and acceptance criteria.

---

## 0. Multi-Module Queue Check

See `references/multi-module-queue.md` for full module queue initialization, detection, and loading.

Quick: If `glob('.ralph/prd-*.json').length > 1` → create modules-queue.json and load first module's PRD.

---

## PrdJson → Ralph v2 Transformation (DEPRECATED)

See [references/task-transform-legacy.md](../references/task-transform-legacy.md) for the full `transformPrdJsonToRalphV2()` function including:
- Layer-ordered task generation (domain → infra → app → api → frontend → seedData → tests)
- Consolidated frontend + i18n tasks
- Guardrails 1d-1j (inject missing infrastructure, API, frontend, tests, seed data, migration)

---

## 1. Check for Existing prd.json

```javascript
// PRD PATH RESOLUTION (MANDATORY - runs before any PRD access)
const queuePath = '.ralph/modules-queue.json';
let currentPrdPath = '.ralph/prd.json';
if (fileExists(queuePath)) {
  const queue = readJSON(queuePath);
  currentPrdPath = queue.queue[queue.currentIndex].prdFile;
}
```

If `{currentPrdPath}` exists:
1. Read the PRD
2. **v3 FAST PATH:** If `$version === "3.0.0"` AND `tasks[]` exists:
   - Inject runtime fields if missing (status, config, feature, created, updated_at, history)
   - Write back to `{currentPrdPath}`
   - **Run CATEGORY COMPLETENESS CHECK (section 4b) before proceeding**
   - Skip directly to section 5 (find next task)
3. **v2 legacy:** If `$version === "2.0.0"` → find next eligible task (section 5)
4. **FORMAT A (deprecated):** If `.project && .requirements && !.$version` → run `transformPrdJsonToRalphV2()` → section 5

If `{currentPrdPath}` does not exist: continue to section 1b.

### 1b. BA Handoff Quality Gate (BLOCKING when BA artifacts present)

> **CRITICAL:** Before generating ANY tasks, verify the BA handoff is complete.
> A missing handoff means no frontend files, no test plan, no BR-to-code mapping.

> **INTENTIONAL SKIP:** If `sourceFeatureJson` is null (manual start via `{task_description}`, no BA run),
> this quality gate is **intentionally skipped** — the loop proceeds in "manual mode" and generates tasks
> from the task description. This is by design, not a bug. Inform the user in the summary (section 6 of step-00).

```javascript
const sourceFeatureJson = prd?.metadata?.sourceFeatureJson
  || findFile('docs/business/**/business-analyse/**/feature.json');

if (!sourceFeatureJson) {
  // Manual mode: no BA artifacts found — skip quality gate
  console.log('Manual mode: no BA artifacts detected — tasks will be generated from task description');
  // Continue to section 2 (Analyze Task Description)
} else {
  const feature = readJSON(sourceFeatureJson);
  const handoff = feature.handoff || {};

  // CHECK 1: Handoff status must be "handed-off"
  if (handoff.status !== 'handed-off') {
    console.error(`BLOCKING: BA HANDOFF INCOMPLETE
handoff.status = "${handoff.status || 'missing'}" (expected "handed-off")
Run /derive-prd to generate the handoff, then re-run /ralph-loop.`);
    STOP;
  }

  // CHECK 2: filesToCreate must have all categories
  const filesToCreate = handoff.filesToCreate || {};
  const requiredCategories = ['domain', 'application', 'infrastructure', 'api', 'frontend', 'seedData', 'tests', 'documentation'];
  const missingInHandoff = requiredCategories.filter(c => !filesToCreate[c] || filesToCreate[c].length === 0);

  if (missingInHandoff.length > 0) {
    console.warn(`⚠ Handoff has empty categories: ${missingInHandoff.join(', ')}`);
  }
}
```

## 2. Analyze Task Description

Check for BA handoff source (priority):
1. Per-module PRD files (handled by section 0)
2. Single prd.json already present
3. Markdown handoff: `find . -path "*development-handoff*" -name "*.md"`
4. Direct task from `{task_description}`

Generate **3-30 subtasks** by category (domain → infrastructure → application → api → frontend → i18n → test → validation).

## 3. Create prd.json

Write `{currentPrdPath}` with all tasks (every task MUST have: id, description, status, category, dependencies, acceptance_criteria, started_at, completed_at, iteration, commit_hash, files_changed, validation, error).

Initialize `.ralph/progress.txt`:
```markdown
# Ralph Loop Progress
## Task: {task_description}
## Started: {timestamp}
## Schema: v3.0.0
```

## 4. Validate Task Integrity

- All tasks have required fields
- Dependency references are valid (exist, no forward/circular)
- Task count 3-30

### 4b. Category Completeness Check (BLOCKING)

See `references/category-completeness.md` for full implementation.

Quick: `checkCategoryCompleteness(prd)` → inject guardrails if needed

---

### 4c. Section Split Detection (Large Module)

```javascript
const domainTasks = prd.tasks.filter(t => t.category === 'domain');
const sections = prd.architecture?.sections ?? [];

if (domainTasks.length > 4 && sections.length > 1 && !prd._sectionSplit?.enabled) {
  console.log(`SECTION SPLIT: ${domainTasks.length} domain tasks, ${sections.length} sections → activating`);
  // LOAD references/section-splitting.md — execute sections 2-6
}
```

**Activation:** `> 4 domain tasks` AND `> 1 architecture section`

## 5. Find Current Task

```javascript
function findNextTask(tasks) {
  for (const task of tasks) {
    if (task.status !== 'pending') continue;
    const depsBlocked = task.dependencies.some(d => {
      const dep = tasks.find(t => t.id === d);
      return dep && (dep.status === 'failed' || dep.status === 'blocked');
    });
    if (depsBlocked) { task.status = 'blocked'; task.error = 'Blocked by failed dependency'; continue; }
    const depsOk = task.dependencies.every(d => tasks.find(t => t.id === d)?.status === 'completed');
    if (depsOk) return task;
  }
  return null;
}
```

If no eligible task: check if all completed → step-05, else STOP.

## 6. Show Task Status

```
[{iteration}/{max}] {completed}/{total} done | Task [{id}] {description} ({category})
-> Executing...
```

---

## NEXT STEP:

After task loaded, proceed to `./step-02-execute.md`
