@atlashub/smartstack-cli 3.32.0 → 3.34.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 (55) hide show
  1. package/.documentation/agents.html +5 -1
  2. package/.documentation/apex.html +644 -0
  3. package/.documentation/business-analyse.html +81 -1
  4. package/.documentation/cli-commands.html +5 -1
  5. package/.documentation/commands.html +5 -1
  6. package/.documentation/efcore.html +5 -1
  7. package/.documentation/gitflow.html +5 -1
  8. package/.documentation/hooks.html +5 -1
  9. package/.documentation/index.html +60 -2
  10. package/.documentation/init.html +5 -1
  11. package/.documentation/installation.html +5 -1
  12. package/.documentation/ralph-loop.html +365 -216
  13. package/.documentation/test-web.html +5 -1
  14. package/package.json +1 -1
  15. package/templates/agents/ba-writer.md +142 -15
  16. package/templates/skills/apex/SKILL.md +7 -1
  17. package/templates/skills/apex/_shared.md +49 -4
  18. package/templates/skills/{ralph-loop → apex}/references/core-seed-data.md +20 -11
  19. package/templates/skills/{ralph-loop → apex}/references/error-classification.md +2 -1
  20. package/templates/skills/apex/references/post-checks.md +238 -3
  21. package/templates/skills/apex/references/smartstack-api.md +47 -7
  22. package/templates/skills/apex/references/smartstack-frontend.md +47 -1
  23. package/templates/skills/apex/references/smartstack-layers.md +3 -1
  24. package/templates/skills/apex/steps/step-00-init.md +48 -1
  25. package/templates/skills/apex/steps/step-01-analyze.md +37 -0
  26. package/templates/skills/apex/steps/step-02-plan.md +36 -0
  27. package/templates/skills/apex/steps/step-03-execute.md +42 -2
  28. package/templates/skills/apex/steps/step-04-examine.md +110 -2
  29. package/templates/skills/business-analyse/SKILL.md +29 -19
  30. package/templates/skills/business-analyse/_module-loop.md +68 -9
  31. package/templates/skills/business-analyse/_shared.md +71 -21
  32. package/templates/skills/business-analyse/questionnaire/00-application.md +4 -2
  33. package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -0
  34. package/templates/skills/business-analyse/references/deploy-modes.md +69 -0
  35. package/templates/skills/business-analyse/references/team-orchestration.md +158 -7
  36. package/templates/skills/business-analyse/schemas/application-schema.json +2 -1
  37. package/templates/skills/business-analyse/schemas/project-schema.json +490 -0
  38. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -1
  39. package/templates/skills/business-analyse/steps/step-00-init.md +30 -4
  40. package/templates/skills/business-analyse/steps/step-01-cadrage.md +62 -2
  41. package/templates/skills/business-analyse/steps/step-01b-applications.md +252 -0
  42. package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
  43. package/templates/skills/business-analyse/steps/step-03d-validate.md +27 -6
  44. package/templates/skills/business-analyse/steps/step-04a-collect.md +111 -0
  45. package/templates/skills/business-analyse/steps/step-05a-handoff.md +296 -103
  46. package/templates/skills/business-analyse/steps/step-05b-deploy.md +46 -14
  47. package/templates/skills/documentation/SKILL.md +92 -2
  48. package/templates/skills/ralph-loop/SKILL.md +9 -17
  49. package/templates/skills/ralph-loop/references/category-rules.md +43 -692
  50. package/templates/skills/ralph-loop/references/compact-loop.md +104 -427
  51. package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
  52. package/templates/skills/ralph-loop/steps/step-02-execute.md +49 -704
  53. package/templates/skills/ralph-loop/steps/step-03-commit.md +38 -79
  54. package/templates/skills/ralph-loop/steps/step-04-check.md +39 -58
  55. package/templates/skills/ralph-loop/steps/step-05-report.md +12 -123
@@ -32,8 +32,20 @@ Build the development handoff data: verify consolidation, choose implementation
32
32
  Use ba-reader to locate the feature and verify consolidation status:
33
33
 
