@atlashub/smartstack-cli 3.8.0 → 3.9.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.
Files changed (120) hide show
  1. package/dist/index.js +365 -2
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -1
  4. package/templates/agents/action.md +1 -0
  5. package/templates/agents/ba-writer.md +33 -0
  6. package/templates/agents/explore-codebase.md +1 -0
  7. package/templates/agents/explore-docs.md +1 -0
  8. package/templates/agents/fix-grammar.md +1 -0
  9. package/templates/agents/snipper.md +1 -0
  10. package/templates/skills/admin/SKILL.md +6 -0
  11. package/templates/skills/ai-prompt/SKILL.md +32 -136
  12. package/templates/skills/ai-prompt/steps/step-01-implementation.md +122 -0
  13. package/templates/skills/apex/SKILL.md +120 -0
  14. package/templates/skills/apex/_shared.md +86 -0
  15. package/templates/skills/apex/references/agent-teams-protocol.md +164 -0
  16. package/templates/skills/apex/references/smartstack-layers.md +173 -0
  17. package/templates/skills/apex/steps/step-00-init.md +156 -0
  18. package/templates/skills/apex/steps/step-01-analyze.md +169 -0
  19. package/templates/skills/apex/steps/step-02-plan.md +160 -0
  20. package/templates/skills/apex/steps/step-03-execute.md +166 -0
  21. package/templates/skills/apex/steps/step-04-validate.md +138 -0
  22. package/templates/skills/apex/steps/step-05-examine.md +124 -0
  23. package/templates/skills/apex/steps/step-06-resolve.md +105 -0
  24. package/templates/skills/apex/steps/step-07-tests.md +130 -0
  25. package/templates/skills/apex/steps/step-08-run-tests.md +115 -0
  26. package/templates/skills/application/SKILL.md +10 -0
  27. package/templates/skills/application/references/backend-controller-hierarchy.md +58 -0
  28. package/templates/skills/application/references/backend-entity-seeding.md +72 -0
  29. package/templates/skills/application/references/backend-verification.md +88 -0
  30. package/templates/skills/application/references/frontend-verification.md +111 -0
  31. package/templates/skills/application/references/nav-fallback-procedure.md +200 -0
  32. package/templates/skills/application/references/provider-template.md +134 -0
  33. package/templates/skills/application/references/test-frontend.md +73 -0
  34. package/templates/skills/application/references/test-prerequisites.md +72 -0
  35. package/templates/skills/application/steps/step-01-navigation.md +7 -198
  36. package/templates/skills/application/steps/step-03b-provider.md +4 -128
  37. package/templates/skills/application/steps/step-04-backend.md +20 -350
  38. package/templates/skills/application/steps/step-05-frontend.md +12 -101
  39. package/templates/skills/application/steps/step-07-tests.md +12 -132
  40. package/templates/skills/business-analyse/SKILL.md +11 -2
  41. package/templates/skills/business-analyse/html/ba-interactive.html +176 -14
  42. package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +1 -0
  43. package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +16 -4
  44. package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +7 -2
  45. package/templates/skills/business-analyse/html/src/scripts/09-export.js +103 -0
  46. package/templates/skills/business-analyse/html/src/scripts/10-comments.js +12 -6
  47. package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +24 -2
  48. package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +12 -0
  49. package/templates/skills/business-analyse/html/src/template.html +1 -0
  50. package/templates/skills/business-analyse/references/cadrage-structure-cards.md +78 -0
  51. package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +97 -0
  52. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +92 -0
  53. package/templates/skills/business-analyse/references/deploy-data-build.md +121 -0
  54. package/templates/skills/business-analyse/references/deploy-modes.md +49 -0
  55. package/templates/skills/business-analyse/references/handoff-file-templates.md +119 -0
  56. package/templates/skills/business-analyse/references/handoff-mappings.md +81 -0
  57. package/templates/skills/business-analyse/references/html-data-mapping.md +10 -2
  58. package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -0
  59. package/templates/skills/business-analyse/references/review-data-mapping.md +363 -0
  60. package/templates/skills/business-analyse/references/spec-auto-inference.md +57 -0
  61. package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -0
  62. package/templates/skills/business-analyse/references/ui-resource-cards.md +110 -0
  63. package/templates/skills/business-analyse/references/validate-incremental-html.md +55 -0
  64. package/templates/skills/business-analyse/steps/step-00-init.md +35 -68
  65. package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -194
  66. package/templates/skills/business-analyse/steps/step-03a-data.md +6 -49
  67. package/templates/skills/business-analyse/steps/step-03b-ui.md +12 -178
  68. package/templates/skills/business-analyse/steps/step-03d-validate.md +3 -48
  69. package/templates/skills/business-analyse/steps/step-04-consolidation.md +9 -104
  70. package/templates/skills/business-analyse/steps/step-05a-handoff.md +25 -441
  71. package/templates/skills/business-analyse/steps/step-05b-deploy.md +19 -187
  72. package/templates/skills/business-analyse/steps/step-06-review.md +277 -0
  73. package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -0
  74. package/templates/skills/cc-agent/steps/step-02-generate.md +5 -78
  75. package/templates/skills/check-version/SKILL.md +7 -0
  76. package/templates/skills/controller/references/controller-code-templates.md +159 -0
  77. package/templates/skills/controller/references/permission-sync-templates.md +152 -0
  78. package/templates/skills/controller/steps/step-03-generate.md +6 -158
  79. package/templates/skills/controller/steps/step-04-perms.md +5 -144
  80. package/templates/skills/debug/SKILL.md +7 -0
  81. package/templates/skills/explore/SKILL.md +6 -0
  82. package/templates/skills/feature-full/SKILL.md +39 -142
  83. package/templates/skills/feature-full/steps/step-01-implementation.md +120 -0
  84. package/templates/skills/gitflow/references/init-config-template.md +135 -0
  85. package/templates/skills/gitflow/references/init-name-normalization.md +103 -0
  86. package/templates/skills/gitflow/references/plan-template.md +69 -0
  87. package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -0
  88. package/templates/skills/gitflow/references/start-local-config.md +110 -0
  89. package/templates/skills/gitflow/steps/step-init.md +18 -289
  90. package/templates/skills/gitflow/steps/step-plan.md +6 -63
  91. package/templates/skills/gitflow/steps/step-start.md +16 -126
  92. package/templates/skills/mcp/SKILL.md +9 -213
  93. package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -0
  94. package/templates/skills/mcp/steps/step-02-tools.md +73 -0
  95. package/templates/skills/notification/SKILL.md +7 -0
  96. package/templates/skills/quick-search/SKILL.md +5 -0
  97. package/templates/skills/ralph-loop/SKILL.md +99 -381
  98. package/templates/skills/ralph-loop/references/category-rules.md +259 -0
  99. package/templates/skills/ralph-loop/references/compact-loop.md +182 -0
  100. package/templates/skills/ralph-loop/references/task-transform-legacy.md +259 -0
  101. package/templates/skills/ralph-loop/references/team-orchestration.md +189 -0
  102. package/templates/skills/ralph-loop/steps/step-00-init.md +111 -383
  103. package/templates/skills/ralph-loop/steps/step-01-task.md +79 -896
  104. package/templates/skills/ralph-loop/steps/step-02-execute.md +68 -680
  105. package/templates/skills/ralph-loop/steps/step-03-commit.md +47 -277
  106. package/templates/skills/ralph-loop/steps/step-04-check.md +124 -607
  107. package/templates/skills/ralph-loop/steps/step-05-report.md +68 -367
  108. package/templates/skills/refactor/SKILL.md +12 -176
  109. package/templates/skills/refactor/steps/step-01-discover.md +60 -0
  110. package/templates/skills/refactor/steps/step-02-execute.md +67 -0
  111. package/templates/skills/review-code/SKILL.md +19 -257
  112. package/templates/skills/review-code/steps/step-01-smartstack.md +96 -0
  113. package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -0
  114. package/templates/skills/review-code/steps/step-03-react.md +44 -0
  115. package/templates/skills/ui-components/SKILL.md +7 -0
  116. package/templates/skills/utils/SKILL.md +6 -0
  117. package/templates/skills/validate/SKILL.md +6 -0
  118. package/templates/skills/validate-feature/SKILL.md +8 -0
  119. package/templates/skills/workflow/SKILL.md +40 -118
  120. package/templates/skills/workflow/steps/step-01-implementation.md +84 -0
