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

# Step 1: Load Task

## YOUR TASK:

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

**ULTRA THINK about task decomposition.**

---

## EXECUTION SEQUENCE:

### 0. Multi-Module Queue Check

**Before any other logic, check for modules-queue.json:**

```javascript
const queuePath = '.ralph/modules-queue.json';
const hasQueue = fileExists(queuePath);

if (hasQueue) {
  const queue = readJSON(queuePath);
  const currentModule = queue.modules[queue.currentIndex];

  console.log(`Module ${queue.currentIndex + 1}/${queue.totalModules}: ${currentModule.code}`);

  // Copy current module's prd file to prd.json (overwrite)
  const modulePrd = readJSON(currentModule.prdFile);

  // Transform PrdJson format to Ralph v2 if needed
  if (modulePrd.project && modulePrd.requirements && !modulePrd.$version) {
    // This is a PrdJson format from ss derive-prd → transform to Ralph v2
    const transformedPrd = transformPrdJsonToRalphV2(modulePrd, currentModule.code);
    writeJSON('.ralph/prd.json', transformedPrd);
  } else {
    // Already Ralph v2 format
    writeJSON('.ralph/prd.json', modulePrd);
  }

  console.log(`Loaded PRD for module: ${currentModule.code}`);
}
```

**PrdJson → Ralph v2 transformation:**

The PrdJson format (from `ss derive-prd`) has this structure:
```
{ version, source, project, requirements: { useCases[], functionalRequirements[] },
  businessRules[], architecture: { entities[], apiEndpoints[], sections[] },
  implementation: { filesToCreate: { domain[], application[], infrastructure[], api[], frontend[], tests[] } },
  seedData }
```

```javascript
function transformPrdJsonToRalphV2(prdJson, moduleCode) {
  const tasks = [];
  let taskId = 1;

  // Layer processing order (SmartStack convention)
  const layerOrder = [
    { key: "domain",         category: "domain" },
    { key: "infrastructure", category: "infrastructure" },
    { key: "application",    category: "application" },
    { key: "api",            category: "api" },
    { key: "frontend",       category: "frontend" },
    { key: "seedData",       category: "infrastructure" },
    { key: "tests",          category: "test" }
  ];

  const filesToCreate = prdJson.implementation?.filesToCreate || {};

  // 1. Generate tasks from implementation.filesToCreate (primary source)
  const lastIdByCategory = {};

  for (const layer of layerOrder) {
    const files = filesToCreate[layer.key] || [];
    for (const fileSpec of files) {
      const depIds = [];
      // Depend on last task in same category (sequential within layer)
      if (lastIdByCategory[layer.category]) {
        depIds.push(lastIdByCategory[layer.category]);
      }
      // Cross-layer: api depends on last application task, frontend depends on last api task
      if (layer.category === "api" && lastIdByCategory["application"]) {
        depIds.push(lastIdByCategory["application"]);
      }
      if (layer.category === "frontend" && lastIdByCategory["api"]) {
        depIds.push(lastIdByCategory["api"]);
      }
      if (layer.category === "test" && lastIdByCategory["api"]) {
        depIds.push(lastIdByCategory["api"]);
      }

      // Build acceptance criteria from linked FRs and UCs
      const linkedFRs = (fileSpec.linkedFRs || [])
        .map(frId => {
          const fr = prdJson.requirements?.functionalRequirements?.find(f => f.id === frId);
          return fr ? fr.statement : frId;
        });
      const criteria = linkedFRs.length > 0
        ? `Implements: ${linkedFRs.join("; ")}`
        : `File ${fileSpec.path} created and compiles correctly`;

      tasks.push({
        id: taskId,
        description: fileSpec.description
          ? `[${layer.category}] ${fileSpec.description} → ${fileSpec.path}`
          : `[${layer.category}] Create ${fileSpec.type}: ${fileSpec.path}`,
        status: "pending",
        category: layer.category,
        dependencies: [...new Set(depIds)],
        acceptance_criteria: criteria,
        started_at: null,
        completed_at: null,
        iteration: null,
        commit_hash: null,
        files_changed: { created: [fileSpec.path], modified: [] },
        validation: null,
        error: null,
        module: moduleCode
      });

      lastIdByCategory[layer.category] = taskId;
      taskId++;
    }
  }

  // 2. Add IClientSeedDataProvider task for client projects (ExtensionsDbContext)
  // This is MANDATORY - without it, core seed data (navigation, permissions, roles) is dead code
  const hasClientSeedData = filesToCreate["seedData"]?.some(f =>
    f.type === "IClientSeedDataProvider" || f.path?.includes("SeedDataProvider"));
  if (!hasClientSeedData && filesToCreate["seedData"]?.length > 0) {
    tasks.push({
      id: taskId,
      description: `[infrastructure] Create IClientSeedDataProvider to inject core seed data at runtime`,
      status: "pending",
      category: "infrastructure",
      dependencies: [lastIdByCategory["infrastructure"] || lastIdByCategory["domain"]].filter(Boolean),
      acceptance_criteria: "IClientSeedDataProvider implements SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync; registered in DI",
      started_at: null, completed_at: null, iteration: null, commit_hash: null,
      files_changed: { created: ["src/Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs"], modified: ["src/Infrastructure/DependencyInjection.cs"] },
      validation: null, error: null, module: moduleCode
    });
    lastIdByCategory["infrastructure"] = taskId;
    taskId++;
  }

  // 3. Add a final validation task
  tasks.push({
    id: taskId,
    description: `[validation] Build, test, and MCP validate module ${moduleCode}`,
    status: "pending",
    category: "validation",
    dependencies: Object.values(lastIdByCategory),
    acceptance_criteria: "dotnet build succeeds, dotnet test passes, MCP validate_conventions clean",
    started_at: null,
    completed_at: null,
    iteration: null,
    commit_hash: null,
    files_changed: { created: [], modified: [] },
    validation: null,
    error: null,
    module: moduleCode
  });

  return {
    $version: "2.0.0",
    feature: `${prdJson.project?.module || moduleCode} (${prdJson.project?.application || "app"})`,
    status: "in_progress",
    created: new Date().toISOString(),
    updated_at: new Date().toISOString(),
    metadata: {
      cli_version: "unknown",
      branch: getCurrentBranch(),
      project_path: process.cwd(),
      mcp_servers: { smartstack: true, context7: true },
      module: moduleCode
    },
    config: {
      max_iterations: {max_iterations},
      completion_promise: "{completion_promise}",
      current_iteration: 1
    },
    source: {
      type: "ba-handoff",
      handoff_path: null,
      feature_json_path: prdJson.source?.featureJson || null,
      module: moduleCode
    },
    tasks: tasks,
    history: []
  };
}
```

