---
name: step-05-report
description: Generate final report from prd.json v2 structured data
next_step: null
---

# Step 5: Generate Report

## YOUR TASK:

Generate a comprehensive feature report using structured data from prd.json v2 (history, task metadata, file tracking).

---

## EXECUTION SEQUENCE:

### 1. Load Structured Data from prd.json

**Read all data from the single source of truth:**

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

const stats = {
  feature: prd.feature,
  status: prd.status,
  schema: prd.$version,
  started: prd.created,
  completed: new Date().toISOString(),
  iterations_used: prd.config.current_iteration - 1,
  max_iterations: prd.config.max_iterations,
  tasks: {
    total: prd.tasks.length,
    completed: prd.tasks.filter(t => t.status === 'completed').length,
    failed: prd.tasks.filter(t => t.status === 'failed').length,
    blocked: prd.tasks.filter(t => t.status === 'blocked').length,
    skipped: prd.tasks.filter(t => t.status === 'skipped').length,
    pending: prd.tasks.filter(t => t.status === 'pending').length
  },
  source: prd.source ? prd.source.type : 'direct',
  branch: prd.metadata.branch
};

// Aggregate files from all tasks
const allFilesCreated = [];
const allFilesModified = [];
for (const task of prd.tasks) {
  if (task.files_changed) {
    allFilesCreated.push(...task.files_changed.created);
    allFilesModified.push(...task.files_changed.modified);
  }
}

// Deduplicate
const filesCreated = [...new Set(allFilesCreated)];
const filesModified = [...new Set(allFilesModified)];

// Calculate total duration from history
const totalDuration = prd.history.reduce((sum, h) => sum + (h.duration_seconds || 0), 0);
```

### 1b. Multi-Module Aggregation (if modules-queue.json exists)

**Check for multi-module context:**

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

let moduleStats = null;

if (hasQueue) {
  const queue = readJSON(queuePath);

  moduleStats = {
    totalModules: queue.totalModules,
    completedModules: queue.completedModules,
    modules: []
  };

  // Aggregate stats from each module's prd file
  for (const mod of queue.modules) {
    const modPrd = readJSON(mod.prdFile);
    const modTasks = modPrd.tasks || [];

    const modData = {
      code: mod.code,
      status: mod.status,
      tasks: {
        total: modTasks.length,
        completed: modTasks.filter(t => t.status === 'completed').length,
        failed: modTasks.filter(t => t.status === 'failed').length,
        blocked: modTasks.filter(t => t.status === 'blocked').length,
        skipped: modTasks.filter(t => t.status === 'skipped').length
      },
      filesCreated: [],
      filesModified: [],
      commits: []
    };

    for (const task of modTasks) {
      if (task.files_changed) {
        modData.filesCreated.push(...task.files_changed.created);
        modData.filesModified.push(...task.files_changed.modified);
      }
      if (task.commit_hash) {
        modData.commits.push(task.commit_hash);
      }
    }

    modData.filesCreated = [...new Set(modData.filesCreated)];
    modData.filesModified = [...new Set(modData.filesModified)];
    modData.commits = [...new Set(modData.commits)];

    moduleStats.modules.push(modData);
  }

  // Merge all module files into main aggregation
  for (const mod of moduleStats.modules) {
    allFilesCreated.push(...mod.filesCreated);
    allFilesModified.push(...mod.filesModified);
  }
}
```

### 2. Collect MCP Usage (from logs if available)

**Parse from verbose logs:**

```bash
# Count MCP calls from log files
MCP_CALLS=$(grep -c "\[MCP\]" .ralph/logs/*.log 2>/dev/null || echo "0")
SMARTSTACK_CALLS=$(grep -c "mcp__smartstack" .ralph/logs/*.log 2>/dev/null || echo "0")
CONTEXT7_CALLS=$(grep -c "mcp__context7" .ralph/logs/*.log 2>/dev/null || echo "0")
```

**Or extract from task validations:**

```javascript
const validationStats = {
  passed: prd.tasks.filter(t => t.validation === 'passed').length,
  failed: prd.tasks.filter(t => t.validation && t.validation.startsWith('failed')).length,
  skipped: prd.tasks.filter(t => t.validation === null).length
};
```

### 3. Generate Report File

**Write to `.ralph/reports/{feature-slug}.md`:**

