@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.
- package/dist/index.js +87 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
- package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
- package/templates/skills/apex/references/core-seed-data.md +20 -18
- package/templates/skills/apex/references/post-checks.md +18 -1
- package/templates/skills/apex/references/smartstack-api.md +4 -4
- package/templates/skills/apex/references/smartstack-frontend.md +1 -1
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +1 -1
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
- package/templates/skills/apex/steps/step-04-examine.md +163 -0
- package/templates/skills/apex-verify/SKILL.md +110 -0
- package/templates/skills/apex-verify/references/audit-rules.md +50 -0
- package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
- package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
- package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
- package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
- package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
- package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
- package/templates/skills/application/templates-frontend.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +3 -3
- package/templates/skills/business-analyse/_shared.md +37 -0
- package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
- package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
- package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +6 -3
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
- package/templates/skills/business-analyse-html/references/data-build.md +2 -0
- package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
- package/templates/skills/business-analyse-quick/SKILL.md +807 -0
- package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
- package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
- package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
- package/templates/skills/dev-start/SKILL.md +7 -7
- package/templates/skills/sketch/SKILL.md +15 -153
- package/templates/skills/ui-components/SKILL.md +1 -1
- package/templates/skills/ui-components/patterns/data-table.md +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Domain Heuristics — /
|
|
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
|
|
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=$(
|
|
91
|
-
SN=$(echo "$CS" |
|
|
92
|
-
DB=$(echo "$CS" |
|
|
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" |
|
|
181
|
-
PASS=$(echo "$RESULT" |
|
|
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" |
|
|
193
|
-
PASS=$(echo "$RESULT" |
|
|
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
|