@atlashub/smartstack-cli 4.48.0 → 4.50.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/.documentation/testing-ba-e2e.md +76 -24
- package/package.json +1 -1
- package/templates/agents/gitflow/init.md +26 -0
- package/templates/skills/apex/references/parallel-execution.md +22 -4
- package/templates/skills/apex/steps/step-00-init.md +38 -0
- package/templates/skills/apex/steps/step-03a-layer0-domain.md +21 -0
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +60 -0
- package/templates/skills/apex/steps/step-03c-layer2-backend.md +124 -13
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +32 -0
- package/templates/skills/application/references/backend-controller-hierarchy.md +14 -4
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +2 -0
- package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +2 -0
- package/templates/skills/business-analyse/schemas/application-schema.json +36 -1
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +19 -0
- package/templates/skills/business-analyse/steps/step-00-init.md +64 -14
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +49 -2
- package/templates/skills/business-analyse/steps/step-02-structure.md +41 -17
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +171 -0
- package/templates/skills/business-analyse-develop/references/quality-gates.md +91 -1
- package/templates/skills/business-analyse-develop/steps/step-01-task.md +147 -1
- package/templates/skills/business-analyse-handoff/references/acceptance-criteria.md +53 -1
- package/templates/skills/business-analyse-handoff/references/handoff-file-templates.md +42 -0
- package/templates/skills/business-analyse-handoff/references/handoff-mappings.md +15 -1
- package/templates/skills/business-analyse-handoff/references/prd-generation.md +59 -0
- package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +25 -1
- package/templates/skills/business-analyse-handoff/steps/step-02-export.md +32 -4
- package/templates/skills/business-analyse-html/html/ba-interactive.html +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +4 -2
- package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +16 -2
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +19 -4
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +1 -1
- package/templates/skills/business-analyse-html/html/src/styles/03-navigation.css +2 -2
- package/templates/skills/business-analyse-html/html/src/styles/05-modules.css +38 -0
- package/templates/skills/business-analyse-html/references/data-build.md +4 -1
- package/templates/skills/business-analyse-html/references/data-mapping.md +4 -1
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +113 -1
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +17 -1
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +8 -4
- package/templates/skills/controller/steps/step-05-validate.md +2 -2
- package/templates/skills/controller/templates.md +4 -3
- package/templates/skills/feature-full/steps/step-01-implementation.md +18 -5
|
@@ -89,7 +89,11 @@ modules: master.modules.map(m => ({
|
|
|
89
89
|
description: m.description || "",
|
|
90
90
|
featureType: m.featureType || "data-centric",
|
|
91
91
|
entities: m.entities || [],
|
|
92
|
-
anticipatedSections: m.anticipatedSections || []
|
|
92
|
+
anticipatedSections: (m.anticipatedSections || []).map(s =>
|
|
93
|
+
typeof s === 'string'
|
|
94
|
+
? { code: s, name: s, description: "", resources: [] }
|
|
95
|
+
: { code: s.code || s.name || "", name: s.label || s.name || s.code || "", description: s.description || "", resources: s.resources || [], route: s.route || "", permission: s.permission || "" }
|
|
96
|
+
),
|
|
93
97
|
dependencies: m.dependencies || [],
|
|
94
98
|
dependents: m.dependents || [],
|
|
95
99
|
estimatedComplexity: m.estimatedComplexity || "medium",
|
|
@@ -135,6 +139,50 @@ moduleSpecs[moduleCode] = {
|
|
|
135
139
|
permissions: buildPermissionKeys(mod.permissions),
|
|
136
140
|
apiEndpoints: mod.usecases?.apiEndpoints || []
|
|
137
141
|
}
|
|
142
|
+
|
|
143
|
+
// BUILD screens[] for HTML interactive mockups (SmartTable/SmartForm rendering)
|
|
144
|
+
const flatScr = mod.screens?.screens || [];
|
|
145
|
+
let screens = [];
|
|
146
|
+
if (flatScr.length > 0) {
|
|
147
|
+
const bySec = {};
|
|
148
|
+
flatScr.forEach(s => {
|
|
149
|
+
const sec = s.section || "default";
|
|
150
|
+
if (!bySec[sec]) bySec[sec] = {
|
|
151
|
+
sectionCode: sec,
|
|
152
|
+
sectionLabel: s.sectionLabel || s.sectionDescription || sec,
|
|
153
|
+
resources: []
|
|
154
|
+
};
|
|
155
|
+
bySec[sec].resources.push({
|
|
156
|
+
code: s.screen || s.name || "",
|
|
157
|
+
label: s.sectionLabel || s.description || s.screen || "",
|
|
158
|
+
type: s.componentType || "unknown",
|
|
159
|
+
columns: (s.columns || []).map(c => ({
|
|
160
|
+
field: c.code || c.field || "", label: c.label || c.code || "",
|
|
161
|
+
type: c.type || c.dataType || "text", sortable: !!c.sortable
|
|
162
|
+
})),
|
|
163
|
+
filters: (s.filters || []).map(f =>
|
|
164
|
+
typeof f === 'string' ? f : (f.label || f.filterLabel || f.code || f.filterCode || "")),
|
|
165
|
+
fields: [],
|
|
166
|
+
tabs: (s.tabs || []).map(t => ({
|
|
167
|
+
label: t.label || t.tabLabel || t.code || "",
|
|
168
|
+
fields: (t.fields || []).map(f => ({
|
|
169
|
+
field: f.code || f.fieldCode || f.field || "",
|
|
170
|
+
label: f.label || f.code || "",
|
|
171
|
+
type: f.type || f.dataType || "text",
|
|
172
|
+
required: !!f.required
|
|
173
|
+
}))
|
|
174
|
+
})),
|
|
175
|
+
actions: (s.actions || []).map(a =>
|
|
176
|
+
typeof a === 'string' ? a : (a.code || a.label || a.actionCode || "")),
|
|
177
|
+
kpis: s.kpis || [],
|
|
178
|
+
options: s.options || [],
|
|
179
|
+
permission: s.permission || "",
|
|
180
|
+
notes: s.description || ""
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
screens = Object.values(bySec);
|
|
184
|
+
}
|
|
185
|
+
moduleSpecs[moduleCode].screens = screens;
|
|
138
186
|
```
|
|
139
187
|
|
|
140
188
|
> See `references/data-mapping.md` for `buildPermissionKeys()` implementation.
|
|
@@ -156,6 +204,44 @@ FEATURE_DATA.modules.forEach(m => {
|
|
|
156
204
|
|
|
157
205
|
> This step is CRITICAL for the HTML viewer to display UC/BR grouped by section.
|
|
158
206
|
|
|
207
|
+
### Post-Build Self-Check (MANDATORY — BLOCKING)
|
|
208
|
+
|
|
209
|
+
After enriching anticipatedSections, run this self-check to detect data loss:
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
// SELF-CHECK: compare source file counts vs FEATURE_DATA counts
|
|
213
|
+
const errors = [];
|
|
214
|
+
FEATURE_DATA.modules.forEach(m => {
|
|
215
|
+
const spec = FEATURE_DATA.moduleSpecs[m.code];
|
|
216
|
+
const source = collected_data.modules[m.code];
|
|
217
|
+
if (!spec) { errors.push(`MISSING moduleSpecs[${m.code}]`); return; }
|
|
218
|
+
|
|
219
|
+
const srcUC = (source?.usecases?.useCases || []).length;
|
|
220
|
+
const bltUC = (spec.useCases || []).length;
|
|
221
|
+
if (srcUC > 0 && bltUC === 0)
|
|
222
|
+
errors.push(`${m.code}: useCases EMPTY but source has ${srcUC}`);
|
|
223
|
+
|
|
224
|
+
const srcBR = (source?.rules?.rules || []).length;
|
|
225
|
+
const bltBR = (spec.businessRules || []).length;
|
|
226
|
+
if (srcBR > 0 && bltBR === 0)
|
|
227
|
+
errors.push(`${m.code}: businessRules EMPTY but source has ${srcBR}`);
|
|
228
|
+
|
|
229
|
+
const srcEnt = (source?.entities?.entities || []).length;
|
|
230
|
+
const bltEnt = (spec.entities || []).length;
|
|
231
|
+
if (srcEnt > 0 && bltEnt === 0)
|
|
232
|
+
errors.push(`${m.code}: entities EMPTY but source has ${srcEnt}`);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (errors.length > 0) {
|
|
236
|
+
Display("⛔ SELF-CHECK FAILED — data loss detected:");
|
|
237
|
+
errors.forEach(e => Display(" - " + e));
|
|
238
|
+
// FIX: re-map the failing modules from collected_data before continuing
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
> If self-check detects errors, you MUST re-read the source data and re-build the failing moduleSpecs entries.
|
|
243
|
+
> Do NOT proceed with empty arrays when source data exists.
|
|
244
|
+
|
|
159
245
|
**consolidation:**
|
|
160
246
|
```javascript
|
|
161
247
|
consolidation: {
|
|
@@ -223,6 +309,31 @@ for (const section of sections) {
|
|
|
223
309
|
}
|
|
224
310
|
}
|
|
225
311
|
|
|
312
|
+
// Source C: screens without mockup → auto-generate text description as wireframe fallback
|
|
313
|
+
const screensWithoutMockup = flatScreens.filter(s => !s.mockup && !s.mockupFormat);
|
|
314
|
+
for (const screen of screensWithoutMockup) {
|
|
315
|
+
const type = screen.componentType || "";
|
|
316
|
+
let desc = "";
|
|
317
|
+
if (type.includes("Table") || type.includes("Grid")) {
|
|
318
|
+
const cols = screen.columns || [];
|
|
319
|
+
desc = "Tableau avec " + cols.length + " colonnes : " + cols.map(c => c.label || c.code).join(", ");
|
|
320
|
+
} else if (type.includes("Form")) {
|
|
321
|
+
const tabs = screen.tabs || [];
|
|
322
|
+
desc = "Formulaire " + (tabs.length > 0 ? "avec " + tabs.length + " onglet(s)" : "");
|
|
323
|
+
} else if (type.includes("Dashboard")) {
|
|
324
|
+
desc = "Tableau de bord avec KPIs";
|
|
325
|
+
} else if (type.includes("Kanban")) {
|
|
326
|
+
desc = "Vue Kanban";
|
|
327
|
+
}
|
|
328
|
+
if (desc) {
|
|
329
|
+
rawWireframes.push({
|
|
330
|
+
screen: screen.screen || "", section: screen.section || "",
|
|
331
|
+
mockupFormat: "text", mockup: desc,
|
|
332
|
+
description: screen.sectionDescription || "", elements: [], componentMapping: []
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
226
337
|
// STEP 2: Map to HTML format (RENAME: mockupFormat → format, mockup → content)
|
|
227
338
|
wireframes: {
|
|
228
339
|
[moduleCode]: rawWireframes.map(wf => ({
|
|
@@ -287,6 +398,7 @@ dependencyGraph: {
|
|
|
287
398
|
- FEATURE_DATA.moduleSpecs must have ONE entry per module
|
|
288
399
|
- cadrage.scope uses HTML keys (inscope/outofscope)
|
|
289
400
|
- Wireframe fields use format/content (NOT mockupFormat/mockup)
|
|
401
|
+
- Per-module: useCases/businessRules/entities count must match source (empty when source has data = BUG)
|
|
290
402
|
|
|
291
403
|
## NEXT STEP
|
|
292
404
|
|
|
@@ -8,7 +8,7 @@ model: opus
|
|
|
8
8
|
|
|
9
9
|
## YOUR TASK
|
|
10
10
|
|
|
11
|
-
Run
|
|
11
|
+
Run 6 blocking validations on the generated HTML file and display the completion summary.
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -53,6 +53,22 @@ FOR each module in FEATURE_DATA.modules:
|
|
|
53
53
|
BLOCKING_ERROR("Module {module.code} not in wireframes")
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
**Check 6 — Per-module data completeness (source vs HTML):**
|
|
57
|
+
```
|
|
58
|
+
FOR each module in FEATURE_DATA.modules:
|
|
59
|
+
Read source: docs/{app}/{module.code}/business-analyse/v{version}/usecases.json
|
|
60
|
+
sourceUCCount = count of useCases in source file
|
|
61
|
+
htmlUCCount = count in FEATURE_DATA.moduleSpecs[module.code].useCases
|
|
62
|
+
IF sourceUCCount > 0 AND htmlUCCount === 0:
|
|
63
|
+
BLOCKING_ERROR("Module {module.code}: 0 useCases in HTML but {sourceUCCount} in source")
|
|
64
|
+
|
|
65
|
+
Read source: docs/{app}/{module.code}/business-analyse/v{version}/rules.json
|
|
66
|
+
sourceBRCount = count of rules in source file
|
|
67
|
+
htmlBRCount = count in FEATURE_DATA.moduleSpecs[module.code].businessRules
|
|
68
|
+
IF sourceBRCount > 0 AND htmlBRCount === 0:
|
|
69
|
+
BLOCKING_ERROR("Module {module.code}: 0 businessRules in HTML but {sourceBRCount} in source")
|
|
70
|
+
```
|
|
71
|
+
|
|
56
72
|
> **IF any check fails:** fix the issue and re-run the failing step before completing.
|
|
57
73
|
|
|
58
74
|
---
|
|
@@ -220,14 +220,18 @@ console.log(`✅ All checks passed — controller ready for step-04`);
|
|
|
220
220
|
If MCP doesn't generate [NavRoute] attribute:
|
|
221
221
|
|
|
222
222
|
```csharp
|
|
223
|
-
// Manually add
|
|
223
|
+
// Manually add — REPLACE [Route] with [NavRoute], do NOT keep both (causes 404s):
|
|
224
224
|
[ApiController]
|
|
225
|
-
[
|
|
226
|
-
[
|
|
225
|
+
[NavRoute("app.module.resource")]
|
|
226
|
+
[Microsoft.AspNetCore.Authorization.Authorize]
|
|
227
|
+
[Produces("application/json")]
|
|
228
|
+
[Tags("{EntityNamePlural}")]
|
|
227
229
|
public class {EntityName}Controller : ControllerBase
|
|
228
230
|
{
|
|
229
231
|
// ...
|
|
230
232
|
}
|
|
231
233
|
```
|
|
232
234
|
|
|
233
|
-
|
|
235
|
+
**CRITICAL:** Remove `[Route("api/...")]` if present — combining `[Route]` and `[NavRoute]` causes 404 errors.
|
|
236
|
+
|
|
237
|
+
This indicates MCP failure and should not be needed in normal operation.
|
|
@@ -40,8 +40,8 @@ mcp__smartstack__validate_conventions:
|
|
|
40
40
|
|
|
41
41
|
| Convention | Check | Status |
|
|
42
42
|
|------------|-------|--------|
|
|
43
|
-
|
|
|
44
|
-
| Authorize attribute | `[Authorize]`
|
|
43
|
+
| NavRoute attribute | `[NavRoute("{app}.{module}")]` present (NOT `[Route]`) | ✅/❌ |
|
|
44
|
+
| Authorize attribute | `[Microsoft.AspNetCore.Authorization.Authorize]` (fully qualified) | ✅/❌ |
|
|
45
45
|
| RequirePermission | Uses `Permissions.*` constants | ✅/❌ |
|
|
46
46
|
| ProducesResponseType | All endpoints documented | ✅/❌ |
|
|
47
47
|
| Logging | All CRUD operations logged | ✅/❌ |
|
|
@@ -30,12 +30,13 @@ using SmartStack.Domain.{DomainNamespace};
|
|
|
30
30
|
namespace SmartStack.Api.Controllers.{Area};
|
|
31
31
|
|
|
32
32
|
[ApiController]
|
|
33
|
-
[
|
|
34
|
-
[Authorize]
|
|
33
|
+
[NavRoute("{area}.{module}")]
|
|
34
|
+
[Microsoft.AspNetCore.Authorization.Authorize]
|
|
35
|
+
[Produces("application/json")]
|
|
35
36
|
[Tags("{Module}")]
|
|
36
37
|
public class {Module}Controller : ControllerBase
|
|
37
38
|
{
|
|
38
|
-
private readonly
|
|
39
|
+
private readonly ISender _mediator; // MediatR — NOT IApplicationDbContext
|
|
39
40
|
private readonly ICurrentUserService _currentUser;
|
|
40
41
|
private readonly ILogger<{Module}Controller> _logger;
|
|
41
42
|
|
|
@@ -58,15 +58,28 @@ services.AddScoped<I{Entity}Service, {Entity}Service>();
|
|
|
58
58
|
## Phase 5: API
|
|
59
59
|
|
|
60
60
|
```csharp
|
|
61
|
-
[ApiController]
|
|
61
|
+
[ApiController]
|
|
62
|
+
[NavRoute("{area}.{module}")]
|
|
63
|
+
[Microsoft.AspNetCore.Authorization.Authorize]
|
|
64
|
+
[Produces("application/json")]
|
|
65
|
+
[Tags("{EntityPlural}")]
|
|
62
66
|
public class {Entity}Controller : ControllerBase
|
|
63
67
|
{
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
private readonly ISender _mediator;
|
|
69
|
+
|
|
70
|
+
[HttpGet]
|
|
71
|
+
[RequirePermission(Permissions.{Area}.{Module}.View)]
|
|
72
|
+
[ProducesResponseType(typeof(PaginatedResult<{Entity}Dto>), StatusCodes.Status200OK)]
|
|
73
|
+
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
74
|
+
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
66
75
|
public async Task<ActionResult<PaginatedResult<{Entity}Dto>>> GetAll(...);
|
|
67
76
|
|
|
68
|
-
[HttpPost]
|
|
69
|
-
[
|
|
77
|
+
[HttpPost]
|
|
78
|
+
[RequirePermission(Permissions.{Area}.{Module}.Create)]
|
|
79
|
+
[ProducesResponseType(typeof({Entity}Dto), StatusCodes.Status201Created)]
|
|
80
|
+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
81
|
+
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
82
|
+
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
70
83
|
public async Task<ActionResult<{Entity}Dto>> Create([FromBody] request, CancellationToken ct);
|
|
71
84
|
}
|
|
72
85
|
```
|