@atlashub/smartstack-cli 4.24.0 → 4.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +765 -517
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/agents/ba-writer.md +7 -3
- package/templates/skills/ba-generate-html/references/data-build.md +22 -13
- package/templates/skills/ba-generate-html/references/data-mapping.md +33 -24
- package/templates/skills/ba-generate-html/steps/step-01-collect.md +15 -6
- package/templates/skills/ba-generate-html/steps/step-02-build-data.md +37 -22
- package/templates/skills/business-analyse/steps/step-00-init.md +9 -11
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +15 -0
- package/templates/skills/derive-prd/steps/step-01-transform.md +6 -2
- package/templates/skills/derive-prd/steps/step-02-export.md +12 -0
- package/templates/skills/ralph-loop/references/category-completeness.md +3 -3
- package/templates/skills/ralph-loop/references/compact-loop.md +47 -11
- package/templates/skills/ralph-loop/references/init-resume-recovery.md +1 -1
- package/templates/skills/ralph-loop/references/module-transition.md +30 -5
- package/templates/skills/ralph-loop/references/multi-module-queue.md +4 -4
- package/templates/skills/ralph-loop/references/section-splitting.md +6 -6
- package/templates/skills/ralph-loop/steps/step-01-task.md +14 -4
- package/templates/skills/ralph-loop/steps/step-02-execute.md +14 -6
- package/templates/skills/ralph-loop/steps/step-03-commit.md +15 -4
- package/templates/skills/ralph-loop/steps/step-04-check.md +19 -5
- package/templates/skills/ralph-loop/steps/step-05-report.md +35 -3
|
@@ -54,15 +54,17 @@ if (missing.length === 0) {
|
|
|
54
54
|
timestamp: new Date().toISOString()
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
// Mark current PRD as complete
|
|
57
|
+
// Mark current PRD as complete (write to module-specific file, NOT .ralph/prd.json)
|
|
58
58
|
prd.status = 'completed';
|
|
59
|
-
writeJSON(
|
|
59
|
+
writeJSON(currentModule.prdFile, prd);
|
|
60
60
|
|
|
61
|
-
//
|
|
61
|
+
// Prepare next module's PRD (work directly with prd-{moduleCode}.json)
|
|
62
62
|
const nextPrd = readJSON(queue.modules[nextIndex].prdFile);
|
|
63
63
|
nextPrd.config.current_iteration = 1;
|
|
64
64
|
nextPrd.config.max_iterations = prd.config.max_iterations;
|
|
65
|
-
writeJSON(
|
|
65
|
+
writeJSON(queue.modules[nextIndex].prdFile, nextPrd);
|
|
66
|
+
// NOTE: Ralph works directly with prd-{moduleCode}.json via currentPrdPath resolution.
|
|
67
|
+
// No need to copy to .ralph/prd.json — this avoids overwriting between modules.
|
|
66
68
|
|
|
67
69
|
console.log(`MODULE COMPLETE: ${currentModule.code} → NEXT: ${queue.modules[nextIndex].code}`);
|
|
68
70
|
|
|
@@ -76,6 +78,29 @@ if (missing.length === 0) {
|
|
|
76
78
|
}
|
|
77
79
|
```
|
|
78
80
|
|
|
81
|
+
### MANDATORY POST-CHECK: modules-queue.json integrity
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// VERIFY: modules-queue.json updated correctly after advancement
|
|
85
|
+
const queueCheck = readJSON(queuePath);
|
|
86
|
+
const actualCompleted = queueCheck.modules.filter(m => m.status === 'completed').length;
|
|
87
|
+
if (queueCheck.completedModules !== actualCompleted) {
|
|
88
|
+
console.error(`QUEUE SYNC ERROR: completedModules=${queueCheck.completedModules} but actual=${actualCompleted}`);
|
|
89
|
+
queueCheck.completedModules = actualCompleted;
|
|
90
|
+
writeJSON(queuePath, queueCheck);
|
|
91
|
+
console.log('FIXED: completedModules synced to actual count');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (nextIndex < queueCheck.totalModules) {
|
|
95
|
+
if (queueCheck.currentIndex !== nextIndex) {
|
|
96
|
+
console.error(`QUEUE SYNC ERROR: currentIndex=${queueCheck.currentIndex} but expected=${nextIndex}`);
|
|
97
|
+
queueCheck.currentIndex = nextIndex;
|
|
98
|
+
writeJSON(queuePath, queueCheck);
|
|
99
|
+
console.log('FIXED: currentIndex synced');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
79
104
|
---
|
|
80
105
|
|
|
81
106
|
## Check: All Modules Complete
|
|
@@ -121,7 +146,7 @@ if (fileExists(moduleChangePath)) {
|
|
|
121
146
|
├─ step-04: Check completion
|
|
122
147
|
│ └─ If all categories complete:
|
|
123
148
|
│ ├─ Write module-changed.json
|
|
124
|
-
│ ├─ Write next PRD to
|
|
149
|
+
│ ├─ Write next PRD to prd-{moduleCode}.json (via currentPrdPath)
|
|
125
150
|
│ └─ Return to step-01 ✓
|
|
126
151
|
│
|
|
127
152
|
└─ [Phase B: Module 2]
|
|
@@ -123,7 +123,7 @@ if (fileExists(queuePath)) {
|
|
|
123
123
|
const queue = readJSON(queuePath);
|
|
124
124
|
const currentModule = queue.modules[queue.currentIndex];
|
|
125
125
|
|
|
126
|
-
const currentPrd = readJSON(
|
|
126
|
+
const currentPrd = readJSON(currentPrdPath);
|
|
127
127
|
if (currentPrd.metadata?.moduleCode !== currentModule.code) {
|
|
128
128
|
// Module has changed — load new PRD
|
|
129
129
|
const modulePrd = readJSON(currentModule.prdFile);
|
|
@@ -143,16 +143,16 @@ if (fileExists(queuePath)) {
|
|
|
143
143
|
modulePrd.metadata.project_path = modulePrd.metadata.project_path || process.cwd();
|
|
144
144
|
modulePrd.metadata.mcp_servers = modulePrd.metadata.mcp_servers || { smartstack: true, context7: true };
|
|
145
145
|
modulePrd.history = modulePrd.history || [];
|
|
146
|
-
writeJSON(
|
|
146
|
+
writeJSON(currentModule.prdFile, modulePrd);
|
|
147
147
|
}
|
|
148
148
|
// LEGACY: FORMAT A
|
|
149
149
|
else if (modulePrd.project && modulePrd.requirements && !modulePrd.$version) {
|
|
150
150
|
console.warn('⚠ DEPRECATED: FORMAT A prd.json detected. Re-run `ss derive-prd` to generate v3 format.');
|
|
151
|
-
writeJSON(
|
|
151
|
+
writeJSON(currentModule.prdFile, transformPrdJsonToRalphV2(modulePrd, currentModule.code));
|
|
152
152
|
}
|
|
153
153
|
// v2 legacy
|
|
154
154
|
else {
|
|
155
|
-
writeJSON(
|
|
155
|
+
writeJSON(currentModule.prdFile, modulePrd);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
@@ -50,7 +50,7 @@ EF Core creates a single ModelSnapshot per DbContext. If Phase 1 creates a migra
|
|
|
50
50
|
## 1. Detection
|
|
51
51
|
|
|
52
52
|
```javascript
|
|
53
|
-
const prd = readJSON(
|
|
53
|
+
const prd = readJSON(currentPrdPath);
|
|
54
54
|
const domainTasks = prd.tasks.filter(t => t.category === 'domain');
|
|
55
55
|
const sections = prd.architecture?.sections ?? [];
|
|
56
56
|
|
|
@@ -313,7 +313,7 @@ prd._sectionSplit = {
|
|
|
313
313
|
entityToSection
|
|
314
314
|
};
|
|
315
315
|
|
|
316
|
-
writeJSON(
|
|
316
|
+
writeJSON(currentPrdPath, prd);
|
|
317
317
|
```
|
|
318
318
|
|
|
319
319
|
---
|
|
@@ -344,7 +344,7 @@ if (!depsOk) {
|
|
|
344
344
|
// After apex returns:
|
|
345
345
|
nextPhase.status = 'completed';
|
|
346
346
|
prd._sectionSplit.currentPhase = nextPhase.phase;
|
|
347
|
-
writeJSON(
|
|
347
|
+
writeJSON(currentPrdPath, prd);
|
|
348
348
|
```
|
|
349
349
|
|
|
350
350
|
---
|
|
@@ -369,7 +369,7 @@ for (const phaseTask of phasePrd.tasks) {
|
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
writeJSON(
|
|
372
|
+
writeJSON(currentPrdPath, prd);
|
|
373
373
|
```
|
|
374
374
|
|
|
375
375
|
---
|
|
@@ -413,13 +413,13 @@ After all phases complete or on final report:
|
|
|
413
413
|
if (prd._sectionSplit?.enabled) {
|
|
414
414
|
// Delete temporary phase PRD files
|
|
415
415
|
for (const phase of prd._sectionSplit.phases) {
|
|
416
|
-
if (fileExists(phase.prdFile) && phase.prdFile !==
|
|
416
|
+
if (fileExists(phase.prdFile) && phase.prdFile !== currentPrdPath) {
|
|
417
417
|
deleteFile(phase.prdFile);
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
// Remove split state from master PRD
|
|
421
421
|
delete prd._sectionSplit;
|
|
422
|
-
writeJSON(
|
|
422
|
+
writeJSON(currentPrdPath, prd);
|
|
423
423
|
}
|
|
424
424
|
```
|
|
425
425
|
|
|
@@ -46,17 +46,27 @@ See [references/task-transform-legacy.md](../references/task-transform-legacy.md
|
|
|
46
46
|
|
|
47
47
|
## 1. Check for Existing prd.json
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
```javascript
|
|
50
|
+
// PRD PATH RESOLUTION (MANDATORY - runs before any PRD access)
|
|
51
|
+
const queuePath = '.ralph/modules-queue.json';
|
|
52
|
+
let currentPrdPath = '.ralph/prd.json';
|
|
53
|
+
if (fileExists(queuePath)) {
|
|
54
|
+
const queue = readJSON(queuePath);
|
|
55
|
+
currentPrdPath = queue.queue[queue.currentIndex].prdFile;
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If `{currentPrdPath}` exists:
|
|
50
60
|
1. Read the PRD
|
|
51
61
|
2. **v3 FAST PATH:** If `$version === "3.0.0"` AND `tasks[]` exists:
|
|
52
62
|
- Inject runtime fields if missing (status, config, feature, created, updated_at, history)
|
|
53
|
-
- Write back to
|
|
63
|
+
- Write back to `{currentPrdPath}`
|
|
54
64
|
- **Run CATEGORY COMPLETENESS CHECK (section 4b) before proceeding**
|
|
55
65
|
- Skip directly to section 5 (find next task)
|
|
56
66
|
3. **v2 legacy:** If `$version === "2.0.0"` → find next eligible task (section 5)
|
|
57
67
|
4. **FORMAT A (deprecated):** If `.project && .requirements && !.$version` → run `transformPrdJsonToRalphV2()` → section 5
|
|
58
68
|
|
|
59
|
-
If
|
|
69
|
+
If `{currentPrdPath}` does not exist: continue to section 1b.
|
|
60
70
|
|
|
61
71
|
### 1b. BA Handoff Quality Gate (BLOCKING when BA artifacts present)
|
|
62
72
|
|
|
@@ -110,7 +120,7 @@ Generate **3-30 subtasks** by category (domain → infrastructure → applicatio
|
|
|
110
120
|
|
|
111
121
|
## 3. Create prd.json
|
|
112
122
|
|
|
113
|
-
Write
|
|
123
|
+
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).
|
|
114
124
|
|
|
115
125
|
Initialize `.ralph/progress.txt`:
|
|
116
126
|
```markdown
|
|
@@ -30,13 +30,21 @@ Delegate the current module's tasks to `/apex -d` for code generation. Ralph han
|
|
|
30
30
|
### 1. Verify Dependencies
|
|
31
31
|
|
|
32
32
|
```javascript
|
|
33
|
-
|
|
33
|
+
// PRD PATH RESOLUTION
|
|
34
|
+
const queuePath = '.ralph/modules-queue.json';
|
|
35
|
+
let currentPrdPath = '.ralph/prd.json';
|
|
36
|
+
if (fileExists(queuePath)) {
|
|
37
|
+
const queue = readJSON(queuePath);
|
|
38
|
+
currentPrdPath = queue.queue[queue.currentIndex].prdFile;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const prd = readJSON(currentPrdPath);
|
|
34
42
|
const task = prd.tasks.find(t => t.id === {current_task_id});
|
|
35
43
|
for (const depId of task.dependencies) {
|
|
36
44
|
const dep = prd.tasks.find(t => t.id === depId);
|
|
37
45
|
if (!dep || dep.status !== 'completed') {
|
|
38
46
|
task.status = 'blocked'; task.error = `Dependency ${depId} not completed`;
|
|
39
|
-
writeJSON(
|
|
47
|
+
writeJSON(currentPrdPath, prd);
|
|
40
48
|
STOP — return to step-01
|
|
41
49
|
}
|
|
42
50
|
}
|
|
@@ -47,7 +55,7 @@ for (const depId of task.dependencies) {
|
|
|
47
55
|
```javascript
|
|
48
56
|
task.status = 'in_progress';
|
|
49
57
|
task.started_at = new Date().toISOString();
|
|
50
|
-
writeJSON(
|
|
58
|
+
writeJSON(currentPrdPath, prd);
|
|
51
59
|
```
|
|
52
60
|
|
|
53
61
|
### 3. Delegate to /apex
|
|
@@ -106,7 +114,7 @@ Read `references/section-splitting.md` sections 7-9 for execution, merging, and
|
|
|
106
114
|
|
|
107
115
|
nextPhase.status = 'completed';
|
|
108
116
|
prd._sectionSplit.currentPhase = nextPhase.phase;
|
|
109
|
-
writeJSON(
|
|
117
|
+
writeJSON(currentPrdPath, prd);
|
|
110
118
|
|
|
111
119
|
// Continue to step-03 (commit), then step-04 loops back for next phase
|
|
112
120
|
}
|
|
@@ -116,7 +124,7 @@ Read `references/section-splitting.md` sections 7-9 for execution, merging, and
|
|
|
116
124
|
|
|
117
125
|
#### 3c. Standard Mode (no split, no parallel)
|
|
118
126
|
|
|
119
|
-
**INVOKE `/apex -d
|
|
127
|
+
**INVOKE `/apex -d {currentPrdPath}`**
|
|
120
128
|
|
|
121
129
|
This delegates ALL remaining tasks for the current module to apex:
|
|
122
130
|
|
|
@@ -134,7 +142,7 @@ This delegates ALL remaining tasks for the current module to apex:
|
|
|
134
142
|
|
|
135
143
|
```javascript
|
|
136
144
|
// Re-read PRD after apex execution
|
|
137
|
-
const updatedPrd = readJSON(
|
|
145
|
+
const updatedPrd = readJSON(currentPrdPath);
|
|
138
146
|
const moduleTasks = updatedPrd.tasks.filter(t => t.module === {current_module});
|
|
139
147
|
|
|
140
148
|
const completed = moduleTasks.filter(t => t.status === 'completed').length;
|
|
@@ -18,8 +18,19 @@ Commit PRD state changes after apex delegation. Apex already committed code —
|
|
|
18
18
|
|
|
19
19
|
### 1. Finalize Task in prd.json
|
|
20
20
|
|
|
21
|
+
> **BLOCKING:** If prd.tasks has any task with status='pending' that should be 'completed'
|
|
22
|
+
> based on actual code commits, FIX prd.json BEFORE committing.
|
|
23
|
+
|
|
21
24
|
```javascript
|
|
22
|
-
|
|
25
|
+
// PRD PATH RESOLUTION
|
|
26
|
+
const queuePath = '.ralph/modules-queue.json';
|
|
27
|
+
let currentPrdPath = '.ralph/prd.json';
|
|
28
|
+
if (fileExists(queuePath)) {
|
|
29
|
+
const queue = readJSON(queuePath);
|
|
30
|
+
currentPrdPath = queue.queue[queue.currentIndex].prdFile;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const prd = readJSON(currentPrdPath);
|
|
23
34
|
const now = new Date().toISOString();
|
|
24
35
|
|
|
25
36
|
// Apex updates individual task statuses. Ralph updates overall PRD state.
|
|
@@ -29,7 +40,7 @@ prd.status = allDone ? 'completed' : prd.tasks.some(t => t.status === 'failed' |
|
|
|
29
40
|
prd.config.current_iteration++;
|
|
30
41
|
prd.updated_at = now;
|
|
31
42
|
|
|
32
|
-
writeJSON(
|
|
43
|
+
writeJSON(currentPrdPath, prd);
|
|
33
44
|
```
|
|
34
45
|
|
|
35
46
|
### 2. Update progress.txt
|
|
@@ -44,7 +55,7 @@ Module: {current_module}
|
|
|
44
55
|
### 3. Stage and Commit (PRD state only)
|
|
45
56
|
|
|
46
57
|
```bash
|
|
47
|
-
git add
|
|
58
|
+
git add {currentPrdPath} .ralph/progress.txt
|
|
48
59
|
[ -f .ralph/modules-queue.json ] && git add .ralph/modules-queue.json
|
|
49
60
|
|
|
50
61
|
git commit -m "$(cat <<'EOF'
|
|
@@ -69,7 +80,7 @@ prd.history.push({
|
|
|
69
80
|
commit_hash: COMMIT_HASH,
|
|
70
81
|
notes: "Delegated to /apex"
|
|
71
82
|
});
|
|
72
|
-
writeJSON(
|
|
83
|
+
writeJSON(currentPrdPath, prd);
|
|
73
84
|
```
|
|
74
85
|
|
|
75
86
|
### 5. Verify
|
|
@@ -15,7 +15,15 @@ Check if all tasks are complete and decide: output completion promise, advance m
|
|
|
15
15
|
## 1. Read Current State
|
|
16
16
|
|
|
17
17
|
```javascript
|
|
18
|
-
|
|
18
|
+
// PRD PATH RESOLUTION
|
|
19
|
+
const queuePath = '.ralph/modules-queue.json';
|
|
20
|
+
let currentPrdPath = '.ralph/prd.json';
|
|
21
|
+
if (fileExists(queuePath)) {
|
|
22
|
+
const queue = readJSON(queuePath);
|
|
23
|
+
currentPrdPath = queue.queue[queue.currentIndex].prdFile;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const prd = readJSON(currentPrdPath);
|
|
19
27
|
const tasksCompleted = prd.tasks.filter(t => t.status === 'completed').length;
|
|
20
28
|
const tasksSkipped = prd.tasks.filter(t => t.status === 'skipped').length;
|
|
21
29
|
const tasksFailed = prd.tasks.filter(t => t.status === 'failed').length;
|
|
@@ -41,7 +49,7 @@ if [ $BUILD_RC -ne 0 ]; then
|
|
|
41
49
|
prd.tasks.push({ id: maxId+1, description: "Fix build regression",
|
|
42
50
|
status: "pending", category: "validation", dependencies: [],
|
|
43
51
|
acceptance_criteria: "dotnet build passes" });
|
|
44
|
-
writeJSON(
|
|
52
|
+
writeJSON(currentPrdPath, prd);
|
|
45
53
|
fi
|
|
46
54
|
```
|
|
47
55
|
|
|
@@ -57,7 +65,7 @@ const { missing, guardrailsNeeded } = checkCategoryCompleteness(prd);
|
|
|
57
65
|
|
|
58
66
|
if (guardrailsNeeded.length > 0) {
|
|
59
67
|
prd.tasks.push(...guardrailsNeeded);
|
|
60
|
-
writeJSON(
|
|
68
|
+
writeJSON(currentPrdPath, prd);
|
|
61
69
|
console.log(`PRD updated: +${guardrailsNeeded.length} guardrails, ${prd.tasks.filter(t => t.status === 'pending').length} pending tasks`);
|
|
62
70
|
}
|
|
63
71
|
// Artifact verification is handled inside checkCategoryCompleteness() — see reference file.
|
|
@@ -117,6 +125,8 @@ if (fileExists(queuePath)) {
|
|
|
117
125
|
} else {
|
|
118
126
|
// All categories complete — advance to next module
|
|
119
127
|
// (detailed logic in references/module-transition.md)
|
|
128
|
+
// MANDATORY: If this was the LAST module, ALWAYS proceed to step-05-report.md.
|
|
129
|
+
// NEVER stop without generating the final report.
|
|
120
130
|
}
|
|
121
131
|
}
|
|
122
132
|
```
|
|
@@ -128,7 +138,11 @@ ALL TASKS COMPLETE | Iterations: {n} | Tasks: {completed}/{total}
|
|
|
128
138
|
<promise>{completion_promise}</promise>
|
|
129
139
|
```
|
|
130
140
|
|
|
131
|
-
Set `prd.status = 'completed'
|
|
141
|
+
Set `prd.status = 'completed'`.
|
|
142
|
+
|
|
143
|
+
**MANDATORY: After displaying completion, ALWAYS load step-05-report.md and generate the report.**
|
|
144
|
+
**NEVER stop after displaying "ALL TASKS COMPLETE" without generating the report.**
|
|
145
|
+
→ step-05.
|
|
132
146
|
|
|
133
147
|
## 4. Dead-End Check
|
|
134
148
|
|
|
@@ -149,7 +163,7 @@ if (!hasPending && !allDone && retriableCount > 0) {
|
|
|
149
163
|
// TRUE dead-end: all retries exhausted
|
|
150
164
|
console.log(`DEAD-END: ${tasksCompletedNow} done, ${tasksFailed} failed (retries exhausted), ${tasksBlocked} blocked`);
|
|
151
165
|
prd.status = 'failed';
|
|
152
|
-
writeJSON(
|
|
166
|
+
writeJSON(currentPrdPath, prd);
|
|
153
167
|
// → step-05
|
|
154
168
|
}
|
|
155
169
|
```
|
|
@@ -15,7 +15,17 @@ Generate a comprehensive feature report using structured data from prd.json.
|
|
|
15
15
|
## 1. Load Data
|
|
16
16
|
|
|
17
17
|
```javascript
|
|
18
|
-
|
|
18
|
+
// PRD PATH RESOLUTION
|
|
19
|
+
const queuePath = '.ralph/modules-queue.json';
|
|
20
|
+
let currentPrdPath = '.ralph/prd.json';
|
|
21
|
+
if (fileExists(queuePath)) {
|
|
22
|
+
const queue = readJSON(queuePath);
|
|
23
|
+
// For report, use last module's PRD (or current if still in progress)
|
|
24
|
+
const idx = Math.min(queue.currentIndex, queue.modules.length - 1);
|
|
25
|
+
currentPrdPath = queue.modules[idx].prdFile;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const prd = readJSON(currentPrdPath);
|
|
19
29
|
const stats = {
|
|
20
30
|
feature: prd.feature, status: prd.status,
|
|
21
31
|
iterations_used: prd.config.current_iteration - 1,
|
|
@@ -53,6 +63,28 @@ if (fileExists(queuePath)) {
|
|
|
53
63
|
}
|
|
54
64
|
```
|
|
55
65
|
|
|
66
|
+
### 1d. Artifact Verification (Post-Execution Validation)
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// Verify key artifacts exist on disk
|
|
70
|
+
const allFiles = prd.implementation?.filesToCreate || {};
|
|
71
|
+
const missingFiles = [];
|
|
72
|
+
for (const cat of Object.keys(allFiles)) {
|
|
73
|
+
for (const file of allFiles[cat] || []) {
|
|
74
|
+
if (!fileExists(file.path)) missingFiles.push(file.path);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (missingFiles.length > 0) {
|
|
78
|
+
console.warn(`WARNING: ${missingFiles.length} files declared in PRD but not found on disk`);
|
|
79
|
+
for (const f of missingFiles.slice(0, 10)) {
|
|
80
|
+
console.warn(` MISSING: ${f}`);
|
|
81
|
+
}
|
|
82
|
+
if (missingFiles.length > 10) {
|
|
83
|
+
console.warn(` ... and ${missingFiles.length - 10} more`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
56
88
|
### 1c. Test Metrics (from PRD)
|
|
57
89
|
|
|
58
90
|
> **Note:** Apex already ran tests and recorded results. Source metrics from PRD task data.
|
|
@@ -136,7 +168,7 @@ Write to `.ralph/reports/{feature-slug}.md`:
|
|
|
136
168
|
// can resume with `-r` — deleting them here would make recovery impossible.
|
|
137
169
|
if (prd._sectionSplit?.enabled && prd.status === 'completed') {
|
|
138
170
|
for (const phase of prd._sectionSplit.phases) {
|
|
139
|
-
if (fileExists(phase.prdFile) && phase.prdFile !==
|
|
171
|
+
if (fileExists(phase.prdFile) && phase.prdFile !== currentPrdPath) {
|
|
140
172
|
deleteFile(phase.prdFile);
|
|
141
173
|
}
|
|
142
174
|
}
|
|
@@ -146,7 +178,7 @@ if (prd._sectionSplit?.enabled && prd.status === 'completed') {
|
|
|
146
178
|
}
|
|
147
179
|
|
|
148
180
|
prd.updated_at = new Date().toISOString();
|
|
149
|
-
writeJSON(
|
|
181
|
+
writeJSON(currentPrdPath, prd);
|
|
150
182
|
```
|
|
151
183
|
|
|
152
184
|
## 4. Display Summary
|