@@ -1,974 +1,157 @@
1
1
  ---
2
2
  name: step-01-task
3
- description: Load or create tasks from prd.json (v2 schema)
3
+ description: Load or create tasks from prd.json (v3 unified or v2 legacy)
4
4
  next_step: steps/step-02-execute.md
5
5
  ---
6
6
 
7
7
  # Step 1: Load Task
8
8
 
9
- > **MODULE TRANSITION CHECK (MANDATORY):**
10
- > Before applying the "Only Read Once" rule, check for module transition marker:
9
+ > **MODULE TRANSITION CHECK:** Before "Only Read Once" rule, check for module transition:
11
10
 
12
11
  ```javascript
13
- const moduleChangedPath = '.ralph/module-changed.json';
14
- const moduleTransition = fileExists(moduleChangedPath);
15
-
16
- if (moduleTransition) {
17
- const transitionData = readJSON(moduleChangedPath);
18
- console.log(`🔄 Module transition detected: ${transitionData.fromModule} → ${transitionData.toModule}`);
19
- console.log(` Reloading step-01 to initialize new module...`);
20
-
21
- // Delete flag (consume it)
22
- deleteFile(moduleChangedPath);
23
-
24
- // PROCEED with step-01 execution (skip "Only Read Once" rule below)
25
- // This is the EXCEPTION case for multi-module workflows
12
+ if (fileExists('.ralph/module-changed.json')) {
13
+ const t = readJSON('.ralph/module-changed.json');
14
+ console.log(`Module transition: ${t.fromModule} → ${t.toModule}`);
15
+ deleteFile('.ralph/module-changed.json');
16
+ // PROCEED (exception to "Only Read Once" rule)
26
17
  }
27
18
  ```
28
19
 
29
- > **CONTEXT OPTIMIZATION:** This file is normally read ONCE per module (first iteration).
30
- > After the first full iteration (step-01step-02 step-03 step-04),
31
- > ALL subsequent iterations within the SAME module MUST use the COMPACT LOOP in step-04-check.md section 5.
32
- > **DO NOT re-read this file for iterations > 1 of the same module.**
33
- >
34
- > **EXCEPTION:** When transitioning between modules (multi-module workflows), step-01 MUST be re-read
35
- > to load the next module's PRD. This is signaled by `.ralph/module-changed.json` file (checked above).
36
- >
37
- > If you are re-reading this file on iteration > 1 AND no module transition detected, STOP and go to step-04 section 5 instead.
20
+ > **CONTEXT OPTIMIZATION:** Read ONCE per module (first iteration).
21
+ > Iterations > 1 within same moduleuse COMPACT LOOP in step-04.
22
+ > Re-read ONLY on module transition (module-changed.json detected above).
38
23
 
39
24
  ## YOUR TASK:
40
25
 
41
- Load the current task from prd.json or create initial task breakdown with categories, dependencies, and acceptance criteria.
42
-
43
- **ULTRA THINK about task decomposition.**
26
+ Load task from prd.json or create initial task breakdown with categories, dependencies, and acceptance criteria.
44
27
 
45
28
  ---
46
29
 
47
- ## EXECUTION SEQUENCE:
48
-
49
- ### 0. Multi-Module Queue Check
50
-
51
- **✅ ALWAYS check for modules-queue.json, even on iteration > 1:**
52
-
53
- > **CRITICAL:** This section MUST execute even when step-01 is re-read for module transitions.
54
- > The module-changed.json flag (checked above) ensures this section runs when needed.
30
+ ## 0. Multi-Module Queue Check
55
31
 
56
32
  ```javascript
57
33
  const queuePath = '.ralph/modules-queue.json';
58
- const hasQueue = fileExists(queuePath);
59
-
60
- if (hasQueue) {
34
+ if (fileExists(queuePath)) {
61
35
  const queue = readJSON(queuePath);
62
36
  const currentModule = queue.modules[queue.currentIndex];
37
+ console.log(`Multi-module: ${currentModule.code} (${queue.currentIndex + 1}/${queue.totalModules})`);
63
38
 
64
- console.log(`
65
- ╔══════════════════════════════════════════════════════════════════╗
66
- ║ Multi-Module Workflow Detected ║
67
- ╠══════════════════════════════════════════════════════════════════╣
68
- ║ Current Module: ${currentModule.code} (${queue.currentIndex + 1}/${queue.totalModules}) ║
69
- ║ Status: ${currentModule.status} ║
70
- ║ PRD File: ${currentModule.prdFile} ║
71
- ╚══════════════════════════════════════════════════════════════════╝
72
- `);
73
-
74
- // ✅ FIX #3: RESTORE multi-module state variables
75
- // These variables persist across step files and are critical for multi-module coordination
76
39
  {modules_queue} = queue;
77
40
  {current_module} = currentModule.code;
78
41
 
79
- // Load or verify current module's PRD is in .ralph/prd.json
80
- // On module transition, step-04 already loaded it, but we verify here
42
+ // Verify PRD matches current module
81
43
  const currentPrd = readJSON('.ralph/prd.json');
82
-
83
- // Verify PRD matches current module (sanity check)
84
- if (currentPrd.metadata && currentPrd.metadata.moduleCode !== currentModule.code) {
85
- console.log(`⚠️ PRD mismatch detected. Loading correct PRD for module ${currentModule.code}...`);
86
-
87
- // Load current module's PRD
44
+ if (currentPrd.metadata?.moduleCode !== currentModule.code) {
88
45
  const modulePrd = readJSON(currentModule.prdFile);
89
46
 
90
- // Transform PrdJson format to Ralph v2 if needed
91
- if (modulePrd.project && modulePrd.requirements && !modulePrd.$version) {
92
- // This is a PrdJson format from ss derive-prd → transform to Ralph v2
93
- const transformedPrd = transformPrdJsonToRalphV2(modulePrd, currentModule.code);
94
- writeJSON('.ralph/prd.json', transformedPrd);
95
- console.log(`✅ Transformed and loaded PRD for module: ${currentModule.code}`);
96
- } else {
97
- // Already Ralph v2 format
47
+ // v3 FAST PATH: unified PRD from ss derive-prd (tasks pre-computed)
48
+ if (modulePrd.$version === '3.0.0') {
49
+ // Inject runtime fields and use directly
50
+ modulePrd.status = 'in_progress';
51
+ modulePrd.feature = `${modulePrd.project?.module || currentModule.code} (${modulePrd.project?.application || 'app'})`;
52
+ modulePrd.created = modulePrd.created || new Date().toISOString();
53
+ modulePrd.updated_at = new Date().toISOString();
54
+ modulePrd.config = modulePrd.config || { max_iterations: {max_iterations}, completion_promise: "{completion_promise}", current_iteration: 1 };
55
+ modulePrd.metadata.branch = modulePrd.metadata.branch || getCurrentBranch();
56
+ modulePrd.metadata.project_path = modulePrd.metadata.project_path || process.cwd();
57
+ modulePrd.metadata.mcp_servers = modulePrd.metadata.mcp_servers || { smartstack: true, context7: true };
58
+ modulePrd.history = modulePrd.history || [];
59
+ writeJSON('.ralph/prd.json', modulePrd);
60
+ }
61
+ // LEGACY: FORMAT A (no $version) — transform + warning
62
+ else if (modulePrd.project && modulePrd.requirements && !modulePrd.$version) {
63
+ console.warn('⚠ DEPRECATED: FORMAT A prd.json detected. Re-run `ss derive-prd` to generate v3 format.');
64
+ writeJSON('.ralph/prd.json', transformPrdJsonToRalphV2(modulePrd, currentModule.code));
65
+ }
66
+ // v2 legacy or already runtime format
67
+ else {
98
68
  writeJSON('.ralph/prd.json', modulePrd);
99
- console.log(`✅ Loaded PRD for module: ${currentModule.code}`);
100
69
  }
101
- } else {
102
- console.log(`✅ PRD already loaded for module: ${currentModule.code}`);
103
70
  }
104
-
105
- console.log(`State restored: modules_queue and current_module set`);
106
71
  } else {
107
- // Single-module workflow
108
72
  {modules_queue} = null;
109
73
  {current_module} = null;
110
74
  }
111
75
  ```