```markdown
# Feature Report: {feature}

## Summary

| Field | Value |
|-------|-------|
| **Status** | {status} |
| **Started** | {started} |
| **Completed** | {completed} |
| **Iterations** | {iterations_used} / {max_iterations} |
| **Branch** | {branch} |
| **Source** | {source} |
| **Schema** | {schema} |

## Task Progress

| # | Task | Category | Status | Iteration | Duration | Commit |
|---|------|----------|--------|-----------|----------|--------|
{for each task in prd.tasks:}
| {id} | {description} | {category} | {status_emoji} | {iteration || '-'} | {duration}s | {commit_hash || '-'} |
{end for}

**Summary: {completed}/{total} completed, {failed} failed, {blocked} blocked, {skipped} skipped**

{status_emoji mapping: completed=✅, failed=❌, blocked=🚫, skipped=⏭️, pending=⏳}

{if moduleStats:}
## Module Progress

| Module | Status | Tasks | Completed | Failed | Files Created | Files Modified | Commits |
|--------|--------|-------|-----------|--------|---------------|----------------|---------|
{for each mod in moduleStats.modules:}
| {mod.code} | {mod.status} | {mod.tasks.total} | {mod.tasks.completed} | {mod.tasks.failed} | {mod.filesCreated.length} | {mod.filesModified.length} | {mod.commits.length} |
{end for}

**Modules: {moduleStats.completedModules}/{moduleStats.totalModules} completed**
{end if}

## Failed Tasks

{if any failed tasks:}
| # | Task | Error |
|---|------|-------|
{for each failed task:}
| {id} | {description} | {error} |
{end for}
{else:}
No failed tasks.
{end if}

## Blocked Tasks

{if any blocked tasks:}
| # | Task | Blocked By | Error |
|---|------|------------|-------|
{for each blocked task:}
| {id} | {description} | Tasks {dependencies} | {error} |
{end for}
{else:}
No blocked tasks.
{end if}

## Iteration History

{for each entry in prd.history:}
### Iteration {iteration} — Task [{task_id}]
- **Action**: {action}
- **Timestamp**: {timestamp}
- **Commit**: {commit_hash}
- **Duration**: {duration_seconds}s
- **Notes**: {notes}

{end for}

## MCP Validation Results

| Task | Category | Validation |
|------|----------|------------|
{for each task:}
| [{id}] {description} | {category} | {validation || 'N/A'} |
{end for}

## Files Created ({filesCreated.length})

{if filesCreated.length > 0:}
{for each file:}
- `{file}`
{end for}
{else:}
- (none)
{end if}

## Files Modified ({filesModified.length})

{if filesModified.length > 0:}
{for each file:}
- `{file}`
{end for}
{else:}
- (none)
{end if}

## Git Commits

{for each unique commit_hash in tasks:}
`{commit_hash}` {commit message from task description}
{end for}

## Metadata

| Field | Value |
|-------|-------|
| CLI Version | {prd.metadata.cli_version} |
| Branch | {prd.metadata.branch} |
| Project | {prd.metadata.project_path} |
| MCP SmartStack | {prd.metadata.mcp_servers.smartstack ? '✅' : '❌'} |
| MCP Context7 | {prd.metadata.mcp_servers.context7 ? '✅' : '❌'} |
| Total Duration | {totalDuration}s ({Math.round(totalDuration/60)} min) |

---
*Auto-generated by Ralph Loop v2 — SmartStack CLI*
```

### 4. Finalize prd.json

**Set final status (if not already set by step-04):**

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

### 5. Display Final Summary

```
╔══════════════════════════════════════════════════════════════════╗
║                    RALPH LOOP COMPLETE                           ║
╠══════════════════════════════════════════════════════════════════╣
║  Feature:    {feature}                                           ║
║  Status:     {status}                                            ║
║  Iterations: {iterations_used}                                   ║
║  Tasks:      {completed}/{total} completed                       ║
║              {failed} failed, {blocked} blocked                  ║
║  {moduleStats ? "Modules: " + moduleStats.completedModules + "/" + moduleStats.totalModules + " completed" : ""} ║
╠══════════════════════════════════════════════════════════════════╣
║  Files Created:  {filesCreated.length}                           ║
║  Files Modified: {filesModified.length}                          ║
║  Commits:        {unique_commits.length}                         ║
║  Duration:       {totalDuration}s ({minutes} min)                ║
╠══════════════════════════════════════════════════════════════════╣
║  Report saved to:                                                ║
║  .ralph/reports/{feature-slug}.md                                ║
╚══════════════════════════════════════════════════════════════════╝
```

### 6. Output Completion (if applicable)

**If prd.status === 'completed' and not already output:**

```
<promise>{completion_promise}</promise>
```

---

## CLEANUP SUGGESTIONS:

**Suggest to user:**

```
Next steps:
1. Review report: .ralph/reports/{feature-slug}.md
2. Run full test suite: dotnet test
3. Create PR: /gitflow pr
4. Clean up Ralph files: rm -rf .ralph/ (optional)
```

---

## SUCCESS CRITERIA:

- Report generated in `.ralph/reports/` with ALL structured data from prd.json
- Task progress table with categories, status, iterations, and commits
- Failed/blocked tasks documented with error messages
- Iteration history from `history[]` included
- File lists from task-level tracking (not git log)
- Metadata section with execution context
- Duration calculated from history entries
- Final summary displayed
- prd.json `status` and `updated_at` finalized
- **Multi-module:** Per-module statistics table included (if modules-queue.json exists)
- **Multi-module:** Cross-module file and commit aggregation

## COMPLETION:

Ralph loop complete. No more steps.
