@atlashub/smartstack-cli 3.8.0 → 3.10.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 (130) 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 +211 -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/application-roles-template.md +227 -0
  28. package/templates/skills/application/references/backend-controller-hierarchy.md +58 -0
  29. package/templates/skills/application/references/backend-entity-seeding.md +72 -0
  30. package/templates/skills/application/references/backend-verification.md +88 -0
  31. package/templates/skills/application/references/frontend-verification.md +111 -0
  32. package/templates/skills/application/references/nav-fallback-procedure.md +200 -0
  33. package/templates/skills/application/references/provider-template.md +158 -0
  34. package/templates/skills/application/references/test-frontend.md +73 -0
  35. package/templates/skills/application/references/test-prerequisites.md +72 -0
  36. package/templates/skills/application/steps/step-01-navigation.md +7 -198
  37. package/templates/skills/application/steps/step-03-roles.md +45 -7
  38. package/templates/skills/application/steps/step-03b-provider.md +15 -132
  39. package/templates/skills/application/steps/step-04-backend.md +20 -350
  40. package/templates/skills/application/steps/step-05-frontend.md +12 -101
  41. package/templates/skills/application/steps/step-07-tests.md +12 -132
  42. package/templates/skills/business-analyse/SKILL.md +67 -6
  43. package/templates/skills/business-analyse/html/ba-interactive.html +176 -14
  44. package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +1 -0
  45. package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +16 -4
  46. package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +7 -2
  47. package/templates/skills/business-analyse/html/src/scripts/09-export.js +103 -0
  48. package/templates/skills/business-analyse/html/src/scripts/10-comments.js +12 -6
  49. package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +24 -2
  50. package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +12 -0
  51. package/templates/skills/business-analyse/html/src/template.html +1 -0
  52. package/templates/skills/business-analyse/references/agent-pooling-best-practices.md +477 -0
  53. package/templates/skills/business-analyse/references/cache-warming-strategy.md +578 -0
  54. package/templates/skills/business-analyse/references/cadrage-structure-cards.md +78 -0
  55. package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +97 -0
  56. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +92 -0
  57. package/templates/skills/business-analyse/references/deploy-data-build.md +121 -0
  58. package/templates/skills/business-analyse/references/deploy-modes.md +49 -0
  59. package/templates/skills/business-analyse/references/handoff-file-templates.md +119 -0
  60. package/templates/skills/business-analyse/references/handoff-mappings.md +81 -0
  61. package/templates/skills/business-analyse/references/html-data-mapping.md +10 -2
  62. package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -0
  63. package/templates/skills/business-analyse/references/review-data-mapping.md +363 -0
  64. package/templates/skills/business-analyse/references/robustness-checks.md +538 -0
  65. package/templates/skills/business-analyse/references/spec-auto-inference.md +57 -0
  66. package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -0
  67. package/templates/skills/business-analyse/references/ui-resource-cards.md +110 -0
  68. package/templates/skills/business-analyse/references/validate-incremental-html.md +55 -0
  69. package/templates/skills/business-analyse/schemas/sections/specification-schema.json +33 -1
  70. package/templates/skills/business-analyse/steps/step-00-init.md +186 -53
  71. package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -194
  72. package/templates/skills/business-analyse/steps/step-03a-data.md +42 -49
  73. package/templates/skills/business-analyse/steps/step-03b-ui.md +12 -178
  74. package/templates/skills/business-analyse/steps/step-03c-compile.md +71 -2
  75. package/templates/skills/business-analyse/steps/step-03d-validate.md +277 -48
  76. package/templates/skills/business-analyse/steps/step-04-consolidation.md +175 -104
  77. package/templates/skills/business-analyse/steps/step-05a-handoff.md +66 -438
  78. package/templates/skills/business-analyse/steps/step-05b-deploy.md +35 -184
  79. package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +526 -0
  80. package/templates/skills/business-analyse/steps/step-06-review.md +277 -0
  81. package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -0
  82. package/templates/skills/cc-agent/steps/step-02-generate.md +5 -78
  83. package/templates/skills/check-version/SKILL.md +7 -0
  84. package/templates/skills/controller/references/controller-code-templates.md +159 -0
  85. package/templates/skills/controller/references/permission-sync-templates.md +152 -0
  86. package/templates/skills/controller/steps/step-03-generate.md +166 -158
  87. package/templates/skills/controller/steps/step-04-perms.md +5 -144
  88. package/templates/skills/controller/templates.md +11 -2
  89. package/templates/skills/debug/SKILL.md +7 -0
  90. package/templates/skills/explore/SKILL.md +6 -0
  91. package/templates/skills/feature-full/SKILL.md +39 -142
  92. package/templates/skills/feature-full/steps/step-01-implementation.md +120 -0
  93. package/templates/skills/gitflow/references/init-config-template.md +135 -0
  94. package/templates/skills/gitflow/references/init-name-normalization.md +103 -0
  95. package/templates/skills/gitflow/references/plan-template.md +69 -0
  96. package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -0
  97. package/templates/skills/gitflow/references/start-local-config.md +110 -0
  98. package/templates/skills/gitflow/steps/step-init.md +18 -289
  99. package/templates/skills/gitflow/steps/step-plan.md +6 -63
  100. package/templates/skills/gitflow/steps/step-start.md +16 -126
  101. package/templates/skills/mcp/SKILL.md +9 -213
  102. package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -0
  103. package/templates/skills/mcp/steps/step-02-tools.md +73 -0
  104. package/templates/skills/notification/SKILL.md +7 -0
  105. package/templates/skills/quick-search/SKILL.md +5 -0
  106. package/templates/skills/ralph-loop/SKILL.md +99 -381
  107. package/templates/skills/ralph-loop/references/category-rules.md +259 -0
  108. package/templates/skills/ralph-loop/references/compact-loop.md +182 -0
  109. package/templates/skills/ralph-loop/references/core-seed-data.md +173 -21
  110. package/templates/skills/ralph-loop/references/task-transform-legacy.md +259 -0
  111. package/templates/skills/ralph-loop/references/team-orchestration.md +189 -0
  112. package/templates/skills/ralph-loop/steps/step-00-init.md +111 -383
  113. package/templates/skills/ralph-loop/steps/step-01-task.md +79 -896
  114. package/templates/skills/ralph-loop/steps/step-02-execute.md +68 -680
  115. package/templates/skills/ralph-loop/steps/step-03-commit.md +47 -277
  116. package/templates/skills/ralph-loop/steps/step-04-check.md +124 -607
  117. package/templates/skills/ralph-loop/steps/step-05-report.md +68 -367
  118. package/templates/skills/refactor/SKILL.md +12 -176
  119. package/templates/skills/refactor/steps/step-01-discover.md +60 -0
  120. package/templates/skills/refactor/steps/step-02-execute.md +67 -0
  121. package/templates/skills/review-code/SKILL.md +19 -257
  122. package/templates/skills/review-code/steps/step-01-smartstack.md +96 -0
  123. package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -0
  124. package/templates/skills/review-code/steps/step-03-react.md +44 -0
  125. package/templates/skills/ui-components/SKILL.md +7 -0
  126. package/templates/skills/utils/SKILL.md +6 -0
  127. package/templates/skills/validate/SKILL.md +6 -0
  128. package/templates/skills/validate-feature/SKILL.md +8 -0
  129. package/templates/skills/workflow/SKILL.md +40 -118
  130. package/templates/skills/workflow/steps/step-01-implementation.md +84 -0