112
76
 
113
- **PrdJson → Ralph v2 transformation:**
114
-
115
- The PrdJson format (from `ss derive-prd`) has this structure:
116
- ```
117
- { version, source, project, requirements: { useCases[], functionalRequirements[] },
118
- businessRules[], architecture: { entities[], apiEndpoints[], sections[] },
119
- implementation: { filesToCreate: { domain[], application[], infrastructure[], api[], frontend[], tests[] } },
120
- seedData }
121
- ```
122
-
123
- ```javascript
124
- function transformPrdJsonToRalphV2(prdJson, moduleCode) {
125
- const tasks = [];
126
- let taskId = 1;
127
-
128
- // Layer processing order (SmartStack convention)
129
- const layerOrder = [
130
- { key: "domain", category: "domain" },
131
- { key: "infrastructure", category: "infrastructure" },
132
- { key: "application", category: "application" },
133
- { key: "api", category: "api" },
134
- { key: "frontend", category: "frontend" },
135
- { key: "seedData", category: "infrastructure" },
136
- { key: "tests", category: "test" }
137
- ];
138
-
139
- // Support BOTH formats: nested (from ss derive-prd) and flat (LLM-generated)
140
- const filesToCreate = prdJson.implementation?.filesToCreate
141
- || prdJson.filesToCreate
142
- || {};
143
-
144
- // 1. Generate tasks from implementation.filesToCreate (primary source)
145
- // CRITICAL: Frontend and i18n tasks are CONSOLIDATED into ONE module-level task each.
146
- // These use MCP scaffolding tools, NOT manual file-by-file generation.
147
- const lastIdByCategory = {};
148
- const frontendFiles = []; // Collect frontend files for consolidated task
149
- const i18nFiles = []; // Collect i18n files for consolidated task
150
-
151
- for (const layer of layerOrder) {
152
- const files = filesToCreate[layer.key] || [];
153
-
154
- // FRONTEND & I18N: Collect files, do NOT create individual tasks
155
- if (layer.category === "frontend") {
156
- frontendFiles.push(...files);
157
- continue; // Skip individual task creation
158
- }
159
-
160
- for (const fileSpec of files) {
161
- // Check if this is an i18n file (category "infrastructure" but path contains i18n)
162
- if (fileSpec.path?.includes('i18n/') || fileSpec.path?.includes('/locales/')) {
163
- i18nFiles.push(fileSpec);
164
- continue; // Skip individual task creation
165
- }
166
-
167
- const depIds = [];
168
- // Depend on last task in same category (sequential within layer)
169
- if (lastIdByCategory[layer.category]) {
170
- depIds.push(lastIdByCategory[layer.category]);
171
- }
172
- // Cross-layer: api depends on last application task
173
- if (layer.category === "api" && lastIdByCategory["application"]) {
174
- depIds.push(lastIdByCategory["application"]);
175
- }
176
- if (layer.category === "test" && lastIdByCategory["api"]) {
177
- depIds.push(lastIdByCategory["api"]);
178
- }
179
-
180
- // Build acceptance criteria from linked FRs and UCs
181
- // Support both nested (requirements.functionalRequirements) and flat (functionalRequirements) formats
182
- const allFRs = prdJson.requirements?.functionalRequirements || prdJson.functionalRequirements || [];
183
- const linkedFRs = (fileSpec.linkedFRs || [])
184
- .map(frId => {
185
- const fr = allFRs.find(f => f.id === frId);
186
- return fr ? fr.statement : frId;
187
- });
188
- let criteria = linkedFRs.length > 0
189
- ? `Implements: ${linkedFRs.join("; ")}`
190
- : `File ${fileSpec.path} created and compiles correctly`;
191
-
192
- // Enhance acceptance criteria for API tasks with permission enforcement
193
- const permMatrix = prdJson.architecture?.permissionMatrix || prdJson.permissionMatrix;
194
- if (layer.category === "api" && permMatrix?.permissions?.length > 0) {
195
- criteria += "; MANDATORY: [RequirePermission] attributes on every endpoint (NOT [Authorize])";
196
- }
197
-
198
- tasks.push({
199
- id: taskId,
200
- description: fileSpec.description
201
- ? `[${layer.category}] ${fileSpec.description} → ${fileSpec.path}`
202
- : `[${layer.category}] Create ${fileSpec.type}: ${fileSpec.path}`,
203
- status: "pending",
204
- category: layer.category,
205
- dependencies: [...new Set(depIds)],
206
- acceptance_criteria: criteria,
207
- started_at: null,
208
- completed_at: null,
209
- iteration: null,
210
- commit_hash: null,
211
- files_changed: { created: [fileSpec.path], modified: [] },
212
- validation: null,
213
- error: null,
214
- module: moduleCode
215
- });
216
-
217
- lastIdByCategory[layer.category] = taskId;
218
- taskId++;
219
- }
220
- }
221
-
222
- // 1b. Create CONSOLIDATED frontend task (ONE task per module, uses MCP tools)
223
- if (frontendFiles.length > 0) {
224
- const apiDepId = lastIdByCategory["api"] || lastIdByCategory["application"];
225
- const allLinkedUCs = [...new Set(frontendFiles.flatMap(f => f.linkedUCs || []))];
226
- const allLinkedWireframes = [...new Set(frontendFiles.flatMap(f => f.linkedWireframes || []))];
227
- const allPaths = frontendFiles.map(f => f.path);
228
-
229
- tasks.push({
230
- id: taskId,
231
- description: `[frontend] Generate COMPLETE frontend for module ${moduleCode} via MCP scaffold tools (${frontendFiles.length} files: pages, components, hooks, API client)`,
232
- status: "pending",
233
- category: "frontend",
234
- dependencies: apiDepId ? [apiDepId] : [],
235
- acceptance_criteria: [
236
- "MCP scaffold_api_client called → API client + types generated",
237
- "MCP scaffold_routes called → routes.tsx updated with nested routes inside Layout wrapper",
238
- "Pages match wireframes: " + allLinkedWireframes.join(", "),
239
- "SmartTable for lists (NOT HTML tables), EntityCard for grids (NOT custom divs)",
240
- "CSS variables ONLY (NO hardcoded Tailwind colors like bg-blue-600)",
241
- "useNavigate + useParams for routing, NOT window.location",
242
- "Loading/error/empty states on all pages",
243
- "4-language i18n keys (fr, en, it, de)",
244
- "npm run typecheck passes"
245
- ].join("; "),
246
- started_at: null,
247
- completed_at: null,
248
- iteration: null,
249
- commit_hash: null,
250
- files_changed: { created: allPaths, modified: [] },
251
- validation: null,
252
- error: null,
253
- module: moduleCode,
254
- _frontendMeta: {
255
- wireframes: allLinkedWireframes,
256
- linkedUCs: allLinkedUCs,
257
- filesToCreate: frontendFiles
258
- }
259
- });
260
-
261
- lastIdByCategory["frontend"] = taskId;
262
- taskId++;
263
- }
264
-
265
- // 1c. Create CONSOLIDATED i18n task (ONE task per module)
266
- if (i18nFiles.length > 0) {
267
- tasks.push({
268
- id: taskId,
269
- description: `[i18n] Generate i18n translations for module ${moduleCode} (4 languages: fr, en, it, de)`,
270
- status: "pending",
271
- category: "i18n",
272
- dependencies: lastIdByCategory["frontend"] ? [lastIdByCategory["frontend"]] : [],
273
- acceptance_criteria: "4 JSON files (fr, en, it, de) with identical keys; all UI labels translated",
274
- started_at: null,
275
- completed_at: null,
276
- iteration: null,
277
- commit_hash: null,
278
- files_changed: { created: i18nFiles.map(f => f.path), modified: [] },
279
- validation: null,
280
- error: null,
281
- module: moduleCode
282
- });
283
- lastIdByCategory["i18n"] = taskId;
284
- taskId++;
285
- }
286
-
287
- // 1d. GUARDRAIL: Inject missing layer tasks when filesToCreate is incomplete
288
- // This handles LLM-generated PRDs that omit layers present in the source data.
289
- // Uses architecture data (entities, apiEndpoints, sections, wireframes) as fallback.
290
-
291
- const entities = prdJson.architecture?.entities || prdJson.entities || [];
292
- const apiEndpoints = prdJson.architecture?.apiEndpoints || prdJson.apiEndpoints || [];
293
- const sections = prdJson.architecture?.sections || prdJson.sections || [];
294
- const wireframes = prdJson.specification?.uiWireframes || prdJson.uiWireframes || [];
295
- const dashboards = prdJson.specification?.dashboards || prdJson.dashboards || [];
296
-
297
- const hasDomainTasks = tasks.some(t => t.category === 'domain');
298
- const hasInfraTasks = tasks.some(t => t.category === 'infrastructure');
299
- const hasApiTasks = tasks.some(t => t.category === 'api');
300
- const hasFrontendTasks = frontendFiles.length > 0; // Already handled in 1b
301
- const hasTestTasks = tasks.some(t => t.category === 'test');
302
-
303
- // INJECT consolidated infrastructure task if missing
304
- if (!hasInfraTasks && entities.length > 0) {
305
- const domainDepId = lastIdByCategory["domain"];
306
- tasks.push({
307
- id: taskId,
308
- description: `[infrastructure] Create EF Core configurations, services, and seed data for module ${moduleCode} (${entities.length} entities)`,
309
- status: "pending",
310
- category: "infrastructure",
311
- dependencies: domainDepId ? [domainDepId] : [],
312
- acceptance_criteria: `EF Core configs for all ${entities.length} entities; Service implementations; DbSet in ExtensionsDbContext; DI registration; Seed data`,
313
- started_at: null, completed_at: null, iteration: null, commit_hash: null,
314
- files_changed: { created: [], modified: [] },
315
- validation: null, error: null, module: moduleCode
316
- });
317
- lastIdByCategory["infrastructure"] = taskId;
318
- taskId++;
319
- console.log(`⚠️ GUARDRAIL: Injected missing [infrastructure] task from ${entities.length} entities`);
320
- }
321
-
322
- // INJECT consolidated API task if missing
323
- if (!hasApiTasks && apiEndpoints.length > 0) {
324
- const appDepId = lastIdByCategory["application"] || lastIdByCategory["infrastructure"];
325
- const permMatrix = prdJson.architecture?.permissionMatrix || prdJson.permissionMatrix;
326
- const hasPermissions = permMatrix?.permissions?.length > 0;
327
-
328
- tasks.push({
329
- id: taskId,
330
- description: `[api] Create API controllers for module ${moduleCode} (${apiEndpoints.length} endpoints)`,
331
- status: "pending",
332
- category: "api",
333
- dependencies: appDepId ? [appDepId] : [],
334
- acceptance_criteria: [
335
- `Controllers for all ${apiEndpoints.length} endpoints`,
336
- hasPermissions
337
- ? "MANDATORY: [RequirePermission(Permissions.{Action})] on EVERY endpoint (NOT generic [Authorize])"
338
- : "Authorization attributes",
339
- "Swagger docs"
340
- ].join("; "),
341
- started_at: null, completed_at: null, iteration: null, commit_hash: null,
342
- files_changed: { created: [], modified: [] },
343
- validation: null, error: null, module: moduleCode
344
- });
345
- lastIdByCategory["api"] = taskId;
346
- taskId++;
347
- console.log(`⚠️ GUARDRAIL: Injected missing [api] task from ${apiEndpoints.length} endpoints`);
348
- }
77
+ ## PrdJson → Ralph v2 Transformation (DEPRECATED)
349
78
 
350
- // CRITICAL: INJECT consolidated frontend task if missing
351
- // This is the MOST COMMON failure mode LLM-generated PRDs often omit frontend
352
- if (!hasFrontendTasks && (sections.length > 0 || wireframes.length > 0 || apiEndpoints.length > 0)) {
353
- const apiDepId = lastIdByCategory["api"] || lastIdByCategory["application"];
354
-
355
- // Derive frontend file list from available data sources
356
- const derivedFrontendFiles = [];
357
-
358
- // From wireframes → pages
359
- for (const wf of wireframes) {
360
- const screenId = wf.screen || wf.id;
361
- derivedFrontendFiles.push({
362
- path: `web/src/pages/${moduleCode}/${screenId}.tsx`,
363
- type: screenId.includes('dashboard') ? 'DashboardPage' : 'Page',
364
- linkedWireframes: [screenId],
365
- linkedUCs: wf.linkedUCs || [],
366
- });
367
- }
79
+ > **DEPRECATED:** This transformation is a fallback for old FORMAT A PRDs (pre-v3.9.0).
80
+ > New PRDs generated by `ss derive-prd` use unified v3 format with pre-computed tasks.
81
+ > This function will be removed in a future release.
368
82
 
369
- // From dashboards dashboard pages (if not already from wireframes)
370
- for (const dash of dashboards) {
371
- const dashId = dash.code || dash.id;
372
- if (!derivedFrontendFiles.some(f => f.path.includes(dashId))) {
373
- derivedFrontendFiles.push({
374
- path: `web/src/pages/${moduleCode}/${dashId}.tsx`,
375
- type: 'DashboardPage',
376
- linkedWireframes: [dashId],
377
- dashboardRef: dashId,
378
- });
379
- }
380
- }
381
-
382
- // From API endpoints → API client service
383
- if (apiEndpoints.length > 0) {
384
- derivedFrontendFiles.push({
385
- path: `web/src/services/api/${moduleCode}Api.ts`,
386
- type: 'ApiService',
387
- });
388
- }
389
-
390
- const allWireframeIds = derivedFrontendFiles.flatMap(f => f.linkedWireframes || []);
391
-
392
- tasks.push({
393
- id: taskId,
394
- description: `[frontend] Generate COMPLETE frontend for module ${moduleCode} via MCP scaffold tools (${derivedFrontendFiles.length} files: pages, components, hooks, API client)`,
395
- status: "pending",
396
- category: "frontend",
397
- dependencies: apiDepId ? [apiDepId] : [],
398
- acceptance_criteria: [
399
- "MCP scaffold_api_client called → API client + types generated",
400
- "MCP scaffold_routes called → routes.tsx updated with nested routes inside Layout wrapper",
401
- allWireframeIds.length > 0 ? "Pages match wireframes: " + allWireframeIds.join(", ") : "Pages created for all UI sections",
402
- "SmartTable for lists (NOT HTML tables), EntityCard for grids (NOT custom divs)",
403
- "CSS variables ONLY (NO hardcoded Tailwind colors like bg-blue-600)",
404
- "useNavigate + useParams for routing, NOT window.location",
405
- "Loading/error/empty states on all pages",
406
- "4-language i18n keys (fr, en, it, de)",
407
- "npm run typecheck passes"
408
- ].join("; "),
409
- started_at: null, completed_at: null, iteration: null, commit_hash: null,
410
- files_changed: { created: derivedFrontendFiles.map(f => f.path), modified: [] },
411
- validation: null, error: null, module: moduleCode,
412
- _frontendMeta: {
413
- wireframes: allWireframeIds,
414
- filesToCreate: derivedFrontendFiles,
415
- source: "guardrail-derived"
416
- }
417
- });
418
- lastIdByCategory["frontend"] = taskId;
419
- taskId++;
420
- console.log(`⚠️ GUARDRAIL: Injected missing [frontend] task derived from ${wireframes.length} wireframes + ${dashboards.length} dashboards + ${apiEndpoints.length} endpoints`);
421
-
422
- // Also inject i18n task if not already present
423
- if (i18nFiles.length === 0) {
424
- tasks.push({
425
- id: taskId,
426
- description: `[i18n] Generate i18n translations for module ${moduleCode} (4 languages: fr, en, it, de)`,
427
- status: "pending",
428
- category: "i18n",
429
- dependencies: [lastIdByCategory["frontend"]],
430
- acceptance_criteria: "4 JSON files (fr, en, it, de) with identical keys; all UI labels translated",
431
- started_at: null, completed_at: null, iteration: null, commit_hash: null,
432
- files_changed: { created: [], modified: [] },
433
- validation: null, error: null, module: moduleCode
434
- });
435
- lastIdByCategory["i18n"] = taskId;
436
- taskId++;
437
- }
438
- }
439
-
440
- // INJECT consolidated test task if missing
441
- if (!hasTestTasks && entities.length > 0) {
442
- const apiDepId = lastIdByCategory["api"] || lastIdByCategory["application"];
443
- tasks.push({
444
- id: taskId,
445
- description: `[test] Create unit and integration tests for module ${moduleCode}`,
446
- status: "pending",
447
- category: "test",
448
- dependencies: apiDepId ? [apiDepId] : [],
449
- acceptance_criteria: "Domain unit tests + service unit tests + controller integration tests; dotnet test passes; coverage >= 80%",
450
- started_at: null, completed_at: null, iteration: null, commit_hash: null,
451
- files_changed: { created: [], modified: [] },
452
- validation: null, error: null, module: moduleCode
453
- });
454
- lastIdByCategory["test"] = taskId;
455
- taskId++;
456
- console.log(`⚠️ GUARDRAIL: Injected missing [test] task`);
457
- }
458
-
459
- // 1e. GUARDRAIL: Inject seedData tasks when core seed data exists but filesToCreate.seedData is empty
460
- // This is the MOST CRITICAL guardrail — without core seed data, the application has:
461
- // - No navigation menu entries
462
- // - No RBAC permissions in the database
463
- // - No role-permission mappings
464
- // - Controllers accessible to ANY authenticated user
465
- const coreSeedData = prdJson.seedData?.core
466
- || prdJson.seedDataCore
467
- || prdJson.specification?.seedDataCore;
468
- const hasSeedDataTasks = (filesToCreate["seedData"]?.length ?? 0) > 0;
469
- const hasSeedDataInTasks = tasks.some(t =>
470
- t.description?.includes("SeedData") || t.description?.includes("seed data"));
471
-
472
- if (coreSeedData && !hasSeedDataTasks && !hasSeedDataInTasks) {
473
- const infraDepId = lastIdByCategory["infrastructure"] || lastIdByCategory["domain"];
474
-
475
- // Derive navigation/permissions/roles from coreSeedData
476
- const navModules = coreSeedData.navigationModules || coreSeedData.navigation || [];
477
- const permissions = coreSeedData.permissions || [];
478
- const rolePerms = coreSeedData.rolePermissions || [];
479
-
480
- // Inject consolidated core seedData task
481
- tasks.push({
482
- id: taskId,
483
- description: `[infrastructure] Create core seed data files for module ${moduleCode}: NavigationModuleSeedData, PermissionsSeedData, RolesSeedData, SeedConstants`,
484
- status: "pending",
485
- category: "infrastructure",
486
- dependencies: infraDepId ? [infraDepId] : [],
487
- acceptance_criteria: [
488
- `NavigationModuleSeedData.cs with ${navModules.length} navigation module(s)`,
489
- `PermissionsSeedData.cs with ${permissions.length} permission(s) from seedData.core`,
490
- `RolesSeedData.cs with ${rolePerms.length} role-permission mapping(s)`,
491
- "SeedConstants.cs with deterministic GUIDs",
492
- "All seed data classes are static with GetSeedData() method"
493
- ].join("; "),
494
- started_at: null, completed_at: null, iteration: null, commit_hash: null,
495
- files_changed: { created: [
496
- `src/Infrastructure/Persistence/Seeding/Data/${moduleCode}/NavigationModuleSeedData.cs`,
497
- `src/Infrastructure/Persistence/Seeding/Data/${moduleCode}/PermissionsSeedData.cs`,
498
- `src/Infrastructure/Persistence/Seeding/Data/${moduleCode}/RolesSeedData.cs`,
499
- "src/Infrastructure/Persistence/Seeding/Data/SeedConstants.cs"
500
- ], modified: [] },
501
- validation: null, error: null, module: moduleCode,
502
- _seedDataMeta: {
503
- source: "guardrail-derived",
504
- coreSeedData,
505
- navRoute: prdJson.project?.navRoute
506
- || (permissions?.[0]?.path?.split('.').slice(0, -1).join('.'))
507
- || `business.${prdJson.project?.application || 'app'}.${moduleCode}`,
508
- contextCode: prdJson.project?.context
509
- || coreSeedData.navigationModules?.[0]?.route?.split('/')?.[1]
510
- || 'business',
511
- appCode: prdJson.project?.application
512
- || coreSeedData.navigationModules?.[0]?.route?.split('/')?.[2],
513
- appLabels: prdJson.project?.labels || null
514
- }
515
- });
516
- lastIdByCategory["infrastructure"] = taskId;
517
- taskId++;
518
- console.log(`GUARDRAIL: Injected missing [infrastructure] core seed data task from seedData.core (${navModules.length} nav, ${permissions.length} perms, ${rolePerms.length} roles)`);
519
- }
520
-
521
- // 2. Add IClientSeedDataProvider task for client projects (ExtensionsDbContext)
522
- // This is MANDATORY - without it, core seed data (navigation, permissions, roles) is dead code
523
- const hasClientSeedData = filesToCreate["seedData"]?.some(f =>
524
- f.type === "IClientSeedDataProvider" || f.path?.includes("SeedDataProvider"));
525
- const hasProviderInTasks = tasks.some(t =>
526
- t.description?.includes("IClientSeedDataProvider") || t.description?.includes("SeedDataProvider"));
527
-
528
- // Trigger when: seedData files exist OR coreSeedData exists (fallback for incomplete PRDs)
529
- if (!hasClientSeedData && !hasProviderInTasks &&
530
- (filesToCreate["seedData"]?.length > 0 || coreSeedData)) {
531
- tasks.push({
532
- id: taskId,
533
- description: `[infrastructure] Create IClientSeedDataProvider to inject core seed data (navigation, permissions, roles) at runtime`,
534
- status: "pending",
535
- category: "infrastructure",
536
- dependencies: [lastIdByCategory["infrastructure"] || lastIdByCategory["domain"]].filter(Boolean),
537
- acceptance_criteria: [
538
- "Implements IClientSeedDataProvider with SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync",
539
- "Registered in DI: services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>()",
540
- "All methods are idempotent (check existence before inserting)",
541
- "Consumes seed data from NavigationModuleSeedData, PermissionsSeedData, RolesSeedData"
542
- ].join("; "),
543
- started_at: null, completed_at: null, iteration: null, commit_hash: null,
544
- files_changed: { created: ["src/Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs"], modified: ["src/Infrastructure/DependencyInjection.cs"] },
545
- validation: null, error: null, module: moduleCode,
546
- _providerMeta: {
547
- consumesSeedDataFiles: [
548
- `Infrastructure/Persistence/Seeding/Data/${moduleCode}/NavigationModuleSeedData.cs`,
549
- `Infrastructure/Persistence/Seeding/Data/${moduleCode}/PermissionsSeedData.cs`,
550
- `Infrastructure/Persistence/Seeding/Data/${moduleCode}/RolesSeedData.cs`
551
- ],
552
- navRoute: prdJson.project?.navRoute
553
- || `business.${prdJson.project?.application || 'app'}.${moduleCode}`,
554
- contextCode: prdJson.project?.context || 'business',
555
- appCode: prdJson.project?.application
556
- }
557
- });
558
- lastIdByCategory["infrastructure"] = taskId;
559
- taskId++;
560
- }
561
-
562
- // 3. Add a final validation task
563
- tasks.push({
564
- id: taskId,
565
- description: `[validation] Build, test, and MCP validate module ${moduleCode}`,
566
- status: "pending",
567
- category: "validation",
568
- dependencies: Object.values(lastIdByCategory),
569
- acceptance_criteria: "dotnet build succeeds, dotnet test passes, MCP validate_conventions clean",
570
- started_at: null,
571
- completed_at: null,
572
- iteration: null,
573
- commit_hash: null,
574
- files_changed: { created: [], modified: [] },
575
- validation: null,
576
- error: null,
577
- module: moduleCode
578
- });
579
-
580
- return {
581
- $version: "2.0.0",
582
- feature: `${prdJson.project?.module || moduleCode} (${prdJson.project?.application || "app"})`,
583
- status: "in_progress",
584
- created: new Date().toISOString(),
585
- updated_at: new Date().toISOString(),
586
- metadata: {
587
- cli_version: "unknown",
588
- branch: getCurrentBranch(),
589
- project_path: process.cwd(),
590
- mcp_servers: { smartstack: true, context7: true },
591
- module: moduleCode
592
- },
593
- config: {
594
- max_iterations: {max_iterations},
595
- completion_promise: "{completion_promise}",
596
- current_iteration: 1
597
- },
598
- source: {
599
- type: "ba-handoff",
600
- handoff_path: null,
601
- feature_json_path: prdJson.source?.featureJson || null,
602
- module: moduleCode
603
- },
604
- tasks: tasks,
605
- history: []
606
- };
607
- }
608
- ```
609
-
610
- **If no queue exists:** proceed with standard logic below (backward compatible).
83
+ See [references/task-transform-legacy.md](../references/task-transform-legacy.md) for the full `transformPrdJsonToRalphV2()` function including:
84
+ - Layer-ordered task generation (domain infra → app → api → frontend → seedData → tests)
85
+ - Consolidated frontend + i18n tasks
86
+ - Guardrails 1d-1j (inject missing infrastructure, API, frontend, tests, seed data, migration)
611
87
 
