@atlashub/smartstack-cli 3.9.0 → 3.12.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 +2544 -2461
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +479 -6185
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/ba-writer.md +178 -0
- package/templates/agents/db-reader.md +149 -0
- package/templates/skills/application/references/application-roles-template.md +227 -0
- package/templates/skills/application/references/provider-template.md +30 -6
- package/templates/skills/application/steps/step-03-roles.md +45 -7
- package/templates/skills/application/steps/step-03b-provider.md +13 -6
- package/templates/skills/business-analyse/SKILL.md +56 -4
- package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +477 -0
- package/templates/skills/business-analyse/references/cache-warming-strategy.md +578 -0
- package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +9 -19
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +12 -2
- package/templates/skills/business-analyse/references/deploy-data-build.md +36 -25
- package/templates/skills/business-analyse/references/detection-strategies.md +424 -0
- package/templates/skills/business-analyse/references/html-data-mapping.md +4 -0
- package/templates/skills/business-analyse/references/prd-generation.md +258 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +538 -0
- package/templates/skills/business-analyse/references/validate-incremental-html.md +47 -4
- package/templates/skills/business-analyse/references/validation-checklist.md +281 -0
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +33 -1
- package/templates/skills/business-analyse/steps/step-00-init.md +70 -75
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +8 -22
- package/templates/skills/business-analyse/steps/step-03a-data.md +20 -410
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +356 -0
- package/templates/skills/business-analyse/steps/step-03a2-analysis.md +143 -0
- package/templates/skills/business-analyse/steps/step-03b-ui.md +3 -0
- package/templates/skills/business-analyse/steps/step-03c-compile.md +72 -3
- package/templates/skills/business-analyse/steps/step-03d-validate.md +36 -3
- package/templates/skills/business-analyse/steps/step-04-consolidation.md +21 -440
- package/templates/skills/business-analyse/steps/step-04a-collect.md +304 -0
- package/templates/skills/business-analyse/steps/step-04b-analyze.md +239 -0
- package/templates/skills/business-analyse/steps/step-04c-decide.md +186 -0
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +44 -0
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +42 -2
- package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +518 -0
- package/templates/skills/controller/steps/step-03-generate.md +184 -24
- package/templates/skills/controller/templates.md +11 -2
- package/templates/skills/debug/SKILL.md +156 -53
- package/templates/skills/debug/references/team-protocol.md +232 -0
- package/templates/skills/ralph-loop/references/category-rules.md +46 -0
- package/templates/skills/ralph-loop/references/compact-loop.md +32 -2
- package/templates/skills/ralph-loop/references/core-seed-data.md +233 -21
- package/templates/skills/ralph-loop/steps/step-00-init.md +64 -1
- package/templates/skills/ralph-loop/steps/step-04-check.md +27 -2
|
@@ -279,7 +279,129 @@ If MCP `generate_permissions` fails, use the template above directly with values
|
|
|
279
279
|
|
|
280
280
|
---
|
|
281
281
|
|
|
282
|
-
## 4.
|
|
282
|
+
## 4. ApplicationRolesSeedData.cs (Application-Level, Once per Application)
|
|
283
|
+
|
|
284
|
+
**File:** `Infrastructure/Persistence/Seeding/Data/ApplicationRolesSeedData.cs`
|
|
285
|
+
|
|
286
|
+
### Purpose
|
|
287
|
+
|
|
288
|
+
Creates the 4 standard application-scoped roles: Admin, Manager, Contributor, Viewer.
|
|
289
|
+
|
|
290
|
+
**CRITICAL:** This file is created **ONCE per application** (not per module).
|
|
291
|
+
Without these roles, role-permission mappings in `SeedRolePermissionsAsync()` will fail silently.
|
|
292
|
+
|
|
293
|
+
### GUID Generation Rule
|
|
294
|
+
|
|
295
|
+
```csharp
|
|
296
|
+
// Deterministic GUID from application ID + role type
|
|
297
|
+
private static Guid GenerateRoleGuid(string roleType)
|
|
298
|
+
{
|
|
299
|
+
using var sha256 = System.Security.Cryptography.SHA256.Create();
|
|
300
|
+
var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"role-{ApplicationId}-{roleType}"));
|
|
301
|
+
return new Guid(hash.Take(16).ToArray());
|
|
302
|
+
}
|
|
303
|
+
// roleType values: "admin", "manager", "contributor", "viewer"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Template
|
|
307
|
+
|
|
308
|
+
```csharp
|
|
309
|
+
using SmartStack.Domain.Platform.Administration.Roles;
|
|
310
|
+
|
|
311
|
+
namespace {BaseNamespace}.Infrastructure.Persistence.Seeding.Data;
|
|
312
|
+
|
|
313
|
+
/// <summary>
|
|
314
|
+
/// Application-scoped role seed data for {AppLabel}.
|
|
315
|
+
/// Defines the 4 standard application roles: Admin, Manager, Contributor, Viewer.
|
|
316
|
+
/// Consumed by IClientSeedDataProvider at application startup.
|
|
317
|
+
/// </summary>
|
|
318
|
+
public static class ApplicationRolesSeedData
|
|
319
|
+
{
|
|
320
|
+
// Application ID from NavigationApplicationSeedData
|
|
321
|
+
private static readonly Guid ApplicationId = {ApplicationGuid};
|
|
322
|
+
|
|
323
|
+
public static readonly Guid AdminRoleId = GenerateRoleGuid("admin");
|
|
324
|
+
public static readonly Guid ManagerRoleId = GenerateRoleGuid("manager");
|
|
325
|
+
public static readonly Guid ContributorRoleId = GenerateRoleGuid("contributor");
|
|
326
|
+
public static readonly Guid ViewerRoleId = GenerateRoleGuid("viewer");
|
|
327
|
+
|
|
328
|
+
public static IEnumerable<ApplicationRoleSeedEntry> GetRoleEntries()
|
|
329
|
+
{
|
|
330
|
+
yield return new ApplicationRoleSeedEntry
|
|
331
|
+
{
|
|
332
|
+
Id = AdminRoleId,
|
|
333
|
+
Code = "admin",
|
|
334
|
+
Name = "{AppLabel} Admin",
|
|
335
|
+
Description = "Full administrative access to {AppLabel}",
|
|
336
|
+
ApplicationId = ApplicationId,
|
|
337
|
+
IsSystem = false,
|
|
338
|
+
IsActive = true,
|
|
339
|
+
DisplayOrder = 1
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
yield return new ApplicationRoleSeedEntry
|
|
343
|
+
{
|
|
344
|
+
Id = ManagerRoleId,
|
|
345
|
+
Code = "manager",
|
|
346
|
+
Name = "{AppLabel} Manager",
|
|
347
|
+
Description = "Management access to {AppLabel} (Create, Read, Update)",
|
|
348
|
+
ApplicationId = ApplicationId,
|
|
349
|
+
IsSystem = false,
|
|
350
|
+
IsActive = true,
|
|
351
|
+
DisplayOrder = 2
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
yield return new ApplicationRoleSeedEntry
|
|
355
|
+
{
|
|
356
|
+
Id = ContributorRoleId,
|
|
357
|
+
Code = "contributor",
|
|
358
|
+
Name = "{AppLabel} Contributor",
|
|
359
|
+
Description = "Contributor access to {AppLabel} (Create, Read)",
|
|
360
|
+
ApplicationId = ApplicationId,
|
|
361
|
+
IsSystem = false,
|
|
362
|
+
IsActive = true,
|
|
363
|
+
DisplayOrder = 3
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
yield return new ApplicationRoleSeedEntry
|
|
367
|
+
{
|
|
368
|
+
Id = ViewerRoleId,
|
|
369
|
+
Code = "viewer",
|
|
370
|
+
Name = "{AppLabel} Viewer",
|
|
371
|
+
Description = "Read-only access to {AppLabel}",
|
|
372
|
+
ApplicationId = ApplicationId,
|
|
373
|
+
IsSystem = false,
|
|
374
|
+
IsActive = true,
|
|
375
|
+
DisplayOrder = 4
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private static Guid GenerateRoleGuid(string roleType)
|
|
380
|
+
{
|
|
381
|
+
using var sha256 = System.Security.Cryptography.SHA256.Create();
|
|
382
|
+
var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes($"role-{ApplicationId}-{roleType}"));
|
|
383
|
+
return new Guid(hash.Take(16).ToArray());
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
public class ApplicationRoleSeedEntry
|
|
388
|
+
{
|
|
389
|
+
public Guid Id { get; init; }
|
|
390
|
+
public string Code { get; init; } = null!;
|
|
391
|
+
public string Name { get; init; } = null!;
|
|
392
|
+
public string Description { get; init; } = null!;
|
|
393
|
+
public Guid ApplicationId { get; init; }
|
|
394
|
+
public bool IsSystem { get; init; }
|
|
395
|
+
public bool IsActive { get; init; }
|
|
396
|
+
public int DisplayOrder { get; init; }
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Replace placeholders** with values from PRD and navigation metadata.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## 5. {Module}RolesSeedData.cs (Per Module)
|
|
283
405
|
|
|
284
406
|
**File:** `Infrastructure/Persistence/Seeding/Data/{ModulePascal}/RolesSeedData.cs`
|
|
285
407
|
|
|
@@ -335,7 +457,7 @@ public class RolePermissionSeedEntry
|
|
|
335
457
|
|
|
336
458
|
---
|
|
337
459
|
|
|
338
|
-
##
|
|
460
|
+
## 6. IClientSeedDataProvider Implementation
|
|
339
461
|
|
|
340
462
|
**File:** `Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`
|
|
341
463
|
|
|
@@ -343,10 +465,11 @@ public class RolePermissionSeedEntry
|
|
|
343
465
|
|
|
344
466
|
| Rule | Description |
|
|
345
467
|
|------|-------------|
|
|
346
|
-
| Factory methods | `NavigationModule.Create(...)`, `Permission.CreateForModule(...)`, `RolePermission.Create(...)` — NEVER `new Entity()` |
|
|
468
|
+
| Factory methods | `NavigationModule.Create(...)`, `Role.Create(...)`, `Permission.CreateForModule(...)`, `RolePermission.Create(...)` — NEVER `new Entity()` |
|
|
347
469
|
| Idempotence | Each Seed method checks existence before inserting |
|
|
348
|
-
|
|
|
349
|
-
|
|
|
470
|
+
| Execution order | Navigation → Roles → Permissions → RolePermissions (roles MUST exist before mapping) |
|
|
471
|
+
| SaveChanges per group | Navigation -> save -> Roles -> save -> Permissions -> save -> RolePermissions -> save |
|
|
472
|
+
| FK resolution by Code | Parent entities (modules, roles) found by `Code`, not hardcoded GUID |
|
|
350
473
|
| DI registration | `services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>()` |
|
|
351
474
|
|
|
352
475
|
### Template
|
|
@@ -357,13 +480,14 @@ using SmartStack.Application.Common.Interfaces;
|
|
|
357
480
|
using SmartStack.Application.Common.Interfaces.Seeding;
|
|
358
481
|
using SmartStack.Domain.Navigation;
|
|
359
482
|
using SmartStack.Domain.Platform.Administration.Roles;
|
|
483
|
+
using {BaseNamespace}.Infrastructure.Persistence.Seeding.Data;
|
|
360
484
|
using {BaseNamespace}.Infrastructure.Persistence.Seeding.Data.{Module1Pascal};
|
|
361
485
|
// using {BaseNamespace}.Infrastructure.Persistence.Seeding.Data.{Module2Pascal}; // Add per module
|
|
362
486
|
|
|
363
487
|
namespace {BaseNamespace}.Infrastructure.Persistence.Seeding;
|
|
364
488
|
|
|
365
489
|
/// <summary>
|
|
366
|
-
/// Seeds {AppLabel} navigation, permissions, and role-permission data
|
|
490
|
+
/// Seeds {AppLabel} navigation, roles, permissions, and role-permission data
|
|
367
491
|
/// into the SmartStack Core schema at application startup.
|
|
368
492
|
/// </summary>
|
|
369
493
|
public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
|
|
@@ -420,6 +544,28 @@ public class {AppPascalName}SeedDataProvider : IClientSeedDataProvider
|
|
|
420
544
|
await ((DbContext)context).SaveChangesAsync(ct);
|
|
421
545
|
}
|
|
422
546
|
|
|
547
|
+
public async Task SeedRolesAsync(ICoreDbContext context, CancellationToken ct)
|
|
548
|
+
{
|
|
549
|
+
// Check idempotence
|
|
550
|
+
var applicationId = ApplicationRolesSeedData.ApplicationId;
|
|
551
|
+
var exists = await context.Roles
|
|
552
|
+
.AnyAsync(r => r.ApplicationId == applicationId, ct);
|
|
553
|
+
if (exists) return;
|
|
554
|
+
|
|
555
|
+
// Create application-scoped roles (Admin, Manager, Contributor, Viewer)
|
|
556
|
+
foreach (var entry in ApplicationRolesSeedData.GetRoleEntries())
|
|
557
|
+
{
|
|
558
|
+
var role = Role.Create(
|
|
559
|
+
entry.Code,
|
|
560
|
+
entry.Name,
|
|
561
|
+
entry.Description,
|
|
562
|
+
entry.ApplicationId,
|
|
563
|
+
entry.IsSystem);
|
|
564
|
+
context.Roles.Add(role);
|
|
565
|
+
}
|
|
566
|
+
await ((DbContext)context).SaveChangesAsync(ct);
|
|
567
|
+
}
|
|
568
|
+
|
|
423
569
|
public async Task SeedPermissionsAsync(ICoreDbContext context, CancellationToken ct)
|
|
424
570
|
{
|
|
425
571
|
var exists = await context.Permissions
|
|
@@ -495,44 +641,110 @@ services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>();
|
|
|
495
641
|
|
|
496
642
|
---
|
|
497
643
|
|
|
498
|
-
##
|
|
644
|
+
## 7. Multi-Module Handling
|
|
499
645
|
|
|
500
646
|
When processing multiple modules in the same ralph-loop run:
|
|
501
647
|
|
|
502
648
|
### Module 1 (first): Creates everything from scratch
|
|
503
649
|
|
|
504
|
-
1. `
|
|
505
|
-
2. `{Module1}
|
|
506
|
-
3. `{Module1}
|
|
507
|
-
4. `{
|
|
508
|
-
5.
|
|
650
|
+
1. `ApplicationRolesSeedData.cs` (application-level, once per app)
|
|
651
|
+
2. `{Module1}NavigationSeedData.cs`
|
|
652
|
+
3. `{Module1}PermissionsSeedData.cs`
|
|
653
|
+
4. `{Module1}RolesSeedData.cs`
|
|
654
|
+
5. `{AppPascalName}SeedDataProvider.cs` (new, with 4 methods)
|
|
655
|
+
6. DI registration (new)
|
|
509
656
|
|
|
510
657
|
### Module 2+ (subsequent): Append to existing provider
|
|
511
658
|
|
|
512
|
-
1. `
|
|
513
|
-
2. `{Module2}
|
|
514
|
-
3. `{Module2}
|
|
515
|
-
4. `{
|
|
516
|
-
5.
|
|
659
|
+
1. `ApplicationRolesSeedData.cs` (already exists — skip)
|
|
660
|
+
2. `{Module2}NavigationSeedData.cs` (new file)
|
|
661
|
+
3. `{Module2}PermissionsSeedData.cs` (new file)
|
|
662
|
+
4. `{Module2}RolesSeedData.cs` (new file)
|
|
663
|
+
5. `{AppPascalName}SeedDataProvider.cs` (**modify** — add using, add entries in Navigation/Permissions/RolePermissions methods)
|
|
664
|
+
6. DI registration (already exists — skip)
|
|
517
665
|
|
|
518
|
-
**Detection:** Check if `{AppPascalName}SeedDataProvider.cs` exists. If yes, READ it and ADD the new module's entries to
|
|
666
|
+
**Detection:** Check if `{AppPascalName}SeedDataProvider.cs` exists. If yes, READ it and ADD the new module's entries to the appropriate methods (Navigation, Permissions, RolePermissions). Do NOT modify SeedRolesAsync().
|
|
519
667
|
|
|
520
668
|
---
|
|
521
669
|
|
|
522
|
-
##
|
|
670
|
+
## 8. Verification Checklist (BLOCKING)
|
|
523
671
|
|
|
524
672
|
Before marking the task as completed, verify ALL:
|
|
525
673
|
|
|
526
674
|
- [ ] Deterministic GUIDs (NEVER `Guid.NewGuid()`) — SHA256 of path
|
|
527
675
|
- [ ] 4 languages for each navigation entity (fr, en, it, de)
|
|
676
|
+
- [ ] `ApplicationRolesSeedData.cs` created (once per application)
|
|
677
|
+
- [ ] 4 application roles defined: Admin, Manager, Contributor, Viewer
|
|
678
|
+
- [ ] Each role has a valid `Code` value ("admin", "manager", "contributor", "viewer")
|
|
528
679
|
- [ ] `Permissions.cs` constants match seed data paths
|
|
529
680
|
- [ ] MCP `generate_permissions` called (or fallback used)
|
|
530
681
|
- [ ] Role-permission mappings assigned (Admin, Manager, Contributor, Viewer)
|
|
531
|
-
- [ ] `IClientSeedDataProvider` generated with
|
|
682
|
+
- [ ] `IClientSeedDataProvider` generated with 4 methods (Navigation, Roles, Permissions, RolePermissions)
|
|
683
|
+
- [ ] Execution order: Navigation → Roles → Permissions → RolePermissions
|
|
532
684
|
- [ ] Each Seed method is idempotent (checks existence before inserting)
|
|
533
685
|
- [ ] Factory methods used throughout (NEVER `new Entity()`)
|
|
534
|
-
- [ ] `SaveChangesAsync` called per group (Navigation → Permissions → RolePermissions)
|
|
686
|
+
- [ ] `SaveChangesAsync` called per group (Navigation → Roles → Permissions → RolePermissions)
|
|
535
687
|
- [ ] DI registration added: `services.AddScoped<IClientSeedDataProvider, ...>()`
|
|
536
688
|
- [ ] `dotnet build` passes after generation
|
|
537
689
|
|
|
538
690
|
**If ANY check fails, the task status = 'failed'.**
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## 9. Business Seed Data (DevDataSeeder) — TenantId Rules
|
|
695
|
+
|
|
696
|
+
> **Applies to:** Seed data for business entities (reference types, categories, statuses).
|
|
697
|
+
> **NOT the same as** core seed data above (navigation, permissions, roles).
|
|
698
|
+
|
|
699
|
+
### Rules
|
|
700
|
+
|
|
701
|
+
| Rule | Description |
|
|
702
|
+
|------|-------------|
|
|
703
|
+
| TenantId MANDATORY | ALL business seed entities MUST set `TenantId` |
|
|
704
|
+
| Deterministic TenantId | Use `SeedConstants.DefaultTenantId` (NEVER inline GUID) |
|
|
705
|
+
| DevDataSeeder pattern | Implement `IDevDataSeeder` with `SeedAsync()` method |
|
|
706
|
+
| Idempotency | Each seeder MUST check `AnyAsync()` before inserting |
|
|
707
|
+
| Order | DevDataSeeder `Order >= 200` (after core seed data at Order 100) |
|
|
708
|
+
|
|
709
|
+
### Template (Reference Types)
|
|
710
|
+
|
|
711
|
+
```csharp
|
|
712
|
+
public class {Module}DevDataSeeder : IDevDataSeeder
|
|
713
|
+
{
|
|
714
|
+
public int Order => 200; // After core seed data (Order 100)
|
|
715
|
+
|
|
716
|
+
public async Task SeedAsync(ExtensionsDbContext context, CancellationToken ct)
|
|
717
|
+
{
|
|
718
|
+
if (await context.Set<{EntityType}>().AnyAsync(ct)) return;
|
|
719
|
+
|
|
720
|
+
var items = new[]
|
|
721
|
+
{
|
|
722
|
+
new {EntityType}
|
|
723
|
+
{
|
|
724
|
+
Id = GenerateDeterministicGuid("{entity-type}-{code}"),
|
|
725
|
+
Code = "{code}",
|
|
726
|
+
Name = "{name}",
|
|
727
|
+
TenantId = SeedConstants.DefaultTenantId, // MANDATORY
|
|
728
|
+
IsActive = true
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
context.Set<{EntityType}>().AddRange(items);
|
|
733
|
+
await context.SaveChangesAsync(ct);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
private static Guid GenerateDeterministicGuid(string seed)
|
|
737
|
+
{
|
|
738
|
+
using var sha256 = System.Security.Cryptography.SHA256.Create();
|
|
739
|
+
var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(seed));
|
|
740
|
+
return new Guid(hash.Take(16).ToArray());
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### FORBIDDEN
|
|
746
|
+
|
|
747
|
+
- Seeding business entities WITHOUT `TenantId`
|
|
748
|
+
- Using `Guid.NewGuid()` for TenantId
|
|
749
|
+
- Omitting idempotency check (`AnyAsync`)
|
|
750
|
+
- Hardcoding TenantId inline (use `SeedConstants.DefaultTenantId`)
|
|
@@ -48,7 +48,70 @@ mcp__context7__resolve-library-id: libraryName: "test" → {mcp_context7} = tru
|
|
|
48
48
|
|
|
49
49
|
If ANY fails: show error, suggest `smartstack check-mcp`, STOP.
|
|
50
50
|
|
|
51
|
-
## 3.
|
|
51
|
+
## 3. Human-in-the-Loop Checkpoint (RECOMMENDED)
|
|
52
|
+
|
|
53
|
+
> **Best Practice:** Commencer en mode supervisé avant le mode autonome complet.
|
|
54
|
+
|
|
55
|
+
### Why HITL First?
|
|
56
|
+
|
|
57
|
+
Ralph-loop est puissant mais peut diverger si :
|
|
58
|
+
- Les requirements sont ambigus
|
|
59
|
+
- Les tests sont lents (>30s)
|
|
60
|
+
- Le code généré ne compile pas immédiatement
|
|
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 :
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
if (!dirExists('.ralph/logs') && !resume_mode) {
|
|
79
|
+
AskUserQuestion({
|
|
80
|
+
questions: [{
|
|
81
|
+
question: "Premier usage de ralph-loop détecté. Démarrer en mode supervisé ou autonome ?",
|
|
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
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Feedback Speed Warning
|
|
102
|
+
|
|
103
|
+
⚠️ **IMPORTANT :** Si vos tests prennent >30 secondes, ralph-loop peut devenir inefficace.
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Temps test recommandés :
|
|
107
|
+
- Unitaires : <5s
|
|
108
|
+
- Intégration : <15s
|
|
109
|
+
- E2E : <30s (à exécuter en dehors du loop)
|
|
110
|
+
```
|
|
111
|
+
|
|
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
|
+
## 4. Resume Mode
|
|
52
115
|
|
|
53
116
|
If `{resume_mode} = true`:
|
|
54
117
|
|
|
@@ -40,12 +40,20 @@ if [ -d "$TEST_PROJECT" ]; then
|
|
|
40
40
|
dotnet test "$TEST_PROJECT" --no-build --verbosity normal
|
|
41
41
|
if [ $? -ne 0 ]; then
|
|
42
42
|
echo "TEST REGRESSION — creating fix task"
|
|
43
|
-
# Inject fix task into prd.json:
|
|
44
43
|
prd.tasks.push({ id: maxId+1, description: "Fix test regression — all tests must pass",
|
|
45
44
|
status: "pending", category: "validation", dependencies: [],
|
|
46
45
|
acceptance_criteria: "dotnet test exits 0, all tests pass" });
|
|
47
46
|
writeJSON('.ralph/prd.json', prd);
|
|
48
47
|
fi
|
|
48
|
+
else
|
|
49
|
+
# CRITICAL: Test project MUST exist if test tasks are marked completed
|
|
50
|
+
const testTasksDone = prd.tasks.filter(t => t.category === 'test' && t.status === 'completed');
|
|
51
|
+
if (testTasksDone.length > 0) {
|
|
52
|
+
echo "ARTIFACT MISSING — test tasks marked completed but no test project exists"
|
|
53
|
+
// Reset test tasks to pending and inject guardrail
|
|
54
|
+
testTasksDone.forEach(t => { t.status = 'pending'; t.error = null; t.completed_at = null; });
|
|
55
|
+
writeJSON('.ralph/prd.json', prd);
|
|
56
|
+
fi
|
|
49
57
|
fi
|
|
50
58
|
```
|
|
51
59
|
|
|
@@ -99,11 +107,28 @@ if (fileExists(queuePath)) {
|
|
|
99
107
|
const queue = readJSON(queuePath);
|
|
100
108
|
const currentModule = queue.modules[queue.currentIndex];
|
|
101
109
|
|
|
102
|
-
// MODULE COMPLETENESS CHECK: verify all expected layers
|
|
110
|
+
// MODULE COMPLETENESS CHECK: verify all expected layers AND their artifacts
|
|
103
111
|
const completedCats = new Set(prd.tasks.filter(t => t.status === 'completed').map(t => t.category));
|
|
104
112
|
const expected = ['domain', 'infrastructure', 'application', 'api', 'frontend', 'test'];
|
|
105
113
|
const missing = expected.filter(c => !completedCats.has(c));
|
|
106
114
|
|
|
115
|
+
// ARTIFACT EXISTENCE CHECK: categories with completed tasks must have real files
|
|
116
|
+
const artifactChecks = {
|
|
117
|
+
'infrastructure': () => fs.existsSync('src/*/Persistence/Migrations') && fs.readdirSync('src/*/Persistence/Migrations').length > 0,
|
|
118
|
+
'test': () => fs.existsSync(`tests/${projectName}.Tests.Unit`) && glob.sync('tests/**/*Tests.cs').length > 0,
|
|
119
|
+
'api': () => glob.sync('src/*/Controllers/**/*Controller.cs').length > 0,
|
|
120
|
+
'frontend': () => glob.sync('**/src/pages/**/*.tsx').length > 0
|
|
121
|
+
};
|
|
122
|
+
for (const [cat, check] of Object.entries(artifactChecks)) {
|
|
123
|
+
if (completedCats.has(cat) && !check()) {
|
|
124
|
+
// Tasks marked completed but artifacts missing — reset to pending
|
|
125
|
+
prd.tasks.filter(t => t.category === cat && t.status === 'completed')
|
|
126
|
+
.forEach(t => { t.status = 'pending'; t.error = 'Artifacts missing — re-execute'; t.completed_at = null; });
|
|
127
|
+
missing.push(cat);
|
|
128
|
+
console.log(`ARTIFACT RESET: ${cat} tasks reset to pending (no files found)`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
107
132
|
if (missing.length > 0) {
|
|
108
133
|
// Inject guardrail tasks for missing layers
|
|
109
134
|
let maxId = Math.max(...prd.tasks.map(t => t.id));
|