**If no queue exists:** proceed with standard logic below (backward compatible).

---

### 1. Check for Existing prd.json

**If `.ralph/prd.json` exists:**
- Read and parse the file
- Verify `$version` is `"2.0.0"` (if not, STOP - run step-00 migration first)
- Find the next eligible task (see step 5)
- Skip to step 5

**If `.ralph/prd.json` does NOT exist:**
- Continue to step 2 (create tasks)

### 2. Analyze Task Description

**ULTRA THINK about the task:**

- What are the logical subtasks?
- What order should they be executed?
- What are the success criteria for each?
- What files will be modified?
- What layer does each task belong to?
- What dependencies exist between tasks?

**Check for BA handoff source (priority order):**

```bash
# Priority 1: Per-module PRD files (from ss derive-prd / BA handoff)
# These are already handled by Section 0 if modules-queue.json exists
PRD_MODULE=$(ls .ralph/prd-*.json 2>/dev/null | head -1)

# Priority 2: Single prd.json already present (from BA or previous run)
PRD_SINGLE=".ralph/prd.json"

# Priority 3: Markdown handoff document
HANDOFF=$(find . -path "*development-handoff*" -name "*.md" | head -1)
BA_OUTPUT=$(find . -path ".claude/output/ba/*" -name "4-*.md" | head -1)
SOURCE_PATH=${HANDOFF:-$BA_OUTPUT}
```

**If prd-*.json files exist but no modules-queue.json:**
- This means step-00 didn't create a queue (single prd file)
- Copy the single `prd-*.json` to `prd.json` and transform if needed

**If markdown handoff found:**
- Read the handoff document
- Derive tasks from its specifications (per-layer breakdown)
- Set `source.type = "ba-handoff"` and `source.handoff_path`
- Look for feature.json in the same directory

**If no handoff:**
- Generate task breakdown from `{task_description}`
- Set `source = null`

**Generate task breakdown:**