612
88
  ---
613
89
 
614
- ### 1. Check for Existing prd.json
615
-
616
- **If `.ralph/prd.json` exists:**
617
- - Read and parse the file
618
- - Verify `$version` is `"2.0.0"` (if not, STOP - run step-00 migration first)
619
- - Find the next eligible task (see step 5)
620
- - Skip to step 5
90
+ ## 1. Check for Existing prd.json
621
91
 
622
- **If `.ralph/prd.json` does NOT exist:**
623
- - Continue to step 2 (create tasks)
92
+ If `.ralph/prd.json` exists:
93
+ 1. Read the PRD
94
+ 2. **v3 FAST PATH:** If `$version === "3.0.0"` AND `tasks[]` exists:
95
+ - Inject runtime fields if missing (status, config, feature, created, updated_at, history)
96
+ - Write back to `.ralph/prd.json`
97
+ - Skip directly to section 5 (find next task)
98
+ 3. **v2 legacy:** If `$version === "2.0.0"` → find next eligible task (section 5), skip to section 5
99
+ 4. **FORMAT A (deprecated):** If `.project && .requirements && !.$version` → run `transformPrdJsonToRalphV2()` → section 5
624
100
 
625
- ### 2. Analyze Task Description
101
+ If `.ralph/prd.json` does not exist: continue to section 2.
626
102
 