@@ -279,7 +279,129 @@ If MCP `generate_permissions` fails, use the template above directly with values
279
279
 
280
280
  ---
281
281
 
282
- ## 4. RolesSeedData.cs
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
- ## 5. IClientSeedDataProvider Implementation
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
- | SaveChanges per group | Navigation -> save -> Permissions -> save -> RolePermissions -> save |
349
- | FK resolution by Code | Parent entities found by `Code`, not hardcoded GUID |
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,43 +641,49 @@ services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>();
495
641
 
496
642
  ---
497
643
 
498
- ## 6. Multi-Module Handling
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. `{Module1}NavigationSeedData.cs`
505
- 2. `{Module1}PermissionsSeedData.cs`
506
- 3. `{Module1}RolesSeedData.cs`
507
- 4. `{AppPascalName}SeedDataProvider.cs` (new)
508
- 5. DI registration (new)
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. `{Module2}NavigationSeedData.cs` (new file)
513
- 2. `{Module2}PermissionsSeedData.cs` (new file)
514
- 3. `{Module2}RolesSeedData.cs` (new file)
515
- 4. `{AppPascalName}SeedDataProvider.cs` (**modify** — add using, add entries in each Seed method)
516
- 5. DI registration (already exists skip)
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 each of the 3 methods.
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
- ## 7. Verification Checklist (BLOCKING)
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 3 methods
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
 