34
34
  ```
35
- ba-reader.findFeature({feature_id})
36
- Check status = "consolidated"
35
+ // Determine feature source based on workflow mode
36
+ IF workflow.mode === "project":
37
+ projectFeature = ba-reader.findProjectFeature()
38
+ // Verify ALL applications are specified
39
+ FOR each app in projectFeature.applications:
40
+ appFeature = ba-reader.findFeature(app.featureJsonPath)
41
+ IF appFeature.status !== "consolidated":
42
+ BLOCKING ERROR: "Application {app.code} not consolidated (status: {appFeature.status})"
43
+ → Return to step-04a-collect.md
44
+ // Use project-level feature for overall consolidation
45
+ feature = projectFeature
46
+ ELSE:
47
+ ba-reader.findFeature({feature_id})
48
+ → Check status = "consolidated"
37
49
  ```
38
50
 
39
51
  **IF** status ≠ "consolidated" → **STOP**. Return to step-04a-collect.md.
@@ -42,18 +54,22 @@ Display validation summary:
42
54
 
43
55
  ```
44
56
  ✓ Consolidation: APPROVED
57
+ ✓ Applications: {app_count} (project mode only)
45
58
  ✓ Modules: {count} specified and validated
46
59
  ✓ Cross-module: interactions mapped
60
+ ✓ Cross-application: interactions mapped (project mode only)
47
61
  ✓ Permissions: coherent
48
62
  → Proceeding to handoff...
49
63
  ```
50
64
 
51
65
  Include:
52
- - Number of modules
66
+ - Number of applications (project mode only)
67
+ - Number of modules across all applications
53
68
  - Number of entities across all modules
54
69
  - Number of use cases across all modules
55
70
  - Number of business rules across all modules
56
71
  - Cross-module interaction count
72
+ - Cross-application interaction count (project mode only)
57
73
 
58
74
  ---
59
75
 