627
- **ULTRA THINK about the task:**
103
+ ## 2. Analyze Task Description
628
104
 
629
- - What are the logical subtasks?
630
- - What order should they be executed?
631
- - What are the success criteria for each?
632
- - What files will be modified?
633
- - What layer does each task belong to?
634
- - What dependencies exist between tasks?
105
+ Check for BA handoff source (priority):
106
+ 1. Per-module PRD files (handled by section 0)
107
+ 2. Single prd.json already present
108
+ 3. Markdown handoff: `find . -path "*development-handoff*" -name "*.md"`
109
+ 4. Direct task from `{task_description}`
635
110
 
636
- **Check for BA handoff source (priority order):**
111
+ Generate **3-30 subtasks** by category (domain → infrastructure → application → api → frontend → i18n → test → validation).
637
112
 
638
- ```bash
639
- # Priority 1: Per-module PRD files (from ss derive-prd / BA handoff)
640
- # These are already handled by Section 0 if modules-queue.json exists
641
- PRD_MODULE=$(ls .ralph/prd-*.json 2>/dev/null | head -1)
113
+ ## 3. Create prd.json
642
114
 
643
- # Priority 2: Single prd.json already present (from BA or previous run)
644
- PRD_SINGLE=".ralph/prd.json"
645
-
646
- # Priority 3: Markdown handoff document
647
- HANDOFF=$(find . -path "*development-handoff*" -name "*.md" | head -1)
648
- BA_OUTPUT=$(find . -path ".claude/output/ba/*" -name "4-*.md" | head -1)
649
- SOURCE_PATH=${HANDOFF:-$BA_OUTPUT}
650
- ```
651
-
652
- **If prd-*.json files exist but no modules-queue.json:**
653
- - This means step-00 didn't create a queue (single prd file)
654
- - Copy the single `prd-*.json` to `prd.json` and transform if needed
655
-
656
- **If markdown handoff found:**
657
- - Read the handoff document
658
- - Derive tasks from its specifications (per-layer breakdown)
659
- - Set `source.type = "ba-handoff"` and `source.handoff_path`
660
- - Look for feature.json in the same directory
661
-
662
- **If no handoff:**
663
- - Generate task breakdown from `{task_description}`
664
- - Set `source = null`
665
-
666
- **Generate task breakdown:**
667
-
668
- For `{task_description}`, identify **3-30 subtasks** organized by category.
669
-
670
- **Task categories (use SmartStack layer order):**
671
-
672
- | Category | Description | Examples |
673
- |----------|-------------|---------|
674
- | `domain` | Domain entities and value objects | Create entities, enums, interfaces |
675
- | `application` | CQRS handlers, DTOs, validators | Commands, Queries, DTOs, FluentValidation |
676
- | `infrastructure` | EF Core, external services | DbContext configs, migrations, services |
677
- | `api` | Controllers, middleware | REST endpoints, filters, middleware |
678
- | `frontend` | React pages, components, hooks | Pages, API services, React Query hooks |
679
- | `i18n` | Translations | Translation JSON files |
680
- | `test` | Unit and integration tests | xUnit tests, React testing library |
681
- | `validation` | Build, lint, MCP checks | dotnet build, pnpm build, MCP validate |
682
- | `other` | Anything else | Documentation, configuration |
683
-
684
- **Example for "implement user authentication":**
685
- ```json
686
- {
687
- "tasks": [
688
- {
689
- "id": 1,
690
- "description": "Create User entity in SmartStack.Domain/Entities/Business/",
691
- "status": "pending",
692
- "category": "domain",
693
- "dependencies": [],
694
- "acceptance_criteria": "Entity compiles with all required properties per handoff spec"
695
- },
696
- {
697
- "id": 2,
698
- "description": "Add User DbSet to ApplicationDbContext and create EF Core configuration",
699
- "status": "pending",
700
- "category": "infrastructure",
701
- "dependencies": [1],
702
- "acceptance_criteria": "DbContext compiles, configuration maps all properties"
703
- },
704
- {
705
- "id": 3,
706
- "description": "Create EF Core migration for User table",
707
- "status": "pending",
708
- "category": "infrastructure",
709
- "dependencies": [2],
710
- "acceptance_criteria": "Migration applies without errors, 3 files present"
711
- },
712
- {
713
- "id": 4,
714
- "description": "Implement IPasswordService and IJwtService",
715
- "status": "pending",
716
- "category": "application",
717
- "dependencies": [1],
718
- "acceptance_criteria": "Services compile, follow existing patterns"
719
- },
720
- {
721
- "id": 5,
722
- "description": "Create CQRS Commands: LoginCommand + RegisterCommand with handlers",
723
- "status": "pending",
724
- "category": "application",
725
- "dependencies": [4],
726
- "acceptance_criteria": "Handlers compile, validators defined"
727
- },
728
- {
729
- "id": 6,
730
- "description": "Create AuthController with login/register endpoints",
731
- "status": "pending",
732
- "category": "api",
733
- "dependencies": [5],
734
- "acceptance_criteria": "Endpoints respond correctly, Swagger displays them"
735
- },
736
- {
737
- "id": 7,
738
- "description": "Write unit tests for User entity and validators",
739
- "status": "pending",
740
- "category": "test",
741
- "dependencies": [5],
742
- "acceptance_criteria": "All tests pass, >80% coverage on new code"
743
- },
744
- {
745
- "id": 8,
746
- "description": "Run dotnet build, dotnet test, MCP validate_conventions",
747
- "status": "pending",
748
- "category": "validation",
749
- "dependencies": [6, 7],
750
- "acceptance_criteria": "Build succeeds, all tests pass, MCP validation clean"
751
- }
752
- ]
753
- }
754
- ```
755
-
756
- **Task count validation:**
757
- - MINIMUM: 3 tasks (anything less is too coarse)
758
- - MAXIMUM: 30 tasks (anything more needs re-grouping)
759
- - If over 30, merge related subtasks into larger units
760
-
761
- ### 3. Create prd.json (v2)
762
-
763
- **Write `.ralph/prd.json`:**
764
-
765
- ```json
766
- {
767
- "$version": "2.0.0",
768
- "feature": "{task_description}",
769
- "status": "in_progress",
770
- "created": "{ISO_TIMESTAMP}",
771
- "updated_at": "{ISO_TIMESTAMP}",
772
- "metadata": {metadata},
773
- "config": {
774
- "max_iterations": {max_iterations},
775
- "completion_promise": "{completion_promise}",
776
- "current_iteration": 1
777
- },
778
- "source": {source_object_or_null},
779
- "tasks": [{generated_tasks_with_all_fields}],
780
- "history": []
781
- }
782
- ```
783
-
784
- **IMPORTANT: Every task MUST have ALL fields:**
785
- ```json
786
- {
787
- "id": 1,
788
- "description": "...",
789
- "status": "pending",
790
- "category": "domain|application|infrastructure|api|frontend|i18n|test|validation|other",
791
- "dependencies": [],
792
- "acceptance_criteria": "...",
793
- "started_at": null,
794
- "completed_at": null,
795
- "iteration": null,
796
- "commit_hash": null,
797
- "files_changed": { "created": [], "modified": [] },
798
- "validation": null,
799
- "error": null
800
- }
801
- ```
802
-
803
- **Initialize `.ralph/progress.txt`:**
115
+ Write `.ralph/prd.json` 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).
804
116
 
