@atlashub/smartstack-cli 3.17.0 → 3.19.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 +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/agents/gitflow/start.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +5 -5
- package/templates/skills/business-analyse/_shared.md +46 -20
- package/templates/skills/business-analyse/html/ba-interactive.html +57 -107
- package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +13 -0
- package/templates/skills/business-analyse/html/src/scripts/02-navigation.js +1 -1
- package/templates/skills/business-analyse/html/src/scripts/03-render-cadrage.js +11 -20
- package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +1 -3
- package/templates/skills/business-analyse/html/src/template.html +31 -83
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +71 -3
- package/templates/skills/business-analyse/references/cadrage-pre-analysis.md +11 -8
- package/templates/skills/business-analyse/references/cadrage-structure-cards.md +7 -5
- package/templates/skills/business-analyse/references/deploy-data-build.md +42 -14
- package/templates/skills/business-analyse/references/deploy-modes.md +1 -1
- package/templates/skills/business-analyse/references/entity-architecture-decision.md +218 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +2 -1
- package/templates/skills/business-analyse/references/spec-auto-inference.md +70 -16
- package/templates/skills/business-analyse/references/ui-resource-cards.md +149 -0
- package/templates/skills/business-analyse/steps/step-00-init.md +23 -5
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +220 -32
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +35 -26
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +122 -32
- package/templates/skills/business-analyse/steps/step-03a2-analysis.md +8 -0
- package/templates/skills/business-analyse/steps/step-03b-ui.md +68 -5
- package/templates/skills/business-analyse/steps/step-03d-validate.md +34 -1
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +99 -2
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +44 -8
- package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +226 -41
- package/templates/skills/business-analyse/steps/step-06-review.md +2 -1
- package/templates/skills/business-analyse/templates/tpl-handoff.md +5 -4
- package/templates/skills/business-analyse/templates/tpl-launch-displays.md +4 -1
- package/templates/skills/business-analyse/templates-frd.md +5 -4
- package/templates/skills/gitflow/references/start-local-config.md +6 -3
- package/templates/skills/gitflow/steps/step-start.md +2 -2
- package/templates/skills/ralph-loop/SKILL.md +41 -1
- package/templates/skills/ralph-loop/references/category-rules.md +96 -2
- package/templates/skills/ralph-loop/references/compact-loop.md +85 -24
- package/templates/skills/ralph-loop/steps/step-00-init.md +30 -54
- package/templates/skills/ralph-loop/steps/step-01-task.md +102 -1
- package/templates/skills/ralph-loop/steps/step-04-check.md +87 -40
|
@@ -141,13 +141,86 @@ Rules:
|
|
|
141
141
|
- DTOs separate from domain entities
|
|
142
142
|
- Service interfaces in Application, implementations in Infrastructure
|
|
143
143
|
|
|
144
|
-
**Tenant isolation (BLOCKING):**
|
|
144
|
+
**Tenant isolation (BLOCKING — SECURITY CRITICAL):**
|
|
145
|
+
|
|
146
|
+
> **ROOT CAUSE (test-v4-005):** Services were generated WITHOUT TenantId filtering,
|
|
147
|
+
> creating cross-tenant data leakage on ALL 70+ CRUD endpoints.
|
|
148
|
+
> This is an OWASP A01 (Broken Access Control) vulnerability.
|
|
149
|
+
|
|
145
150
|
- ALL queries on tenant entities MUST include `.Where(x => x.TenantId == _currentUser.TenantId)`
|
|
146
151
|
- ALL entity creation MUST pass `_currentUser.TenantId` as first parameter to `Entity.Create(tenantId, ...)`
|
|
147
152
|
- NEVER use `new Entity { }` without `TenantId =` — always prefer factory method `Entity.Create()`
|
|
148
153
|
- NEVER use `Guid.Empty` as a placeholder for userId, tenantId, or any business identifier
|
|
149
154
|
- Service constructor MUST inject `ICurrentUserService _currentUser` to access TenantId
|
|
150
155
|
|
|
156
|
+
**MANDATORY Service Template (use as skeleton for ALL services):**
|
|
157
|
+
|
|
158
|
+
```csharp
|
|
159
|
+
public class {Entity}Service : I{Entity}Service
|
|
160
|
+
{
|
|
161
|
+
private readonly IExtensionsDbContext _db;
|
|
162
|
+
private readonly ICurrentUserService _currentUser;
|
|
163
|
+
|
|
164
|
+
public {Entity}Service(IExtensionsDbContext db, ICurrentUserService currentUser)
|
|
165
|
+
{
|
|
166
|
+
_db = db;
|
|
167
|
+
_currentUser = currentUser;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public async Task<PagedResult<{Entity}Response>> GetAllAsync(/* filters */, CancellationToken ct)
|
|
171
|
+
{
|
|
172
|
+
var tenantId = _currentUser.TenantId;
|
|
173
|
+
var query = _db.{Entities}
|
|
174
|
+
.Where(x => x.TenantId == tenantId) // ← MANDATORY tenant filter
|
|
175
|
+
.AsQueryable();
|
|
176
|
+
// ... apply filters, pagination, projection to Response DTO
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public async Task<{Entity}Response?> GetByIdAsync(Guid id, CancellationToken ct)
|
|
180
|
+
{
|
|
181
|
+
var tenantId = _currentUser.TenantId;
|
|
182
|
+
var entity = await _db.{Entities}
|
|
183
|
+
.Where(x => x.TenantId == tenantId) // ← MANDATORY tenant filter
|
|
184
|
+
.FirstOrDefaultAsync(x => x.Id == id, ct);
|
|
185
|
+
// ...
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public async Task<{Entity}Response> CreateAsync(Create{Entity}Dto dto, CancellationToken ct)
|
|
189
|
+
{
|
|
190
|
+
var entity = {Entity}.Create(_currentUser.TenantId, /* dto fields */);
|
|
191
|
+
// ← TenantId as FIRST parameter
|
|
192
|
+
_db.{Entities}.Add(entity);
|
|
193
|
+
await _db.SaveChangesAsync(ct);
|
|
194
|
+
return MapToResponse(entity);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public async Task<{Entity}Response> UpdateAsync(Guid id, Update{Entity}Dto dto, CancellationToken ct)
|
|
198
|
+
{
|
|
199
|
+
var entity = await _db.{Entities}
|
|
200
|
+
.Where(x => x.TenantId == _currentUser.TenantId) // ← MANDATORY
|
|
201
|
+
.FirstOrDefaultAsync(x => x.Id == id, ct)
|
|
202
|
+
?? throw new NotFoundException(nameof({Entity}), id);
|
|
203
|
+
// ... update fields
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public async Task DeleteAsync(Guid id, CancellationToken ct)
|
|
207
|
+
{
|
|
208
|
+
var entity = await _db.{Entities}
|
|
209
|
+
.Where(x => x.TenantId == _currentUser.TenantId) // ← MANDATORY
|
|
210
|
+
.FirstOrDefaultAsync(x => x.Id == id, ct)
|
|
211
|
+
?? throw new NotFoundException(nameof({Entity}), id);
|
|
212
|
+
// ...
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private static {Entity}Response MapToResponse({Entity} entity) => new()
|
|
216
|
+
{
|
|
217
|
+
// Map entity fields to response DTO
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**POST-CHECK after writing ANY service:** Grep the file for `TenantId`. If 0 occurrences → FAIL, rewrite with tenant filtering.
|
|
223
|
+
|
|
151
224
|
**Lifecycle-aware services:**
|
|
152
225
|
- Services operating on entities with a `lifeCycle` (status field) MUST validate entity state before mutations
|
|
153
226
|
- Example: `if (entity.Status == EmployeeStatus.Terminated) throw new BusinessException("Cannot update terminated employee")`
|
|
@@ -161,6 +234,22 @@ Rules:
|
|
|
161
234
|
- DependencyInjection.cs MUST NOT be empty or contain only TODO comments
|
|
162
235
|
- After writing validators, VERIFY DI registration exists — if missing, add it immediately
|
|
163
236
|
|
|
237
|
+
**POST-CHECK after writing validators:**
|
|
238
|
+
```bash
|
|
239
|
+
# Count Create validators vs Update validators
|
|
240
|
+
CREATE_COUNT=$(find . -path "*/Validators/*" -name "Create*Validator.cs" | wc -l)
|
|
241
|
+
UPDATE_COUNT=$(find . -path "*/Validators/*" -name "Update*Validator.cs" | wc -l)
|
|
242
|
+
if [ "$CREATE_COUNT" -ne "$UPDATE_COUNT" ]; then
|
|
243
|
+
echo "VALIDATOR MISMATCH: $CREATE_COUNT Create vs $UPDATE_COUNT Update → MUST be equal"
|
|
244
|
+
# List missing UpdateValidators and CREATE them
|
|
245
|
+
fi
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Mapper pattern (DRY):**
|
|
249
|
+
- Each service MUST include a `private static {Entity}Response MapToResponse({Entity} entity)` method
|
|
250
|
+
- For complex mappings with related entities, use an extension method in `Application/Mappings/{Module}Mappings.cs`
|
|
251
|
+
- NEVER duplicate mapping logic between GetAll, GetById, Create, Update — always call MapToResponse
|
|
252
|
+
|
|
164
253
|
**FORBIDDEN:**
|
|
165
254
|
- Empty DependencyInjection.cs with `// TODO` placeholder
|
|
166
255
|
- CreateValidator without matching UpdateValidator
|
|
@@ -169,6 +258,7 @@ Rules:
|
|
|
169
258
|
- `new Entity { }` without TenantId assignment
|
|
170
259
|
- `Guid.Empty` as a business value in services or controllers
|
|
171
260
|
- Entity.Create() without tenantId as first parameter
|
|
261
|
+
- Duplicated mapping logic (entity→response) in multiple methods
|
|
172
262
|
|
|
173
263
|
---
|
|
174
264
|
|
|
@@ -280,9 +370,13 @@ useXxx with raw axios inside hooks → hooks MUST use the shared apiCl
|
|
|
280
370
|
|
|
281
371
|
**MCP:** `scaffold_tests`, `analyze_test_coverage`
|
|
282
372
|
|
|
373
|
+
> **CRITICAL:** Test generation is a MANDATORY category. If the PRD has no test tasks,
|
|
374
|
+
> the category completeness check (step-01 section 4b) will inject a guardrail task.
|
|
375
|
+
> Test projects MUST be created as the FIRST action in this category — before generating any test files.
|
|
376
|
+
|
|
283
377
|
**Execution sequence:**
|
|
284
378
|
|
|
285
|
-
1. **Ensure test projects exist:**
|
|
379
|
+
1. **Ensure test projects exist (FIRST — before any test generation):**
|
|
286
380
|
```bash
|
|
287
381
|
# Unit test project
|
|
288
382
|
UNIT_TEST_PROJECT="tests/${PROJECT_NAME}.Tests.Unit"
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# Compact Loop — Inline Execution
|
|
2
2
|
|
|
3
3
|
> **Loaded by:** step-04 section 5 (after first full iteration)
|
|
4
|
-
> **Purpose:** Execute
|
|
5
|
-
> **
|
|
4
|
+
> **Purpose:** Execute ALL remaining tasks autonomously without stopping.
|
|
5
|
+
> **EXECUTION GUARANTEE:** This loop runs until ALL tasks are done, max iterations, or dead-end.
|
|
6
|
+
> **ABSOLUTE RULES:**
|
|
7
|
+
> - NEVER stop the loop
|
|
8
|
+
> - NEVER wait for user input
|
|
9
|
+
> - NEVER ask for confirmation
|
|
10
|
+
> - NEVER re-read step files
|
|
11
|
+
> - NEVER pause between iterations
|
|
12
|
+
> - Execute A → B → C → D → back to step-04 section 1 → repeat
|
|
6
13
|
|
|
7
14
|
---
|
|
8
15
|
|
|
@@ -208,10 +215,49 @@ Batch: {batch.length} [{firstCategory}] → {batch.map(t => `[${t.id}] ${t.descr
|
|
|
208
215
|
|
|
209
216
|
## C. Commit Batch
|
|
210
217
|
|
|
218
|
+
### C1. Update PRD Status (MANDATORY — BEFORE git commit)
|
|
219
|
+
|
|
220
|
+
> **CRITICAL:** PRD status MUST be updated BEFORE committing.
|
|
221
|
+
> In test-v4-005, the compact loop executed 44 tasks but NEVER updated prd.json status.
|
|
222
|
+
> This broke ALL downstream guardrails (module completeness check never triggered).
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
const now = new Date().toISOString();
|
|
226
|
+
const COMMIT_HASH_PENDING = 'pending-commit'; // Updated after git commit
|
|
227
|
+
|
|
228
|
+
for (const task of batch) {
|
|
229
|
+
if (task.status !== 'failed') task.status = 'completed';
|
|
230
|
+
task.completed_at = now;
|
|
231
|
+
task.iteration = prd.config.current_iteration;
|
|
232
|
+
task.commit_hash = COMMIT_HASH_PENDING;
|
|
233
|
+
}
|
|
234
|
+
prd.config.current_iteration++;
|
|
235
|
+
prd.updated_at = now;
|
|
236
|
+
writeJSON('.ralph/prd.json', prd);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### C2. Update Progress File (MANDATORY)
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
const completed = prd.tasks.filter(t => t.status === 'completed').length;
|
|
243
|
+
const total = prd.tasks.length;
|
|
244
|
+
const moduleName = prd.project?.module || 'unknown';
|
|
245
|
+
|
|
246
|
+
// Append to progress.txt (NOT overwrite)
|
|
247
|
+
appendFile('.ralph/progress.txt',
|
|
248
|
+
`Iteration ${prd.config.current_iteration - 1}: ${batch.map(t => t.id).join('/')} COMPLETED — ` +
|
|
249
|
+
`${batch[0].category} (${batch.length} tasks). ${completed}/${total} done.\n` +
|
|
250
|
+
` Files: ${batch.reduce((acc, t) => acc + (t.files_changed?.created?.length || 0), 0)} created\n`
|
|
251
|
+
);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### C3. Git Commit
|
|
255
|
+
|
|
211
256
|
```bash
|
|
212
257
|
# Stage all changed files from batch
|
|
213
258
|
git add {all_files_from_batch}
|
|
214
|
-
git add .ralph/prd.json
|
|
259
|
+
git add .ralph/prd.json .ralph/progress.txt
|
|
260
|
+
[ -f .ralph/modules-queue.json ] && git add .ralph/modules-queue.json
|
|
215
261
|
|
|
216
262
|
git commit -m "$(cat <<'EOF'
|
|
217
263
|
feat({scope}): [{category}] {batch.length} tasks — {short summary}
|
|
@@ -227,44 +273,59 @@ EOF
|
|
|
227
273
|
COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
228
274
|
```
|
|
229
275
|
|
|
230
|
-
|
|
276
|
+
### C4. Finalize PRD with Commit Hash
|
|
277
|
+
|
|
231
278
|
```javascript
|
|
232
|
-
|
|
279
|
+
// Update commit hash now that we have the real one
|
|
233
280
|
for (const task of batch) {
|
|
234
|
-
if (task.
|
|
235
|
-
task.completed_at = now;
|
|
236
|
-
task.iteration = prd.config.current_iteration;
|
|
237
|
-
task.commit_hash = COMMIT_HASH;
|
|
281
|
+
if (task.commit_hash === 'pending-commit') task.commit_hash = COMMIT_HASH;
|
|
238
282
|
}
|
|
239
283
|
prd.history.push({
|
|
240
|
-
iteration: prd.config.current_iteration,
|
|
284
|
+
iteration: prd.config.current_iteration - 1,
|
|
241
285
|
task_ids: batch.map(t => t.id),
|
|
242
286
|
action: 'batch-completed',
|
|
243
287
|
timestamp: now,
|
|
244
288
|
commit_hash: COMMIT_HASH,
|
|
245
289
|
notes: "{summary}"
|
|
246
290
|
});
|
|
247
|
-
prd.config.current_iteration++;
|
|
248
|
-
prd.updated_at = now;
|
|
249
291
|
writeJSON('.ralph/prd.json', prd);
|
|
250
292
|
```
|
|
251
293
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
294
|
+
### C5. PRD Sync Verification (HARD CHECK)
|
|
295
|
+
|
|
296
|
+
> **MANDATORY:** Verify prd.json reflects reality before looping back.
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
const prdCheck = readJSON('.ralph/prd.json');
|
|
300
|
+
const batchIds = batch.map(t => t.id);
|
|
301
|
+
const actuallyCompleted = prdCheck.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed');
|
|
302
|
+
|
|
303
|
+
if (actuallyCompleted.length !== batch.filter(t => t.status !== 'failed').length) {
|
|
304
|
+
console.error('PRD SYNC ERROR: Tasks executed but not marked completed in prd.json');
|
|
305
|
+
console.error(`Expected: ${batch.filter(t => t.status !== 'failed').length} completed, Got: ${actuallyCompleted.length}`);
|
|
306
|
+
// Force re-write
|
|
307
|
+
for (const task of batch) {
|
|
308
|
+
const prdTask = prdCheck.tasks.find(t => t.id === task.id);
|
|
309
|
+
if (prdTask && task.status === 'completed') prdTask.status = 'completed';
|
|
310
|
+
if (prdTask) prdTask.commit_hash = COMMIT_HASH;
|
|
311
|
+
}
|
|
312
|
+
writeJSON('.ralph/prd.json', prdCheck);
|
|
313
|
+
console.log('PRD SYNC REPAIRED');
|
|
314
|
+
}
|
|
257
315
|
```
|
|
258
316
|
|
|
259
317
|
---
|
|
260
318
|
|
|
261
|
-
## D. Loop Back
|
|
319
|
+
## D. Loop Back (MANDATORY — NO PAUSE)
|
|
262
320
|
|
|
263
321
|
**IMMEDIATELY return to step-04 section 1 (Read Current State).**
|
|
264
|
-
DO NOT stop. DO NOT wait for user. DO NOT re-read step files.
|
|
265
322
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
-
|
|
269
|
-
|
|
270
|
-
-
|
|
323
|
+
DO NOT stop. DO NOT wait for user. DO NOT ask "should I continue?". DO NOT re-read step files.
|
|
324
|
+
DO NOT display a summary and pause. DO NOT output partial results and wait.
|
|
325
|
+
**Go directly to step-04 section 1. NOW.**
|
|
326
|
+
|
|
327
|
+
The ONLY exit conditions (enforced by step-04 sections 2-4):
|
|
328
|
+
- ALL tasks complete (all modules if multi-module) → step-05
|
|
329
|
+
- Max iterations reached → step-05
|
|
330
|
+
- Dead-end (all remaining blocked/failed) → step-05
|
|
331
|
+
- User Ctrl+C (external interrupt)
|
|
@@ -8,9 +8,12 @@ next_step: steps/step-01-task.md
|
|
|
8
8
|
|
|
9
9
|
## MANDATORY RULES:
|
|
10
10
|
|
|
11
|
+
- **FULLY AUTONOMOUS** — execute ALL tasks without stopping or asking the user
|
|
11
12
|
- NEVER skip MCP verification
|
|
12
13
|
- ALWAYS parse ALL flags before any action
|
|
13
14
|
- ONLY check resume if -r flag is set
|
|
15
|
+
- **NEVER ask the user** to choose a mode, confirm execution, or select options
|
|
16
|
+
- **NEVER set max_iterations = 1** — unless the user explicitly passed `-m 1`
|
|
14
17
|
- CONTEXT BUDGET: Keep output COMPACT
|
|
15
18
|
- **NEVER DELEGATE** the main Ralph loop to a sub-agent (single module)
|
|
16
19
|
- **Multi-module (2+ modules):** Use Agent Teams for parallel execution
|
|
@@ -48,69 +51,36 @@ mcp__context7__resolve-library-id: libraryName: "test" → {mcp_context7} = tru
|
|
|
48
51
|
|
|
49
52
|
If ANY fails: show error, suggest `smartstack check-mcp`, STOP.
|
|
50
53
|
|
|
51
|
-
## 3.
|
|
54
|
+
## 3. Execution Mode (FULLY AUTONOMOUS)
|
|
52
55
|
|
|
53
|
-
> **
|
|
56
|
+
> **CRITICAL:** Ralph-loop is FULLY AUTONOMOUS by default.
|
|
57
|
+
> It executes ALL tasks from start to finish WITHOUT stopping.
|
|
58
|
+
> The user launched `/ralph-loop` — this IS the instruction to execute everything.
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
**DO NOT** ask the user to confirm, choose a mode, or approve execution.
|
|
61
|
+
**DO NOT** set `max_iterations = 1` or any reduced value.
|
|
62
|
+
**DO NOT** stop after the first task to "check quality".
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
**Stratégie recommandée :**
|
|
63
|
-
|
|
64
|
-
1. **Première itération supervisée** - Lancer manuellement la première tâche pour vérifier :
|
|
65
|
-
- La qualité du code généré
|
|
66
|
-
- Le temps d'exécution des tests
|
|
67
|
-
- La clarté des messages d'erreur
|
|
68
|
-
|
|
69
|
-
2. **Mode autonome limité** - Si première itération OK, relancer avec `--max-iterations 5-10`
|
|
70
|
-
|
|
71
|
-
3. **Mode autonome complet** - Une fois confiant, augmenter à `--max-iterations 50`
|
|
72
|
-
|
|
73
|
-
### Checkpoint Prompt (Optional)
|
|
74
|
-
|
|
75
|
-
Si vous détectez que c'est la première utilisation de ralph-loop sur ce projet (pas de `.ralph/logs/`), proposer :
|
|
64
|
+
The loop runs autonomously until:
|
|
65
|
+
- ALL tasks are completed (success)
|
|
66
|
+
- `max_iterations` is reached (user explicitly set via `-m N`)
|
|
67
|
+
- Dead-end detected (all remaining tasks blocked/failed)
|
|
68
|
+
- User Ctrl+C (external interrupt)
|
|
76
69
|
|
|
77
70
|
```javascript
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
header: "Mode",
|
|
83
|
-
multiSelect: false,
|
|
84
|
-
options: [
|
|
85
|
-
{ label: "Supervisé (recommandé)", description: "Exécuter 1 tâche, puis demander confirmation" },
|
|
86
|
-
{ label: "Autonome limité", description: "Max 10 itérations automatiques" },
|
|
87
|
-
{ label: "Autonome complet", description: "Jusqu'à 50 itérations (mode AFK)" }
|
|
88
|
-
]
|
|
89
|
-
}]
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
if (answer === "Supervisé") {
|
|
93
|
-
max_iterations = 1;
|
|
94
|
-
console.log("Mode supervisé : 1 tâche sera exécutée. Relancez avec -r pour continuer.");
|
|
95
|
-
} else if (answer === "Autonome limité") {
|
|
96
|
-
max_iterations = Math.min(max_iterations, 10);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
71
|
+
// NO user prompt. NO mode selection. JUST EXECUTE.
|
|
72
|
+
// max_iterations is already set from flags (default: 50).
|
|
73
|
+
// If user wants fewer iterations, they use -m N explicitly.
|
|
74
|
+
console.log(`Mode: AUTONOMOUS | Max iterations: ${max_iterations}`);
|
|
99
75
|
```
|
|
100
76
|
|
|
101
|
-
###
|
|
102
|
-
|
|
103
|
-
⚠️ **IMPORTANT :** Si vos tests prennent >30 secondes, ralph-loop peut devenir inefficace.
|
|
77
|
+
### Speed Warning (informational only — does NOT block)
|
|
104
78
|
|
|
79
|
+
If tests are slow (>30s detected in previous logs), display a one-line warning but **continue execution**:
|
|
105
80
|
```
|
|
106
|
-
|
|
107
|
-
- Unitaires : <5s
|
|
108
|
-
- Intégration : <15s
|
|
109
|
-
- E2E : <30s (à exécuter en dehors du loop)
|
|
81
|
+
⚠ Slow tests detected. Consider disabling E2E during ralph-loop.
|
|
110
82
|
```
|
|
111
83
|
|
|
112
|
-
Si tests lents détectés (via logs), avertir l'utilisateur et suggérer de désactiver tests E2E pendant le loop.
|
|
113
|
-
|
|
114
84
|
## 4. Resume Mode
|
|
115
85
|
|
|
116
86
|
If `{resume_mode} = true`:
|
|
@@ -213,9 +183,15 @@ if (PRD_COUNT > 1) {
|
|
|
213
183
|
}
|
|
214
184
|
```
|
|
215
185
|
|
|
216
|
-
## 5. Completion Promise
|
|
186
|
+
## 5. Completion Promise (auto-set)
|
|
217
187
|
|
|
218
|
-
|
|
188
|
+
```javascript
|
|
189
|
+
// Auto-set if not provided — NEVER ask the user
|
|
190
|
+
if (!completion_promise) {
|
|
191
|
+
completion_promise = "COMPLETE";
|
|
192
|
+
}
|
|
193
|
+
// The promise is output ONLY when ALL tasks are done. No user interaction needed.
|
|
194
|
+
```
|
|
219
195
|
|
|
220
196
|
## 6. Collect Metadata
|
|
221
197
|
|
|
@@ -94,11 +94,62 @@ If `.ralph/prd.json` exists:
|
|
|
94
94
|
2. **v3 FAST PATH:** If `$version === "3.0.0"` AND `tasks[]` exists:
|
|
95
95
|
- Inject runtime fields if missing (status, config, feature, created, updated_at, history)
|
|
96
96
|
- Write back to `.ralph/prd.json`
|
|
97
|
+
- **Run CATEGORY COMPLETENESS CHECK (section 4b) before proceeding**
|
|
97
98
|
- Skip directly to section 5 (find next task)
|
|
98
99
|
3. **v2 legacy:** If `$version === "2.0.0"` → find next eligible task (section 5), skip to section 5
|
|
99
100
|
4. **FORMAT A (deprecated):** If `.project && .requirements && !.$version` → run `transformPrdJsonToRalphV2()` → section 5
|
|
100
101
|
|
|
101
|
-
If `.ralph/prd.json` does not exist: continue to section
|
|
102
|
+
If `.ralph/prd.json` does not exist: continue to section 1b.
|
|
103
|
+
|
|
104
|
+
### 1b. BA Handoff Quality Gate (BLOCKING)
|
|
105
|
+
|
|
106
|
+
> **CRITICAL:** Before generating ANY tasks, verify the BA handoff is complete.
|
|
107
|
+
> A missing handoff means no frontend files, no test plan, no BR-to-code mapping.
|
|
108
|
+
> Generating tasks without handoff produces an INCOMPLETE PRD (backend-only).
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// Find the source feature.json for the current module
|
|
112
|
+
const sourceFeatureJson = prd?.metadata?.sourceFeatureJson
|
|
113
|
+
|| findFile('docs/business/**/business-analyse/**/feature.json');
|
|
114
|
+
|
|
115
|
+
if (sourceFeatureJson) {
|
|
116
|
+
const feature = readJSON(sourceFeatureJson);
|
|
117
|
+
const handoff = feature.handoff || {};
|
|
118
|
+
|
|
119
|
+
// CHECK 1: Handoff status must be "handed-off"
|
|
120
|
+
if (handoff.status !== 'handed-off') {
|
|
121
|
+
console.error(`
|
|
122
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
123
|
+
║ BLOCKING: BA HANDOFF INCOMPLETE ║
|
|
124
|
+
║ ║
|
|
125
|
+
║ feature.json handoff.status = "${handoff.status || 'missing'}"
|
|
126
|
+
║ Expected: "handed-off" ║
|
|
127
|
+
║ ║
|
|
128
|
+
║ The /business-analyse did not complete steps 04a→05c. ║
|
|
129
|
+
║ Without handoff, the PRD will be MISSING: ║
|
|
130
|
+
║ - Frontend tasks (pages, components, routes) ║
|
|
131
|
+
║ - Test tasks (unit, integration, security) ║
|
|
132
|
+
║ - BR-to-code mappings ║
|
|
133
|
+
║ - API endpoint summary ║
|
|
134
|
+
║ ║
|
|
135
|
+
║ ACTION: Run /business-analyse to complete the handoff, ║
|
|
136
|
+
║ then re-run /ralph-loop. ║
|
|
137
|
+
╚══════════════════════════════════════════════════════════════╝`);
|
|
138
|
+
STOP;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// CHECK 2: filesToCreate must have all 7 categories
|
|
142
|
+
const filesToCreate = handoff.filesToCreate || {};
|
|
143
|
+
const requiredCategories = ['domain', 'application', 'infrastructure', 'api', 'frontend', 'seedData', 'tests'];
|
|
144
|
+
const missingInHandoff = requiredCategories.filter(c => !filesToCreate[c] || filesToCreate[c].length === 0);
|
|
145
|
+
|
|
146
|
+
if (missingInHandoff.length > 0) {
|
|
147
|
+
console.warn(`⚠ Handoff has empty categories: ${missingInHandoff.join(', ')}`);
|
|
148
|
+
console.warn('PRD generation may produce incomplete tasks. Consider re-running /business-analyse step-05a.');
|
|
149
|
+
// WARNING only — allow proceeding if handoff.status is correct
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
102
153
|
|
|
103
154
|
## 2. Analyze Task Description
|
|
104
155
|
|
|
@@ -128,6 +179,56 @@ Initialize `.ralph/progress.txt`:
|
|
|
128
179
|
- Dependency references are valid (exist, no forward/circular)
|
|
129
180
|
- Task count 3-30
|
|
130
181
|
|
|
182
|
+
### 4b. Category Completeness Check (BLOCKING)
|
|
183
|
+
|
|
184
|
+
> **CRITICAL:** The PRD MUST contain tasks for ALL required categories.
|
|
185
|
+
> A PRD with only backend categories (domain, infrastructure, application, api) is INCOMPLETE.
|
|
186
|
+
> This check prevents the "no frontend generated" failure mode.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
const REQUIRED_CATEGORIES = ['domain', 'infrastructure', 'application', 'api', 'frontend', 'test'];
|
|
190
|
+
const presentCategories = new Set(prd.tasks.map(t => t.category));
|
|
191
|
+
const missingCategories = REQUIRED_CATEGORIES.filter(c => !presentCategories.has(c));
|
|
192
|
+
|
|
193
|
+
if (missingCategories.length > 0) {
|
|
194
|
+
console.warn(`⚠ PRD MISSING CATEGORIES: ${missingCategories.join(', ')}`);
|
|
195
|
+
|
|
196
|
+
// AUTO-INJECT guardrail tasks for missing categories
|
|
197
|
+
let maxIdNum = Math.max(...prd.tasks.map(t => {
|
|
198
|
+
const num = parseInt(t.id.replace(/[^0-9]/g, ''), 10);
|
|
199
|
+
return isNaN(num) ? 0 : num;
|
|
200
|
+
}));
|
|
201
|
+
const prefix = prd.tasks[0]?.id?.replace(/[0-9]+$/, '') || 'GUARD-';
|
|
202
|
+
const lastBackendTask = prd.tasks.filter(t => t.category === 'api').pop()?.id || prd.tasks[prd.tasks.length - 1]?.id;
|
|
203
|
+
|
|
204
|
+
for (const cat of missingCategories) {
|
|
205
|
+
maxIdNum++;
|
|
206
|
+
const taskId = `${prefix}${String(maxIdNum).padStart(3, '0')}`;
|
|
207
|
+
const guardrailTask = {
|
|
208
|
+
id: taskId,
|
|
209
|
+
description: `[GUARDRAIL] Generate ${cat} layer for ${prd.project?.module || 'module'}`,
|
|
210
|
+
status: 'pending',
|
|
211
|
+
category: cat,
|
|
212
|
+
dependencies: cat === 'test' ? [lastBackendTask] : (cat === 'frontend' ? [lastBackendTask] : []),
|
|
213
|
+
acceptance_criteria: [
|
|
214
|
+
cat === 'frontend' ? 'React pages created via MCP scaffold_api_client + scaffold_routes, wired to App.tsx' :
|
|
215
|
+
cat === 'test' ? 'Unit + Integration test projects created, scaffold_tests MCP called, dotnet test passes' :
|
|
216
|
+
`${cat} layer fully implemented per category-rules.md`
|
|
217
|
+
],
|
|
218
|
+
started_at: null, completed_at: null, iteration: null,
|
|
219
|
+
commit_hash: null, files_changed: [], validation: null, error: null
|
|
220
|
+
};
|
|
221
|
+
prd.tasks.push(guardrailTask);
|
|
222
|
+
console.log(` → Injected guardrail: [${taskId}] ${cat}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
writeJSON('.ralph/prd.json', prd);
|
|
226
|
+
console.log(`PRD updated: ${prd.tasks.length} tasks (${missingCategories.length} guardrails added)`);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Why this matters:** In test-v4-005, the PRD was generated with only backend categories (domain, infrastructure, application, api, seedData, validation). The frontend and test categories were entirely absent, resulting in 0 frontend pages and 0 tests generated across 3 modules.
|
|
231
|
+
|
|
131
232
|
## 5. Find Current Task
|
|
132
233
|
|
|
133
234
|
```javascript
|