@atlashub/smartstack-cli 4.75.0 → 4.76.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 (54) hide show
  1. package/dist/index.js +87 -41
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/skills/apex/SKILL.md +2 -2
  5. package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
  6. package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
  7. package/templates/skills/apex/references/core-seed-data.md +20 -18
  8. package/templates/skills/apex/references/post-checks.md +18 -1
  9. package/templates/skills/apex/references/smartstack-api.md +4 -4
  10. package/templates/skills/apex/references/smartstack-frontend.md +1 -1
  11. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  12. package/templates/skills/apex/steps/step-00-init.md +1 -1
  13. package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
  14. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
  15. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  16. package/templates/skills/apex-verify/SKILL.md +110 -0
  17. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  18. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  19. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
  20. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  21. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  22. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  23. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  24. package/templates/skills/application/templates-frontend.md +2 -2
  25. package/templates/skills/business-analyse/SKILL.md +3 -3
  26. package/templates/skills/business-analyse/_shared.md +37 -0
  27. package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
  28. package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
  29. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  30. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  31. package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
  32. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
  33. package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
  34. package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
  35. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
  36. package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
  37. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
  38. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  39. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  40. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +6 -3
  41. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
  42. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
  43. package/templates/skills/business-analyse-html/references/data-build.md +2 -0
  44. package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
  45. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
  46. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  47. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  48. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  49. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  50. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  51. package/templates/skills/dev-start/SKILL.md +7 -7
  52. package/templates/skills/sketch/SKILL.md +15 -153
  53. package/templates/skills/ui-components/SKILL.md +1 -1
  54. package/templates/skills/ui-components/patterns/data-table.md +1 -1
@@ -1,8 +1,8 @@
1
- # Domain Heuristics — /sketch
1
+ # Domain Heuristics — /business-analyse-quick
2
2
 
3
- > **Purpose:** Accelerate entity/property inference for common business domains.
3
+ > **Purpose:** Accelerate entity/property/section/rule inference for common business domains.
4
4
  > This table is an **accelerator**, not a limiter. If the user's domain is not listed,
5
- > the LLM infers entities and properties from general knowledge.
5
+ > the LLM infers entities, properties, sections, and rules from general knowledge.
6
6
 
7
7
  ---
8
8
 
@@ -12,6 +12,10 @@
12
12
  labels: { fr: "Employé", en: "Employee", it: "Dipendente", de: "Mitarbeiter" }
13
13
  - **Department** (Name:string, Code:string, ParentDepartmentId:Guid?→Department)
14
14
  labels: { fr: "Département", en: "Department", it: "Dipartimento", de: "Abteilung" }
15
+ - typicalSections: list, detail, orgchart (functional)
16
+ - typicalRules: "Employee must have unique email", "Department cannot be its own parent"
17
+ - personExtension: Employee
18
+ - typicalRoles: [{ role: "HR Admin", level: "admin" }, { role: "HR Manager", level: "manager" }, { role: "Employee", level: "viewer" }]
15
19
 
16
20
  ## Absences / Congés
17
21
 
@@ -19,6 +23,10 @@
19
23
  labels: { fr: "Absence", en: "Absence", it: "Assenza", de: "Abwesenheit" }
20
24
  - **AbsenceType** (Name:string, Code:string, MaxDays:int, IsPaid:bool)
21
25
  labels: { fr: "Type d'absence", en: "Absence Type", it: "Tipo di assenza", de: "Abwesenheitstyp" }
26
+ - typicalSections: list, detail, calendar (functional), approve (functional)
27
+ - typicalRules: "StartDate must be before EndDate", "Cannot exceed MaxDays per type per year", "Approval required for > 3 days"
28
+ - workflow: Draft → Submitted → Approved/Rejected
29
+ - typicalRoles: [{ role: "HR Admin", level: "admin" }, { role: "Manager", level: "manager" }, { role: "Employee", level: "contributor" }]
22
30
 
23
31
  ## CRM / Clients
24
32
 
@@ -26,6 +34,10 @@
26
34
  labels: { fr: "Client", en: "Customer", it: "Cliente", de: "Kunde" }
27
35
  - **Contact** (FirstName:string, LastName:string, Email:string, Phone:string, CustomerId:Guid→Customer)
28
36
  labels: { fr: "Contact", en: "Contact", it: "Contatto", de: "Kontakt" }