@@ -101,9 +117,9 @@ See [references/cache-warming-strategy.md](../references/cache-warming-strategy.
101
117
 
102
118
  ---
103
119
 
104
- ### 2. Implementation Strategy Choice (Multi-Module)
120
+ ### 2. Implementation Strategy Choice (Multi-Module/Multi-App)
105
121
 
106
- **IF** more than 1 module defined in feature.json:
122
+ **IF** more than 1 module defined in feature.json (or project mode with multiple applications):
107
123
 
108
124
  Ask via AskUserQuestion:
109
125
 
@@ -117,8 +133,12 @@ options:
117
133
  description: "Implémenter toutes les entités, puis tous les services, puis tous les contrôleurs, etc. Plus de parallélisation possible mais plus complexe."
118
134
  - label: "Hybride"
119
135
  description: "Modules fondation en premier (couche par couche), puis modules dépendants (module par module)"
136
+ - label: "Application par application (Recommandé multi-app)"
137
+ description: "Implémenter chaque application complètement avant de passer à la suivante. Suit l'ordre topologique inter-applications."
120
138
  ```
121
139
 
140
+ > **Note:** "Application par application" only appears for project mode with 2+ applications.
141
+
122
142
  Store the chosen strategy in `handoff.implementationStrategy`.
123
143
 
124
144
  **IF** only 1 module → default to "Module par module" (no choice needed).
@@ -335,6 +355,31 @@ Generate `.ralph/prd-CrossModule.json` with:
335
355
 
336
356
  Add to progress.txt after all module tasks.
337
357
 
358
+ ### 6d. Cross-Application PRD (Project Mode Only)
359
+
360
+ > **Only generated when `workflow.mode === "project"`.**
361
+ > Contains cross-application integration tests and shared entity validation.
362
+
363
+ ```json
364
+ {
365
+ "$version": "3.0.0",
366
+ "implementation": {
367
+ "filesToCreate": {
368
+ "tests": [
369
+ { "path": "src/Tests/Integration/CrossApplication/SharedEntityValidationTests.cs", "type": "IntegrationTests", "module": "CrossApplication" },
370
+ { "path": "src/Tests/Integration/CrossApplication/CrossAppPermissionTests.cs", "type": "SecurityTests", "module": "CrossApplication" },
371
+ { "path": "src/Tests/Integration/CrossApplication/CrossAppE2EFlowTests.cs", "type": "E2ETests", "module": "CrossApplication" }
372
+ ]
373
+ }
374
+ }
375
+ }
376
+ ```
377
+
378
+ Write to: `.ralph/prd-CrossApplication.json`
379
+
380
+ > This PRD is generated IN ADDITION to the per-module cross-module PRD.
381
+ > It validates interactions between applications, not just between modules within the same application.
382
+
338
383
  ---
339
384
 
340
385
  ### 7. Write Handoff to Feature.json
@@ -434,53 +479,119 @@ const lang = master.metadata.language || "fr";
434
479
 
435
480
  const seedDataCore = {
436
481
  // Application-level navigation (MANDATORY — one entry per application)
437
- navigationApplications: [{
438
- code: appCode.toLowerCase(),
439
- name: appCode,
440
- labels: {
441
- fr: lang === "fr" ? (master.cadrage?.applicationName || appLabel) : appLabel,
442
- en: lang === "en" ? (master.cadrage?.applicationName || appLabel) : appLabel,
443
- it: appLabel,
444
- de: appLabel
445
- },
446
- description: {
447
- fr: lang === "fr" ? appDesc : `Gestion ${appLabel}`,
448
- en: lang === "en" ? appDesc : `${appLabel} management`,
449
- it: `Gestione ${appLabel}`,
450
- de: `${appLabel} Verwaltung`
451
- },
452
- icon: inferIconFromApplication(master) || "layout-grid",
453
- iconType: "lucide",
454
- context: contextCode,
455
- route: `/${contextCode}/${toKebabCase(appCode)}`,
456
- displayOrder: 1
457
- }],
458
-
459
- navigationModules: master.modules.map((m, i) => ({
460
- code: m.code,
461
- label: m.name || m.code,
462
- description: m.description,
463
- icon: inferIconFromModule(m) || "folder", // MUST be non-null — use sensible default
464
- iconType: "lucide",
465
- route: `/${contextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}`,
466
- displayOrder: (i + 1) * 10
467
- })),
468
-
469
- navigationSections: master.modules.flatMap(m =>
470
- (m.anticipatedSections || []).map((s, j) => ({
471
- moduleCode: m.code,
472
- code: s.code,
473
- label: s.description?.split(':')[0] || s.code,
474
- description: s.description || "",
475
- route: s.code === "list"
476
- ? `/${contextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}`
477
- : s.code === "detail"
478
- ? `/${contextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}/:id`
479
- : `/${contextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}/${toKebabCase(s.code)}`,
480
- displayOrder: (j + 1) * 10,
481
- navigation: s.code === "detail" ? "hidden" : "visible"
482
- }))
483
- ),
482
+ // PROJECT MODE: Generate one entry per application from project feature.json
483
+ // SINGLE-APP MODE: Generate one entry from master feature.json
484
+ navigationApplications: (() => {
485
+ if (workflow.mode === "project") {
486
+ // Multi-app: one navigation entry per application
487
+ return projectFeature.applications.map((app, idx) => {
488
+ const appLabel = toHumanReadable(app.code);
489
+ const appContext = app.context || "business";
490
+ return {
491
+ code: app.code.toLowerCase(),
492
+ name: app.code,
493
+ labels: {
494
+ fr: lang === "fr" ? (app.name || appLabel) : appLabel,
495
+ en: lang === "en" ? (app.name || appLabel) : appLabel,
496
+ it: appLabel,
497
+ de: appLabel
498
+ },
499
+ description: {
500
+ fr: lang === "fr" ? (app.description || `Gestion ${appLabel}`) : `Gestion ${appLabel}`,
501
+ en: lang === "en" ? (app.description || `${appLabel} management`) : `${appLabel} management`,
502
+ it: `Gestione ${appLabel}`,
503
+ de: `${appLabel} Verwaltung`
504
+ },
505
+ icon: app.icon || inferIconFromApplication({ metadata: { application: app.code } }) || "layout-grid",
506
+ iconType: "lucide",
507
+ context: appContext,
508
+ route: `/${appContext}/${toKebabCase(app.code)}`,
509
+ displayOrder: (idx + 1) * 10
510
+ };
511
+ });
512
+ } else {
513
+ // Single-app: one navigation entry
514
+ return [{
515
+ code: appCode.toLowerCase(),
516
+ name: appCode,
517
+ labels: {
518
+ fr: lang === "fr" ? (master.cadrage?.applicationName || appLabel) : appLabel,
519
+ en: lang === "en" ? (master.cadrage?.applicationName || appLabel) : appLabel,
520
+ it: appLabel,
521
+ de: appLabel
522
+ },
523
+ description: {
524
+ fr: lang === "fr" ? appDesc : `Gestion ${appLabel}`,
525
+ en: lang === "en" ? appDesc : `${appLabel} management`,
526
+ it: `Gestione ${appLabel}`,
527
+ de: `${appLabel} Verwaltung`
528
+ },
529
+ icon: inferIconFromApplication(master) || "layout-grid",
530
+ iconType: "lucide",
531
+ context: contextCode,
532
+ route: `/${contextCode}/${toKebabCase(appCode)}`,
533
+ displayOrder: 1
534
+ }];
535
+ }
536
+ })(),
537
+
538
+ // PROJECT MODE: Flatten modules from ALL applications
539
+ // SINGLE-APP MODE: Use master.modules directly
540
+ navigationModules: (() => {
541
+ if (workflow.mode === "project") {
542
+ let order = 0;
543
+ return projectFeature.applications.flatMap(app => {
544
+ const appContext = app.context || "business";
545
+ return (app.modules || []).map(m => ({
546
+ code: m.code,
547
+ applicationCode: app.code,
548
+ label: m.name || m.code,
549
+ description: m.description,
550
+ icon: inferIconFromModule(m) || "folder",
551
+ iconType: "lucide",
552
+ route: `/${appContext}/${toKebabCase(app.code)}/${toKebabCase(m.code)}`,
553
+ displayOrder: (++order) * 10
554
+ }));
555
+ });
556
+ } else {
557
+ return master.modules.map((m, i) => ({
558
+ code: m.code,
559
+ label: m.name || m.code,
560
+ description: m.description,
561
+ icon: inferIconFromModule(m) || "folder",
562
+ iconType: "lucide",
563
+ route: `/${contextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}`,
564
+ displayOrder: (i + 1) * 10
565
+ }));
566
+ }
567
+ })(),
568
+
569
+ navigationSections: (() => {
570
+ const buildSections = (modules, appContextCode, appCode) =>
571
+ modules.flatMap(m =>
572
+ (m.anticipatedSections || []).map((s, j) => ({
573
+ moduleCode: m.code,
574
+ code: s.code,
575
+ label: s.description?.split(':')[0] || s.code,
576
+ description: s.description || "",
577
+ route: s.code === "list"
578
+ ? `/${appContextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}`
579
+ : s.code === "detail"
580
+ ? `/${appContextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}/:id`
581
+ : `/${appContextCode}/${toKebabCase(appCode)}/${toKebabCase(m.code)}/${toKebabCase(s.code)}`,
582
+ displayOrder: (j + 1) * 10,
583
+ navigation: s.code === "detail" ? "hidden" : "visible"
584
+ }))
585
+ );
586
+
587
+ if (workflow.mode === "project") {
588
+ return projectFeature.applications.flatMap(app =>
589
+ buildSections(app.modules || [], app.context || "business", app.code)
590
+ );
591
+ } else {
592
+ return buildSections(master.modules, contextCode, appCode);
593
+ }
594
+ })(),
484
595
 
485
596
  navigationResources: master.cadrage.coverageMatrix
486
597
  .filter(cm => cm.module && cm.anticipatedResources?.length > 0)
@@ -504,37 +615,73 @@ const seedDataCore = {
504
615
  }));
505
616
  }),
506
617
 
507
- permissions: master.modules.flatMap(m => {
508
- const basePath = `business.${master.metadata.application.toLowerCase()}.${m.code.toLowerCase()}`;
509
- const actions = ['read', 'create', 'update', 'delete'];
510
- return [
511
- { path: `${basePath}.*`, action: '*', description: `Full ${m.name || m.code} access` },
512
- ...actions.map(action => ({
513
- path: `${basePath}.${action}`,
514
- action: action,
515
- description: `${action.charAt(0).toUpperCase() + action.slice(1)} ${m.name || m.code}`
516
- }))
517
- ];
518
- }),
519
-
520
- rolePermissions: master.cadrage.applicationRoles.map(role => ({
521
- role: role.role,
522
- level: role.level,
523
- permissions: master.modules.map(m => `business.${master.metadata.application.toLowerCase()}.${m.code.toLowerCase()}.${
524
- role.level === 'admin' ? '*' :
525
- role.level === 'manager' ? 'read,create,update' :
526
- role.level === 'contributor' ? 'read,create' : 'read'
527
- }`)
528
- })),
529
-
530
- permissionConstants: master.modules.flatMap(m =>
531
- ['Read', 'Create', 'Update', 'Delete', 'Validate', 'Export'].map(action => ({
532
- module: m.code,
533
- action: action,
534
- constant: `${master.metadata.application}${m.code}${action}`,
535
- path: `business.${master.metadata.application.toLowerCase()}.${m.code.toLowerCase()}.${action.toLowerCase()}`
536
- }))
537
- )
618
+ // PROJECT MODE: permissions scoped per application context
619
+ // SINGLE-APP MODE: all permissions under business.{app}
620
+ permissions: (() => {
621
+ const buildPermissions = (modules, appContext, appCode) =>
622
+ modules.flatMap(m => {
623
+ const basePath = `${appContext}.${toKebabCase(appCode)}.${toKebabCase(m.code)}`;
624
+ const actions = ['read', 'create', 'update', 'delete'];
625
+ return [
626
+ { path: `${basePath}.*`, action: '*', description: `Full ${m.name || m.code} access` },
627
+ ...actions.map(action => ({
628
+ path: `${basePath}.${action}`,
629
+ action: action,
630
+ description: `${action.charAt(0).toUpperCase() + action.slice(1)} ${m.name || m.code}`
631
+ }))
632
+ ];
633
+ });
634
+
635
+ if (workflow.mode === "project") {
636
+ return projectFeature.applications.flatMap(app =>
637
+ buildPermissions(app.modules || [], app.context || "business", app.code)
638
+ );
639
+ } else {
640
+ return buildPermissions(master.modules, contextCode, appCode);
641
+ }
642
+ })(),
643
+
644
+ rolePermissions: (() => {
645
+ const buildRolePermissions = (roles, modules, appContext, appCode) =>
646
+ roles.map(role => ({
647
+ role: role.role,
648
+ level: role.level,
649
+ permissions: modules.map(m => `${appContext}.${toKebabCase(appCode)}.${toKebabCase(m.code)}.${
650
+ role.level === 'admin' ? '*' :
651
+ role.level === 'manager' ? 'read,create,update' :
652
+ role.level === 'contributor' ? 'read,create' : 'read'
653
+ }`)
654
+ }));
655
+
656
+ if (workflow.mode === "project") {
657
+ return projectFeature.applications.flatMap(app =>
658
+ buildRolePermissions(app.applicationRoles || [], app.modules || [], app.context || "business", app.code)
659
+ );
660
+ } else {
661
+ return buildRolePermissions(master.cadrage.applicationRoles, master.modules, contextCode, appCode);
662
+ }
663
+ })(),
664
+
665
+ permissionConstants: (() => {
666
+ const buildConstants = (modules, appCode) =>
667
+ modules.flatMap(m =>
668
+ ['Read', 'Create', 'Update', 'Delete', 'Validate', 'Export'].map(action => ({
669
+ module: m.code,
670
+ application: appCode,
671
+ action: action,
672
+ constant: `${appCode}${m.code}${action}`,
673
+ path: `${contextCode}.${toKebabCase(appCode)}.${toKebabCase(m.code)}.${action.toLowerCase()}`
674
+ }))
675
+ );
676
+
677
+ if (workflow.mode === "project") {
678
+ return projectFeature.applications.flatMap(app =>
679
+ buildConstants(app.modules || [], app.code)
680
+ );
681
+ } else {
682
+ return buildConstants(master.modules, appCode);
683
+ }
684
+ })()
538
685
  };
539
686
 
540
687
  // Icon inference for APPLICATION level (NEVER leave as null)
@@ -609,26 +756,72 @@ IF seedDataCore.navigationSections.length === 0:
609
756
  #### 7c. Master Handoff (after ALL modules written + seedDataCore generated)
610
757
 
611
758
  ```
612
- ba-writer.enrichSection({
613
- featureId: {feature_id},
614
- section: "handoff",
615
- data: {
616
- status: "handed-off",
617
- complexity: "{simple|medium|complex}",
618
- implementationStrategy: "{strategy}",
619
- moduleCount: {count},
620
- moduleOrder: [...],
621
- totalFilesToCreate: {sum across all modules},
622
- totalTasks: {sum across all modules},
623
- prdStructure: "per-module | consolidated",
624
- prdFiles: [
625
- { module: "{module1}", path: ".ralph/prd-{module1}.json" },
626
- { module: "{module2}", path: ".ralph/prd-{module2}.json" }
627
- ],
628
- progressTrackerPath: ".ralph/progress.txt",
629
- handedOffAt: "{ISO timestamp}"
630
- }
631
- })
759
+ // Write handoff to application-level feature.json (single-app mode)
760
+ // OR to each application feature.json + project feature.json (project mode)
761
+
762
+ IF workflow.mode === "project":
763
+ // Write handoff to EACH application feature.json
764
+ FOR each app in projectFeature.applications:
765
+ ba-writer.enrichSection({
766
+ featureId: app.featureJsonPath,
767
+ section: "handoff",
768
+ data: {
769
+ status: "handed-off",
770
+ complexity: "{app-level complexity}",
771
+ implementationStrategy: "{strategy}",
772
+ moduleCount: app.modules.length,
773
+ moduleOrder: app.modules.map(m => m.code),
774
+ totalFilesToCreate: {sum across app modules},
775
+ totalTasks: {sum across app modules},
776
+ prdFiles: app.modules.map(m => ({ module: m.code, path: `.ralph/prd-${m.code}.json` })),
777
+ handedOffAt: "{ISO timestamp}"
778
+ }
779
+ })
780
+
781
+ // Write project-level handoff summary
782
+ ba-writer.enrichSection({
783
+ featureId: {project_id},
784
+ section: "handoff",
785
+ data: {
786
+ status: "handed-off",
787
+ complexity: "{global complexity}",
788
+ implementationStrategy: "{strategy}",
789
+ applicationCount: projectFeature.applications.length,
790
+ applicationOrder: projectFeature.applicationDependencyGraph.topologicalOrder,
791
+ totalModuleCount: {sum of all app module counts},
792
+ totalFilesToCreate: {sum across ALL applications and modules},
793
+ totalTasks: {sum across ALL applications and modules},
794
+ prdStructure: "per-module",
795
+ prdFiles: [
796
+ ...allModulePrdFiles,
797
+ { module: "CrossModule", path: ".ralph/prd-CrossModule.json" },
798
+ { module: "CrossApplication", path: ".ralph/prd-CrossApplication.json" }
799
+ ],
800
+ progressTrackerPath: ".ralph/progress.txt",
801
+ handedOffAt: "{ISO timestamp}"
802
+ }
803
+ })
804
+ ELSE:
805
+ ba-writer.enrichSection({
806
+ featureId: {feature_id},
807
+ section: "handoff",
808
+ data: {
809
+ status: "handed-off",
810
+ complexity: "{simple|medium|complex}",
811
+ implementationStrategy: "{strategy}",
812
+ moduleCount: {count},
813
+ moduleOrder: [...],
814
+ totalFilesToCreate: {sum across all modules},
815
+ totalTasks: {sum across all modules},
816
+ prdStructure: "per-module | consolidated",
817
+ prdFiles: [
818
+ { module: "{module1}", path: ".ralph/prd-{module1}.json" },
819
+ { module: "{module2}", path: ".ralph/prd-{module2}.json" }
820
+ ],
821
+ progressTrackerPath: ".ralph/progress.txt",
822
+ handedOffAt: "{ISO timestamp}"
823
+ }
824
+ })
632
825
  ```
633
826
 
634
827
  #### 7d. Final Verification (BLOCKING)
@@ -176,21 +176,38 @@ IF ANY category shows FAIL → **STOP AND RE-RUN `ss derive-prd`**. DO NOT proce
176
176
  **Update logic:**
177
177
 
178
178
  1. Read existing manifest at `docs/business/index.json` (or create empty `{ "version": "1.0", "updatedAt": "", "analyses": [] }`)
179
- 2. For the APPLICATION-level feature.json:
180
- - Find existing entry where `appCode == {app_code}` AND `moduleCode == null` AND `version == {version}`
181
- - If found: update `status`, `updatedAt`, `featureDescription`
182
- - If not found: append new entry with `moduleCode: null` and `path: "{app_code}/business-analyse/v{version}/feature.json"`
183
- 3. For EACH MODULE-level feature.json:
184
- - Find existing entry where `appCode == {app_code}` AND `moduleCode == {module_code}` AND `version == {version}`
185
- - If found: update `status`, `updatedAt`, `featureDescription`
186
- - If not found: append new entry with `moduleCode: "{module_code}"` and `path: "{app_code}/{module_code}/business-analyse/v{version}/feature.json"`
179
+
180
+ 2. **PROJECT MODE (`workflow.mode === "project"`):**
181
+ a. Add/update PROJECT-level entry:
182
+ - Find existing entry where `projectId == {project_id}` AND `scope == "project"`
183
+ - If found: update `status`, `updatedAt`
184
+ - If not found: append new entry with `scope: "project"`, `projectId: "{project_id}"`, `path: "business-analyse/v{version}/feature.json"`
185
+ b. For EACH APPLICATION:
186
+ - Find existing entry where `appCode == {app_code}` AND `moduleCode == null` AND `version == {version}`
187
+ - If found: update `status`, `updatedAt`, `featureDescription`
188
+ - If not found: append new entry with `moduleCode: null`, `context: "{app.context}"`, and `path: "{context}/{app_code}/business-analyse/v{version}/feature.json"`
189
+ c. For EACH MODULE within each application:
190
+ - Find existing entry where `appCode == {app_code}` AND `moduleCode == {module_code}` AND `version == {version}`
191
+ - If found: update `status`, `updatedAt`, `featureDescription`
192
+ - If not found: append new entry with `path: "{context}/{app_code}/{module_code}/business-analyse/v{version}/feature.json"`
193
+
194
+ 3. **SINGLE-APP MODE (default):**
195
+ a. For the APPLICATION-level feature.json:
196
+ - Find existing entry where `appCode == {app_code}` AND `moduleCode == null` AND `version == {version}`
197
+ - If found: update `status`, `updatedAt`, `featureDescription`
198
+ - If not found: append new entry with `moduleCode: null` and `path: "{app_code}/business-analyse/v{version}/feature.json"`
199
+ b. For EACH MODULE-level feature.json:
200
+ - Find existing entry where `appCode == {app_code}` AND `moduleCode == {module_code}` AND `version == {version}`
201
+ - If found: update `status`, `updatedAt`, `featureDescription`
202
+ - If not found: append new entry with `moduleCode: "{module_code}"` and `path: "{app_code}/{module_code}/business-analyse/v{version}/feature.json"`
203
+
187
204
  4. Update root `updatedAt` to current timestamp
188
205
  5. Write manifest back to `docs/business/index.json`
189
206
 
190
207
  **Display confirmation:**
191
208
  ```
192
209
  ✓ BA manifest updated: docs/business/index.json
193
- Entries: {total_count} ({app_count} applications, {module_count} modules)
210
+ Entries: {total_count} ({project_count} projects, {app_count} applications, {module_count} modules)
194
211
  Web viewer: /system/docs/ba
195
212
  ```
196
213
 
@@ -205,20 +222,28 @@ IF ANY category shows FAIL → **STOP AND RE-RUN `ss derive-prd`**. DO NOT proce
205
222
  ### 4. Read Source Data for HTML
206
223
 
207
224
  1. Read the HTML template from skill directory: `html/ba-interactive.html` (relative to `~/.claude/skills/business-analyse/`)
208
- 2. Read the master feature.json (application level)
209
- 3. Read EACH module feature.json (module level)
225
+ 2. **SINGLE-APP MODE:** Read the master feature.json (application level)
226
+ 3. **PROJECT MODE:** Read the project feature.json + ALL application feature.json files
227
+ 4. Read EACH module feature.json (module level, across all applications in project mode)
210
228
 
211
229
  > **Reference:** Read `references/html-data-mapping.md` for the complete FEATURE_DATA and EMBEDDED_ARTIFACTS mapping specification.
212
230
 
213
231
  **Key requirements:**
214
232
  - FEATURE_DATA must be pre-populated with ALL analysis data (empty data = BUG)
215
- - FEATURE_DATA.**moduleSpecs** MUST be populated for EACH module (use cases, business rules, entities, permissions) — empty moduleSpecs = BROKEN specification tabs
233
+ - FEATURE_DATA.**moduleSpecs** MUST be populated for EACH module across ALL applications (use cases, business rules, entities, permissions) — empty moduleSpecs = BROKEN specification tabs
216
234
  - FEATURE_DATA.**cadrage.scope** must use HTML keys (vital/important/optional/excluded) NOT feature.json keys (mustHave/shouldHave/couldHave/outOfScope)
217
235
  - FEATURE_DATA.**metadata.analysisMode** must be set from `master.metadata.analysisMode` (always "interactive")
218
236
  - EMBEDDED_ARTIFACTS contains wireframes, E2E diagrams, dependency graph
219
237
  - Both objects are serialized as JSON with 2-space indentation
220
238
  - All data mapped from master and module feature.json files — follow `html-data-mapping.md` EXACTLY
221
239
 
240
+ **PROJECT MODE additions to FEATURE_DATA:**
241
+ - `FEATURE_DATA.project` — project-level metadata (name, applicationCount, applicationOrder)
242
+ - `FEATURE_DATA.applications` — array of application summaries with their modules
243
+ - `FEATURE_DATA.modules` — flattened list of ALL modules across ALL applications (each with `applicationCode` field)
244
+ - `FEATURE_DATA.consolidation.crossApplicationInteractions` — from project feature.json
245
+ - FEATURE_DATA.**metadata.workflowMode** must be `"project"` (enables multi-app tabs in HTML)
246
+
222
247
  ---
223
248
 
224
249
  ### 5-7. Build FEATURE_DATA, EMBEDDED_ARTIFACTS & Replace Placeholders
@@ -426,9 +451,13 @@ Display completion using the template from `templates/tpl-launch-displays.md`:
426
451
 
427
452
  ```
428
453
  ═══════════════════════════════════════════════════════════════
429
- [OK] BUSINESS ANALYSE TERMINEE - {application_name}
454
+ [OK] BUSINESS ANALYSE TERMINEE - {application_name OR project_name}
430
455
  ═══════════════════════════════════════════════════════════════
431
456
 
457
+ {IF workflow.mode === "project":}
458
+ Project: {project_name}
459
+ Apps: {app_count} ({app_names})
460
+ {ENDIF}
432
461
  Modules: {count} ({names})
433
462
  Strategy: {strategy}
434
463
  Files: {total files across all modules}
@@ -437,8 +466,11 @@ Complexity: {complexity}
437
466
  Effort: {total_days} days ({total_hours} hours)
438
467
 
439
468
  [DIR] Artefacts générés:
440
- ✓ feature.json (master + per-module) - spécification complète
469
+ ✓ feature.json ({IF project: "project + " ENDIF}master + per-module) - spécification complète
441
470
  ✓ .ralph/prd-{module}.json - task breakdown per module
471
+ {IF workflow.mode === "project":}
472
+ ✓ .ralph/prd-CrossApplication.json - cross-application integration tests
473
+ {ENDIF}
442
474
  ✓ .ralph/progress.txt - tracker de progression
443
475
  ✓ ba-interactive.html - document de revue interactif
444
476