For `{task_description}`, identify **3-30 subtasks** organized by category.

**Task categories (use SmartStack layer order):**

| Category | Description | Examples |
|----------|-------------|---------|
| `domain` | Domain entities and value objects | Create entities, enums, interfaces |
| `application` | CQRS handlers, DTOs, validators | Commands, Queries, DTOs, FluentValidation |
| `infrastructure` | EF Core, external services | DbContext configs, migrations, services |
| `api` | Controllers, middleware | REST endpoints, filters, middleware |
| `frontend` | React pages, components, hooks | Pages, API services, React Query hooks |
| `i18n` | Translations | Translation JSON files |
| `test` | Unit and integration tests | xUnit tests, React testing library |
| `validation` | Build, lint, MCP checks | dotnet build, pnpm build, MCP validate |
| `other` | Anything else | Documentation, configuration |

**Example for "implement user authentication":**
```json
{
  "tasks": [
    {
      "id": 1,
      "description": "Create User entity in SmartStack.Domain/Entities/Business/",
      "status": "pending",
      "category": "domain",
      "dependencies": [],
      "acceptance_criteria": "Entity compiles with all required properties per handoff spec"
    },
    {
      "id": 2,
      "description": "Add User DbSet to ApplicationDbContext and create EF Core configuration",
      "status": "pending",
      "category": "infrastructure",
      "dependencies": [1],
      "acceptance_criteria": "DbContext compiles, configuration maps all properties"
    },
    {
      "id": 3,
      "description": "Create EF Core migration for User table",
      "status": "pending",
      "category": "infrastructure",
      "dependencies": [2],
      "acceptance_criteria": "Migration applies without errors, 3 files present"
    },
    {
      "id": 4,
      "description": "Implement IPasswordService and IJwtService",
      "status": "pending",
      "category": "application",
      "dependencies": [1],
      "acceptance_criteria": "Services compile, follow existing patterns"
    },
    {
      "id": 5,
      "description": "Create CQRS Commands: LoginCommand + RegisterCommand with handlers",
      "status": "pending",
      "category": "application",
      "dependencies": [4],
      "acceptance_criteria": "Handlers compile, validators defined"
    },
    {
      "id": 6,
      "description": "Create AuthController with login/register endpoints",
      "status": "pending",
      "category": "api",
      "dependencies": [5],
      "acceptance_criteria": "Endpoints respond correctly, Swagger displays them"
    },
    {
      "id": 7,
      "description": "Write unit tests for User entity and validators",
      "status": "pending",
      "category": "test",
      "dependencies": [5],
      "acceptance_criteria": "All tests pass, >80% coverage on new code"
    },
    {
      "id": 8,
      "description": "Run dotnet build, dotnet test, MCP validate_conventions",
      "status": "pending",
      "category": "validation",
      "dependencies": [6, 7],
      "acceptance_criteria": "Build succeeds, all tests pass, MCP validation clean"
    }
  ]
}
```

**Task count validation:**
- MINIMUM: 3 tasks (anything less is too coarse)
- MAXIMUM: 30 tasks (anything more needs re-grouping)
- If over 30, merge related subtasks into larger units

### 3. Create prd.json (v2)

**Write `.ralph/prd.json`:**

```json
{
  "$version": "2.0.0",
  "feature": "{task_description}",
  "status": "in_progress",
  "created": "{ISO_TIMESTAMP}",
  "updated_at": "{ISO_TIMESTAMP}",
  "metadata": {metadata},
  "config": {
    "max_iterations": {max_iterations},
    "completion_promise": "{completion_promise}",
    "current_iteration": 1
  },
  "source": {source_object_or_null},
  "tasks": [{generated_tasks_with_all_fields}],
  "history": []
}
```

**IMPORTANT: Every task MUST have ALL fields:**
```json
{
  "id": 1,
  "description": "...",
  "status": "pending",
  "category": "domain|application|infrastructure|api|frontend|i18n|test|validation|other",
  "dependencies": [],
  "acceptance_criteria": "...",
  "started_at": null,
  "completed_at": null,
  "iteration": null,
  "commit_hash": null,
  "files_changed": { "created": [], "modified": [] },
  "validation": null,
  "error": null
}
```

**Initialize `.ralph/progress.txt`:**