37
+ - typicalSections: list, detail, dashboard (embedded)
38
+ - typicalRules: "Customer must have unique email or phone", "Contact requires valid Customer"
39
+ - personExtension: Customer, Contact
40
+ - typicalRoles: [{ role: "CRM Admin", level: "admin" }, { role: "Sales Manager", level: "manager" }, { role: "Sales Rep", level: "contributor" }]
29
41
 
30
42
  ## Commandes / Orders
31
43
 
@@ -33,6 +45,10 @@
33
45
  labels: { fr: "Commande", en: "Order", it: "Ordine", de: "Bestellung" }
34
46
  - **OrderLine** (Quantity:int, UnitPrice:decimal, TotalPrice:decimal, ProductId:Guid→Product, OrderId:Guid→Order)
35
47
  labels: { fr: "Ligne de commande", en: "Order Line", it: "Riga d'ordine", de: "Bestellposition" }
48
+ - typicalSections: list, detail, dashboard (embedded)
49
+ - typicalRules: "TotalPrice = Quantity * UnitPrice", "Order TotalAmount = sum of lines", "Cannot delete confirmed order"
50
+ - workflow: Draft → Confirmed → Shipped → Delivered/Cancelled
51
+ - typicalRoles: [{ role: "Sales Admin", level: "admin" }, { role: "Sales Manager", level: "manager" }, { role: "Sales Rep", level: "contributor" }]
36
52
 
37
53
  ## Facturation / Invoicing
38
54
 
@@ -40,6 +56,10 @@
40
56
  labels: { fr: "Facture", en: "Invoice", it: "Fattura", de: "Rechnung" }
41
57
  - **InvoiceLine** (Description:string, Quantity:int, UnitPrice:decimal, TotalPrice:decimal, InvoiceId:Guid→Invoice)
42
58
  labels: { fr: "Ligne de facture", en: "Invoice Line", it: "Riga di fattura", de: "Rechnungsposition" }
59
+ - typicalSections: list, detail, dashboard (embedded)
60
+ - typicalRules: "DueDate must be after InvoiceDate", "TotalPrice = Quantity * UnitPrice", "Cannot modify paid invoice"
61
+ - workflow: Draft → Sent → Paid/Overdue/Cancelled
62
+ - typicalRoles: [{ role: "Finance Admin", level: "admin" }, { role: "Accountant", level: "contributor" }, { role: "Viewer", level: "viewer" }]
43
63
 
44
64
  ## Projets / Projects
45
65
 
@@ -49,6 +69,10 @@
49
69
  labels: { fr: "Tâche", en: "Task", it: "Attività", de: "Aufgabe" }
50
70
  - **Milestone** (Name:string, DueDate:DateTime, Status:string, ProjectId:Guid→Project)
51
71
  labels: { fr: "Jalon", en: "Milestone", it: "Traguardo", de: "Meilenstein" }
72
+ - typicalSections: list, detail, board (functional), timeline (functional)
73
+ - typicalRules: "EndDate must be after StartDate", "Task DueDate cannot exceed project EndDate", "Milestone blocks project completion"
74
+ - workflow: Planning → InProgress → Completed/OnHold/Cancelled
75
+ - typicalRoles: [{ role: "Project Admin", level: "admin" }, { role: "Project Manager", level: "manager" }, { role: "Team Member", level: "contributor" }, { role: "Stakeholder", level: "viewer" }]
52
76
 
53
77
  ## Stock / Inventaire
54
78
 
@@ -58,6 +82,9 @@
58
82
  labels: { fr: "Catégorie", en: "Category", it: "Categoria", de: "Kategorie" }
59
83
  - **StockMovement** (Quantity:int, Type:string, Date:DateTime, ProductId:Guid→Product, WarehouseId:Guid?→Warehouse)
60
84
  labels: { fr: "Mouvement de stock", en: "Stock Movement", it: "Movimento di stock", de: "Lagerbewegung" }
85
+ - typicalSections: list, detail, movements (functional), inventory (functional)
86
+ - typicalRules: "Product code must be unique", "Stock cannot go negative", "Category cannot be its own parent"
87
+ - typicalRoles: [{ role: "Inventory Admin", level: "admin" }, { role: "Warehouse Manager", level: "manager" }, { role: "Stock Clerk", level: "contributor" }]
61
88
 