117
+ Initialize `.ralph/progress.txt`:
805
118
  ```markdown
806
119
  # Ralph Loop Progress
807
-
808
120
  ## Task: {task_description}
809
121
  ## Started: {timestamp}
810
- ## Schema: v2.0.0
811
- ## Source: {source_type or "direct"}
812
-
813
- ---
814
-
815
- ## Iteration 1
816
-
817
- ### Context
818
- Starting fresh implementation.
819
-
820
- ### Learnings
821
- (To be updated after each iteration)
122
+ ## Schema: v3.0.0
822
123
  ```
823
124
 
824
- ### 4. Validate Task Integrity
825
-
826
- **After creation, verify:**
827
-
828
- ```javascript
829
- // Check all tasks have required fields
830
- const requiredFields = ['id', 'description', 'status', 'category',
831
- 'dependencies', 'acceptance_criteria', 'started_at', 'completed_at',
832
- 'iteration', 'commit_hash', 'files_changed', 'validation', 'error'];
833
-
834
- for (const task of prd.tasks) {
835
- for (const field of requiredFields) {
836
- if (!(field in task)) {
837
- ERROR: "Task ${task.id} missing field: ${field}";
838
- }
839
- }
840
- }
841
-
842
- // Check dependency references are valid
843
- const taskIds = prd.tasks.map(t => t.id);
844
- for (const task of prd.tasks) {
845
- for (const dep of task.dependencies) {
846
- if (!taskIds.includes(dep)) {
847
- ERROR: "Task ${task.id} depends on non-existent task ${dep}";
848
- }
849
- }
850
- }
851
-
852
- // Check no circular dependencies
853
- // (simple: a task cannot depend on a task with higher or equal id)
854
- for (const task of prd.tasks) {
855
- for (const dep of task.dependencies) {
856
- if (dep >= task.id) {
857
- ERROR: "Task ${task.id} has forward/circular dependency on task ${dep}";
858
- }
859
- }
860
- }
861
-
862
- // Check task count
863
- if (prd.tasks.length < 3 || prd.tasks.length > 30) {
864
- WARNING: "Task count ${prd.tasks.length} outside recommended range (3-30)";
865
- }
866
- ```
125
+ ## 4. Validate Task Integrity
867
126
 
