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

# Step 5: Generate Report

## YOUR TASK:

Generate a comprehensive feature report using structured data from prd.json.

---

## 1. Load Data

```javascript
// PRD PATH RESOLUTION
const queuePath = '.ralph/modules-queue.json';
let currentPrdPath = '.ralph/prd.json';
if (fileExists(queuePath)) {
  const queue = readJSON(queuePath);
  // For report, use last module's PRD (or current if still in progress)
  const idx = Math.min(queue.currentIndex, queue.modules.length - 1);
  currentPrdPath = queue.modules[idx].prdFile;
}

const prd = readJSON(currentPrdPath);
const stats = {
  feature: prd.feature, status: prd.status,
  iterations_used: prd.config.current_iteration - 1,
  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
  }
};

// Aggregate files from all tasks (populated by apex during execution)
const filesCreated = [...new Set(prd.tasks.flatMap(t => t.files_changed?.created || []))];
const filesModified = [...new Set(prd.tasks.flatMap(t => t.files_changed?.modified || []))];
const totalDuration = prd.history.reduce((sum, h) => sum + (h.duration_seconds || 0), 0);
```

### 1b. Multi-Module Aggregation

```javascript
const queuePath = '.ralph/modules-queue.json';
let moduleStats = null;
if (fileExists(queuePath)) {
  const queue = readJSON(queuePath);
  moduleStats = { totalModules: queue.totalModules, completedModules: queue.completedModules, modules: [] };
  for (const mod of queue.modules) {
    const modPrd = readJSON(mod.prdFile);
    moduleStats.modules.push({
      code: mod.code, status: mod.status,
      tasks: { total: modPrd.tasks.length, completed: modPrd.tasks.filter(t => t.status === 'completed').length, failed: modPrd.tasks.filter(t => t.status === 'failed').length },
      filesCreated: [...new Set(modPrd.tasks.flatMap(t => t.files_changed?.created || []))].length,
      commits: [...new Set(modPrd.tasks.filter(t => t.commit_hash).map(t => t.commit_hash))].length
    });
  }
}
```

### 1d. Handoff Reconciliation (Post-Execution Validation)

> **LESSON LEARNED (audit ba-002):** Simple "missing files" warnings were ignored.
> Structured reconciliation tables make gaps impossible to miss.

```javascript
// === RECONCILIATION 1: Handoff File Coverage ===
const filesToCreate = prd.implementation?.filesToCreate || {};
const handoffReconciliation = { categories: [], totalDeclared: 0, totalPresent: 0, missingFiles: [] };

for (const [cat, files] of Object.entries(filesToCreate)) {
  let declared = 0, present = 0;
  for (const file of (files || [])) {
    declared++;
    const filePath = file.path || file;
    if (fileExists(filePath)) {
      present++;
    } else {
      handoffReconciliation.missingFiles.push({ category: cat, path: filePath });
    }
  }
  handoffReconciliation.categories.push({
    category: cat, declared, present, missing: declared - present,
    coverage: declared > 0 ? Math.round((present / declared) * 100) : 100
  });
  handoffReconciliation.totalDeclared += declared;
  handoffReconciliation.totalPresent += present;
}

const totalHandoffCoverage = handoffReconciliation.totalDeclared > 0
  ? Math.round((handoffReconciliation.totalPresent / handoffReconciliation.totalDeclared) * 100)
  : 100;

// === RECONCILIATION 2: Business Rule Coverage ===
const brMapping = prd.implementation?.brToCodeMapping || [];
const brReconciliation = { total: brMapping.length, implemented: 0, missing: [], notTested: [] };

for (const br of brMapping) {
  const componentFile = br.component || br.file;
  const methodName = br.method || br.function;

  if (!componentFile || !fileExists(componentFile)) {
    brReconciliation.missing.push({ id: br.id, rule: br.rule, reason: 'file not found' });
    continue;
  }
  if (methodName) {
    const content = readFile(componentFile);
    if (!content.includes(methodName)) {
      brReconciliation.missing.push({ id: br.id, rule: br.rule, reason: `method ${methodName} not found` });
      continue;
    }
  }
  brReconciliation.implemented++;

  if (br.testFile && !fileExists(br.testFile)) {
    brReconciliation.notTested.push({ id: br.id, rule: br.rule });
  }
}

// === RECONCILIATION 3: API Endpoint Coverage ===
const apiEndpoints = prd.implementation?.apiEndpointSummary || [];
const apiReconciliation = { total: apiEndpoints.length, found: 0, missing: [] };

const ctrlFilesForReconciliation = glob('src/**/Controllers/**/*Controller.cs');
for (const ep of apiEndpoints) {
  const opName = ep.operation || ep.name;
  let found = false;
  for (const f of ctrlFilesForReconciliation) {
    const content = readFile(f);
    if (content.includes(opName)) { found = true; break; }
  }
  if (found) {
    apiReconciliation.found++;
  } else {
    apiReconciliation.missing.push({ operation: opName, method: ep.method, path: ep.path });
  }
}

// Log summary
console.log(`Handoff files: ${handoffReconciliation.totalPresent}/${handoffReconciliation.totalDeclared} (${totalHandoffCoverage}%)`);
console.log(`Business rules: ${brReconciliation.implemented}/${brReconciliation.total} implemented`);
console.log(`API endpoints: ${apiReconciliation.found}/${apiReconciliation.total} found`);
```