62
89
  ## Contrats / Contracts
63
90
 
@@ -65,6 +92,10 @@
65
92
  labels: { fr: "Contrat", en: "Contract", it: "Contratto", de: "Vertrag" }
66
93
  - **ContractAmendment** (Description:string, Date:DateTime, Amount:decimal?, ContractId:Guid→Contract)
67
94
  labels: { fr: "Avenant", en: "Amendment", it: "Emendamento", de: "Nachtrag" }
95
+ - typicalSections: list, detail, renewals (functional)
96
+ - typicalRules: "EndDate must be after StartDate", "Amendment Date must be within contract period", "Cannot amend terminated contract"
97
+ - workflow: Draft → Active → Expired/Terminated/Renewed
98
+ - typicalRoles: [{ role: "Contract Admin", level: "admin" }, { role: "Contract Manager", level: "manager" }, { role: "Viewer", level: "viewer" }]
68
99
 
69
100
  ## Tickets / Support
70
101
 
@@ -72,6 +103,10 @@
72
103
  labels: { fr: "Ticket", en: "Ticket", it: "Ticket", de: "Ticket" }
73
104
  - **TicketComment** (Content:string, IsInternal:bool, TicketId:Guid→Ticket)
74
105
  labels: { fr: "Commentaire", en: "Comment", it: "Commento", de: "Kommentar" }
106
+ - typicalSections: list, detail, dashboard (embedded), board (functional)
107
+ - typicalRules: "Priority must be set before assignment", "Internal comments not visible to customer", "SLA timer based on priority"
108
+ - workflow: Open → InProgress → Resolved → Closed/Reopened
109
+ - typicalRoles: [{ role: "Support Admin", level: "admin" }, { role: "Support Agent", level: "contributor" }, { role: "Customer", level: "viewer" }]
75
110
 
76
111
  ## Fournisseurs / Suppliers
77
112
 
@@ -79,6 +114,10 @@
79
114
  labels: { fr: "Fournisseur", en: "Supplier", it: "Fornitore", de: "Lieferant" }
80
115
  - **PurchaseOrder** (OrderDate:DateTime, Status:string, TotalAmount:decimal, SupplierId:Guid→Supplier)
81
116
  labels: { fr: "Bon de commande", en: "Purchase Order", it: "Ordine d'acquisto", de: "Bestellung" }
117
+ - typicalSections: list, detail
118
+ - typicalRules: "Supplier must have unique VatNumber", "PO requires valid supplier", "Cannot modify approved PO"
119
+ - workflow: Draft → Submitted → Approved → Received/Cancelled
120
+ - typicalRoles: [{ role: "Procurement Admin", level: "admin" }, { role: "Procurement Manager", level: "manager" }, { role: "Buyer", level: "contributor" }]
82
121
 
83
122
  ## Documents / GED
84
123
 
@@ -86,6 +125,9 @@
86
125
  labels: { fr: "Document", en: "Document", it: "Documento", de: "Dokument" }
87
126
  - **Folder** (Name:string, ParentFolderId:Guid?→Folder)
88
127
  labels: { fr: "Dossier", en: "Folder", it: "Cartella", de: "Ordner" }
128
+ - typicalSections: list, detail, explorer (functional)
129
+ - typicalRules: "Folder cannot be its own parent", "File size limit per tenant", "Unique filename within folder"
130
+ - typicalRoles: [{ role: "Document Admin", level: "admin" }, { role: "Editor", level: "contributor" }, { role: "Reader", level: "viewer" }]
89
131
 
90
132
  ## Réservations / Bookings
91
133
 
@@ -93,6 +135,10 @@
93
135
  labels: { fr: "Réservation", en: "Booking", it: "Prenotazione", de: "Buchung" }
94
136
  - **Resource** (Name:string, Type:string, Capacity:int?, Location:string?)
95
137
  labels: { fr: "Ressource", en: "Resource", it: "Risorsa", de: "Ressource" }
138
+ - typicalSections: list, detail, calendar (functional)
139
+ - typicalRules: "EndDate must be after StartDate", "No overlapping bookings for same resource", "Cannot book past dates"
140
+ - workflow: Pending → Confirmed → Completed/Cancelled
141
+ - typicalRoles: [{ role: "Booking Admin", level: "admin" }, { role: "Booking Manager", level: "manager" }, { role: "Customer", level: "contributor" }]
96
142
 