868
- ### 5. Find Current Task
127
+ - All tasks have required fields
128
+ - Dependency references are valid (exist, no forward/circular)
129
+ - Task count 3-30
869
130
 
870
- **Find the next eligible task:**
131
+ ## 5. Find Current Task
871
132
 
872
133
  ```javascript
873
134
  function findNextTask(tasks) {
874
135
  for (const task of tasks) {
875
136
  if (task.status !== 'pending') continue;
876
-
877
- // Check all dependencies are completed
878
- const depsOk = task.dependencies.every(depId => {
879
- const dep = tasks.find(t => t.id === depId);
880
- return dep && dep.status === 'completed';
881
- });
882
-
883
- // Check if any dependency failed (→ block this task)
884
- const depsBlocked = task.dependencies.some(depId => {
885
- const dep = tasks.find(t => t.id === depId);
137
+ const depsBlocked = task.dependencies.some(d => {
138
+ const dep = tasks.find(t => t.id === d);
886
139
  return dep && (dep.status === 'failed' || dep.status === 'blocked');
887
140
  });
888
-
889
- if (depsBlocked) {
890
- task.status = 'blocked';
891
- task.error = `Blocked by failed dependency`;
892
- continue;
893
- }
894
-
141
+ if (depsBlocked) { task.status = 'blocked'; task.error = 'Blocked by failed dependency'; continue; }
142
+ const depsOk = task.dependencies.every(d => tasks.find(t => t.id === d)?.status === 'completed');
895
143
  if (depsOk) return task;
896
144
  }
897
- return null; // No eligible tasks
145
+ return null;
898
146
  }
899
-
900
- const currentTask = findNextTask(prd.tasks);
901
147
  ```
