@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.
- package/dist/index.js +365 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/templates/agents/action.md +1 -0
- package/templates/agents/ba-writer.md +211 -0
- package/templates/agents/explore-codebase.md +1 -0
- package/templates/agents/explore-docs.md +1 -0
- package/templates/agents/fix-grammar.md +1 -0
- package/templates/agents/snipper.md +1 -0
- package/templates/skills/admin/SKILL.md +6 -0
- package/templates/skills/ai-prompt/SKILL.md +32 -136
- package/templates/skills/ai-prompt/steps/step-01-implementation.md +122 -0
- package/templates/skills/apex/SKILL.md +120 -0
- package/templates/skills/apex/_shared.md +86 -0
- package/templates/skills/apex/references/agent-teams-protocol.md +164 -0
- package/templates/skills/apex/references/smartstack-layers.md +173 -0
- package/templates/skills/apex/steps/step-00-init.md +156 -0
- package/templates/skills/apex/steps/step-01-analyze.md +169 -0
- package/templates/skills/apex/steps/step-02-plan.md +160 -0
- package/templates/skills/apex/steps/step-03-execute.md +166 -0
- package/templates/skills/apex/steps/step-04-validate.md +138 -0
- package/templates/skills/apex/steps/step-05-examine.md +124 -0
- package/templates/skills/apex/steps/step-06-resolve.md +105 -0
- package/templates/skills/apex/steps/step-07-tests.md +130 -0
- package/templates/skills/apex/steps/step-08-run-tests.md +115 -0
- package/templates/skills/application/SKILL.md +10 -0
- package/templates/skills/application/references/application-roles-template.md +227 -0
- package/templates/skills/application/references/backend-controller-hierarchy.md +58 -0
- package/templates/skills/application/references/backend-entity-seeding.md +72 -0
- package/templates/skills/application/references/backend-verification.md +88 -0
- package/templates/skills/application/references/frontend-verification.md +111 -0
- package/templates/skills/application/references/nav-fallback-procedure.md +200 -0
- package/templates/skills/application/references/provider-template.md +158 -0
- package/templates/skills/application/references/test-frontend.md +73 -0
- package/templates/skills/application/references/test-prerequisites.md +72 -0
- package/templates/skills/application/steps/step-01-navigation.md +7 -198
- package/templates/skills/application/steps/step-03-roles.md +45 -7
- package/templates/skills/application/steps/step-03b-provider.md +15 -132
- package/templates/skills/application/steps/step-04-backend.md +20 -350
- package/templates/skills/application/steps/step-05-frontend.md +12 -101
- package/templates/skills/application/steps/step-07-tests.md +12 -132
- package/templates/skills/business-analyse/SKILL.md +67 -6
- package/templates/skills/business-analyse/html/ba-interactive.html +176 -14
- package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +1 -0
- package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +16 -4
- package/templates/skills/business-analyse/html/src/scripts/06-render-consolidation.js +7 -2
- package/templates/skills/business-analyse/html/src/scripts/09-export.js +103 -0
- package/templates/skills/business-analyse/html/src/scripts/10-comments.js +12 -6
- package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +24 -2
- package/templates/skills/business-analyse/html/src/styles/08-review-panel.css +12 -0
- package/templates/skills/business-analyse/html/src/template.html +1 -0
- 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-structure-cards.md +78 -0
- package/templates/skills/business-analyse/references/cadrage-vibe-coding.md +97 -0
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +92 -0
- package/templates/skills/business-analyse/references/deploy-data-build.md +121 -0
- package/templates/skills/business-analyse/references/deploy-modes.md +49 -0
- package/templates/skills/business-analyse/references/handoff-file-templates.md +119 -0
- package/templates/skills/business-analyse/references/handoff-mappings.md +81 -0
- package/templates/skills/business-analyse/references/html-data-mapping.md +10 -2
- package/templates/skills/business-analyse/references/init-schema-deployment.md +65 -0
- package/templates/skills/business-analyse/references/review-data-mapping.md +363 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +538 -0
- package/templates/skills/business-analyse/references/spec-auto-inference.md +57 -0
- package/templates/skills/business-analyse/references/ui-dashboard-spec.md +85 -0
- package/templates/skills/business-analyse/references/ui-resource-cards.md +110 -0
- package/templates/skills/business-analyse/references/validate-incremental-html.md +55 -0
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +33 -1
- package/templates/skills/business-analyse/steps/step-00-init.md +186 -53
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +5 -194
- package/templates/skills/business-analyse/steps/step-03a-data.md +42 -49
- package/templates/skills/business-analyse/steps/step-03b-ui.md +12 -178
- package/templates/skills/business-analyse/steps/step-03c-compile.md +71 -2
- package/templates/skills/business-analyse/steps/step-03d-validate.md +277 -48
- package/templates/skills/business-analyse/steps/step-04-consolidation.md +175 -104
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +66 -438
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +35 -184
- package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +526 -0
- package/templates/skills/business-analyse/steps/step-06-review.md +277 -0
- package/templates/skills/cc-agent/references/agent-behavior-patterns.md +95 -0
- package/templates/skills/cc-agent/steps/step-02-generate.md +5 -78
- package/templates/skills/check-version/SKILL.md +7 -0
- package/templates/skills/controller/references/controller-code-templates.md +159 -0
- package/templates/skills/controller/references/permission-sync-templates.md +152 -0
- package/templates/skills/controller/steps/step-03-generate.md +166 -158
- package/templates/skills/controller/steps/step-04-perms.md +5 -144
- package/templates/skills/controller/templates.md +11 -2
- package/templates/skills/debug/SKILL.md +7 -0
- package/templates/skills/explore/SKILL.md +6 -0
- package/templates/skills/feature-full/SKILL.md +39 -142
- package/templates/skills/feature-full/steps/step-01-implementation.md +120 -0
- package/templates/skills/gitflow/references/init-config-template.md +135 -0
- package/templates/skills/gitflow/references/init-name-normalization.md +103 -0
- package/templates/skills/gitflow/references/plan-template.md +69 -0
- package/templates/skills/gitflow/references/start-efcore-preflight.md +70 -0
- package/templates/skills/gitflow/references/start-local-config.md +110 -0
- package/templates/skills/gitflow/steps/step-init.md +18 -289
- package/templates/skills/gitflow/steps/step-plan.md +6 -63
- package/templates/skills/gitflow/steps/step-start.md +16 -126
- package/templates/skills/mcp/SKILL.md +9 -213
- package/templates/skills/mcp/steps/step-01-healthcheck.md +108 -0
- package/templates/skills/mcp/steps/step-02-tools.md +73 -0
- package/templates/skills/notification/SKILL.md +7 -0
- package/templates/skills/quick-search/SKILL.md +5 -0
- package/templates/skills/ralph-loop/SKILL.md +99 -381
- package/templates/skills/ralph-loop/references/category-rules.md +259 -0
- package/templates/skills/ralph-loop/references/compact-loop.md +182 -0
- package/templates/skills/ralph-loop/references/core-seed-data.md +173 -21
- package/templates/skills/ralph-loop/references/task-transform-legacy.md +259 -0
- package/templates/skills/ralph-loop/references/team-orchestration.md +189 -0
- package/templates/skills/ralph-loop/steps/step-00-init.md +111 -383
- package/templates/skills/ralph-loop/steps/step-01-task.md +79 -896
- package/templates/skills/ralph-loop/steps/step-02-execute.md +68 -680
- package/templates/skills/ralph-loop/steps/step-03-commit.md +47 -277
- package/templates/skills/ralph-loop/steps/step-04-check.md +124 -607
- package/templates/skills/ralph-loop/steps/step-05-report.md +68 -367
- package/templates/skills/refactor/SKILL.md +12 -176
- package/templates/skills/refactor/steps/step-01-discover.md +60 -0
- package/templates/skills/refactor/steps/step-02-execute.md +67 -0
- package/templates/skills/review-code/SKILL.md +19 -257
- package/templates/skills/review-code/steps/step-01-smartstack.md +96 -0
- package/templates/skills/review-code/steps/step-02-detailed-review.md +80 -0
- package/templates/skills/review-code/steps/step-03-react.md +44 -0
- package/templates/skills/ui-components/SKILL.md +7 -0
- package/templates/skills/utils/SKILL.md +6 -0
- package/templates/skills/validate/SKILL.md +6 -0
- package/templates/skills/validate-feature/SKILL.md +8 -0
- package/templates/skills/workflow/SKILL.md +40 -118
- 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.
|
|
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,43 +641,49 @@ 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
|
|
|
@@ -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) |
|