97
143
  ## Événements / Events
98
144
 
@@ -100,6 +146,9 @@
100
146
  labels: { fr: "Événement", en: "Event", it: "Evento", de: "Veranstaltung" }
101
147
  - **Registration** (Status:string, RegisteredAt:DateTime, EventId:Guid→Event, ContactId:Guid?→Contact)
102
148
  labels: { fr: "Inscription", en: "Registration", it: "Iscrizione", de: "Anmeldung" }
149
+ - typicalSections: list, detail, registrations (functional)
150
+ - typicalRules: "Cannot exceed MaxAttendees", "Registration closes at event StartDate", "EndDate must be after StartDate"
151
+ - typicalRoles: [{ role: "Event Admin", level: "admin" }, { role: "Event Organizer", level: "manager" }, { role: "Attendee", level: "viewer" }]
103
152
 
104
153
  ## Formation / Training
105
154
 
@@ -107,6 +156,9 @@
107
156
  labels: { fr: "Formation", en: "Training", it: "Formazione", de: "Schulung" }
108
157
  - **Enrollment** (Status:string, EnrolledAt:DateTime, TrainingId:Guid→Training, EmployeeId:Guid→Employee)
109
158
  labels: { fr: "Inscription", en: "Enrollment", it: "Iscrizione", de: "Anmeldung" }
159
+ - typicalSections: list, detail, enrollments (functional)
160
+ - typicalRules: "Cannot exceed MaxParticipants", "Employee cannot enroll twice in same training", "Trainer cannot be enrolled"
161
+ - typicalRoles: [{ role: "Training Admin", level: "admin" }, { role: "Trainer", level: "manager" }, { role: "Employee", level: "contributor" }]
110
162
 
111
163
  ## Maintenance / Equipment
112
164
 
@@ -114,3 +166,7 @@
114
166
  labels: { fr: "Équipement", en: "Equipment", it: "Attrezzatura", de: "Ausrüstung" }
115
167
  - **MaintenanceRequest** (Title:string, Description:string, Priority:string, Status:string, EquipmentId:Guid→Equipment, RequestedById:Guid?→Employee)
116
168
  labels: { fr: "Demande de maintenance", en: "Maintenance Request", it: "Richiesta di manutenzione", de: "Wartungsanfrage" }