902
148
 
903
- **If no eligible task found:**
904
- - Check if all tasks are completed → skip to step-05 (report)
905
- - Check if remaining tasks are all blocked → report as partial, step-05
906
- - Otherwise → unexpected state, STOP
149
+ If no eligible task: all completed → step-05, all blocked → step-05, else STOP.
907
150
 
908
- **Store in state:**
909
- ```
910
- {current_task_id} = currentTask.id
911
- {current_task_description} = currentTask.description
912
- {current_task_category} = currentTask.category
913
- {current_task_criteria} = currentTask.acceptance_criteria
914
- {tasks_completed} = tasks.filter(t => t.status === 'completed').length
915
- {tasks_total} = tasks.length
916
- {tasks_blocked} = tasks.filter(t => t.status === 'blocked').length
917
- ```
918
-
919
- ### 6. Read Previous Progress
920
-
921
- **If iteration > 1:**
922
-
923
- Read `.ralph/progress.txt` to understand:
924
- - What was done in previous iterations
925
- - Key learnings and context
926
- - Files that were modified
927
- - Issues encountered
928
-
929
- **Also read `history[]` from prd.json for structured context.**
930
-
931
- ### 7. Show Task Status
932
-
933
- **Display current state:**
151
+ ## 6. Show Task Status
934
152
 
935
153
  ```
936
- ╔══════════════════════════════════════════════════════════════════╗
937
- ║ ITERATION {current_iteration} / {max_iterations} ║
938
- ║ {modules_queue ? "Module " + (currentIndex+1) + "/" + totalModules + ": " + current_module : ""} ║
939
- ╠══════════════════════════════════════════════════════════════════╣
940
- ║ Progress: {tasks_completed} / {tasks_total} tasks complete ║
941
- ║ Blocked: {tasks_blocked} ║
942
- ║ ║
943
- ║ Current Task: ║
944
- ║ [{current_task_id}] {current_task_description} ║
945
- ║ Category: {current_task_category} ║
946
- ║ Criteria: {current_task_criteria} ║
947
- ╠══════════════════════════════════════════════════════════════════╣
948
- ║ Previous Learnings: ║
949
- ║ {summary from progress.txt} ║
950
- ╚══════════════════════════════════════════════════════════════════╝
951
-
952
- -> Executing task...
953
- ```
954
-
955
- ---
956
-
957
- ## OUTPUT FORMAT:
958
-
959
- ```
960
- Task Loaded:
961
-
962
- | Field | Value |
963
- |-------|-------|
964
- | Iteration | {current_iteration} / {max_iterations} |
965
- | Task ID | {current_task_id} |
966
- | Task | {current_task_description} |
967
- | Category | {current_task_category} |
968
- | Criteria | {current_task_criteria} |
969
- | Progress | {tasks_completed} / {tasks_total} |
970
- | Blocked | {tasks_blocked} |
971
-
154
+ [{iteration}/{max}] {completed}/{total} done | Task [{id}] {description} ({category})
972
155
  -> Executing...
973
156
  ```
974
157