@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.
Files changed (121) hide show
  1. package/dist/index.js +152 -31
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +14 -3
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/agents/ba-reader.md +17 -15
  7. package/templates/agents/ba-writer.md +49 -51
  8. package/templates/skills/apex/SKILL.md +2 -2
  9. package/templates/skills/apex/_shared.md +1 -1
  10. package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
  11. package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
  12. package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
  13. package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
  14. package/templates/skills/apex/references/core-seed-data.md +20 -18
  15. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
  16. package/templates/skills/apex/references/post-checks.md +23 -3
  17. package/templates/skills/apex/references/smartstack-api.md +4 -4
  18. package/templates/skills/apex/references/smartstack-frontend.md +54 -8
  19. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  20. package/templates/skills/apex/steps/step-00-init.md +75 -1
  21. package/templates/skills/apex/steps/step-03-execute.md +16 -4
  22. package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
  23. package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
  24. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
  25. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  26. package/templates/skills/apex-verify/SKILL.md +110 -0
  27. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  28. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  29. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
  30. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  31. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  32. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  33. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  34. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
  35. package/templates/skills/application/templates-frontend.md +2 -2
  36. package/templates/skills/business-analyse/SKILL.md +17 -3
  37. package/templates/skills/business-analyse/_shared.md +64 -0
  38. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
  39. package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
  40. package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
  41. package/templates/skills/business-analyse/questionnaire.md +86 -9
  42. package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
  43. package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
  44. package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
  45. package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
  46. package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
  47. package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
  48. package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
  49. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  50. package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
  51. package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
  52. package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
  53. package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
  54. package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
  55. package/templates/skills/business-analyse/references/portal-classification.md +52 -0
  56. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  57. package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
  58. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
  59. package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
  60. package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
  61. package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
  62. package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
  63. package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
  64. package/templates/skills/business-analyse-design/SKILL.md +10 -0
  65. package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
  66. package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
  67. package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
  68. package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
  69. package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
  70. package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
  71. package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
  72. package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
  73. package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
  74. package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
  75. package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
  76. package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
  77. package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
  78. package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
  79. package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
  80. package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
  81. package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
  82. package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
  83. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
  84. package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
  85. package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
  86. package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
  87. package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
  88. package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
  89. package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
  90. package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
  91. package/templates/skills/business-analyse-html/SKILL.md +10 -0
  92. package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
  93. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
  94. package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
  95. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  96. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  97. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +94 -36
  98. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
  99. package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
  100. package/templates/skills/business-analyse-html/html/src/template.html +2 -0
  101. package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
  102. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
  103. package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
  104. package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
  105. package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
  106. package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
  107. package/templates/skills/business-analyse-html/references/data-build.md +24 -1
  108. package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
  109. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
  110. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  111. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  112. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  113. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  114. package/templates/skills/business-analyse-review/SKILL.md +10 -0
  115. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  116. package/templates/skills/business-analyse-status/SKILL.md +8 -0
  117. package/templates/skills/dev-start/SKILL.md +143 -307
  118. package/templates/skills/efcore/SKILL.md +13 -0
  119. package/templates/skills/sketch/SKILL.md +15 -153
  120. package/templates/skills/ui-components/SKILL.md +1 -1
  121. 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 application: routes, menus, breadcrumbs, and seedDataCore entries. This ensures consistent navigation across all modules.
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
- Application
30
- |-- Module 1
31
- | |-- Section 1 (e.g., "Liste")
32
- | | |-- Resource 1 (e.g., "employees-grid")
33
- | |-- Section 2 (e.g., "Fiche")
34
- | | |-- Resource 1 (e.g., "employee-form")
35
- | |-- Section 3 (e.g., "Dashboard")
36
- | |-- Resource 1 (e.g., "hr-dashboard")
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
- "application": {
46
- "code": "{AppCode}",
47
- "name": "{AppName}",
48
- "icon": "app-icon"
49
- },
50
- "modules": [
50
+ "applications": [
51
51
  {
52
- "code": "{ModuleCode}",
53
- "name": "{ModuleName}",
54
- "icon": "module-icon",
55
- "route": "/{app-code}/{module-code}",
56
- "permission": "{AppCode}.{ModuleCode}.Read",
57
- "sections": [
52
+ "code": "{AppCode}",
53
+ "name": "{AppName}",
54
+ "icon": "app-icon",
55
+ "route": "/{app-code}",
56
+ "modules": [
58
57
  {
59
- "code": "{sectionCode}",
60
- "label": "{sectionLabel}",
61
- "route": "/{app-code}/{module-code}/{section-code}",
62
- "icon": "section-icon",
63
- "resources": [
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": "{resourceCode}",
66
- "type": "{SmartTable|SmartForm|SmartDashboard|SmartKanban}",
67
- "route": "/{app-code}/{module-code}/{section-code}/{resource-code}",
68
- "permission": "{AppCode}.{ModuleCode}.{Action}"
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": "{ModuleName}",
78
- "icon": "module-icon",
79
- "route": "/{app-code}/{module-code}",
80
- "permission": "{AppCode}.{ModuleCode}.Read",
86
+ "label": "{AppName}",
87
+ "icon": "app-icon",
88
+ "route": "/{app-code}",
81
89
  "children": [
82
90
  {
83
- "label": "{sectionLabel}",
84
- "route": "/{app-code}/{module-code}/{section-code}",
85
- "permission": "{AppCode}.{ModuleCode}.Read"
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 in screens.json has a corresponding menu item
109
- - [ ] Every route is unique
110
- - [ ] Every menu item has a valid permission reference
111
- - [ ] Breadcrumbs cover all routes
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
+ ```