169
+ - typicalSections: list, detail, schedule (functional)
170
+ - typicalRules: "Equipment must have unique SerialNumber", "Cannot request maintenance on decommissioned equipment"
171
+ - workflow: Submitted → Assigned → InProgress → Completed/Rejected
172
+ - typicalRoles: [{ role: "Maintenance Admin", level: "admin" }, { role: "Technician", level: "contributor" }, { role: "Requester", level: "viewer" }]
@@ -0,0 +1,268 @@
1
+ # PRD Schema Reference — /business-analyse-quick
2
+
3
+ > Schemas for PRD v4.0 and companion specification files.
4
+ > Field names are **identical** to BA schemas for APEX delegate mode compatibility.
5
+
6
+ ---
7
+
8
+ ## PRD v4.0 Structure
9
+
10
+ ```json
11
+ {
12
+ "$version": "4.0.0",
13
+ "project": {
14
+ "application": "{AppPascalCase}",
15
+ "module": "{module-kebab-case}"
16
+ },
17
+ "expectedFiles": {
18
+ "domain": [
19
+ { "path": "src/Domain/Entities/{App}/{Module}/{Entity}.cs", "type": "Entity", "module": "{Module}" }
20
+ ],
21
+ "infrastructure": [
22
+ { "path": "src/Infrastructure/Persistence/Configurations/{App}/{Module}/{Entity}Configuration.cs", "type": "EFConfig", "module": "{Module}" }
23
+ ],
24
+ "seedData": [
25
+ { "path": "src/Infrastructure/Persistence/Seeding/Data/{Module}/NavigationModuleSeedData.cs", "type": "NavModule", "module": "{Module}" },
26
+ { "path": "src/Infrastructure/Persistence/Seeding/Data/{Module}/NavigationSectionSeedData.cs", "type": "NavSection", "module": "{Module}" },
27
+ { "path": "src/Infrastructure/Persistence/Seeding/Data/{Module}/PermissionsSeedData.cs", "type": "Permissions", "module": "{Module}" },
28
+ { "path": "src/Infrastructure/Persistence/Seeding/Data/{Module}/RolesSeedData.cs", "type": "Roles", "module": "{Module}" }
29
+ ],
30
+ "application": [
31
+ { "path": "src/Application/Services/{App}/{Module}/{Entity}Service.cs", "type": "Service", "module": "{Module}" },
32
+ { "path": "src/Application/DTOs/{App}/{Module}/Create{Entity}Dto.cs", "type": "DTO", "module": "{Module}" },
33
+ { "path": "src/Application/DTOs/{App}/{Module}/Update{Entity}Dto.cs", "type": "DTO", "module": "{Module}" },
34
+ { "path": "src/Application/DTOs/{App}/{Module}/{Entity}Dto.cs", "type": "DTO", "module": "{Module}" },
35
+ { "path": "src/Application/Validators/{App}/{Module}/{Entity}Validator.cs", "type": "Validator", "module": "{Module}" }
36
+ ],
37
+ "api": [
38
+ { "path": "src/API/Controllers/{App}/{Entity}Controller.cs", "type": "Controller", "module": "{Module}" }
39
+ ],
40
+ "frontend": [
41
+ { "path": "src/pages/{app-kebab}/{module-kebab}/{Entity}ListPage.tsx", "type": "Page", "module": "{Module}" },
42
+ { "path": "src/pages/{app-kebab}/{module-kebab}/{Entity}DetailPage.tsx", "type": "Page", "module": "{Module}" },
43
+ { "path": "src/pages/{app-kebab}/{module-kebab}/Create{Entity}Page.tsx", "type": "Page", "module": "{Module}" },
44
+ { "path": "src/pages/{app-kebab}/{module-kebab}/Edit{Entity}Page.tsx", "type": "Page", "module": "{Module}" },
45
+ { "path": "src/i18n/locales/fr/{module-lower}.json", "type": "i18n", "module": "{Module}" },
46
+ { "path": "src/i18n/locales/en/{module-lower}.json", "type": "i18n", "module": "{Module}" },
47
+ { "path": "src/i18n/locales/it/{module-lower}.json", "type": "i18n", "module": "{Module}" },
48
+ { "path": "src/i18n/locales/de/{module-lower}.json", "type": "i18n", "module": "{Module}" }
49
+ ],
50
+ "tests": [
51
+ { "path": "src/Tests/Unit/Domain/{App}/{Module}/{Entity}Tests.cs", "type": "UnitTest", "module": "{Module}" },
52
+ { "path": "src/Tests/Integration/{App}/{Module}/{Entity}ControllerTests.cs", "type": "IntegrationTest", "module": "{Module}" }
53
+ ],
54
+ "documentation": []
55
+ },
56
+ "specificationFiles": {
57
+ "entities": "prd-{module}.entities.json",
58
+ "permissions": "prd-{module}.permissions.json",
59
+ "rules": "prd-{module}.rules.json",
60
+ "usecases": "prd-{module}.usecases.json",
61
+ "screens": "prd-{module}.screens.json"
62
+ },
63
+ "tasks": []
64
+ }
65
+ ```
66
+
67
+ > **Paths in `specificationFiles`** are relative to the PRD directory (`.ralph/`).
68
+ > APEX resolves them via `path.join(path.dirname(prd_path), specFiles.xxx)`.
69
+
70
+ ---
71
+
72
+ ## entities.json
73
+
74
+ ```json
75
+ {
76
+ "entities": [
77
+ {
78
+ "name": "Employee",
79
+ "description": "Represents a company employee",
80
+ "personRoleConfig": { "variant": "mandatory", "userFields": ["firstName", "lastName", "email"] },
81
+ "attributes": [
82
+ { "name": "code", "type": "string", "required": true, "unique": true, "searchable": true },
83
+ { "name": "firstName", "type": "string", "required": true },
84
+ { "name": "hireDate", "type": "DateTime", "required": true },
85
+ { "name": "status", "type": "enum", "required": true, "values": ["Active", "Inactive"] }
86
+ ],
87
+ "relationships": [
88
+ { "target": "Department", "type": "ManyToOne", "description": "Employee belongs to a department" }
89
+ ],
90
+ "codePattern": { "strategy": "sequential", "prefix": "emp", "digits": 5, "separator": "-", "includeTenantSlug": true },
91
+ "estimatedVolume": { "monthly": 20, "total2y": 500 }
92
+ }
93
+ ]
94
+ }
95
+ ```
96
+
97
+ **Rules:**
98
+ - `personRoleConfig` only if entity has personal attributes (→ FK to auth_Users with type `string`, NOT Guid)
99
+ - Omit boolean flags when `false` (unique, searchable, computed)
100
+ - NO technical fields (Id, TenantId, CreatedBy, ModifiedBy, etc.)
101
+ - `type`: string | int | decimal | long | bool | DateTime | Guid | enum
102
+
103
+ ---
104
+
105
+ ## rules.json
106
+
107
+ ```json
108
+ {
109
+ "rules": [
110
+ {
111
+ "id": "BR-VAL-EMP-001",
112
+ "name": "Hire date validation",
113
+ "category": "validation",
114
+ "statement": "Hire date cannot be in the future",
115
+ "severity": "blocking",
116
+ "priority": "must",
117
+ "entities": ["Employee"],
118
+ "examples": [
119
+ { "input": "hireDate = 2027-01-01", "expected": "Error: date in the future" }
120
+ ]
121
+ }
122
+ ]
123
+ }
124
+ ```
125
+
126
+ **ID format:** `BR-{CAT}-{MODULE}-{NNN}` where CAT = VAL|CALC|WFL|SEC|DAT|NOT
127
+ **Categories:** validation | calculation | workflow | security | data | notification
128
+ - `formula` field required for `calculation` category
129
+ - `conditions` + `consequences` fields required for `workflow` category
130
+
131
+ ---
132
+
133
+ ## usecases.json
134
+
135
+ ```json
136
+ {
137
+ "useCases": [
138
+ {
139
+ "id": "UC-EMP-001",
140
+ "name": "Create employee",
141
+ "sectionCode": "list",
142
+ "primaryActor": "HR Manager",
143
+ "permission": "HumanResources.Employees.Create",
144
+ "preconditions": ["User has HumanResources.Employees.Create permission"],
145
+ "mainScenario": [
146
+ "User opens the creation page",
147
+ "User fills mandatory fields",
148
+ "User submits the form",
149
+ "System validates business rules",
150
+ "System creates the employee with Active status"
151
+ ],
152
+ "alternativeScenarios": [
153
+ { "name": "Invalid data", "steps": ["System displays validation errors", "User corrects and resubmits"] }
154
+ ],
155
+ "postconditions": ["Employee created with status Active"],
156
+ "businessRules": ["BR-VAL-EMP-001"],
157
+ "result": "Employee is created with Active status"
158
+ }
159
+ ]
160
+ }
161
+ ```
162
+
163
+ **Field naming:** `useCases` (NOT usecases), `primaryActor` (NOT actor), `mainScenario` as string[]
164
+
165
+ ---
166
+
167
+ ## permissions.json
168
+
169
+ ```json
170
+ {
171
+ "roles": [
172
+ { "role": "HR Admin", "level": "admin", "description": "Full access to HR module" },
173
+ { "role": "Manager", "level": "manager", "description": "Read, create, update and approve access" },
174
+ { "role": "Viewer", "level": "viewer", "description": "Read-only access" }
175
+ ],
176
+ "permissionPaths": [
177
+ "HumanResources.Employees.Read",
178
+ "HumanResources.Employees.Create",
179
+ "HumanResources.Employees.Update",
180
+ "HumanResources.Employees.Delete",
181
+ "HumanResources.Employees.Export",
182
+ "HumanResources.Employees.Approve"
183
+ ],
184
+ "matrix": {
185
+ "roleAssignments": [
186
+ { "role": "HR Admin", "permissions": ["HumanResources.Employees.*"] },
187
+ { "role": "Manager", "permissions": ["HumanResources.Employees.Read", "HumanResources.Employees.Create", "HumanResources.Employees.Update", "HumanResources.Employees.Approve"] },
188
+ { "role": "Viewer", "permissions": ["HumanResources.Employees.Read"] }
189
+ ]
190
+ }
191
+ }
192
+ ```
193
+
194
+ **Role rules:**
195
+ - `role`: display name, PascalCase words with spaces (e.g., "HR Admin", NOT "hr-admin")
196
+ - `level`: one of `admin` | `manager` | `contributor` | `viewer` — determines hierarchy position
197
+ - `description`: one sentence in `{conversation_language}`
198
+ - MINIMUM 2 roles: one admin-level + one operational role
199
+
200
+ **Permission path format:**
201
+ - 3-segment (standard): `{ApplicationPascalCase}.{ModulePascalCase}.{Action}`
202
+ - 4-segment (custom section): `{ApplicationPascalCase}.{ModulePascalCase}.{SectionPascalCase}.{Action}`
203
+ - All segments are PascalCase — NO hyphens, NO spaces, NO kebab-case
204
+ - Module name conversion: `absence-management` → `AbsenceManagement`
205
+
206
+ **Standard actions:** Read, Create, Update, Delete, Export, Import, Approve, Admin
207
+
208
+ **Matrix rules:**
209
+ - Admin role ALWAYS gets wildcard: `["{base}.*"]`
210
+ - Other roles get EXPLICIT path lists (no wildcards)
211
+ - Every role in `roleAssignments` MUST exist in `roles[]`
212
+ - Every permission path in `roleAssignments` MUST exist in `permissionPaths` (except wildcards ending with `.*`)
213
+
214
+ **Cross-validation requirements:**
215
+ - Every `permission` field in usecases.json MUST exist in `permissionPaths`
216
+ - Every section `permission` in screens.json (where `permissionMode` is not `"inherit"`) MUST exist in `permissionPaths`
217
+ - `permissionPaths.length >= 4` (Read + Create + Update + Delete minimum per module)
218
+
219
+ ---
220
+
221
+ ## screens.json
222
+
223
+ ```json
224
+ {
225
+ "sections": [
226
+ {
227
+ "code": "list",
228
+ "sectionType": "primary",
229
+ "permissionMode": "crud",
230
+ "parentSectionCode": null,
231
+ "labels": { "fr": "Employés", "en": "Employees", "it": "Dipendenti", "de": "Mitarbeiter" },
232
+ "route": "/{app-kebab}/{module-kebab}/list",
233
+ "icon": "users",
234
+ "permission": "HumanResources.Employees.Read",
235
+ "useCases": ["UC-EMP-001", "UC-EMP-002"],
236
+ "resources": [
237
+ {
238
+ "code": "employees-grid",
239
+ "type": "SmartTable",
240
+ "entity": "Employee",
241
+ "columns": [
242
+ { "field": "code", "label": { "fr": "Code", "en": "Code" }, "format": "text", "sortable": true },
243
+ { "field": "firstName", "label": { "fr": "Prénom", "en": "First Name" }, "format": "text" },
244
+ { "field": "status", "label": { "fr": "Statut", "en": "Status" }, "format": "badge" }
245
+ ],
246
+ "actions": ["create", "edit", "delete"],
247
+ "defaultSort": { "field": "code", "direction": "asc" },
248
+ "defaultPageSize": 20
249
+ }
250
+ ]
251
+ }
252
+ ]
253
+ }
254
+ ```
255
+
256
+ **Section types:** primary (menu-visible, includes implicit detail/create/edit pages) | functional (independent workflow) | embedded (widget/tab)
257
+ **Resource types:** SmartTable | SmartForm | DetailCard | KpiPanel | KpiCard | Timeline | Chart | FilterBar | Tabs
258
+
259
+ > **IMPORTANT — Implicit pages (NOT sections):**
260
+ > APEX automatically generates detail (/:id), create (/create), and edit (/:id/edit) pages
261
+ > as routes under each primary section. Do NOT include "detail", "create", "edit", or "*-detail"
262
+ > as sections in screens.json — they are implicit APEX pages, not navigation entries.
263
+ >
264
+ > **API route convention:**
265
+ > - `route` field is the frontend React Router path (e.g., `/human-resources/employees/list`)
266
+ > - The backend API endpoint is `/api/{app-kebab}/{module-kebab}` (API routes never include section codes)
267
+ > - GetById: `GET /api/{app-kebab}/{module-kebab}/{id}`
268
+ > - GetAll: `GET /api/{app-kebab}/{module-kebab}` with pagination query params
@@ -153,11 +153,17 @@ reverseBusinessRule(htmlBR, existingBR) {
153
153
  name: htmlBR.name,
154
154
  category: htmlBR.category,
155
155
  statement: htmlBR.statement,
156
+ severity: htmlBR.severity || existingBR?.severity || "blocking",
157
+ sectionCode: htmlBR.sectionCode || existingBR?.sectionCode || "",
158
+ entities: htmlBR.entities || existingBR?.entities || [],
156
159
  priority: existingBR?.priority || "must",
157
160
  conditions: existingBR?.conditions || [],
158
161
  examples: htmlBR.example
159
162
  ? [{ input: htmlBR.example, expected: "" }]
160
163
  : (existingBR?.examples || []),
164
+ consequences: existingBR?.consequences || [],
165
+ formula: htmlBR.formula || existingBR?.formula || null,
166
+ domainSpecific: existingBR?.domainSpecific ?? false,
161
167
  testability: existingBR?.testability || ""
162
168
  };