@@ -0,0 +1,259 @@
1
+ # Task Transform: Legacy PrdJson → Ralph v2 (DEPRECATED)
2
+
3
+ > Reference for step-01-task.md — fallback transformation for old FORMAT A PRDs (pre-v3.9.0).
4
+ > New PRDs generated by `ss derive-prd` use unified v3 format (`$version: "3.0.0"`)
5
+ > with pre-computed tasks. The v3 fast path in sections 0 and 1 bypasses this entirely.
6
+ > This function will be removed in a future release.
7
+
8
+ ## FORMAT A Input Structure
9
+
10
+ ```
11
+ { version, source, project, requirements, businessRules, architecture, implementation, seedData }
12
+ ```
13
+
14
+ ## transformPrdJsonToRalphV2(prdJson, moduleCode)
15
+
16
+ ```javascript
17
+ function transformPrdJsonToRalphV2(prdJson, moduleCode) {
18
+ const tasks = [];
19
+ let taskId = 1;
20
+
21
+ const layerOrder = [
22
+ { key: "domain", category: "domain" },
23
+ { key: "infrastructure", category: "infrastructure" },
24
+ { key: "application", category: "application" },
25
+ { key: "api", category: "api" },
26
+ { key: "frontend", category: "frontend" },
27
+ { key: "seedData", category: "infrastructure" },
28
+ { key: "tests", category: "test" }
29
+ ];
30
+
31
+ const filesToCreate = prdJson.implementation?.filesToCreate || prdJson.filesToCreate || {};
32
+ const lastIdByCategory = {};
33
+ const frontendFiles = [];
34
+ const i18nFiles = [];
35
+
36
+ // --- Generate tasks from filesToCreate ---
37
+ for (const layer of layerOrder) {
38
+ const files = filesToCreate[layer.key] || [];
39
+ if (layer.category === "frontend") { frontendFiles.push(...files); continue; }
40
+
41
+ for (const fileSpec of files) {
42
+ if (fileSpec.path?.includes('i18n/') || fileSpec.path?.includes('/locales/')) {
43
+ i18nFiles.push(fileSpec); continue;
44
+ }
45
+
46
+ const depIds = [];
47
+ if (lastIdByCategory[layer.category]) depIds.push(lastIdByCategory[layer.category]);
48
+ if (layer.category === "api" && lastIdByCategory["application"]) depIds.push(lastIdByCategory["application"]);
49
+ if (layer.category === "test" && lastIdByCategory["api"]) depIds.push(lastIdByCategory["api"]);
50
+
51
+ const allFRs = prdJson.requirements?.functionalRequirements || prdJson.functionalRequirements || [];
52
+ const linkedFRs = (fileSpec.linkedFRs || []).map(frId => allFRs.find(f => f.id === frId)?.statement || frId);
53
+ let criteria = linkedFRs.length > 0 ? `Implements: ${linkedFRs.join("; ")}` : `File ${fileSpec.path} created and compiles`;
54
+
55
+ const permMatrix = prdJson.architecture?.permissionMatrix || prdJson.permissionMatrix;
56
+ if (layer.category === "api" && permMatrix?.permissions?.length > 0) {
57
+ criteria += "; MANDATORY: [RequirePermission] on every endpoint (NOT [Authorize])";
58
+ }
59
+
60
+ tasks.push({
61
+ id: taskId, description: fileSpec.description ? `[${layer.category}] ${fileSpec.description} → ${fileSpec.path}` : `[${layer.category}] Create ${fileSpec.type}: ${fileSpec.path}`,
62
+ status: "pending", category: layer.category, dependencies: [...new Set(depIds)],
63
+ acceptance_criteria: criteria, started_at: null, completed_at: null, iteration: null,
64
+ commit_hash: null, files_changed: { created: [fileSpec.path], modified: [] },
65
+ validation: null, error: null, module: moduleCode
66
+ });
67
+ lastIdByCategory[layer.category] = taskId;
68
+ taskId++;
69
+ }
70
+ }
71
+
72
+ // --- 1b. Consolidated frontend task ---
73
+ if (frontendFiles.length > 0) {
74
+ const apiDepId = lastIdByCategory["api"] || lastIdByCategory["application"];
75
+ const ctx = (prdJson.project?.context || 'Business').replace(/^./, c => c.toUpperCase());
76
+ const app = prdJson.project?.application || moduleCode;
77
+ tasks.push({
78
+ id: taskId,
79
+ description: `[frontend] Generate COMPLETE frontend for module ${moduleCode} via MCP (${frontendFiles.length} files)`,
80
+ status: "pending", category: "frontend", dependencies: apiDepId ? [apiDepId] : [],
81
+ acceptance_criteria: `Pages in src/pages/${ctx}/${app}/${moduleCode}/; MCP scaffold_api_client + scaffold_routes; SmartTable for lists; CSS variables ONLY; 4-language i18n; npm run typecheck passes`,
82
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
83
+ files_changed: { created: frontendFiles.map(f => f.path), modified: [] },
84
+ validation: null, error: null, module: moduleCode,
85
+ _frontendMeta: { wireframes: [...new Set(frontendFiles.flatMap(f => f.linkedWireframes || []))], filesToCreate: frontendFiles }
86
+ });
87
+ lastIdByCategory["frontend"] = taskId; taskId++;
88
+ }
89
+
90
+ // --- 1c. Consolidated i18n task ---
91
+ if (i18nFiles.length > 0) {
92
+ tasks.push({
93
+ id: taskId, description: `[i18n] Generate i18n for module ${moduleCode} (4 languages)`,
94
+ status: "pending", category: "i18n", dependencies: lastIdByCategory["frontend"] ? [lastIdByCategory["frontend"]] : [],
95
+ acceptance_criteria: "4 JSON files (fr, en, it, de) with identical keys",
96
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
97
+ files_changed: { created: i18nFiles.map(f => f.path), modified: [] },
98
+ validation: null, error: null, module: moduleCode
99
+ });
100
+ lastIdByCategory["i18n"] = taskId; taskId++;
101
+ }
102
+
103
+ // --- GUARDRAILS: Inject missing layers ---
104
+ const entities = prdJson.architecture?.entities || prdJson.entities || [];
105
+ const apiEndpoints = prdJson.architecture?.apiEndpoints || prdJson.apiEndpoints || [];
106
+ const sections = prdJson.architecture?.sections || prdJson.sections || [];
107
+ const wireframes = prdJson.specification?.uiWireframes || prdJson.uiWireframes || [];
108
+ const dashboards = prdJson.specification?.dashboards || prdJson.dashboards || [];
109
+
110
+ // 1d. INJECT missing infrastructure
111
+ if (!tasks.some(t => t.category === 'infrastructure') && entities.length > 0) {
112
+ tasks.push({ id: taskId, description: `[infrastructure] Create EF Core configs and seed data for ${moduleCode} (${entities.length} entities)`,
113
+ status: "pending", category: "infrastructure", dependencies: lastIdByCategory["domain"] ? [lastIdByCategory["domain"]] : [],
114
+ acceptance_criteria: `EF configs for ${entities.length} entities; DbSet in ExtensionsDbContext; DI registration`,
115
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
116
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode });
117
+ lastIdByCategory["infrastructure"] = taskId; taskId++;
118
+ }
119
+
120
+ // 1e. INJECT missing API
121
+ if (!tasks.some(t => t.category === 'api') && apiEndpoints.length > 0) {
122
+ const permMatrix = prdJson.architecture?.permissionMatrix || prdJson.permissionMatrix;
123
+ tasks.push({ id: taskId, description: `[api] Create controllers for ${moduleCode} (${apiEndpoints.length} endpoints)`,
124
+ status: "pending", category: "api", dependencies: [lastIdByCategory["application"] || lastIdByCategory["infrastructure"]].filter(Boolean),
125
+ acceptance_criteria: `${apiEndpoints.length} endpoints; ${permMatrix?.permissions?.length > 0 ? "[RequirePermission] on every endpoint" : "Authorization"}; Swagger`,
126
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
127
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode });
128
+ lastIdByCategory["api"] = taskId; taskId++;
129
+ }
130
+
131
+ // 1f. INJECT missing frontend
132
+ if (frontendFiles.length === 0 && (sections.length > 0 || wireframes.length > 0 || apiEndpoints.length > 0)) {
133
+ const ctx = (prdJson.project?.context || 'Business').replace(/^./, c => c.toUpperCase());
134
+ const app = prdJson.project?.application || moduleCode;
135
+ const derivedFiles = wireframes.map(wf => ({ path: `web/src/pages/${ctx}/${app}/${moduleCode}/${wf.screen || wf.id}.tsx`, type: 'Page' }));
136
+ if (apiEndpoints.length > 0) derivedFiles.push({ path: `web/src/services/api/${moduleCode}Api.ts`, type: 'ApiService' });
137
+
138
+ tasks.push({ id: taskId,
139
+ description: `[frontend] Generate COMPLETE frontend for ${moduleCode} via MCP (${derivedFiles.length} files)`,
140
+ status: "pending", category: "frontend", dependencies: [lastIdByCategory["api"] || lastIdByCategory["application"]].filter(Boolean),
141
+ acceptance_criteria: `Pages in src/pages/${ctx}/${app}/${moduleCode}/; SmartTable for lists; CSS variables ONLY; 4-language i18n; npm run typecheck passes`,
142
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
143
+ files_changed: { created: derivedFiles.map(f => f.path), modified: [] },
144
+ validation: null, error: null, module: moduleCode,
145
+ _frontendMeta: { wireframes: wireframes.map(w => w.screen || w.id), filesToCreate: derivedFiles, source: "guardrail-derived" }
146
+ });
147
+ lastIdByCategory["frontend"] = taskId; taskId++;
148
+
149
+ // Also inject i18n
150
+ if (i18nFiles.length === 0) {
151
+ tasks.push({ id: taskId, description: `[i18n] Generate i18n for ${moduleCode} (4 languages)`,
152
+ status: "pending", category: "i18n", dependencies: [lastIdByCategory["frontend"]],
153
+ acceptance_criteria: "4 JSON files (fr, en, it, de) with identical keys",
154
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
155
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode });
156
+ lastIdByCategory["i18n"] = taskId; taskId++;
157
+ }
158
+ }
159
+
160
+ // 1g. INJECT missing tests
161
+ if (!tasks.some(t => t.category === 'test') && entities.length > 0) {
162
+ const migDepId = lastIdByCategory["migration"] || lastIdByCategory["api"] || lastIdByCategory["application"];
163
+ tasks.push({ id: taskId, description: `[test] Create unit and integration tests for ${moduleCode}`,
164
+ status: "pending", category: "test", dependencies: migDepId ? [migDepId] : [],
165
+ acceptance_criteria: "Domain + service + controller tests; dotnet test passes; coverage >= 80%",
166
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
167
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode });
168
+ lastIdByCategory["test"] = taskId; taskId++;
169
+ }
170
+
171
+ // 1h. GUARDRAIL: Core seed data (navigation, permissions, roles)
172
+ const coreSeedData = prdJson.seedData?.core || prdJson.seedDataCore || prdJson.specification?.seedDataCore;
173
+ const hasSeedDataInTasks = tasks.some(t => t.description?.includes("SeedData") || t.description?.includes("seed data"));
174
+ if (coreSeedData && !hasSeedDataInTasks) {
175
+ const infraDepId = lastIdByCategory["infrastructure"] || lastIdByCategory["domain"];
176
+ const navModules = coreSeedData.navigationModules || coreSeedData.navigation || [];
177
+ const permissions = coreSeedData.permissions || [];
178
+ const rolePerms = coreSeedData.rolePermissions || [];
179
+ tasks.push({
180
+ id: taskId, description: `[infrastructure] Create core seed data for ${moduleCode}: NavigationModuleSeedData, PermissionsSeedData, RolesSeedData`,
181
+ status: "pending", category: "infrastructure", dependencies: infraDepId ? [infraDepId] : [],
182
+ acceptance_criteria: `NavigationModuleSeedData (${navModules.length} nav); PermissionsSeedData (${permissions.length} perms); RolesSeedData (${rolePerms.length} roles); SeedConstants; dotnet build passes`,
183
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
184
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode,
185
+ _seedDataMeta: {
186
+ source: "guardrail-derived", coreSeedData,
187
+ navRoute: prdJson.project?.navRoute || `business.${prdJson.project?.application || 'app'}.${moduleCode}`,
188
+ contextCode: prdJson.project?.context || 'business',
189
+ appCode: prdJson.project?.application, appLabels: prdJson.project?.labels || null
190
+ }
191
+ });
192
+ lastIdByCategory["infrastructure"] = taskId; taskId++;
193
+ }
194
+
195
+ // 1i. GUARDRAIL: IClientSeedDataProvider
196
+ const hasProvider = tasks.some(t => t.description?.includes("IClientSeedDataProvider") || t.description?.includes("SeedDataProvider"));
197
+ if (!hasProvider && (filesToCreate["seedData"]?.length > 0 || coreSeedData)) {
198
+ tasks.push({
199
+ id: taskId, description: `[infrastructure] Create IClientSeedDataProvider for core seed data injection`,
200
+ status: "pending", category: "infrastructure",
201
+ dependencies: [lastIdByCategory["infrastructure"] || lastIdByCategory["domain"]].filter(Boolean),
202
+ acceptance_criteria: "SeedNavigationAsync + SeedPermissionsAsync + SeedRolePermissionsAsync; DI registered; idempotent",
203
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
204
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode,
205
+ _providerMeta: {
206
+ navRoute: prdJson.project?.navRoute || `business.${prdJson.project?.application || 'app'}.${moduleCode}`,
207
+ contextCode: prdJson.project?.context || 'business', appCode: prdJson.project?.application
208
+ }
209
+ });
210
+ lastIdByCategory["infrastructure"] = taskId; taskId++;
211
+ }
212
+
213
+ // 1j. GUARDRAIL: EF Core migration (CRITICAL — without this, no DB schema, no tests)
214
+ const hasEntities = entities.length > 0 || tasks.some(t => t.category === 'domain');
215
+ const hasMigration = tasks.some(t => t.description?.toLowerCase().includes('migration'));
216
+ if (hasEntities && !hasMigration) {
217
+ tasks.push({
218
+ id: taskId, description: `[infrastructure] Create EF Core migration for ${moduleCode}`,
219
+ status: "pending", category: "infrastructure",
220
+ dependencies: [lastIdByCategory["infrastructure"]].filter(Boolean),
221
+ acceptance_criteria: "MCP suggest_migration → name; dotnet ef migrations add; dotnet ef database update; dotnet build passes",
222
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
223
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode,
224
+ _migrationMeta: { entities: entities.map(e => e.name || e.code), moduleCode }
225
+ });
226
+ lastIdByCategory["migration"] = taskId; taskId++;
227
+ }
228
+
229
+ // Final validation task
230
+ tasks.push({
231
+ id: taskId, description: `[validation] Build, test, and MCP validate module ${moduleCode}`,
232
+ status: "pending", category: "validation", dependencies: Object.values(lastIdByCategory),
233
+ acceptance_criteria: "dotnet build succeeds; dotnet test passes; MCP validate_conventions clean",
234
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
235
+ files_changed: { created: [], modified: [] }, validation: null, error: null, module: moduleCode
236
+ });
237
+
238
+ return {
239
+ $version: "2.0.0", feature: `${prdJson.project?.module || moduleCode} (${prdJson.project?.application || "app"})`,
240
+ status: "in_progress", created: new Date().toISOString(), updated_at: new Date().toISOString(),
241
+ metadata: { cli_version: "unknown", branch: getCurrentBranch(), project_path: process.cwd(), mcp_servers: { smartstack: true, context7: true }, moduleCode },
242
+ config: { max_iterations: {max_iterations}, completion_promise: "{completion_promise}", current_iteration: 1 },
243
+ source: { type: "ba-handoff", handoff_path: null, feature_json_path: prdJson.source?.featureJson || null, module: moduleCode },
244
+ tasks, history: []
245
+ };
246
+ }
247
+ ```
248
+
249
+ ## Guardrails Summary
250
+
251
+ | ID | Guardrail | Condition | Injection |
252
+ |----|-----------|-----------|-----------|
253
+ | 1d | Missing infrastructure | entities exist, no infra tasks | EF Core configs + seed data |
254
+ | 1e | Missing API | apiEndpoints exist, no api tasks | Controllers |
255
+ | 1f | Missing frontend | sections/wireframes exist, no frontend tasks | Pages + API service |
256
+ | 1g | Missing tests | entities exist, no test tasks | Unit + integration tests |
257
+ | 1h | Core seed data | coreSeedData exists, no seed tasks | Navigation + Permissions + Roles |
258
+ | 1i | IClientSeedDataProvider | seedData files or coreSeedData, no provider task | Provider class |
259
+ | 1j | EF Core migration | entities exist, no migration task | Migration (CRITICAL) |