@atlashub/smartstack-cli 2.2.0 → 2.3.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.
@@ -227,7 +227,7 @@
227
227
  "required": ["target", "type"],
228
228
  "properties": {
229
229
  "target": { "type": "string" },
230
- "type": { "type": "string", "enum": ["1:1", "1:N", "N:M"] },
230
+ "type": { "type": "string", "enum": ["1:1", "1:N", "N:1", "N:M"] },
231
231
  "description": { "type": "string" }
232
232
  }
233
233
  }
@@ -521,6 +521,118 @@
521
521
  "permissionsRequired": { "type": "array", "items": { "type": "string" } }
522
522
  }
523
523
  }
524
+ },
525
+ "messages": {
526
+ "type": "array",
527
+ "description": "Business messages (success, error, warning, info) with i18n keys. MANDATORY: ≥4 messages.",
528
+ "items": {
529
+ "type": "object",
530
+ "required": ["code", "type", "i18nKey"],
531
+ "properties": {
532
+ "code": { "type": "string", "description": "SCREAMING_SNAKE_CASE identifier (e.g., VEHICLE_CREATED_SUCCESS)" },
533
+ "type": { "type": "string", "enum": ["success", "error", "warning", "info"] },
534
+ "title": { "type": "string" },
535
+ "message": { "type": "string", "description": "User-facing message with {placeholders}" },
536
+ "i18nKey": { "type": "string", "description": "Dot-separated i18n key (e.g., freebike.messages.vehicleCreated)" }
537
+ }
538
+ }
539
+ },
540
+ "lifeCycles": {
541
+ "type": "array",
542
+ "description": "Per-entity state machines for entities with Status fields. MANDATORY: ≥1 entity.",
543
+ "items": {
544
+ "type": "object",
545
+ "required": ["entity", "states"],
546
+ "properties": {
547
+ "entity": { "type": "string", "description": "PascalCase entity name" },
548
+ "states": {
549
+ "type": "array",
550
+ "items": {
551
+ "type": "object",
552
+ "required": ["id", "allowedTransitions"],
553
+ "properties": {
554
+ "id": { "type": "string", "description": "State code (e.g., Draft, Active, Completed)" },
555
+ "displayName": { "type": "string" },
556
+ "description": { "type": "string" },
557
+ "allowedTransitions": { "type": "array", "items": { "type": "string" } },
558
+ "isTerminal": { "type": "boolean", "default": false }
559
+ }
560
+ }
561
+ }
562
+ }
563
+ }
564
+ },
565
+ "seedDataCore": {
566
+ "type": "object",
567
+ "description": "5 mandatory SmartStack core SeedData definitions derived from navigation + permissionMatrix. CRITICAL: without these, module is invisible (403).",
568
+ "properties": {
569
+ "navigationModules": {
570
+ "type": "array",
571
+ "description": "Entries for nav_Modules table (HasData). Derived from navigation.entries.",
572
+ "items": {
573
+ "type": "object",
574
+ "required": ["code", "label", "icon", "route"],
575
+ "properties": {
576
+ "code": { "type": "string" },
577
+ "label": { "type": "string" },
578
+ "icon": { "type": "string" },
579
+ "route": { "type": "string" },
580
+ "parentCode": { "type": ["string", "null"] },
581
+ "sort": { "type": "integer" }
582
+ }
583
+ }
584
+ },
585
+ "navigationTranslations": {
586
+ "type": "array",
587
+ "description": "Translations for nav_Translations table (HasData). One entry per module per language.",
588
+ "items": {
589
+ "type": "object",
590
+ "required": ["moduleCode", "language", "label"],
591
+ "properties": {
592
+ "moduleCode": { "type": "string" },
593
+ "language": { "type": "string", "enum": ["fr", "en", "it", "de"] },
594
+ "label": { "type": "string" }
595
+ }
596
+ }
597
+ },
598
+ "permissions": {
599
+ "type": "array",
600
+ "description": "Entries for nav_Permissions table (HasData). Full paths from permissionMatrix.permissions.",
601
+ "items": {
602
+ "type": "object",
603
+ "required": ["path", "action"],
604
+ "properties": {
605
+ "path": { "type": "string", "description": "Full permission path (business.{app}.{module}.{resource}.{action})" },
606
+ "action": { "type": "string" },
607
+ "description": { "type": "string" }
608
+ }
609
+ }
610
+ },
611
+ "rolePermissions": {
612
+ "type": "array",
613
+ "description": "Entries for auth_RolePermissions table (HasData). Derived from permissionMatrix.roleAssignments.",
614
+ "items": {
615
+ "type": "object",
616
+ "required": ["role", "permissionPath"],
617
+ "properties": {
618
+ "role": { "type": "string" },
619
+ "permissionPath": { "type": "string", "description": "Full permission path (must match permissions[].path)" }
620
+ }
621
+ }
622
+ },
623
+ "permissionConstants": {
624
+ "type": "array",
625
+ "description": "Compile-time constants for Permissions.cs (Application layer). Used in [RequirePermission] attributes.",
626
+ "items": {
627
+ "type": "object",
628
+ "required": ["constantName", "path"],
629
+ "properties": {
630
+ "constantName": { "type": "string", "description": "PascalCase constant (e.g., VehiclesRead)" },
631
+ "path": { "type": "string", "description": "Matching permission path" }
632
+ }
633
+ }
634
+ }
635
+ }
524
636
  }