163
169
  }
@@ -87,9 +87,9 @@ echo "=== MODE ==="
87
87
  echo "=== ENV ==="
88
88
  cat web/smartstack-web/.env.development 2>/dev/null || cat web/smartstack-web/.env.standalone 2>/dev/null || echo "NONE"
89
89
  echo "=== SQL ==="
90
- CS=$(grep -oP '"DefaultConnection":\s*"\K[^"]+' src/SmartStack.Api/appsettings.json 2>/dev/null)
91
- SN=$(echo "$CS" | grep -oiP '(?:Server|Data Source)=\K[^;]+' | sed 's/(local)/./')
92
- DB=$(echo "$CS" | grep -oiP '(?:Database|Initial Catalog)=\K[^;]+')
90
+ CS=$(sed -n 's/.*"DefaultConnection":\s*"\([^"]*\)".*/\1/p' src/SmartStack.Api/appsettings.json 2>/dev/null)
91
+ SN=$(echo "$CS" | sed -n 's/.*[Ss]erver=\([^;]*\).*/\1/p' | sed 's/(local)/./')
92
+ DB=$(echo "$CS" | sed -n 's/.*[Dd]atabase=\([^;]*\).*/\1/p')
93
93
  sqlcmd -S "$SN" -Q "SET NOCOUNT ON;SELECT name FROM sys.databases WHERE name='$DB'" -t 5 -E -C -h -1 -W 2>/dev/null | grep -q "$DB" && echo "ok $SN $DB" || echo "warn $SN $DB"
