@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
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-03-perm-audit
|
|
3
|
+
description: Cross-reference permissions between controllers, Permissions.cs, seed data, and roles
|
|
4
|
+
next_step: steps/step-04-route-audit.md
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Step 3: Permission Audit
|
|
8
|
+
|
|
9
|
+
## YOUR TASK:
|
|
10
|
+
Cross-reference permissions across 4 sources to ensure complete coverage:
|
|
11
|
+
1. Controller `[RequirePermission]` attributes → what the API enforces
|
|
12
|
+
2. `Permissions.cs` static constants → what the code defines
|
|
13
|
+
3. `PermissionsSeedData` → what gets seeded to the database
|
|
14
|
+
4. `RolesSeedData` → what roles have which permissions
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## EXECUTION SEQUENCE:
|
|
19
|
+
|
|
20
|
+
### 1. Extract Controller Permissions
|
|
21
|
+
|
|
22
|
+
For each file in {controller_files}:
|
|
23
|
+
|
|
24
|
+
Grep for `[RequirePermission(` and extract the permission string:
|
|
25
|
+
```csharp
|
|
26
|
+
[RequirePermission(Permissions.Employees.Read)]
|
|
27
|
+
[RequirePermission(Permissions.AbsenceManagement.Create)]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Build a set: `{controller_permissions}` = all unique permission references.
|
|
31
|
+
|
|
32
|
+
Also note which controller and HTTP method uses each permission (for context in findings).
|
|
33
|
+
|
|
34
|
+
### 2. Extract Permissions.cs Constants
|
|
35
|
+
|
|
36
|
+
Read {permissions_file} and extract all `public const string` values:
|
|
37
|
+
```csharp
|
|
38
|
+
public const string Read = "human-resources.employees.read";
|
|
39
|
+
public const string Wildcard = "human-resources.employees.*";
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Build a set: `{defined_permissions}` = all permission path strings.
|
|
43
|
+
Also track the constant name → path mapping.
|
|
44
|
+
|
|
45
|
+
### 3. Extract Seed Data Permissions
|
|
46
|
+
|
|
47
|
+
For each file in {perm_seed_files}:
|
|
48
|
+
|
|
49
|
+
Grep for permission path patterns:
|
|
50
|
+
```csharp
|
|
51
|
+
Path = "human-resources.employees.read"
|
|
52
|
+
new PermissionSeedEntry { ... Path = "..." }
|
|
53
|
+
"human-resources.employees.read" // in HasData() or SeedPermissionsAsync
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Build a set: `{seeded_permissions}` = all permission paths in seed data.
|
|
57
|
+
|
|
58
|
+
### 4. Extract Role Mappings
|
|
59
|
+
|
|
60
|
+
For each file in {role_seed_files}:
|
|
61
|
+
|
|
62
|
+
Grep for role-permission associations. Look for patterns:
|
|
63
|
+
```csharp
|
|
64
|
+
// Role assignment patterns
|
|
65
|
+
new { RoleId = ..., PermissionId = ... }
|
|
66
|
+
SeedRolePermissionsAsync
|
|
67
|
+
rolePermissions.Add(...)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Build a map: `{role_mappings}` = { permission_path: [role_names] }
|
|
71
|
+
|
|
72
|
+
### 5. Cross-Reference Matrix
|
|
73
|
+
|
|
74
|
+
**Check A: Controllers → Permissions.cs**
|
|
75
|
+
For each permission in {controller_permissions}:
|
|
76
|
+
- Verify it resolves to a constant in {defined_permissions}
|
|
77
|
+
- If missing → BLOCKING (controller references undefined permission)
|
|
78
|
+
|
|
79
|
+
**Check B: Permissions.cs → Seed Data**
|
|
80
|
+
For each permission in {defined_permissions} (excluding wildcards):
|
|
81
|
+
- Verify it exists in {seeded_permissions}
|
|
82
|
+
- If missing → BLOCKING (permission defined but never seeded = always 403)
|
|
83
|
+
|
|
84
|
+
**Check C: Seed Data → Roles**
|
|
85
|
+
For each permission in {seeded_permissions}:
|
|
86
|
+
- Verify it has at least one role mapping in {role_mappings}
|
|
87
|
+
- If missing → CRITICAL (permission seeded but no role has it = always 403)
|
|
88
|
+
|
|
89
|
+
**Check D: Admin Role Wildcard**
|
|
90
|
+
- Verify admin role has wildcard (`*`) permissions for each module
|
|
91
|
+
- If missing → CRITICAL (admin cannot access module)
|
|
92
|
+
|
|
93
|
+
**Check E: Role Matrix Sanity**
|
|
94
|
+
- Viewer role must NOT have write permissions (create, update, delete)
|
|
95
|
+
- Admin must have wildcard or all individual permissions
|
|
96
|
+
|
|
97
|
+
### 6. Record Findings
|
|
98
|
+
|
|
99
|
+
| ID | Severity | Rule |
|
|
100
|
+
|----|----------|------|
|
|
101
|
+
| PERM-001 | BLOCKING | Controller uses permission not defined in Permissions.cs |
|
|
102
|
+
| PERM-002 | BLOCKING | Permissions.cs constant not seeded in PermissionsSeedData |
|
|
103
|
+
| PERM-003 | CRITICAL | Seeded permission has no role mapping (always 403 for all users) |
|
|
104
|
+
| PERM-004 | CRITICAL | Admin role missing wildcard for module |
|
|
105
|
+
| PERM-005 | WARNING | Permission path segments not in kebab-case |
|
|
106
|
+
| PERM-006 | WARNING | Viewer role has write permission (create/update/delete) |
|
|
107
|
+
| PERM-007 | WARNING | Permission defined in Permissions.cs but unused by any controller (dead code) |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## SUCCESS METRICS:
|
|
112
|
+
- All 4 sources extracted completely
|
|
113
|
+
- Cross-reference matrix fully evaluated
|
|
114
|
+
- Role matrix sanity checked
|
|
115
|
+
- Findings recorded with file:line evidence
|
|
116
|
+
|
|
117
|
+
## NEXT STEP:
|
|
118
|
+
If {scope} is `all` or `route` → proceed to `./step-04-route-audit.md`
|
|
119
|
+
If {scope} is `perm` → skip to `./step-05-report.md`
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-04-route-audit
|
|
3
|
+
description: Verify route alignment between navigation seed data, componentRegistry, and controllers
|
|
4
|
+
next_step: steps/step-05-report.md
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Step 4: Route Audit
|
|
8
|
+
|
|
9
|
+
## YOUR TASK:
|
|
10
|
+
Verify that routes are consistent across 3 sources: navigation seed data, componentRegistry (PageRegistry), and backend controllers (NavRoute attributes).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## EXECUTION SEQUENCE:
|
|
15
|
+
|
|
16
|
+
### 1. Extract Seed Data Routes
|
|
17
|
+
|
|
18
|
+
From {seed_files}, extract all section routes:
|
|
19
|
+
```
|
|
20
|
+
{seed_routes} = [
|
|
21
|
+
{ code: "departments", route: "/human-resources/employees/departments", file: "...", line: N },
|
|
22
|
+
{ code: "list", route: "/human-resources/employees", file: "...", line: N },
|
|
23
|
+
...
|
|
24
|
+
]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Extract PageRegistry Keys
|
|
28
|
+
|
|
29
|
+
From {registry_file}, extract all `PageRegistry.register()` calls:
|
|
30
|
+
```typescript
|
|
31
|
+
PageRegistry.register('human-resources.employees.departments', ...)
|
|
32
|
+
PageRegistry.register('human-resources.employees', ...)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Build: `{registry_keys}` = all registered page keys.
|
|
36
|
+
|
|
37
|
+
### 3. Extract Controller NavRoutes
|
|
38
|
+
|
|
39
|
+
From {controller_files}, extract `[NavRoute("...")]` attributes:
|
|
40
|
+
```csharp
|
|
41
|
+
[NavRoute("human-resources.employees")]
|
|
42
|
+
[NavRoute("human-resources.employees.departments")]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Build: `{controller_navroutes}` = all NavRoute values.
|
|
46
|
+
|
|
47
|
+
### 4. Cross-Reference
|
|
48
|
+
|
|
49
|
+
**Check A: Seed Routes → PageRegistry**
|
|
50
|
+
For each seed route (excluding reserved sections and /:id routes):
|
|
51
|
+
- Convert route to PageRegistry key: `/human-resources/employees/departments` → `human-resources.employees.departments`
|
|
52
|
+
- Verify key exists in {registry_keys}
|
|
53
|
+
- If missing → BLOCKING (seed route has no frontend page)
|
|
54
|
+
|
|
55
|
+
**Check B: Controller NavRoutes → Seed Data**
|
|
56
|
+
For each NavRoute:
|
|
57
|
+
- Convert to route path: `human-resources.employees` → `/human-resources/employees`
|
|
58
|
+
- Verify exists in seed data routes
|
|
59
|
+
- If missing → WARNING (controller exists but no navigation entry)
|
|
60
|
+
|
|
61
|
+
**Check C: PageRegistry → Page Files**
|
|
62
|
+
For each registry entry:
|
|
63
|
+
- Verify the imported component file exists on disk
|
|
64
|
+
- If missing → BLOCKING (registry references non-existent page)
|
|
65
|
+
|
|
66
|
+
**Check D: Orphan Pages**
|
|
67
|
+
For each page file in {page_files}:
|
|
68
|
+
- Check if it's registered in componentRegistry
|
|
69
|
+
- If NOT registered → WARNING (page exists but has no route — unreachable)
|
|
70
|
+
|
|
71
|
+
**Check E: DynamicRouter Convention Routes**
|
|
72
|
+
For each section with a list page:
|
|
73
|
+
- Verify these implicit routes are handled (either by registry or DynamicRouter convention):
|
|
74
|
+
- `{section}/:id` → detail page
|
|
75
|
+
- `{section}/create` → create page
|
|
76
|
+
- `{section}/:id/edit` → edit page
|
|
77
|
+
- Note: DynamicRouter may resolve these by convention without explicit registry entries. Only flag if BOTH registry AND convention are missing.
|
|
78
|
+
|
|
79
|
+
### 5. Record Findings
|
|
80
|
+
|
|
81
|
+
| ID | Severity | Rule |
|
|
82
|
+
|----|----------|------|
|
|
83
|
+
| ROUTE-001 | BLOCKING | Seed data route has no matching PageRegistry entry |
|
|
84
|
+
| ROUTE-002 | WARNING | Controller NavRoute has no matching seed data entry |
|
|
85
|
+
| ROUTE-003 | BLOCKING | PageRegistry references non-existent page file |
|
|
86
|
+
| ROUTE-004 | WARNING | Orphan page — exists on disk but not registered (unreachable) |
|
|
87
|
+
| ROUTE-005 | WARNING | Implicit route (detail/create/edit) not resolvable |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## SUCCESS METRICS:
|
|
92
|
+
- All 3 sources extracted completely
|
|
93
|
+
- Cross-reference completed for all combinations
|
|
94
|
+
- Orphan detection completed
|
|
95
|
+
- DynamicRouter conventions accounted for
|
|
96
|
+
|
|
97
|
+
## NEXT STEP:
|
|
98
|
+
Proceed to `./step-05-report.md`
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: step-05-report
|
|
3
|
+
description: Generate consolidated audit report with all findings
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Step 5: Consolidated Report
|
|
7
|
+
|
|
8
|
+
## YOUR TASK:
|
|
9
|
+
Compile all findings from previous steps into a structured, actionable report.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## REPORT FORMAT:
|
|
14
|
+
|
|
15
|
+
Display the following report to the user:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
============================================================
|
|
19
|
+
APEX VERIFICATION REPORT
|
|
20
|
+
============================================================
|
|
21
|
+
|
|
22
|
+
Project: {project_name}
|
|
23
|
+
Scope: {scope}
|
|
24
|
+
Date: {current_date}
|
|
25
|
+
|
|
26
|
+
------------------------------------------------------------
|
|
27
|
+
SUMMARY
|
|
28
|
+
------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
| Category | BLOCKING | CRITICAL | WARNING | PASS |
|
|
31
|
+
|-------------|----------|----------|---------|------|
|
|
32
|
+
| Navigation | {count} | {count} | {count} | {Y/N}|
|
|
33
|
+
| CRUD | {count} | {count} | {count} | {Y/N}|
|
|
34
|
+
| Permissions | {count} | {count} | {count} | {Y/N}|
|
|
35
|
+
| Routes | {count} | {count} | {count} | {Y/N}|
|
|
36
|
+
| **TOTAL** | {total} | {total} | {total} | |
|
|
37
|
+
|
|
38
|
+
Result: {PASS / FAIL (N blocking issues)}
|
|
39
|
+
|
|
40
|
+
------------------------------------------------------------
|
|
41
|
+
BLOCKING FINDINGS (must fix)
|
|
42
|
+
------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
[{ID}] {message}
|
|
45
|
+
File: {file_path}:{line}
|
|
46
|
+
Fix: {fix_description}
|
|
47
|
+
|
|
48
|
+
... (repeat for each blocking finding)
|
|
49
|
+
|
|
50
|
+
------------------------------------------------------------
|
|
51
|
+
CRITICAL FINDINGS (should fix)
|
|
52
|
+
------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
[{ID}] {message}
|
|
55
|
+
File: {file_path}:{line}
|
|
56
|
+
Fix: {fix_description}
|
|
57
|
+
|
|
58
|
+
... (repeat for each critical finding)
|
|
59
|
+
|
|
60
|
+
------------------------------------------------------------
|
|
61
|
+
WARNINGS (nice to fix)
|
|
62
|
+
------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
[{ID}] {message}
|
|
65
|
+
File: {file_path}:{line}
|
|
66
|
+
Fix: {fix_description}
|
|
67
|
+
|
|
68
|
+
... (repeat for each warning)
|
|
69
|
+
|
|
70
|
+
============================================================
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## ADDITIONAL SECTIONS (if --fix):
|
|
74
|
+
|
|
75
|
+
When {fix_mode} is true, add a "FIX COMMANDS" section after the findings:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
------------------------------------------------------------
|
|
79
|
+
FIX COMMANDS
|
|
80
|
+
------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
# NAV-001: Remove reserved section 'detail' from menu
|
|
83
|
+
# File: src/.../EmployeesNavigationSeedData.cs
|
|
84
|
+
# Action: Remove the section entry with code "detail" from Sections[] array
|
|
85
|
+
|
|
86
|
+
# CRUD-004: Add Create button to EmployeeListPage
|
|
87
|
+
# File: web/.../EmployeeListPage.tsx
|
|
88
|
+
# Action: Add in the page header:
|
|
89
|
+
# <button onClick={() => navigate('create')} className="...">
|
|
90
|
+
# New Employee
|
|
91
|
+
# </button>
|
|
92
|
+
|
|
93
|
+
... (repeat for each finding with specific fix instructions)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## RULES FOR REPORT GENERATION:
|
|
97
|
+
|
|
98
|
+
1. **Sort by severity**: BLOCKING first, then CRITICAL, then WARNING
|
|
99
|
+
2. **Within same severity**: Sort by category (NAV, CRUD, PERM, ROUTE)
|
|
100
|
+
3. **No false positives**: Only include findings with concrete evidence (file:line)
|
|
101
|
+
4. **No duplicates**: Merge findings that point to the same root cause
|
|
102
|
+
5. **Actionable fixes**: Every finding must have a clear, specific fix instruction
|
|
103
|
+
6. **If no findings in a category**: Display "PASS — No issues found" for that category
|
|
104
|
+
|
|
105
|
+
## SUCCESS CRITERIA:
|
|
106
|
+
- Report is complete and properly formatted
|
|
107
|
+
- All findings from steps 1-4 are included
|
|
108
|
+
- Pass/fail status is correct
|
|
109
|
+
- Fix commands are included if --fix was specified
|
|
110
|
+
- Report is immediately actionable by the developer
|
|
@@ -114,7 +114,7 @@ export function $MODULE_PASCALDetailPage() {
|
|
|
114
114
|
} finally {
|
|
115
115
|
setLoading(false);
|
|
116
116
|
}
|
|
117
|
-
}, [$entityId
|
|
117
|
+
}, [$entityId]);
|
|
118
118
|
|
|
119
119
|
useEffect(() => {
|
|
120
120
|
loadData();
|
|
@@ -239,7 +239,7 @@ export function $MODULE_PASCALEditPage() {
|
|
|
239
239
|
} finally {
|
|
240
240
|
setLoading(false);
|
|
241
241
|
}
|
|
242
|
-
}, [$entityId
|
|
242
|
+
}, [$entityId]);
|
|
243
243
|
|
|
244
244
|
useEffect(() => {
|
|
245
245
|
loadData();
|
|
@@ -38,7 +38,7 @@ Path A — FAST (small module, < 5 min) Path B — FULL (multi-module,
|
|
|
38
38
|
│ │
|
|
39
39
|
▼ ▼
|
|
40
40
|
┌──────────────┐ ┌──────────────────┐
|
|
41
|
-
│ /
|
|
41
|
+
│ /business-analyse-quick│ 0-2 questions │ /business-analyse│ ~40 questions
|
|
42
42
|
│ (2 min) │ Pure inference │ (steps 00-04) │ JSON output
|
|
43
43
|
└──────┬───────┘ └────────┬─────────┘
|
|
44
44
|
│ Natural language prompt │ entities/rules/usecases.json
|
|
@@ -84,7 +84,7 @@ Path A — FAST (small module, < 5 min) Path B — FULL (multi-module,
|
|
|
84
84
|
|
|
85
85
|
| From | To | Data | Format |
|
|
86
86
|
|------|----|------|--------|
|
|
87
|
-
| `/
|
|
87
|
+
| `/business-analyse-quick` | `/apex` | Inferred design (entities, FK, labels, code patterns) | Natural language prompt |
|
|
88
88
|
| `/business-analyse` | `/business-analyse-design` | Entities, use cases, permissions | JSON (`entities.json`, `usecases.json`, `permissions.json`) |
|
|
89
89
|
| `/business-analyse-design` | `/business-analyse-html` | Screen specs, wireframes, navigation | JSON (`screens.json`, `navigation.json`) |
|
|
90
90
|
| `/business-analyse-html` | Client | Interactive review document | HTML (`ba-interactive.html`) |
|
|
@@ -104,7 +104,7 @@ Path A — FAST (small module, < 5 min) Path B — FULL (multi-module,
|
|
|
104
104
|
| Cadrage/scope only | `/business-analyse-html` | Just regenerate HTML |
|
|
105
105
|
| No corrections (approved) | `/business-analyse-handoff` | Proceed to code generation |
|
|
106
106
|
|
|
107
|
-
> **Rule:** Path A (`/
|
|
107
|
+
> **Rule:** Path A (`/business-analyse-quick` → `/apex`) is for quick, single-module additions. Path B (full cycle) is for multi-module projects or when the client needs a formal review process.
|
|
108
108
|
> **PRD generation:** Handoff data and PRD files are generated by the `/business-analyse-handoff` skill.
|
|
109
109
|
|
|
110
110
|
## JSON Architecture — Granular files + Index
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
(date validation, required fields) are the MINIMUM baseline. Domain rules
|
|
14
14
|
(half-day absences, team capacity, carry-over limits, credit note linking,
|
|
15
15
|
sequential invoice numbering) are what make an analysis valuable.
|
|
16
|
+
**Target: >= 2 domain-specific rules per business module (`domainSpecific: true`).**
|
|
17
|
+
Post-check C19 enforces this as a WARNING.
|
|
16
18
|
|
|
17
19
|
4. **ALWAYS propose contextual views.** A workflow module without filtered
|
|
18
20
|
views (open-requests, rejected, history, my-{entity}) is incomplete.
|
|
@@ -25,6 +27,41 @@
|
|
|
25
27
|
"standard billing also includes credit notes, payment tracking, and
|
|
26
28
|
dunning. Would you like to add these?" The user came to you for expertise.
|
|
27
29
|
|
|
30
|
+
7. **NEVER compress or batch module processing in step-03.** Each module MUST be
|
|
31
|
+
processed sequentially in the main context. Sub-agents do NOT have access to the
|
|
32
|
+
JSON schema and WILL produce inconsistent formats. If there are 15+ modules, the
|
|
33
|
+
correct approach is to maintain sequential processing, NOT to "accelerate" by
|
|
34
|
+
batching. The ba-012 audit proved that batching reduced schema conformity from
|
|
35
|
+
95% to 35% — the rework cost exceeded any time saved.
|
|
36
|
+
|
|
37
|
+
8. **ALL generated JSON content values MUST be in `{language}`** (from metadata or config).
|
|
38
|
+
This applies to ALL text values written to JSON files:
|
|
39
|
+
- Entity `description`, attribute `description`, relationship `description`
|
|
40
|
+
- Business rule `name`, `statement`, `examples[].input`, `examples[].expected`
|
|
41
|
+
- Use case `name`, `mainScenario[]` steps, `alternativeScenarios[].steps[]`, `errorScenarios[].steps[]`, `preconditions[]`, `postconditions[]`
|
|
42
|
+
- Permission `description`, role `description`
|
|
43
|
+
- Lifecycle state `displayName`
|
|
44
|
+
- Section and resource labels
|
|
45
|
+
|
|
46
|
+
**Mixed-language content across modules is a BLOCKING quality defect.**
|
|
47
|
+
If `language == "fr"` : write "Créer une absence", NOT "Create an absence".
|
|
48
|
+
If `language == "en"` : write "Create an absence", NOT "Créer une absence".
|
|
49
|
+
Technical identifiers (entity names, field names, enum values, IDs) remain in English/PascalCase.
|
|
50
|
+
|
|
51
|
+
9. **BR `examples[]` are TEST DATA, not documentation.** Each example must be a
|
|
52
|
+
concrete test case with the `{scenario, given, when, then}` format:
|
|
53
|
+
- `given`: object with `Entity.field: concreteValue` pairs (initial state)
|
|
54
|
+
- `when`: string action (`"create"`, `"submit"`, `"calculate_remaining"`)
|
|
55
|
+
- `then`: object with expected outcome (`{result: "success"|"error", Entity.field: value}`)
|
|
56
|
+
|
|
57
|
+
These examples directly feed:
|
|
58
|
+
- Gherkin `Examples:` tables (step-03, F-ter)
|
|
59
|
+
- `brToCodeMapping` in handoff
|
|
60
|
+
- Automated unit/integration tests in `/business-analyse-develop`
|
|
61
|
+
|
|
62
|
+
A rule without test-ready examples = a test gap in development.
|
|
63
|
+
Prose examples like `"code déjà existant"` are INSUFFICIENT — use concrete values.
|
|
64
|
+
|
|
28
65
|
---
|
|
29
66
|
|
|
30
67
|
## JSON-First Architecture
|
|
@@ -38,6 +38,8 @@ Omit flags when `false` (default). Include explicitly when `true`.
|
|
|
38
38
|
|
|
39
39
|
## Business Rules Schema Format
|
|
40
40
|
|
|
41
|
+
> **Source of truth:** `schemas/sections/analysis-schema.json` property `businessRules`.
|
|
42
|
+
|
|
41
43
|
```json
|
|
42
44
|
{
|
|
43
45
|
"id": "BR-VAL-EMPLOYEES-001",
|
|
@@ -45,9 +47,10 @@ Omit flags when `false` (default). Include explicitly when `true`.
|
|
|
45
47
|
"category": "validation",
|
|
46
48
|
"sectionCode": "list",
|
|
47
49
|
"statement": "La date d'embauche ne peut pas être dans le futur",
|
|
48
|
-
"
|
|
50
|
+
"severity": "blocking",
|
|
49
51
|
"entities": ["Employee"],
|
|
50
|
-
"
|
|
52
|
+
"examples": [{ "input": "hireDate = 2027-01-01", "expected": "Erreur : date dans le futur" }],
|
|
53
|
+
"domainSpecific": false
|
|
51
54
|
}
|
|
52
55
|
```
|
|
53
56
|
|
|
@@ -63,10 +66,15 @@ Categories: `validation`, `calculation`, `workflow`, `security`, `data`, `notifi
|
|
|
63
66
|
```json
|
|
64
67
|
{
|
|
65
68
|
"id": "BR-CALC-INV-003",
|
|
69
|
+
"name": "Calcul reste à payer",
|
|
66
70
|
"category": "calculation",
|
|
71
|
+
"sectionCode": "detail",
|
|
67
72
|
"statement": "Le reste à payer = total - montant payé",
|
|
73
|
+
"severity": "blocking",
|
|
68
74
|
"formula": "remainingAmount = total - paidAmount",
|
|
69
|
-
"entities": ["Invoice"]
|
|
75
|
+
"entities": ["Invoice"],
|
|
76
|
+
"examples": [{ "input": "total=1000, paidAmount=300", "expected": "remainingAmount = 700" }],
|
|
77
|
+
"domainSpecific": false
|
|
70
78
|
}
|
|
71
79
|
```
|
|
72
80
|
|
|
@@ -129,6 +129,70 @@ for (const mod of modules) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
// ── Business Rules Schema Conformity (C16-C21) ──────────────────────
|
|
134
|
+
|
|
135
|
+
// Quality gate: BR schema field conformity (C16 — BLOCKING)
|
|
136
|
+
for (const br of brs) {
|
|
137
|
+
if (!br.id) errors.push(mod.code + ": BR missing 'id' field");
|
|
138
|
+
else if (!/^BR-(VAL|CALC|WF|SEC|DATA|NOTIF)-[A-Z]{2,4}-\d{3}$/.test(br.id))
|
|
139
|
+
errors.push(mod.code + ": BR '" + br.id + "' does not match ID pattern BR-{CAT}-{MOD}-{NNN}");
|
|
140
|
+
if (!br.name) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'name'");
|
|
141
|
+
if (!br.category) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'category'");
|
|
142
|
+
if (!br.statement) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'statement'");
|
|
143
|
+
if (!br.severity) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'severity'");
|
|
144
|
+
if (!br.entities || br.entities.length === 0)
|
|
145
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'entities[]'");
|
|
146
|
+
// Reject deprecated field names (common drift from sub-agent schema ignorance)
|
|
147
|
+
if (br.code && !br.id) errors.push(mod.code + ": BR uses 'code' instead of canonical 'id'");
|
|
148
|
+
if (br.label && !br.name) errors.push(mod.code + ": BR uses 'label' instead of canonical 'name'");
|
|
149
|
+
if (br.description && !br.statement) errors.push(mod.code + ": BR uses 'description' instead of canonical 'statement'");
|
|
150
|
+
if (br.rule && !br.statement) errors.push(mod.code + ": BR uses 'rule' instead of canonical 'statement'");
|
|
151
|
+
if (br.type && !br.category) errors.push(mod.code + ": BR uses 'type' instead of canonical 'category'");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Quality gate: BR examples format (C17 — BLOCKING)
|
|
155
|
+
for (const br of brs) {
|
|
156
|
+
if (!br.examples || br.examples.length === 0)
|
|
157
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'examples[]' (min 1 required)");
|
|
158
|
+
if (br.example && !br.examples)
|
|
159
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' uses deprecated 'example' string — use 'examples[]' array of {input, expected}");
|
|
160
|
+
if (br.examples) {
|
|
161
|
+
for (const ex of br.examples) {
|
|
162
|
+
if (typeof ex === 'string')
|
|
163
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' has string in examples[] — must be {input, expected}");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Quality gate: BR count minimum (C18 — BLOCKING if < 4)
|
|
169
|
+
if (brs.length > 0 && brs.length < 4) {
|
|
170
|
+
errors.push(mod.code + ": only " + brs.length + " business rules (minimum: 4)");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Quality gate: Domain-specific ratio (C19 — WARNING if < 2)
|
|
174
|
+
const domainBRs = brs.filter(br => br.domainSpecific === true);
|
|
175
|
+
if (brs.length >= 4 && domainBRs.length < 2) {
|
|
176
|
+
warnings.push(mod.code + ": only " + domainBRs.length + " domain-specific rules out of " + brs.length +
|
|
177
|
+
" (minimum: 2 — see _shared.md rule 3)");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Quality gate: domainSpecific flag presence (C20 — WARNING)
|
|
181
|
+
const brsMissingFlag = brs.filter(br => typeof br.domainSpecific === 'undefined');
|
|
182
|
+
if (brsMissingFlag.length > 0) {
|
|
183
|
+
warnings.push(mod.code + ": " + brsMissingFlag.length + "/" + brs.length +
|
|
184
|
+
" BRs missing 'domainSpecific' flag");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Quality gate: Structured conditions for conditional rules (C21 — WARNING)
|
|
188
|
+
const conditionalBRs = brs.filter(br =>
|
|
189
|
+
br.category === 'workflow' || br.category === 'security' ||
|
|
190
|
+
(br.statement && /\b(IF|Si |si |Quand |quand |Lorsqu)/i.test(br.statement)));
|
|
191
|
+
const brsWithConditions = conditionalBRs.filter(br => br.conditions && br.conditions.length > 0);
|
|
192
|
+
if (conditionalBRs.length > 2 && brsWithConditions.length < 2) {
|
|
193
|
+
warnings.push(mod.code + ": " + brsWithConditions.length + "/" + conditionalBRs.length +
|
|
194
|
+
" conditional BRs have structured conditions[] (recommended: >= 2)");
|
|
195
|
+
}
|
|
132
196
|
}
|
|
133
197
|
|
|
134
198
|
if (errors.length > 0) {
|
|
@@ -83,9 +83,10 @@
|
|
|
83
83
|
"category": "validation",
|
|
84
84
|
"sectionCode": "list",
|
|
85
85
|
"statement": "La date ne peut pas être dans le futur",
|
|
86
|
-
"
|
|
86
|
+
"severity": "blocking",
|
|
87
87
|
"entities": ["Employee"],
|
|
88
|
-
"
|
|
88
|
+
"examples": [{ "input": "date = 2027-01-01", "expected": "Erreur : date dans le futur" }],
|
|
89
|
+
"domainSpecific": false
|
|
89
90
|
}
|
|
90
91
|
]
|
|
91
92
|
}
|
|
@@ -95,7 +96,10 @@
|
|
|
95
96
|
|---------------|------------------------|
|
|
96
97
|
| `rules` (root) | `businessRules` |
|
|
97
98
|
| `statement` | `description` |
|
|
98
|
-
| `id` | `name` (as identifier) |
|
|
99
|
+
| `id` | `name` (as identifier), `code` |
|
|
100
|
+
| `examples` (array of `{input, expected}`) | `example` (singular string) |
|
|
101
|
+
| `category` | `type` |
|
|
102
|
+
| `name` | `label` |
|
|
99
103
|
|
|
100
104
|
Categories: `validation`, `calculation`, `workflow`, `security`, `data`, `notification`
|
|
101
105
|
|
|
@@ -61,7 +61,7 @@ Ensures each module has ALL required components before being marked as "specifie
|
|
|
61
61
|
| 5.1 | Sections count | ≥2 | YES | Modules need list + form minimum |
|
|
62
62
|
| 5.2 | Wireframes count | 1 per section | YES | EVERY section MUST have wireframe |
|
|
63
63
|
| 5.3 | Navigation entries | ≥1 | YES | Module must be accessible in menu |
|
|
64
|
-
| 5.4 | Navigation route patterns | No `/
|
|
64
|
+
| 5.4 | Navigation route patterns | No `/detail/:id` suffix | YES | ALL sections use uniform route: module route + /{section-code}. list route MUST end with `/list`. FORBIDDEN: `/module/detail/:id` (detail is implicit) |
|
|
65
65
|
|
|
66
66
|
#### SECTION 6: I18N & Messages (3 checks)
|
|
67
67
|
|
|
@@ -169,20 +169,20 @@ const checklist = {
|
|
|
169
169
|
},
|
|
170
170
|
|
|
171
171
|
navigationRoutePatterns: {
|
|
172
|
-
check: "Navigation routes do NOT contain
|
|
172
|
+
check: "Navigation routes do NOT contain forbidden patterns (/detail/:id)",
|
|
173
173
|
status: (() => {
|
|
174
174
|
const allRoutes = [
|
|
175
175
|
...(specification.seedDataCore?.navigationSections || []).map(s => s.route),
|
|
176
176
|
...(specification.sections || []).map(s => s.route),
|
|
177
177
|
...(specification.navigation?.entries || []).map(e => e.route)
|
|
178
178
|
].filter(Boolean);
|
|
179
|
-
const forbidden = allRoutes.filter(r => r.match(/\/
|
|
179
|
+
const forbidden = allRoutes.filter(r => r.match(/\/detail\/:id$/));
|
|
180
180
|
return forbidden.length === 0 ? "PASS" : "FAIL";
|
|
181
181
|
})(),
|
|
182
182
|
blocking: true,
|
|
183
|
-
details: "FORBIDDEN: /module/
|
|
184
|
-
"
|
|
185
|
-
"
|
|
183
|
+
details: "FORBIDDEN: /module/detail/:id (detail is implicit, not a section). " +
|
|
184
|
+
"ALL sections use uniform route: module route + /{section-code}. " +
|
|
185
|
+
"list route = module route + /list. " +
|
|
186
186
|
"Other sections (dashboard, approve) add /{section} normally."
|
|
187
187
|
},
|
|
188
188
|
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"type": "object",
|
|
25
25
|
"required": ["id", "name", "category", "statement"],
|
|
26
26
|
"properties": {
|
|
27
|
-
"id": { "type": "string", "pattern": "^BR-(VAL|CALC|WF|SEC|DATA)-[A-Z]{2,4}-\\d{3}$", "description": "Module-prefixed BR ID (e.g., BR-VAL-RM-001)" },
|
|
27
|
+
"id": { "type": "string", "pattern": "^BR-(VAL|CALC|WF|SEC|DATA|NOTIF)-[A-Z]{2,4}-\\d{3}$", "description": "Module-prefixed BR ID (e.g., BR-VAL-RM-001)" },
|
|
28
28
|
"name": { "type": "string" },
|
|
29
|
-
"category": { "type": "string", "enum": ["validation", "calculation", "workflow", "security", "data"] },
|
|
29
|
+
"category": { "type": "string", "enum": ["validation", "calculation", "workflow", "security", "data", "notification"] },
|
|
30
30
|
"statement": { "type": "string", "description": "IF ... THEN ... ELSE ..." },
|
|
31
31
|
"priority": { "type": "string", "enum": ["must", "should", "could"] },
|
|
32
32
|
"conditions": {
|
|
@@ -48,15 +48,26 @@
|
|
|
48
48
|
},
|
|
49
49
|
"examples": {
|
|
50
50
|
"type": "array",
|
|
51
|
+
"description": "Test-ready examples: each example = a test case with concrete values. Minimum 2 per rule (happy_path + error). These feed Gherkin generation and automated tests.",
|
|
51
52
|
"items": {
|
|
52
53
|
"type": "object",
|
|
53
54
|
"required": ["input", "expected"],
|
|
54
55
|
"properties": {
|
|
55
|
-
"input": { "type": "string", "description": "
|
|
56
|
-
"expected": { "type": "string", "description": "
|
|
56
|
+
"input": { "type": "string", "description": "LEGACY fallback. Prefer given/when/then format." },
|
|
57
|
+
"expected": { "type": "string", "description": "LEGACY fallback. Prefer given/when/then format." },
|
|
58
|
+
"scenario": { "type": "string", "enum": ["happy_path", "error", "boundary", "calculation", "security"], "description": "Test scenario type" },
|
|
59
|
+
"description": { "type": "string", "description": "Human-readable test case description" },
|
|
60
|
+
"given": { "type": "object", "description": "Initial state: Entity.field → concrete value. Ex: {\"Employee.code\": \"EMP-001\", \"existingCodes\": [\"EMP-001\"]}", "additionalProperties": true },
|
|
61
|
+
"when": { "type": "string", "description": "Triggering action. Ex: 'create', 'update', 'submit', 'calculate_remaining'" },
|
|
62
|
+
"then": { "type": "object", "description": "Expected outcome: {result: 'success'|'error', Entity.field: value, message: '...', field: '...'}", "additionalProperties": true }
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
},
|
|
66
|
+
"entities": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": { "type": "string" },
|
|
69
|
+
"description": "Entity names this rule applies to"
|
|
70
|
+
},
|
|
60
71
|
"testability": { "type": "string" },
|
|
61
72
|
"formula": { "type": "string", "description": "BR-CALC calculation formulas" },
|
|
62
73
|
"sectionCode": { "type": "string", "description": "Section code the rule applies to" },
|