@atlashub/smartstack-cli 4.74.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 +152 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +14 -3
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/ba-reader.md +17 -15
- package/templates/agents/ba-writer.md +49 -51
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/_shared.md +1 -1
- package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
- package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
- package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
- 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/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/apex/references/post-checks.md +23 -3
- package/templates/skills/apex/references/smartstack-api.md +4 -4
- package/templates/skills/apex/references/smartstack-frontend.md +54 -8
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +75 -1
- package/templates/skills/apex/steps/step-03-execute.md +16 -4
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
- 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/references/frontend-route-wiring-app-tsx.md +3 -0
- package/templates/skills/application/templates-frontend.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +17 -3
- package/templates/skills/business-analyse/_shared.md +64 -0
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
- package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
- package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
- package/templates/skills/business-analyse/questionnaire.md +86 -9
- package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
- package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
- package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
- package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
- package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
- package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
- package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
- package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
- package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
- package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
- package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
- package/templates/skills/business-analyse/references/portal-classification.md +52 -0
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
- package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
- package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
- package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
- package/templates/skills/business-analyse-design/SKILL.md +10 -0
- package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
- package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
- package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
- package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
- package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
- package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
- package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
- package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
- package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
- package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
- package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
- package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
- package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
- package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
- package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
- package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
- package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
- package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
- package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
- package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
- package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
- package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
- package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
- package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
- package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
- package/templates/skills/business-analyse-html/SKILL.md +10 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
- package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
- 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 +94 -36
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
- package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
- package/templates/skills/business-analyse-html/html/src/template.html +2 -0
- package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
- package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
- package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
- package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
- package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
- package/templates/skills/business-analyse-html/references/data-build.md +24 -1
- package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
- 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/SKILL.md +10 -0
- package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
- package/templates/skills/business-analyse-status/SKILL.md +8 -0
- package/templates/skills/dev-start/SKILL.md +143 -307
- package/templates/skills/efcore/SKILL.md +13 -0
- 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
|
@@ -8,7 +8,7 @@ model: opus
|
|
|
8
8
|
|
|
9
9
|
## Objective
|
|
10
10
|
|
|
11
|
-
Define the complete navigation tree for the
|
|
11
|
+
Define the complete navigation tree for the project: routes, menus, breadcrumbs per application. Supports multi-app projects (uses `applications[]` array).
|
|
12
12
|
|
|
13
13
|
## Prerequisites
|
|
14
14
|
|
|
@@ -25,47 +25,56 @@ Read screens.json for every module via ba-reader. Collect all sections and resou
|
|
|
25
25
|
|
|
26
26
|
Build the navigation hierarchy following SmartStack conventions:
|
|
27
27
|
|
|
28
|
+
> **Route depth:** Max 3 segments: `/{app-code}/{module-code}/{section-code}`.
|
|
29
|
+
> Use `:id` for detail sections (e.g., `/rh/employees/:id`).
|
|
30
|
+
> Resources are components WITHIN a page, NOT separate routes.
|
|
31
|
+
|
|
28
32
|
```
|
|
29
|
-
|
|
30
|
-
|--
|
|
31
|
-
| |--
|
|
32
|
-
| | |--
|
|
33
|
-
| |-- Section
|
|
34
|
-
| | |--
|
|
35
|
-
| |--
|
|
36
|
-
|
|
37
|
-
|-- Module 2
|
|
33
|
+
Project
|
|
34
|
+
|-- Application 1 (e.g., HumanResources)
|
|
35
|
+
| |-- Module 1 (e.g., Employees)
|
|
36
|
+
| | |-- Section "list" → /rh/employees (isDefault, showInMenu)
|
|
37
|
+
| | |-- Section "detail" → /rh/employees/:id (showInMenu: false)
|
|
38
|
+
| | |-- Section "dashboard" → /rh/employees/dashboard (showInMenu)
|
|
39
|
+
| |-- Module 2 (e.g., Absences)
|
|
40
|
+
|-- Application 2 (e.g., Billing)
|
|
38
41
|
|-- ...
|
|
39
42
|
```
|
|
40
43
|
|
|
41
44
|
### 3. Generate navigation.json
|
|
42
45
|
|
|
46
|
+
> **Multi-app format is MANDATORY.** Even for single-app projects, use `applications[]` (array of 1).
|
|
47
|
+
|
|
43
48
|
```json
|
|
44
49
|
{
|
|
45
|
-
"
|
|
46
|
-
"code": "{AppCode}",
|
|
47
|
-
"name": "{AppName}",
|
|
48
|
-
"icon": "app-icon"
|
|
49
|
-
},
|
|
50
|
-
"modules": [
|
|
50
|
+
"applications": [
|
|
51
51
|
{
|
|
52
|
-
"code": "{
|
|
53
|
-
"name": "{
|
|
54
|
-
"icon": "
|
|
55
|
-
"route": "/{app-code}
|
|
56
|
-
"
|
|
57
|
-
"sections": [
|
|
52
|
+
"code": "{AppCode}",
|
|
53
|
+
"name": "{AppName}",
|
|
54
|
+
"icon": "app-icon",
|
|
55
|
+
"route": "/{app-code}",
|
|
56
|
+
"modules": [
|
|
58
57
|
{
|
|
59
|
-
"code": "{
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
58
|
+
"code": "{ModuleCode}",
|
|
59
|
+
"name": "{ModuleName}",
|
|
60
|
+
"icon": "module-icon",
|
|
61
|
+
"route": "/{app-code}/{module-code}",
|
|
62
|
+
"permission": "{AppCode}.{ModuleCode}.Read",
|
|
63
|
+
"sections": [
|
|
64
64
|
{
|
|
65
|
-
"code": "{
|
|
66
|
-
"
|
|
67
|
-
"route": "/{app-code}/{module-code}/{section-code}
|
|
68
|
-
"
|
|
65
|
+
"code": "{sectionCode}",
|
|
66
|
+
"label": "{sectionLabel}",
|
|
67
|
+
"route": "/{app-code}/{module-code}/{section-code}",
|
|
68
|
+
"icon": "section-icon",
|
|
69
|
+
"isDefault": false,
|
|
70
|
+
"showInMenu": true,
|
|
71
|
+
"resources": [
|
|
72
|
+
{
|
|
73
|
+
"code": "{resourceCode}",
|
|
74
|
+
"type": "{SmartTable|SmartForm|SmartDashboard|SmartKanban|SmartFilter}",
|
|
75
|
+
"permission": "{AppCode}.{ModuleCode}.{Action}"
|
|
76
|
+
}
|
|
77
|
+
]
|
|
69
78
|
}
|
|
70
79
|
]
|
|
71
80
|
}
|
|
@@ -74,15 +83,22 @@ Application
|
|
|
74
83
|
],
|
|
75
84
|
"menuItems": [
|
|
76
85
|
{
|
|
77
|
-
"label": "{
|
|
78
|
-
"icon": "
|
|
79
|
-
"route": "/{app-code}
|
|
80
|
-
"permission": "{AppCode}.{ModuleCode}.Read",
|
|
86
|
+
"label": "{AppName}",
|
|
87
|
+
"icon": "app-icon",
|
|
88
|
+
"route": "/{app-code}",
|
|
81
89
|
"children": [
|
|
82
90
|
{
|
|
83
|
-
"label": "{
|
|
84
|
-
"
|
|
85
|
-
"
|
|
91
|
+
"label": "{ModuleName}",
|
|
92
|
+
"icon": "module-icon",
|
|
93
|
+
"route": "/{app-code}/{module-code}",
|
|
94
|
+
"permission": "{AppCode}.{ModuleCode}.Read",
|
|
95
|
+
"children": [
|
|
96
|
+
{
|
|
97
|
+
"label": "{sectionLabel}",
|
|
98
|
+
"route": "/{app-code}/{module-code}/{section-code}",
|
|
99
|
+
"permission": "{AppCode}.{ModuleCode}.Read"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
86
102
|
}
|
|
87
103
|
]
|
|
88
104
|
}
|
|
@@ -97,6 +113,33 @@ Application
|
|
|
97
113
|
}
|
|
98
114
|
```
|
|
99
115
|
|
|
116
|
+
**Section properties:**
|
|
117
|
+
|
|
118
|
+
| Property | Type | Description |
|
|
119
|
+
|----------|------|-------------|
|
|
120
|
+
| `isDefault` | boolean | Default section for the module (typically `list`). Only ONE per module. |
|
|
121
|
+
| `showInMenu` | boolean | `true` = visible in sidebar menu, `false` = accessible by navigation only (e.g., detail pages) |
|
|
122
|
+
|
|
123
|
+
**menuItems rules:**
|
|
124
|
+
|
|
125
|
+
| Rule | Description |
|
|
126
|
+
|------|-------------|
|
|
127
|
+
| App-level items have NO `permission` | Applications are visible to all authenticated users; permissions gate modules/sections |
|
|
128
|
+
| Single visible section = no `children` | If a module has only ONE section with `showInMenu: true`, render a direct link (no submenu) |
|
|
129
|
+
| Only `showInMenu: true` sections | Detail sections (`showInMenu: false`) are NOT included in menuItems |
|
|
130
|
+
|
|
131
|
+
**Route conventions:**
|
|
132
|
+
|
|
133
|
+
| Level | Format | Example |
|
|
134
|
+
|-------|--------|---------|
|
|
135
|
+
| Application | `/{app-code}` | `/rh` |
|
|
136
|
+
| Module | `/{app-code}/{module-code}` | `/rh/employees` |
|
|
137
|
+
| Section (list/default) | `/{app-code}/{module-code}` | `/rh/employees` (isDefault) |
|
|
138
|
+
| Section (named) | `/{app-code}/{module-code}/{section-code}` | `/rh/employees/dashboard` |
|
|
139
|
+
| Section (detail) | `/{app-code}/{module-code}/:id` | `/rh/employees/:id` |
|
|
140
|
+
|
|
141
|
+
> **Max 3 segments.** Resources are components within a page, NOT separate routes.
|
|
142
|
+
|
|
100
143
|
### 4. Write navigation.json
|
|
101
144
|
|
|
102
145
|
Write via ba-writer at project level: `{docs_dir}/navigation.json` (e.g., `docs/projet-rh/v1.0/navigation.json`).
|
|
@@ -105,10 +148,14 @@ Update index.json with navigation.json reference.
|
|
|
105
148
|
## Validation
|
|
106
149
|
|
|
107
150
|
- [ ] Every module in index.json has a corresponding navigation entry
|
|
108
|
-
- [ ] Every section
|
|
109
|
-
- [ ] Every route is unique
|
|
110
|
-
- [ ]
|
|
111
|
-
- [ ]
|
|
151
|
+
- [ ] Every section with `showInMenu: true` has a corresponding menu item
|
|
152
|
+
- [ ] Every route is unique (no duplicates across all applications)
|
|
153
|
+
- [ ] Module/section-level menu items have valid permission references
|
|
154
|
+
- [ ] App-level menu items have NO permission (visible to all)
|
|
155
|
+
- [ ] Breadcrumbs cover ALL routes (every route in `applications[].modules[].sections[]` must have a breadcrumb entry)
|
|
156
|
+
- [ ] `isDefault: true` is set on exactly ONE section per module
|
|
157
|
+
- [ ] Routes use max 3 segments (+ `:id` for detail)
|
|
158
|
+
- [ ] Single-section modules have no `children` in menuItems
|
|
112
159
|
|
|
113
160
|
## Completion Summary
|
|
114
161
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Compact Loop — Delegate to /apex
|
|
2
2
|
|
|
3
|
+
+==============================================================================+
|
|
4
|
+
| COMPACT LOOP — AUTONOMOUS EXECUTION |
|
|
5
|
+
| This loop runs until ALL tasks complete or dead-end confirmed. |
|
|
6
|
+
| EVERY transition in this loop is IMMEDIATE — no pause, no summary, |
|
|
7
|
+
| no user interaction. If you stop here = EXECUTION GUARANTEE VIOLATION. |
|
|
8
|
+
+==============================================================================+
|
|
9
|
+
|
|
3
10
|
> **Loaded by:** step-04 section 5 (after first full iteration)
|
|
4
11
|
> **Purpose:** Execute ALL remaining tasks autonomously by delegating to /apex.
|
|
5
12
|
> **EXECUTION GUARANTEE:** This loop runs until ALL tasks are done, max iterations, or dead-end.
|
|
@@ -251,6 +258,8 @@ Apex handles everything for the current module:
|
|
|
251
258
|
- Commits per layer (atomic commits)
|
|
252
259
|
- Validates with MCP (`validate_conventions`)
|
|
253
260
|
- Updates task statuses in the PRD file directly
|
|
261
|
+
>
|
|
262
|
+
> **MCP HARD RULE:** apex Layer 2 MUST use `mcp__smartstack__scaffold_extension` for controllers/services. Direct Write = BLOCKING violation (step-03c).
|
|
254
263
|
|
|
255
264
|
> **FLAGS:** `-d` implies `-a` (auto, no user confirmation) and `-e` (economy, no nested teams).
|
|
256
265
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# BA Handoff Quality Gate
|
|
2
|
+
|
|
3
|
+
> **Used by:** step-01-task.md (section 1b)
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Verify that the Business Analysis handoff is complete before generating tasks. A missing or incomplete handoff means no frontend files, no test plan, and no BR-to-code mapping — making full development impossible.
|
|
8
|
+
|
|
9
|
+
## When This Applies
|
|
10
|
+
|
|
11
|
+
This quality gate runs **ONLY** when BA artifacts are detected:
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
const sourceFeatureJson = prd?.metadata?.sourceFeatureJson
|
|
15
|
+
|| findFile('docs/**/business-analyse/**/index.json')
|
|
16
|
+
|| findFile('docs/business/**/business-analyse/**/feature.json'); // legacy fallback
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If `sourceFeatureJson` is null (manual start via `{task_description}`, no BA run), **skip this gate intentionally** — the loop proceeds in "manual mode" and generates tasks from the task description. This is by design, not a bug.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quality Gate Checks
|
|
24
|
+
|
|
25
|
+
### CHECK 1: Handoff Status
|
|
26
|
+
|
|
27
|
+
**Purpose:** Verify the BA phase completed and formally handed off.
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
const feature = readJSON(sourceFeatureJson);
|
|
31
|
+
const handoff = feature.handoff || {};
|
|
32
|
+
|
|
33
|
+
if (handoff.status !== 'handed-off') {
|
|
34
|
+
console.error(`BLOCKING: BA HANDOFF INCOMPLETE
|
|
35
|
+
handoff.status = "${handoff.status || 'missing'}" (expected "handed-off")
|
|
36
|
+
Run /business-analyse-handoff to generate the handoff, then re-run /business-analyse-develop.`);
|
|
37
|
+
STOP;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Error Message:**
|
|
42
|
+
```
|
|
43
|
+
BLOCKING: BA HANDOFF INCOMPLETE
|
|
44
|
+
handoff.status = "{actual}" (expected "handed-off")
|
|
45
|
+
Run /business-analyse-handoff to generate the handoff, then re-run /business-analyse-develop.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Status Values:**
|
|
49
|
+
- `"handed-off"` — Handoff complete, safe to proceed
|
|
50
|
+
- `"in-progress"` — BA is still running
|
|
51
|
+
- `"not-started"` — No BA handoff created
|
|
52
|
+
- (missing) — No handoff object at all
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### CHECK 2: Category Completeness
|
|
57
|
+
|
|
58
|
+
**Purpose:** Verify all required file categories have entries in filesToCreate.
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
const filesToCreate = handoff.filesToCreate || {};
|
|
62
|
+
const requiredCategories = ['domain', 'application', 'infrastructure', 'api', 'frontend', 'seedData', 'tests', 'documentation'];
|
|
63
|
+
const missingInHandoff = requiredCategories.filter(c => !filesToCreate[c] || filesToCreate[c].length === 0);
|
|
64
|
+
|
|
65
|
+
if (missingInHandoff.length > 0) {
|
|
66
|
+
console.warn(`⚠ Handoff has empty categories: ${missingInHandoff.join(', ')}`);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Warning Message:**
|
|
71
|
+
```
|
|
72
|
+
⚠ Handoff has empty categories: {list}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Impact:** Non-blocking warning. The gate will display which categories are empty but does NOT stop execution. Subsequent checks (e.g., Category Completeness Check in section 4b) may inject guardrails for empty categories.
|
|
76
|
+
|
|
77
|
+
**Required Categories:**
|
|
78
|
+
1. `domain` — Entity definitions
|
|
79
|
+
2. `application` — Service/handler classes
|
|
80
|
+
3. `infrastructure` — EF configurations, DB migrations
|
|
81
|
+
4. `api` — Controllers, endpoints
|
|
82
|
+
5. `frontend` — Pages, components, screens
|
|
83
|
+
6. `seedData` — Navigation, permissions, roles, seed data
|
|
84
|
+
7. `tests` — Unit and integration tests
|
|
85
|
+
8. `documentation` — API docs, README, architecture docs
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Integration with Task Generation
|
|
90
|
+
|
|
91
|
+
**Before** generating any tasks from filesToCreate, ensure:
|
|
92
|
+
1. Handoff status is `"handed-off"` (CHECK 1)
|
|
93
|
+
2. All required categories are present or empty (CHECK 2, non-blocking)
|
|
94
|
+
|
|
95
|
+
**After** this gate passes:
|
|
96
|
+
- If filesToCreate exists and has entries → proceed to PRD v3 Transformation (section 2b)
|
|
97
|
+
- If filesToCreate is absent → proceed to task description analysis (section 2)
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Manual Mode (No BA Artifacts)
|
|
102
|
+
|
|
103
|
+
When `sourceFeatureJson` is null, the quality gate is **intentionally skipped**:
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
if (!sourceFeatureJson) {
|
|
107
|
+
console.log('Manual mode: no BA artifacts detected — tasks will be generated from task description');
|
|
108
|
+
// Continue to section 2 (Analyze Task Description)
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
In manual mode:
|
|
113
|
+
- No handoff status check
|
|
114
|
+
- No filesToCreate validation
|
|
115
|
+
- Task breakdown generated from `{task_description}` (3-30 subtasks)
|
|
116
|
+
- **seedData is still MANDATORY** — generates navigation, permissions, roles
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Error Recovery
|
|
121
|
+
|
|
122
|
+
If CHECK 1 fails (handoff status not "handed-off"):
|
|
123
|
+
|
|
124
|
+
1. Run `/business-analyse-handoff` to complete BA
|
|
125
|
+
2. Verify `feature.handoff.status === "handed-off"`
|
|
126
|
+
3. Re-run `/business-analyse-develop`
|
|
127
|
+
|
|
128
|
+
If CHECK 2 warns about empty categories:
|
|
129
|
+
|
|
130
|
+
1. Review the missing categories
|
|
131
|
+
2. Optionally re-run BA to add files to those categories
|
|
132
|
+
3. Or accept the warning (subsequent guardrails will inject missing categories)
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# PRD v3 Transformation: filesToCreate → tasks
|
|
2
|
+
|
|
3
|
+
> **Used by:** step-01-task.md (section 2b)
|
|
4
|
+
|
|
5
|
+
## Algorithm: Transform filesToCreate to tasks[]
|
|
6
|
+
|
|
7
|
+
When PRD v3.0.0 has `implementation.filesToCreate` but NO `tasks[]`, transform all files into a task list with derived acceptance criteria.
|
|
8
|
+
|
|
9
|
+
### Input
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
prd.implementation.filesToCreate = {
|
|
13
|
+
domain: [ { path: "Domain/Employee.cs", ... }, ... ],
|
|
14
|
+
infrastructure: [ ... ],
|
|
15
|
+
application: [ ... ],
|
|
16
|
+
api: [ ... ],
|
|
17
|
+
seedData: [ ... ],
|
|
18
|
+
frontend: [ ... ],
|
|
19
|
+
tests: [ ... ],
|
|
20
|
+
documentation: [ ... ]
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Companion Specs (Optional)
|
|
25
|
+
|
|
26
|
+
Load companion specification files to enrich acceptance criteria:
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
const specFiles = prd.specificationFiles || {};
|
|
30
|
+
const specsDir = '.ralph';
|
|
31
|
+
let specEntities = null, specRules = null, specUsecases = null, specScreens = null, specPermissions = null;
|
|
32
|
+
try {
|
|
33
|
+
if (specFiles.entities) specEntities = readJSON(`${specsDir}/${specFiles.entities}`);
|
|
34
|
+
if (specFiles.rules) specRules = readJSON(`${specsDir}/${specFiles.rules}`);
|
|
35
|
+
if (specFiles.usecases) specUsecases = readJSON(`${specsDir}/${specFiles.usecases}`);
|
|
36
|
+
if (specFiles.screens) specScreens = readJSON(`${specsDir}/${specFiles.screens}`);
|
|
37
|
+
if (specFiles.permissions) specPermissions = readJSON(`${specsDir}/${specFiles.permissions}`);
|
|
38
|
+
} catch (e) { /* companion files absent — fallback to generic AC */ }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Transformation Logic
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const ftc = prd.implementation.filesToCreate;
|
|
45
|
+
const tasks = [];
|
|
46
|
+
let taskId = 1;
|
|
47
|
+
|
|
48
|
+
// Category order: domain → infrastructure → application → api → seedData → frontend → tests → documentation
|
|
49
|
+
const categoryOrder = ['domain', 'infrastructure', 'application', 'api', 'seedData', 'frontend', 'tests', 'documentation'];
|
|
50
|
+
|
|
51
|
+
for (const category of categoryOrder) {
|
|
52
|
+
const files = ftc[category] || [];
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
tasks.push({
|
|
55
|
+
id: `T${String(taskId++).padStart(3, '0')}`,
|
|
56
|
+
description: `Create ${file.type || category}: ${(file.path || file).split('/').pop()}`,
|
|
57
|
+
status: 'pending',
|
|
58
|
+
category: category === 'tests' ? 'test' : category, // normalize
|
|
59
|
+
dependencies: [], // implicit ordering via category
|
|
60
|
+
acceptance_criteria: deriveAcceptanceCriteria(file, category === 'tests' ? 'test' : category, prd),
|
|
61
|
+
path: file.path || file,
|
|
62
|
+
started_at: null, completed_at: null, iteration: null,
|
|
63
|
+
commit_hash: null, files_changed: [], validation: null, error: null
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
prd.tasks = tasks;
|
|
69
|
+
prd.config = { current_iteration: 1, max_iterations: 50 };
|
|
70
|
+
writeJSON(currentPrdPath, prd);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## deriveAcceptanceCriteria() Function
|
|
76
|
+
|
|
77
|
+
Six category-specific branches derive acceptance criteria from companion specs or fallback to generic statement.
|
|
78
|
+
|
|
79
|
+
### Category: domain
|
|
80
|
+
|
|
81
|
+
Extract entity attributes and relationships from `specEntities.entities`:
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
case 'domain': {
|
|
85
|
+
const entityName = fileName;
|
|
86
|
+
const entity = (specEntities?.entities || []).find(e => e.name === entityName);
|
|
87
|
+
if (entity && entity.attributes) {
|
|
88
|
+
const attrs = entity.attributes.map(a => `${a.name}:${a.type}`).join(', ');
|
|
89
|
+
const rels = (entity.relationships || []).map(r => `${r.type} ${r.target}`).join(', ');
|
|
90
|
+
return `Entity ${entityName} with attributes [${attrs}]${rels ? '. Relations: [' + rels + ']' : ''}`;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Fallback:** `File {path} exists and compiles`
|
|
97
|
+
|
|
98
|
+
### Category: infrastructure
|
|
99
|
+
|
|
100
|
+
Extract EF configuration from entity spec and attribute names:
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
case 'infrastructure': {
|
|
104
|
+
const entityName = fileName.replace('Configuration', '');
|
|
105
|
+
const entity = (specEntities?.entities || []).find(e => e.name === entityName);
|
|
106
|
+
if (entity && entity.attributes) {
|
|
107
|
+
const props = entity.attributes.map(a => a.name).join(', ');
|
|
108
|
+
return `EF Config for ${entityName}. Attributes: [${props}]`;
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Fallback:** `File {path} exists and compiles`
|
|
115
|
+
|
|
116
|
+
### Category: application
|
|
117
|
+
|
|
118
|
+
Link to business rules and use cases via `brToCodeMapping` and `linkedUCs`:
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
case 'application': {
|
|
122
|
+
const linkedBRs = (prd.brToCodeMapping || [])
|
|
123
|
+
.filter(br => br.implementationPoints?.some(ip => ip.component?.includes(fileName)))
|
|
124
|
+
.map(br => `${br.ruleId}: ${br.statement || br.title}`);
|
|
125
|
+
const linkedUCs = (file.linkedUCs || []).join(', ');
|
|
126
|
+
if (linkedBRs.length > 0 || linkedUCs) {
|
|
127
|
+
const parts = [];
|
|
128
|
+
if (linkedBRs.length > 0) parts.push(`Implements BRs: [${linkedBRs.join('; ')}]`);
|
|
129
|
+
if (linkedUCs) parts.push(`Handles UCs: [${linkedUCs}]`);
|
|
130
|
+
return parts.join('. ');
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Fallback:** `File {path} exists and compiles`
|
|
137
|
+
|
|
138
|
+
### Category: api
|
|
139
|
+
|
|
140
|
+
Extract endpoints and permissions from specifications:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
case 'api': {
|
|
144
|
+
const entityName = fileName.replace('Controller', '');
|
|
145
|
+
const endpoints = (prd.apiEndpointSummary || [])
|
|
146
|
+
.filter(ep => ep.operation?.includes(entityName))
|
|
147
|
+
.map(ep => `${ep.method} ${ep.route}`);
|
|
148
|
+
const perms = (specPermissions?.permissionPaths || [])
|
|
149
|
+
.filter(p => p.toLowerCase().includes(entityName.toLowerCase()))
|
|
150
|
+
.slice(0, 5);
|
|
151
|
+
const parts = [`Controller ${entityName}`];
|
|
152
|
+
if (endpoints.length > 0) parts.push(`Endpoints: [${endpoints.join(', ')}]`);
|
|
153
|
+
if (perms.length > 0) parts.push(`Permissions: [${perms.join(', ')}]`);
|
|
154
|
+
return parts.join('. ');
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Fallback:** `File {path} exists and compiles`
|
|
159
|
+
|
|
160
|
+
### Category: frontend
|
|
161
|
+
|
|
162
|
+
Extract screen columns, filters, actions, and KPIs from screen specification:
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
case 'frontend': {
|
|
166
|
+
const screen = (specScreens?.screens || []).find(s =>
|
|
167
|
+
fileName.toLowerCase().includes(s.code?.toLowerCase() || s.name?.toLowerCase() || '')
|
|
168
|
+
);
|
|
169
|
+
if (screen) {
|
|
170
|
+
const parts = [file.type || 'Page'];
|
|
171
|
+
if (screen.columns) parts.push(`columns [${screen.columns.map(c => c.key || c.name || c).join(', ')}]`);
|
|
172
|
+
if (screen.filters) parts.push(`filters [${screen.filters.map(f => f.key || f.name || f).join(', ')}]`);
|
|
173
|
+
if (screen.actions) parts.push(`actions [${screen.actions.map(a => a.key || a.name || a).join(', ')}]`);
|
|
174
|
+
if (screen.kpis) parts.push(`KPIs [${screen.kpis.map(k => k.label || k.name || k).join(', ')}]`);
|
|
175
|
+
return `${parts.join(' with ')}`;
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Fallback:** `File {path} exists and compiles`
|
|
182
|
+
|
|
183
|
+
### Category: seedData
|
|
184
|
+
|
|
185
|
+
Extract seed values for business seed data from entity spec:
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
case 'seedData': {
|
|
189
|
+
if (file.category === 'business') {
|
|
190
|
+
const entityName = fileName.replace('SeedData', '');
|
|
191
|
+
const entity = (specEntities?.entities || []).find(e => e.name === entityName);
|
|
192
|
+
if (entity?.seedValues) {
|
|
193
|
+
const names = entity.seedValues.map(v => v.name || v.label || v.code).filter(Boolean).slice(0, 5);
|
|
194
|
+
return `Seeds ${entityName} with ${entity.seedValues.length} values: [${names.join(', ')}]`;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Fallback:** `File {path} exists and compiles`
|
|
202
|
+
|
|
203
|
+
### Category: test / tests
|
|
204
|
+
|
|
205
|
+
Link to covered business rules and use cases:
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
case 'test': case 'tests': {
|
|
209
|
+
const linkedBRs = (prd.brToCodeMapping || [])
|
|
210
|
+
.filter(br => br.implementationPoints?.some(ip =>
|
|
211
|
+
ip.layer === 'Domain' || ip.layer === 'Application'
|
|
212
|
+
))
|
|
213
|
+
.map(br => br.ruleId);
|
|
214
|
+
const linkedUCs = (file.linkedUCs || []);
|
|
215
|
+
const parts = [];
|
|
216
|
+
if (linkedBRs.length > 0) parts.push(`Covers BRs: [${linkedBRs.join(', ')}]`);
|
|
217
|
+
if (linkedUCs.length > 0) parts.push(`Covers UCs: [${linkedUCs.join(', ')}]`);
|
|
218
|
+
if (parts.length > 0) return parts.join('. ');
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Fallback:** `File {path} exists and compiles`
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Full Function
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
function deriveAcceptanceCriteria(file, category, prd) {
|
|
231
|
+
const fileName = (file.path || file).split('/').pop().replace(/\.\w+$/, '');
|
|
232
|
+
|
|
233
|
+
// If no companion specs available, fallback to generic AC
|
|
234
|
+
if (!specEntities && !specRules) {
|
|
235
|
+
return `File ${file.path || file} exists and compiles`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
switch (category) {
|
|
239
|
+
case 'domain': {
|
|
240
|
+
const entityName = fileName;
|
|
241
|
+
const entity = (specEntities?.entities || []).find(e => e.name === entityName);
|
|
242
|
+
if (entity && entity.attributes) {
|
|
243
|
+
const attrs = entity.attributes.map(a => `${a.name}:${a.type}`).join(', ');
|
|
244
|
+
const rels = (entity.relationships || []).map(r => `${r.type} ${r.target}`).join(', ');
|
|
245
|
+
return `Entity ${entityName} with attributes [${attrs}]${rels ? '. Relations: [' + rels + ']' : ''}`;
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case 'application': {
|
|
250
|
+
const linkedBRs = (prd.brToCodeMapping || [])
|
|
251
|
+
.filter(br => br.implementationPoints?.some(ip => ip.component?.includes(fileName)))
|
|
252
|
+
.map(br => `${br.ruleId}: ${br.statement || br.title}`);
|
|
253
|
+
const linkedUCs = (file.linkedUCs || []).join(', ');
|
|
254
|
+
if (linkedBRs.length > 0 || linkedUCs) {
|
|
255
|
+
const parts = [];
|
|
256
|
+
if (linkedBRs.length > 0) parts.push(`Implements BRs: [${linkedBRs.join('; ')}]`);
|
|
257
|
+
if (linkedUCs) parts.push(`Handles UCs: [${linkedUCs}]`);
|
|
258
|
+
return parts.join('. ');
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case 'infrastructure': {
|
|
263
|
+
const entityName = fileName.replace('Configuration', '');
|
|
264
|
+
const entity = (specEntities?.entities || []).find(e => e.name === entityName);
|
|
265
|
+
if (entity && entity.attributes) {
|
|
266
|
+
const props = entity.attributes.map(a => a.name).join(', ');
|
|
267
|
+
return `EF Config for ${entityName}. Attributes: [${props}]`;
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case 'api': {
|
|
272
|
+
const entityName = fileName.replace('Controller', '');
|
|
273
|
+
const endpoints = (prd.apiEndpointSummary || [])
|
|
274
|
+
.filter(ep => ep.operation?.includes(entityName))
|
|
275
|
+
.map(ep => `${ep.method} ${ep.route}`);
|
|
276
|
+
const perms = (specPermissions?.permissionPaths || [])
|
|
277
|
+
.filter(p => p.toLowerCase().includes(entityName.toLowerCase()))
|
|
278
|
+
.slice(0, 5);
|
|
279
|
+
const parts = [`Controller ${entityName}`];
|
|
280
|
+
if (endpoints.length > 0) parts.push(`Endpoints: [${endpoints.join(', ')}]`);
|
|
281
|
+
if (perms.length > 0) parts.push(`Permissions: [${perms.join(', ')}]`);
|
|
282
|
+
return parts.join('. ');
|
|
283
|
+
}
|
|
284
|
+
case 'frontend': {
|
|
285
|
+
const screen = (specScreens?.screens || []).find(s =>
|
|
286
|
+
fileName.toLowerCase().includes(s.code?.toLowerCase() || s.name?.toLowerCase() || '')
|
|
287
|
+
);
|
|
288
|
+
if (screen) {
|
|
289
|
+
const parts = [file.type || 'Page'];
|
|
290
|
+
if (screen.columns) parts.push(`columns [${screen.columns.map(c => c.key || c.name || c).join(', ')}]`);
|
|
291
|
+
if (screen.filters) parts.push(`filters [${screen.filters.map(f => f.key || f.name || f).join(', ')}]`);
|
|
292
|
+
if (screen.actions) parts.push(`actions [${screen.actions.map(a => a.key || a.name || a).join(', ')}]`);
|
|
293
|
+
if (screen.kpis) parts.push(`KPIs [${screen.kpis.map(k => k.label || k.name || k).join(', ')}]`);
|
|
294
|
+
return `${parts.join(' with ')}`;
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
case 'seedData': {
|
|
299
|
+
if (file.category === 'business') {
|
|
300
|
+
const entityName = fileName.replace('SeedData', '');
|
|
301
|
+
const entity = (specEntities?.entities || []).find(e => e.name === entityName);
|
|
302
|
+
if (entity?.seedValues) {
|
|
303
|
+
const names = entity.seedValues.map(v => v.name || v.label || v.code).filter(Boolean).slice(0, 5);
|
|
304
|
+
return `Seeds ${entityName} with ${entity.seedValues.length} values: [${names.join(', ')}]`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
case 'test': case 'tests': {
|
|
310
|
+
const linkedBRs = (prd.brToCodeMapping || [])
|
|
311
|
+
.filter(br => br.implementationPoints?.some(ip =>
|
|
312
|
+
ip.layer === 'Domain' || ip.layer === 'Application'
|
|
313
|
+
))
|
|
314
|
+
.map(br => br.ruleId);
|
|
315
|
+
const linkedUCs = (file.linkedUCs || []);
|
|
316
|
+
const parts = [];
|
|
317
|
+
if (linkedBRs.length > 0) parts.push(`Covers BRs: [${linkedBRs.join(', ')}]`);
|
|
318
|
+
if (linkedUCs.length > 0) parts.push(`Covers UCs: [${linkedUCs.join(', ')}]`);
|
|
319
|
+
if (parts.length > 0) return parts.join('. ');
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// Fallback: generic AC
|
|
324
|
+
return `File ${file.path || file} exists and compiles`;
|
|
325
|
+
}
|
|
326
|
+
```
|