### 1e. Final Build & Test Verification (TRUTH CHECK)

> **LESSON LEARNED (audit ba-002):** "92/92 COMPLETE" + "Build PASS" was reported but the code
> had compilation errors and critical security flaws. The report MUST reflect actual build/test
> state, not just PRD task statuses.

```bash
# REAL build verification — overrides any previous "Build PASS" claim
dotnet build --no-restore --verbosity quiet
FINAL_BUILD_RC=$?

FINAL_TEST_RC=-1
if [ $FINAL_BUILD_RC -eq 0 ]; then
  dotnet test --no-build --verbosity quiet
  FINAL_TEST_RC=$?
fi
```

```javascript
// Override PRD-based build/test status with REAL results
const buildStatus = FINAL_BUILD_RC === 0 ? 'PASS' : 'FAIL';
const testStatus = FINAL_TEST_RC === 0 ? 'PASS' : (FINAL_TEST_RC === -1 ? 'SKIPPED (build failed)' : 'FAIL');

// CRITICAL: If build fails despite all tasks "completed", flag this prominently
if (FINAL_BUILD_RC !== 0 && stats.tasks.completed === stats.tasks.total) {
  console.error('INTEGRITY WARNING: All tasks marked COMPLETE but build FAILS');
  console.error('This indicates phantom task completion — review code quality');
  stats.buildIntegrityWarning = true;
}
```

### 1f. MCP Security Scan (Final Gate)

```javascript
// Quick MCP security validation on the final codebase
const securityScan = await mcp__smartstack__validate_security();
const conventionScan = await mcp__smartstack__validate_conventions();

// Check for write endpoints with Read permissions (semantic permission check)
const permissionWarnings = [];
const ctrlFiles = glob('src/**/Controllers/**/*Controller.cs');
for (const f of ctrlFiles) {
  const content = readFile(f);
  const writeWithRead = content.matchAll(/\[(HttpPost|HttpPut|HttpDelete|HttpPatch)[^\]]*\][^[]*\[RequirePermission\([^\)]*\.Read\)\]/gs);
  for (const m of writeWithRead) {
    permissionWarnings.push(`${f}: ${m[1]} uses Read permission`);
  }
}
```

### 1c. Test Metrics (from PRD)

> **Note:** Apex already ran tests and recorded results. Source metrics from PRD task data.

```javascript
const testTasks = prd.tasks.filter(t => t.category === 'test');
const testsPassed = testTasks.filter(t => t.status === 'completed').length;
const testsFailed = testTasks.filter(t => t.status === 'failed').length;
```

## 2. Generate Report

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