```markdown
# Ralph Loop Progress

## Task: {task_description}
## Started: {timestamp}
## Schema: v2.0.0
## Source: {source_type or "direct"}

---

## Iteration 1

### Context
Starting fresh implementation.

### Learnings
(To be updated after each iteration)
```

### 4. Validate Task Integrity

**After creation, verify:**

```javascript
// Check all tasks have required fields
const requiredFields = ['id', 'description', 'status', 'category',
  'dependencies', 'acceptance_criteria', 'started_at', 'completed_at',
  'iteration', 'commit_hash', 'files_changed', 'validation', 'error'];

for (const task of prd.tasks) {
  for (const field of requiredFields) {
    if (!(field in task)) {
      ERROR: "Task ${task.id} missing field: ${field}";
    }
  }
}

// Check dependency references are valid
const taskIds = prd.tasks.map(t => t.id);
for (const task of prd.tasks) {
  for (const dep of task.dependencies) {
    if (!taskIds.includes(dep)) {
      ERROR: "Task ${task.id} depends on non-existent task ${dep}";
    }
  }
}

// Check no circular dependencies
// (simple: a task cannot depend on a task with higher or equal id)
for (const task of prd.tasks) {
  for (const dep of task.dependencies) {
    if (dep >= task.id) {
      ERROR: "Task ${task.id} has forward/circular dependency on task ${dep}";
    }
  }
}

// Check task count
if (prd.tasks.length < 3 || prd.tasks.length > 30) {
  WARNING: "Task count ${prd.tasks.length} outside recommended range (3-30)";
}
```

### 5. Find Current Task

**Find the next eligible task:**

```javascript
function findNextTask(tasks) {
  for (const task of tasks) {
    if (task.status !== 'pending') continue;

    // Check all dependencies are completed
    const depsOk = task.dependencies.every(depId => {
      const dep = tasks.find(t => t.id === depId);
      return dep && dep.status === 'completed';
    });

    // Check if any dependency failed (→ block this task)
    const depsBlocked = task.dependencies.some(depId => {
      const dep = 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`;
      continue;
    }

    if (depsOk) return task;
  }
  return null; // No eligible tasks
}

const currentTask = findNextTask(prd.tasks);
```

**If no eligible task found:**
- Check if all tasks are completed → skip to step-05 (report)
- Check if remaining tasks are all blocked → report as partial, step-05
- Otherwise → unexpected state, STOP

**Store in state:**
```
{current_task_id}          = currentTask.id
{current_task_description} = currentTask.description
{current_task_category}    = currentTask.category
{current_task_criteria}    = currentTask.acceptance_criteria
{tasks_completed}          = tasks.filter(t => t.status === 'completed').length
{tasks_total}              = tasks.length
{tasks_blocked}            = tasks.filter(t => t.status === 'blocked').length
```

### 6. Read Previous Progress

**If iteration > 1:**

Read `.ralph/progress.txt` to understand:
- What was done in previous iterations
- Key learnings and context
- Files that were modified
- Issues encountered

**Also read `history[]` from prd.json for structured context.**

### 7. Show Task Status

**Display current state:**

```
╔══════════════════════════════════════════════════════════════════╗
║  ITERATION {current_iteration} / {max_iterations}               ║
║  {modules_queue ? "Module " + (currentIndex+1) + "/" + totalModules + ": " + current_module : ""} ║
╠══════════════════════════════════════════════════════════════════╣
║  Progress: {tasks_completed} / {tasks_total} tasks complete     ║
║  Blocked:  {tasks_blocked}                                      ║
║                                                                  ║
║  Current Task:                                                   ║
║  [{current_task_id}] {current_task_description}                 ║
║  Category: {current_task_category}                               ║
║  Criteria: {current_task_criteria}                               ║
╠══════════════════════════════════════════════════════════════════╣
║  Previous Learnings:                                             ║
║  {summary from progress.txt}                                    ║
╚══════════════════════════════════════════════════════════════════╝

-> Executing task...
```

---

## OUTPUT FORMAT:

```
Task Loaded:

| Field | Value |
|-------|-------|
| Iteration | {current_iteration} / {max_iterations} |
| Task ID | {current_task_id} |
| Task | {current_task_description} |
| Category | {current_task_category} |
| Criteria | {current_task_criteria} |
| Progress | {tasks_completed} / {tasks_total} |
| Blocked | {tasks_blocked} |

-> Executing...
```

---

## NEXT STEP:

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