525
637
  }
526
638
  },
@@ -529,53 +641,74 @@
529
641
  "type": "object",
530
642
  "description": "Enriched by step-03-validate",
531
643
  "properties": {
532
- "decision": { "type": "string", "enum": ["approved", "rejected", "pending"] },
533
644
  "validatedAt": { "type": ["string", "null"], "format": "date-time" },
534
- "completeness": {
535
- "type": "object",
536
- "properties": {
537
- "discovery": { "type": "boolean" },
538
- "analysis": { "type": "boolean" },
539
- "specification": { "type": "boolean" },
540
- "score": { "type": "integer" },
541
- "total": { "type": "integer" },
542
- "details": { "type": "array", "items": { "type": "string" } }
645
+ "completenessChecks": {
646
+ "type": "array",
647
+ "description": "Per-section completeness verification",
648
+ "items": {
649
+ "type": "object",
650
+ "properties": {
651
+ "section": { "type": "string" },
652
+ "count": { "type": "integer" },
653
+ "minimum": { "type": "integer" },
654
+ "status": { "type": "string", "enum": ["PASS", "FAIL", "WARNING"] }
655
+ }
543
656
  }
544
657
  },
545
- "consistency": {
546
- "type": "object",
547
- "description": "Cross-reference validation results",
548
- "properties": {
549
- "rulesToRequirements": { "type": "boolean", "description": "All BR mapped to FR" },
550
- "useCasesToActors": { "type": "boolean", "description": "All UC have valid actors" },
551
- "requirementsToRules": { "type": "boolean", "description": "All FR trace to BR" },
552
- "entitiesToEndpoints": { "type": "boolean", "description": "All entities have CRUD endpoints" },
553
- "details": { "type": "array", "items": { "type": "string" } }
658
+ "consistencyChecks": {
659
+ "type": "array",
660
+ "description": "Cross-reference consistency checks (UC↔FR, FR↔BR, Actor↔Matrix, etc.)",
661
+ "items": {
662
+ "type": "object",
663
+ "properties": {
664
+ "check": { "type": "string" },
665
+ "passed": { "type": "integer" },
666
+ "warnings": { "type": "integer" },
667
+ "errors": { "type": "integer" },
668
+ "status": { "type": "string", "enum": ["PASS", "FAIL", "WARNING"] }
669
+ }
554
670
  }
555
671
  },
556
- "conventions": {
557
- "type": "object",
558
- "description": "SmartStack convention validation",
559
- "properties": {
560
- "permissionPaths": { "type": "boolean" },
561
- "navRoutes": { "type": "boolean" },
562
- "entityNaming": { "type": "boolean" },
563
- "folderStructure": { "type": "boolean" },
564
- "seedDataComplete": { "type": "boolean" },
565
- "details": { "type": "array", "items": { "type": "string" } }
672
+ "conventionChecks": {
673
+ "type": "array",
674
+ "description": "SmartStack convention validation (naming, permissions, routes, etc.)",
675
+ "items": {
676
+ "type": "object",
677
+ "properties": {
678
+ "check": { "type": "string" },
679
+ "status": { "type": "string", "enum": ["PASS", "FAIL", "WARNING"] },
680
+ "details": { "type": "string" }
681
+ }
566
682
  }
567
683
  },
568
- "issues": {
684
+ "riskAssessments": {
569
685
  "type": "array",
686
+ "description": "Scope and complexity risk evaluation",
570
687
  "items": {
571
688
  "type": "object",
572
689
  "properties": {
573
- "severity": { "type": "string", "enum": ["critical", "warning", "info"] },
574
- "category": { "type": "string" },
575
- "description": { "type": "string" },
576
- "suggestion": { "type": "string" }
690
+ "risk": { "type": "string" },
691
+ "value": { "type": "integer" },
692
+ "threshold": { "type": "integer" },
693
+ "status": { "type": "string", "enum": ["ACCEPTABLE", "WARNING", "CRITICAL", "MONITORED"] }
577
694
  }
578
695
  }
696
+ },
697
+ "warnings": {
698
+ "type": "array",
699
+ "description": "Non-blocking warnings and observations",
700
+ "items": { "type": "string" }
701
+ },
702
+ "decision": {
703
+ "type": "object",
704
+ "description": "Final approval decision",
705
+ "properties": {
706
+ "approved": { "type": "boolean" },
707
+ "reason": { "type": "string" },
708
+ "approvalMode": { "type": "string", "enum": ["standard", "micro", "delta", "force"] },
709
+ "approvedBy": { "type": "string" },
710
+ "approvedAt": { "type": "string", "format": "date-time" }
711
+ }
579
712
  }
580
713
  }
581
714
  },
@@ -599,12 +732,40 @@
599
732
  },
600
733
  "brToCodeMapping": {
601
734
  "type": "array",
735
+ "description": "Maps each business rule to its implementation points across layers. Derived from analysis.businessRules[].",
736
+ "items": {
737
+ "type": "object",
738
+ "required": ["ruleId", "implementationPoints"],
739
+ "properties": {
740
+ "ruleId": { "type": "string", "description": "BR-XXX identifier from analysis.businessRules" },
741
+ "title": { "type": "string", "description": "Business rule title" },
742
+ "implementationPoints": {
743
+ "type": "array",
744
+ "items": {
745
+ "type": "object",
746
+ "required": ["layer", "component"],
747
+ "properties": {
748
+ "layer": { "type": "string", "description": "Architecture layer (Domain, Application, Infrastructure, API, Frontend)" },
749
+ "component": { "type": "string", "description": "File name (e.g., Order.cs, OrdersController.cs)" },
750
+ "method": { "type": "string", "description": "Method or attribute (e.g., Validate(), [Authorize])" },
751
+ "implementation": { "type": "string", "description": "How the rule is enforced" }
752
+ }
753
+ }
754
+ }
755
+ }
756
+ }
757
+ },
758
+ "apiEndpointSummary": {
759
+ "type": "array",
760
+ "description": "Subset of specification.apiEndpoints enriched with linkedUC. MUST use EXACT same routes as specification.",
602
761
  "items": {
603
762
  "type": "object",
604
763
  "properties": {
605
- "ruleId": { "type": "string" },
606
- "implementationFile": { "type": "string" },
607
- "pattern": { "type": "string" }
764
+ "operation": { "type": "string" },
765
+ "method": { "type": "string", "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] },
766
+ "route": { "type": "string", "description": "MUST match specification.apiEndpoints[].path exactly" },
767
+ "linkedUC": { "type": "string" },
768
+ "permissions": { "type": "string" }
608
769
  }
609
770
  }
610
771
  },
@@ -619,7 +780,7 @@
619
780
  "properties": {
620
781
  "id": { "type": "integer" },
621
782
  "description": { "type": "string" },
622
- "category": { "type": "string", "enum": ["domain", "application", "infrastructure", "api", "frontend", "i18n", "test", "validation"] },
783
+ "category": { "type": "string", "enum": ["domain", "seedData", "seedDataCore", "application", "infrastructure", "api", "frontend", "i18n", "test", "validation"] },
623
784
  "dependencies": { "type": "array", "items": { "type": "integer" } },
624
785
  "acceptanceCriteria": { "type": "string" }
625
786
  }
@@ -654,15 +815,18 @@
654
815
 
655
816
  "changelog": {
656
817
  "type": "array",
657
- "description": "Version history and change tracking",
818
+ "description": "Version history and change tracking. Each step adds an entry.",
658
819
  "items": {
659
820
  "type": "object",
660
- "required": ["version", "timestamp", "changes"],
821
+ "required": ["timestamp", "changes"],
661
822
  "properties": {
662
- "version": { "type": "string" },
823
+ "step": { "type": "string", "description": "Step identifier (e.g., step-01-analyse, step-02-specify, step-03-validate, step-04-handoff)" },
824
+ "version": { "type": "string", "description": "Feature version at this point" },
663
825
  "timestamp": { "type": "string", "format": "date-time" },
664
826
  "author": { "type": "string" },
665
- "changes": { "type": "array", "items": { "type": "string" } }
827
+ "changes": { "type": "array", "items": { "type": "string" } },
828
+ "warnings": { "type": "array", "items": { "type": "string" }, "description": "Non-blocking warnings from this step" },
829
+ "decision": { "type": "string", "description": "Step decision outcome (e.g., approved, rejected)" }
666
830
  }
667
831
  }
668
832
  }
@@ -679,8 +843,14 @@
679
843
  },
680
844
  "fileSpec": {
681
845
  "type": "object",
846
+ "required": ["path", "type"],
682
847
  "properties": {
683
848
  "path": { "type": "string", "description": "Relative file path" },
849
+ "type": { "type": "string", "description": "File type (Entity, Service, DTO, Repository, Migration, HasData, Constants, ApiController, Page, Component, ApiClient, ReduxSlice, UnitTests, IntegrationTests, Enum)" },
850
+ "linkedFRs": { "type": "array", "items": { "type": "string" }, "description": "Linked functional requirement IDs (FR-XXX)" },
851
+ "linkedUCs": { "type": "array", "items": { "type": "string" }, "description": "Linked use case IDs (UC-XXX)" },
852
+ "category": { "type": "string", "enum": ["core", "business"], "description": "SeedData category: core (navigation/permissions) or business (lookup tables)" },
853
+ "source": { "type": "string", "description": "Source path in feature.json for derivation (e.g., specification.seedDataCore.permissions)" },
684
854
  "description": { "type": "string" },
685
855
  "pattern": { "type": "string", "description": "Reference pattern from existing codebase" },
686
856
  "instructions": { "type": "string" }
@@ -150,7 +150,7 @@ Skip categories already answered in previous version.
150
150
  **ULTRATHINK checkpoints during elicitation:**
151
151
  - After Q1: If answer is solution-oriented → reframe to problem
152
152
  - After Q2: If only 1-2 stakeholder types → probe for hidden users
153
- - After Q3: If > 5 Must-Have → ask to prioritize for v1
153
+ - After Q3: If > 5 Must-Have → ask to prioritize implementation order
154
154
  - If answer is vague → apply Technique 3 (Concrete Scenario)
155
155
  - If any answer contains permission attributes → flag as RBAC violation
156
156
 
@@ -428,6 +428,24 @@ Use **ba-writer** agent to enrich:
428
428
  - `discovery` section: problem, asIs, toBe, trigger, stakeholders, scope, risks, codebaseContext, openQuestions
429
429
  - `analysis` section: objectives, businessRules, entities, processFlow, integrations, crossModuleImpact, dataLifecycle, suggestions
430
430
  - Update `metadata.steps.analyse.status = "completed"`
431
+ - **Initialize changelog** with first entry:
432
+
433
+ ```json
434
+ {
435
+ "changelog": [
436
+ {
437
+ "step": "step-01-analyse",
438
+ "timestamp": "2025-02-01T09:00:00Z",
439
+ "changes": [
440
+ "Discovery: {stakeholder_count} stakeholders, {scope_must} must-haves",
441
+ "Analysis: {br_count} business rules, {entity_count} entities, {obj_count} objectives",
442
+ "Suggestions: {accepted_count}/{total_count} accepted",
443
+ "Risks: {risk_count} identified ({critical_count} critical)"
444
+ ]
445
+ }
446
+ ]
447
+ }
448
+ ```
431
449
 
432
450
  ---
433
451
 
@@ -386,6 +386,55 @@ These tables are:
386
386
 
387
387
  **Micro mode:** Minimal: Status and Category only. Skip Priority, Type, Department.
388
388
 
389
+ ### 6.4 SeedData Core (OBLIGATOIRE)
390
+
391
+ > **CRITIQUE :** Sans ces SeedData, le module sera invisible dans la navigation et toutes les API retourneront 403.
392
+
393
+ En plus des 5 tables metier ci-dessus, generer une section `specification.seedDataCore` derivee automatiquement de `specification.navigation` et `specification.permissionMatrix` :
394
+
395
+ ```json
396
+ {
397
+ "seedDataCore": {
398
+ "navigationModules": [
399
+ {
400
+ "code": "fleet",
401
+ "label": "Fleet Management",
402
+ "icon": "Truck",
403
+ "route": "/business/freebike/fleet",
404
+ "parentCode": "freebike",
405
+ "sort": 1
406
+ }
407
+ ],
408
+ "navigationTranslations": [
409
+ { "moduleCode": "fleet", "language": "fr", "label": "Gestion de Flotte" },
410
+ { "moduleCode": "fleet", "language": "en", "label": "Fleet Management" }
411
+ ],
412
+ "permissions": [
413
+ { "path": "business.freebike.fleetmanagement.vehicles.read", "action": "read", "description": "Consulter vehicules" }
414
+ ],
415
+ "rolePermissions": [
416
+ { "role": "FreeBike Admin", "permissionPath": "business.freebike.fleetmanagement.*" },
417
+ { "role": "FreeBike Manager", "permissionPath": "business.freebike.fleetmanagement.vehicles.read" }
418
+ ],
419
+ "permissionConstants": [
420
+ { "constantName": "VehiclesRead", "path": "business.freebike.fleetmanagement.vehicles.read" }
421
+ ]
422
+ }
423
+ }
424
+ ```
425
+
426
+ **Regles de derivation :**
427
+
428
+ 1. **navigationModules** : Prendre chaque entree de `specification.navigation.entries[]` avec level = module ou section
429
+ 2. **navigationTranslations** : Prendre chaque entree de navigation et generer une traduction par langue definie dans `labels`
430
+ 3. **permissions** : Copier EXACTEMENT `specification.permissionMatrix.permissions[]` (chemins complets)
431
+ 4. **rolePermissions** : Pour chaque `roleAssignments[]`, expander les wildcards et raccourcis en chemins complets. Chaque `permissionPath` DOIT correspondre a un `permissions[].path` existant
432
+ 5. **permissionConstants** : Pour chaque permission, generer un nom PascalCase (ex: `business.freebike.fleetmanagement.vehicles.read` → `VehiclesRead`)
433
+
434
+ **Verification :** Chaque `rolePermissions[].permissionPath` doit exister dans `permissions[].path`. Si un raccourci est utilise (ex: `vehicles.read` au lieu du chemin complet), c'est une ERREUR.
435
+
436
+ **Micro mode :** Generer avec les permissions CRUD minimales pour le role Admin uniquement.
437
+
389
438
  ---
390
439
 
391
440
  ## 7. Section 5: UI Wireframes
@@ -578,7 +627,15 @@ For each input field, specify validation rules:
578
627
  }
579
628
  ```
580
629
 
581
- ### 9.2 Business Messages
630
+ ### 9.2 Business Messages (OBLIGATOIRE - minimum 4 messages)
631
+
632
+ > **OBLIGATOIRE :** Generer au minimum 4 messages metier dans `specification.messages[]` :
633
+ > - 1 success (ex: RESOURCE_CREATED_SUCCESS)
634
+ > - 1 error CRUD (ex: RESOURCE_CREATION_FAILED)
635
+ > - 1 error validation (ex: RESOURCE_NOT_FOUND)
636
+ > - 1 error permission (ex: PERMISSION_DENIED)
637
+ >
638
+ > Pour les modules complexes, ajouter des messages warning (ex: STOCK_LOW_WARNING, SYNC_FAILED_WARNING).
582
639
 
583
640
  Define user-facing messages for operations:
584
641
 
@@ -621,7 +678,11 @@ Define user-facing messages for operations:
621
678
 
622
679
  ## 10. Section 8: Data Lifecycle & Cross-Module Patterns
623
680
 
624
- ### 10.1 Entity Lifecycle States
681
+ ### 10.1 Entity Lifecycle States (OBLIGATOIRE - minimum 1 entite)
682
+
683
+ > **OBLIGATOIRE :** Pour chaque entite ayant un attribut `Status` ou `State` (enum), definir une state machine dans `specification.lifeCycles[]`.
684
+ > Identifier les entites candidates en parcourant `analysis.entities[].attributes[]` pour trouver celles avec un champ de type enum representant un statut.
685
+ > Minimum : 1 entite avec state machine. Pour un full-module, typiquement 2-4 entites.
625
686
 
626
687
  Define state transitions for key entities:
627
688
 
@@ -776,6 +837,8 @@ Ensure:
776
837
 
777
838
  Use `ba-writer.updateSpecification()` to write:
778
839
 
840
+ > **INTERDIT :** Ne JAMAIS generer de fichier `specification.json` separe. Toute la specification DOIT rester dans `feature.json.specification`. Un fichier specification.json separe cree une double source de verite et sera ecrase/ignore.
841
+
779
842
  ```json
780
843
  {
781
844
  "specification": {
@@ -790,6 +853,7 @@ Use `ba-writer.updateSpecification()` to write:
790
853
  "validations": [...],
791
854
  "messages": [...],
792
855
  "lifeCycles": [...],
856
+ "seedDataCore": {...},
793
857
  "crossModuleDependencies": [...],
794
858
  "auditingPolicy": {...},
795
859
  "archivalPolicy": {...}
@@ -813,6 +877,8 @@ Use cases created: 8 (UC-001 → UC-008)
813
877
  Requirements: 12 FRs linked to business rules
814
878
  Permission matrix: 3 resources × 4 actors
815
879
  SeedData tables: 5 mandatory (Status, Priority, Category, Type, Department)
880
+ SeedData Core: 5 files (Navigation, Translations, Permissions, RolePermissions, Constants)
881
+ Messages: 6 business messages (success, error, warning)
816
882
  UI screens: 6 wireframes (List, Create, Detail, Reports, etc.)
817
883
  Gherkin scenarios: 18 scenarios across all UCs
818
884
  Validations: 24 field validation rules
@@ -123,6 +123,56 @@ Validate 5 mandatory SeedData tables defined in navigationHierarchy:
123
123
 
124
124
  **Issue:** Missing SeedData table → validation ERROR, must specify before approval.
125
125
 
126
+ ### 2.4 SeedData Core Presence (BLOQUANT)
127
+
128
+ > **CRITIQUE :** Cette validation est BLOQUANTE. Si `specification.seedDataCore` est absent ou incomplet, la validation DOIT echouer avec une ERREUR (pas un warning). Sans ces fichiers, le module sera invisible et toutes les API retourneront 403.
129
+
130
+ Verifier la presence et le contenu de `specification.seedDataCore` :
131
+
132
+ ```json
133
+ {
134
+ "seedDataCoreChecks": [
135
+ {
136
+ "section": "navigationModules",
137
+ "minimum": 1,
138
+ "status": "PASS|FAIL",
139
+ "description": "Au moins 1 entree de navigation module"
140
+ },
141
+ {
142
+ "section": "navigationTranslations",
143
+ "minimum": 1,
144
+ "status": "PASS|FAIL",
145
+ "description": "Au moins 1 traduction par module"
146
+ },
147
+ {
148
+ "section": "permissions",
149
+ "minimum": 3,
150
+ "status": "PASS|FAIL",
151
+ "description": "Au moins 3 permissions (Read, Create, Update minimum)"
152
+ },
153
+ {
154
+ "section": "rolePermissions",
155
+ "minimum": 2,
156
+ "status": "PASS|FAIL",
157
+ "description": "Au moins 2 associations role-permission (Admin + 1 autre role)"
158
+ },
159
+ {
160
+ "section": "permissionConstants",
161
+ "minimum": 3,
162
+ "status": "PASS|FAIL",
163
+ "description": "Au moins 3 constantes compile-time"
164
+ }
165
+ ]
166
+ }
167
+ ```
168
+
169
+ **Regles :**
170
+ - Chaque `rolePermissions[].permissionPath` DOIT exister dans `permissions[].path` → ERREUR sinon
171
+ - Chaque `permissions[].path` DOIT utiliser le format complet `business.{app}.{module}.{resource}.{action}` → ERREUR sinon
172
+ - Les `permissionConstants[].path` DOIVENT correspondre a `permissions[].path` → ERREUR sinon
173
+
174
+ **Issue:** Section seedDataCore absente ou incomplete → validation ERREUR BLOQUANTE, retour a step-02.
175
+
126
176
  ---
127
177
 
128
178
  ## 3. Consistency Checks
@@ -307,6 +357,63 @@ Cross-check all BR references:
307
357
  - ✓ PASS: UC has ≥2 scenarios
308
358
  - ✗ FAIL: Add Gherkin scenarios or remove UC
309
359
 
360
+ ### 3.7 API Routes Format Consistency
361
+
362
+ **Rule:** All routes in `specification.apiEndpoints[].path` MUST use the same prefix format. No mixing of `/api/freebike/...`, `/api/v1/...`, and `/api/...` in the same specification.
363
+
364
+ ```json
365
+ {
366
+ "checks": [
367
+ {
368
+ "check": "API route prefix consistency",
369
+ "prefixes": ["/api/freebike/"],
370
+ "inconsistent": [],
371
+ "status": "PASS"
372
+ },
373
+ {
374
+ "check": "API route prefix consistency",
375
+ "prefixes": ["/api/freebike/", "/api/v1/"],
376
+ "inconsistent": ["GET /api/v1/vehicles conflicts with POST /api/freebike/vehicles"],
377
+ "status": "FAIL"
378
+ }
379
+ ]
380
+ }
381
+ ```
382
+
383
+ **Resolution:**
384
+ - ✓ PASS: All routes use the same prefix
385
+ - ✗ FAIL: Harmonize all routes to use a single prefix pattern
386
+
387
+ ### 3.8 Permission Format Consistency
388
+
389
+ **Rule:** All permissions in `permissionMatrix.roleAssignments[].permissions[]` MUST use full path format matching `permissionMatrix.permissions[].path`. No shortcuts (e.g., `vehicles.read` instead of `business.freebike.fleetmanagement.vehicles.read`).
390
+
391
+ ```json
392
+ {
393
+ "checks": [
394
+ {
395
+ "check": "Permission path format in roleAssignments",
396
+ "fullPaths": 15,
397
+ "shortcuts": 0,
398
+ "status": "PASS"
399
+ },
400
+ {
401
+ "check": "Permission path format in roleAssignments",
402
+ "fullPaths": 5,
403
+ "shortcuts": 10,
404
+ "inconsistentExamples": ["vehicles.read should be business.freebike.fleetmanagement.vehicles.read"],
405
+ "status": "FAIL"
406
+ }
407
+ ]
408
+ }
409
+ ```
410
+
411
+ **Exception:** Le wildcard `business.{app}.{module}.*` est accepte pour le role Admin.
412
+
413
+ **Resolution:**
414
+ - ✓ PASS: All roleAssignment permissions use full paths
415
+ - ✗ FAIL: Replace shortcuts with full paths from permissionMatrix.permissions[].path
416
+
310
417
  ---
311
418
 
312
419
  ## 4. SmartStack Convention Validation
@@ -441,6 +548,22 @@ Check role escalation:
441
548
  }
442
549
  ```
443
550
 
551
+ ### 4.5 Orphan File Detection
552
+
553
+ **Rule:** The feature.json is the SINGLE source of truth. No separate JSON files should exist.
554
+
555
+ ```
556
+ CHECK orphan files:
557
+ - specification.json exists in output directory → WARNING ⚠
558
+ "Double source of vérité détectée : specification.json est un fichier orphelin.
559
+ Toute la spécification doit être dans feature.json.specification.
560
+ SUPPRIMER specification.json et vérifier que feature.json.specification est complet."
561
+ - analysis.json exists in output directory → WARNING ⚠
562
+ "Fichier analysis.json orphelin détecté. L'analyse doit être dans feature.json.analysis."
563
+ ```
564
+
565
+ **Action :** Si un fichier orphelin est détecté, ajouter un WARNING dans `validation.warnings[]` et recommander la suppression.
566
+
444
567
  ---
445
568
 
446
569
  ## 5. Risk Assessment
@@ -836,7 +959,31 @@ Use `ba-writer.updateStatus("approved")` to mark validation complete.
836
959
 
837
960
  (Or keep status as "specified" if rejected.)
838
961
 
839
- ### 9.3 User Confirmation (Standard Mode)
962
+ ### 9.3 Update Changelog
963
+
964
+ **OBLIGATOIRE :** Ajouter une entrée changelog pour step-03 via `ba-writer` :
965
+
966
+ ```json
967
+ {
968
+ "changelog": [
969
+ {
970
+ "step": "step-03-validate",
971
+ "timestamp": "2025-02-01T10:35:00Z",
972
+ "changes": [
973
+ "Validation complétude : X/Y sections présentes",
974
+ "Validation cohérence : X cross-references vérifiées",
975
+ "Validation conventions : nommage, RBAC, structure",
976
+ "Évaluation risques : NIVEAU (détails)",
977
+ "Décision : APPROVED/REJECTED (mode: standard/micro/force)"
978
+ ],
979
+ "warnings": ["Liste des warnings éventuels"],
980
+ "decision": "approved"
981
+ }
982
+ ]
983
+ }
984
+ ```
985
+
986
+ ### 9.4 User Confirmation (Standard Mode)
840
987
 
841
988
  ```
842
989
  ✓ Validation complete - APPROVED