```markdown
# Feature Report: {feature}

## Summary
| Field | Value |
|-------|-------|
| Status | {status} |
| Iterations | {iterations_used} / {max} |
| Branch | {branch} |
| Duration | {totalDuration}s ({min} min) |
| Execution | Delegated to /apex |

## Tasks
| # | Task | Category | Status | Commit |
|---|------|----------|--------|--------|
{for each task:}
| {id} | {description} | {category} | {status_emoji} | {commit_hash} |

**{completed}/{total} completed, {failed} failed, {blocked} blocked**

{if moduleStats:}
## Modules
| Module | Status | Tasks | Completed | Failed | Files | Commits |
|--------|--------|-------|-----------|--------|-------|---------|
{for each mod:}
| {code} | {status} | {total} | {completed} | {failed} | {filesCreated} | {commits} |

**{completedModules}/{totalModules} modules completed**
{end if}

{if prd._sectionSplit?.enabled:}
## Section Split Execution
| Phase | Type | Section | Entities | Status |
|-------|------|---------|----------|--------|
{for each phase:}
| {phase} | {type} | {sectionCode || '-'} | {entities.length} | {status} |

{end if}

{if handoffReconciliation.totalDeclared > 0:}
## Handoff Reconciliation

### File Coverage by Category
| Category | Declared | Present | Missing | Coverage |
|----------|----------|---------|---------|----------|
{for each cat in handoffReconciliation.categories:}
| {category} | {declared} | {present} | {missing} | {coverage}% |
{end for}

**Total: {totalPresent}/{totalDeclared} ({totalHandoffCoverage}%)**

{if handoffReconciliation.missingFiles.length > 0:}
### Missing Files
{for each mf in handoffReconciliation.missingFiles:}
- **{mf.category}**: `{mf.path}`
{end for}
{end if}
{end if}

{if brReconciliation.total > 0:}
### Business Rule Coverage
| Metric | Value |
|--------|-------|
| Total Rules | {brReconciliation.total} |
| Implemented | {brReconciliation.implemented} |
| Missing | {brReconciliation.missing.length} |
| Not Tested | {brReconciliation.notTested.length} |

{if brReconciliation.missing.length > 0:}
**Missing BR implementations:**
{for each br in brReconciliation.missing:}
- `{br.id}`: {br.rule} — {br.reason}
{end for}
{end if}
{end if}

{if apiReconciliation.total > 0:}
### API Endpoint Coverage
| Metric | Value |
|--------|-------|
| Total Endpoints | {apiReconciliation.total} |
| Found | {apiReconciliation.found} |
| Missing | {apiReconciliation.missing.length} |

{if apiReconciliation.missing.length > 0:}
**Missing API endpoints:**
{for each ep in apiReconciliation.missing:}
- `{ep.method} {ep.path}` — operation: `{ep.operation}`
{end for}
{end if}
{end if}

## Build & Test Verification (Actual)

> These results come from REAL `dotnet build` and `dotnet test` execution,
> not from PRD task statuses.

| Check | Result |
|-------|--------|
| `dotnet build` | {buildStatus} |
| `dotnet test` | {testStatus} |
| MCP validate_security | {securityScan.status} |
| MCP validate_conventions | {conventionScan.status} |
| Permission semantic check | {permissionWarnings.length === 0 ? 'PASS' : `${permissionWarnings.length} issues`} |

{if stats.buildIntegrityWarning:}
> **INTEGRITY WARNING:** All tasks marked COMPLETE but the build FAILS.
> This means some generated code references types or methods that don't exist.
> Review the build output and fix before considering this feature done.
{end if}

{if permissionWarnings.length > 0:}
> **PERMISSION WARNINGS:** Write endpoints using Read permissions detected:
{for each warning:}
> - {warning}
{end for}
{end if}

## Test Metrics (PRD-based)
| Metric | Value |
|--------|-------|
| Test Tasks | {testTasks.length} |
| Passed | {testsPassed} |
| Failed | {testsFailed} |

## Failed Tasks
{table of failed tasks with error messages, or "None"}

## Files
- Created: {filesCreated.length}
- Modified: {filesModified.length}

## Commits
{unique commits list}

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

## 3. Finalize

```javascript
// Clean up section-split temporary files
// GUARD: only delete if the overall PRD status is 'completed'.
// If status is 'partial' or 'failed', keep temporary PRD files so the user
// can resume with `-r` — deleting them here would make recovery impossible.
if (prd._sectionSplit?.enabled && prd.status === 'completed') {
  for (const phase of prd._sectionSplit.phases) {
    if (fileExists(phase.prdFile) && phase.prdFile !== currentPrdPath) {
      deleteFile(phase.prdFile);
    }
  }
  delete prd._sectionSplit;
} else if (prd._sectionSplit?.enabled && prd.status !== 'completed') {
  console.log(`Section-split temp files preserved (status: ${prd.status}) — use /business-analyse-develop -r to resume`);
}

prd.updated_at = new Date().toISOString();
writeJSON(currentPrdPath, prd);
```

## 4. Display Summary

```
RALPH LOOP COMPLETE
Feature: {feature} | Status: {status}
Iterations: {n} | Tasks: {completed}/{total}
{moduleStats ? `Modules: ${completedModules}/${totalModules}` : ""}
Files: {created} created, {modified} modified
Report: .ralph/reports/{slug}.md
```

## 5. Completion Promise (if applicable)

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

## 6. Next Steps

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

---

Ralph loop complete. No more steps.