94
94
  ```
95
95
 
@@ -177,8 +177,8 @@ for i in $(seq 1 20); do
177
177
  sleep 2
178
178
  done
179
179
  RESULT=$(timeout 20 smartstack admin reset --connection '{connection_string}' --force --json 2>&1 | grep -E '^\{')
180
- EMAIL=$(echo "$RESULT" | grep -oP '"email":"\K[^"]+')
181
- PASS=$(echo "$RESULT" | grep -oP '"password":"\K[^"]+')
180
+ EMAIL=$(echo "$RESULT" | sed -n 's/.*"email":"\([^"]*\)".*/\1/p')
181
+ PASS=$(echo "$RESULT" | sed -n 's/.*"password":"\([^"]*\)".*/\1/p')
182
182
  cat > .claude/dev-session.json << SESSEOF
183
183
  {"admin_email":"$EMAIL","admin_password":"$PASS","last_reset":"$(date -Iseconds)","api_port":{api_port},"web_port":{web_port},"environment":"{mode}","status":"ready"}
184
184
  SESSEOF
@@ -189,8 +189,8 @@ echo "CREDENTIALS: $EMAIL / $PASS"
189
189
  Single Bash — reset only (no wait needed):
190
190
  ```bash
191
191
  RESULT=$(timeout 20 smartstack admin reset --connection '{connection_string}' --force --json 2>&1 | grep -E '^\{')
192
- EMAIL=$(echo "$RESULT" | grep -oP '"email":"\K[^"]+')
193
- PASS=$(echo "$RESULT" | grep -oP '"password":"\K[^"]+')
192
+ EMAIL=$(echo "$RESULT" | sed -n 's/.*"email":"\([^"]*\)".*/\1/p')
193
+ PASS=$(echo "$RESULT" | sed -n 's/.*"password":"\([^"]*\)".*/\1/p')
194
194
  cat > .claude/dev-session.json << SESSEOF
195
195
  {"admin_email":"$EMAIL","admin_password":"$PASS","last_reset":"$(date -Iseconds)","api_port":{api_port},"web_port":{web_port},"environment":"{mode}","status":"ready"}
196
196
  SESSEOF