@atlashub/smartstack-cli 3.20.0 → 3.21.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 +53 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +1 -0
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/project/api.ts.template +8 -29
- package/templates/project/appsettings.json.template +1 -0
- package/templates/skills/business-analyse/html/ba-interactive.html +562 -150
- package/templates/skills/business-analyse/html/src/scripts/01-data-init.js +11 -6
- package/templates/skills/business-analyse/html/src/scripts/02-navigation.js +209 -4
- package/templates/skills/business-analyse/html/src/scripts/04-render-modules.js +2 -8
- package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +57 -2
- package/templates/skills/business-analyse/html/src/scripts/07-render-handoff.js +3 -1
- package/templates/skills/business-analyse/html/src/scripts/08-editing.js +112 -22
- package/templates/skills/business-analyse/html/src/scripts/11-review-panel.js +7 -0
- package/templates/skills/business-analyse/html/src/styles/02-layout.css +1 -1
- package/templates/skills/business-analyse/html/src/styles/03-navigation.css +89 -31
- package/templates/skills/business-analyse/html/src/styles/05-modules.css +64 -0
- package/templates/skills/business-analyse/html/src/template.html +8 -76
- package/templates/skills/business-analyse/references/deploy-data-build.md +9 -7
- package/templates/skills/business-analyse/references/html-data-mapping.md +20 -28
- package/templates/skills/business-analyse/references/validate-incremental-html.md +2 -1
- package/templates/skills/business-analyse/steps/step-03c-compile.md +55 -2
- package/templates/skills/business-analyse/steps/step-03d-validate.md +82 -15
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +77 -3
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +27 -0
|
@@ -1,62 +1,120 @@
|
|
|
1
1
|
/* ============================================
|
|
2
|
-
SIDEBAR -
|
|
2
|
+
SIDEBAR - Hierarchical Tree Navigation
|
|
3
3
|
============================================ */
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
/* Sidebar header */
|
|
6
|
+
.sidebar-header {
|
|
7
|
+
padding: 0.75rem 1rem;
|
|
8
|
+
border-bottom: 1px solid var(--border);
|
|
9
|
+
background: var(--bg-hover);
|
|
10
|
+
}
|
|
11
|
+
.sidebar-app-name {
|
|
12
|
+
font-size: 0.95rem;
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
color: var(--primary-light);
|
|
15
|
+
display: block;
|
|
16
|
+
white-space: nowrap;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
text-overflow: ellipsis;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Nav groups (top-level: Cadrage, Modules, Consolidation, Synthese) */
|
|
22
|
+
.nav-group { padding: 0.4rem 0; }
|
|
5
23
|
.nav-group + .nav-group { border-top: 1px solid var(--border); }
|
|
6
24
|
|
|
7
25
|
.nav-group-title {
|
|
8
|
-
font-size: 0.
|
|
26
|
+
font-size: 0.7rem;
|
|
9
27
|
text-transform: uppercase;
|
|
10
28
|
letter-spacing: 0.1em;
|
|
11
29
|
color: var(--text-muted);
|
|
12
|
-
padding: 0 1rem;
|
|
13
|
-
margin-bottom: 0.
|
|
30
|
+
padding: 0.4rem 1rem;
|
|
31
|
+
margin-bottom: 0.15rem;
|
|
14
32
|
font-weight: 600;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: 0.3rem;
|
|
37
|
+
user-select: none;
|
|
38
|
+
transition: color var(--transition-fast);
|
|
39
|
+
}
|
|
40
|
+
.nav-group-title:hover { color: var(--text-bright); }
|
|
41
|
+
|
|
42
|
+
/* Chevron icon for expand/collapse */
|
|
43
|
+
.nav-chevron {
|
|
44
|
+
font-size: 0.6rem;
|
|
45
|
+
display: inline-block;
|
|
46
|
+
transition: transform var(--transition-fast);
|
|
47
|
+
color: var(--text-muted);
|
|
48
|
+
width: 12px;
|
|
49
|
+
text-align: center;
|
|
50
|
+
flex-shrink: 0;
|
|
15
51
|
}
|
|
52
|
+
.nav-chevron.expanded { transform: rotate(90deg); }
|
|
16
53
|
|
|
54
|
+
/* Nav items (leaf nodes) */
|
|
17
55
|
.nav-item {
|
|
18
56
|
display: flex;
|
|
19
57
|
align-items: center;
|
|
20
|
-
gap: 0.
|
|
21
|
-
padding: 0.
|
|
58
|
+
gap: 0.4rem;
|
|
59
|
+
padding: 0.35rem 1rem;
|
|
22
60
|
color: var(--text);
|
|
23
61
|
text-decoration: none;
|
|
24
|
-
font-size: 0.
|
|
62
|
+
font-size: 0.82rem;
|
|
25
63
|
cursor: pointer;
|
|
26
64
|
transition: all var(--transition-fast);
|
|
27
65
|
border-left: 3px solid transparent;
|
|
28
66
|
}
|
|
29
67
|
.nav-item:hover { background: var(--bg-hover); color: var(--text-bright); }
|
|
30
|
-
.nav-item.active {
|
|
68
|
+
.nav-item.active {
|
|
69
|
+
background: rgba(99,102,241,0.1);
|
|
70
|
+
border-left-color: var(--primary);
|
|
71
|
+
color: var(--primary-light);
|
|
72
|
+
font-weight: 500;
|
|
73
|
+
}
|
|
31
74
|
|
|
32
|
-
.nav-item .nav-icon {
|
|
75
|
+
.nav-item .nav-icon {
|
|
76
|
+
font-size: 0.45rem;
|
|
77
|
+
width: 12px;
|
|
78
|
+
text-align: center;
|
|
79
|
+
color: var(--border-light);
|
|
80
|
+
flex-shrink: 0;
|
|
81
|
+
}
|
|
33
82
|
.nav-item .nav-badge {
|
|
34
83
|
margin-left: auto;
|
|
35
|
-
font-size: 0.
|
|
84
|
+
font-size: 0.6rem;
|
|
36
85
|
background: var(--bg-hover);
|
|
37
|
-
padding: 0.1rem 0.
|
|
86
|
+
padding: 0.1rem 0.35rem;
|
|
38
87
|
border-radius: 10px;
|
|
39
88
|
color: var(--text-muted);
|
|
89
|
+
min-width: 16px;
|
|
90
|
+
text-align: center;
|
|
40
91
|
}
|
|
41
92
|
|
|
42
|
-
|
|
43
|
-
.nav-children
|
|
93
|
+
/* Nested children (indented) */
|
|
94
|
+
.nav-children { margin-left: 0; }
|
|
95
|
+
.nav-children .nav-item { padding-left: 1.6rem; font-size: 0.8rem; }
|
|
96
|
+
.nav-children .nav-children .nav-item { padding-left: 2.4rem; font-size: 0.78rem; }
|
|
97
|
+
.nav-children .nav-children .nav-children .nav-item { padding-left: 3rem; font-size: 0.75rem; }
|
|
44
98
|
|
|
45
|
-
/*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
99
|
+
/* Module header in nav (collapsible) */
|
|
100
|
+
.nav-module-header {
|
|
101
|
+
font-weight: 500;
|
|
102
|
+
color: var(--text-bright);
|
|
103
|
+
font-size: 0.85rem;
|
|
104
|
+
}
|
|
105
|
+
.nav-module-header:hover { color: var(--primary-light); }
|
|
106
|
+
|
|
107
|
+
/* Section/Resource items in nav */
|
|
108
|
+
.nav-section-item { margin-top: 0.1rem; }
|
|
109
|
+
.nav-section-link { font-style: italic; }
|
|
110
|
+
.nav-icon-section { font-size: 0.5rem; color: var(--accent); }
|
|
111
|
+
.nav-icon-resource { font-size: 0.7rem; color: var(--border-light); }
|
|
112
|
+
.nav-resource-link {
|
|
113
|
+
cursor: default;
|
|
114
|
+
font-size: 0.72rem;
|
|
115
|
+
color: var(--text-muted);
|
|
116
|
+
padding-top: 0.2rem;
|
|
117
|
+
padding-bottom: 0.2rem;
|
|
58
118
|
}
|
|
59
|
-
.
|
|
60
|
-
.
|
|
61
|
-
.phase-line { flex: 1; height: 2px; background: var(--border); }
|
|
62
|
-
.phase-line.completed { background: var(--success); }
|
|
119
|
+
.nav-resource-link:hover { background: transparent; color: var(--text-muted); }
|
|
120
|
+
.nav-resources { margin-left: 0.5rem; }
|
|
@@ -388,3 +388,67 @@
|
|
|
388
388
|
padding-left: 0.75rem; border-left: 2px solid var(--accent);
|
|
389
389
|
margin-bottom: 0.2rem;
|
|
390
390
|
}
|
|
391
|
+
|
|
392
|
+
/* ============================================
|
|
393
|
+
STRUCTURE TAB (Sections/Resources)
|
|
394
|
+
============================================ */
|
|
395
|
+
.struct-section {
|
|
396
|
+
background: var(--bg-card);
|
|
397
|
+
border: 1px solid var(--border);
|
|
398
|
+
border-radius: 10px;
|
|
399
|
+
margin-bottom: 1rem;
|
|
400
|
+
overflow: hidden;
|
|
401
|
+
}
|
|
402
|
+
.struct-section-header {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
gap: 0.75rem;
|
|
406
|
+
padding: 0.75rem 1rem;
|
|
407
|
+
background: var(--bg-hover);
|
|
408
|
+
border-bottom: 1px solid var(--border);
|
|
409
|
+
}
|
|
410
|
+
.struct-section-code {
|
|
411
|
+
font-weight: 600;
|
|
412
|
+
color: var(--text-bright);
|
|
413
|
+
font-size: 0.95rem;
|
|
414
|
+
}
|
|
415
|
+
.struct-section-desc {
|
|
416
|
+
flex: 1;
|
|
417
|
+
font-size: 0.8rem;
|
|
418
|
+
color: var(--text-muted);
|
|
419
|
+
}
|
|
420
|
+
.struct-section-badge {
|
|
421
|
+
font-size: 0.65rem;
|
|
422
|
+
color: var(--text-muted);
|
|
423
|
+
background: rgba(99,102,241,0.1);
|
|
424
|
+
padding: 0.1rem 0.5rem;
|
|
425
|
+
border-radius: 4px;
|
|
426
|
+
white-space: nowrap;
|
|
427
|
+
}
|
|
428
|
+
.struct-resources {
|
|
429
|
+
padding: 0.5rem 0;
|
|
430
|
+
}
|
|
431
|
+
.struct-resource {
|
|
432
|
+
display: flex;
|
|
433
|
+
align-items: baseline;
|
|
434
|
+
gap: 0.5rem;
|
|
435
|
+
padding: 0.35rem 1rem 0.35rem 1.5rem;
|
|
436
|
+
font-size: 0.85rem;
|
|
437
|
+
transition: background var(--transition-fast);
|
|
438
|
+
}
|
|
439
|
+
.struct-resource:hover {
|
|
440
|
+
background: var(--bg-hover);
|
|
441
|
+
}
|
|
442
|
+
.struct-resource-icon {
|
|
443
|
+
color: var(--primary-light);
|
|
444
|
+
font-size: 0.7rem;
|
|
445
|
+
flex-shrink: 0;
|
|
446
|
+
}
|
|
447
|
+
.struct-resource-name {
|
|
448
|
+
font-weight: 500;
|
|
449
|
+
color: var(--text-bright);
|
|
450
|
+
}
|
|
451
|
+
.struct-resource-desc {
|
|
452
|
+
font-size: 0.8rem;
|
|
453
|
+
color: var(--text-muted);
|
|
454
|
+
}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
<span class="header-app-name" id="appName">{{APPLICATION_NAME}}</span>
|
|
21
21
|
<div class="header-spacer"></div>
|
|
22
22
|
<div class="header-actions">
|
|
23
|
+
<button class="btn btn-sm" onclick="resetToEmbedded()" title="Reinitialiser depuis les donnees d'origine (supprime les modifications locales)">Reset</button>
|
|
23
24
|
<button class="btn btn-sm" onclick="saveToLocalStorage()" title="Sauvegarder les modifications dans le navigateur">Sauvegarder</button>
|
|
24
25
|
<button class="btn btn-sm btn-review" onclick="saveReviewJSON()" title="Sauvegarder les corrections pour creer une nouvelle version">Sauvegarder corrections</button>
|
|
25
26
|
<button class="btn btn-sm btn-primary" onclick="exportJSON()" title="Exporter les donnees au format JSON pour l'extraction">Exporter JSON</button>
|
|
@@ -32,85 +33,16 @@
|
|
|
32
33
|
|
|
33
34
|
<div class="body" id="appBody">
|
|
34
35
|
<!-- ============================================
|
|
35
|
-
SIDEBAR - Navigation
|
|
36
|
+
SIDEBAR - Navigation hierarchique
|
|
36
37
|
============================================ -->
|
|
37
38
|
<aside class="sidebar">
|
|
38
|
-
<!--
|
|
39
|
-
<div class="
|
|
40
|
-
<
|
|
41
|
-
<div class="phase-line" id="pline-1"></div>
|
|
42
|
-
<div class="phase-dot" id="phase-2" title="Decomposition">2</div>
|
|
43
|
-
<div class="phase-line" id="pline-2"></div>
|
|
44
|
-
<div class="phase-dot" id="phase-3" title="Specification">3</div>
|
|
45
|
-
<div class="phase-line" id="pline-3"></div>
|
|
46
|
-
<div class="phase-dot" id="phase-4" title="Consolidation">4</div>
|
|
47
|
-
<div class="phase-line" id="pline-4"></div>
|
|
48
|
-
<div class="phase-dot" id="phase-5" title="Synthese">5</div>
|
|
39
|
+
<!-- Application Name -->
|
|
40
|
+
<div class="sidebar-header">
|
|
41
|
+
<span class="sidebar-app-name" id="sidebarAppName">{{APPLICATION_NAME}}</span>
|
|
49
42
|
</div>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<div class="nav-group-title">1. Cadrage</div>
|
|
54
|
-
<a class="nav-item active" onclick="showSection('cadrage-context')" data-section="cadrage-context">
|
|
55
|
-
<span class="nav-icon">●</span> Contexte
|
|
56
|
-
</a>
|
|
57
|
-
<a class="nav-item" onclick="showSection('cadrage-stakeholders')" data-section="cadrage-stakeholders">
|
|
58
|
-
<span class="nav-icon">●</span> Parties prenantes
|
|
59
|
-
<span class="nav-badge" id="stakeholderCount">0</span>
|
|
60
|
-
</a>
|
|
61
|
-
<a class="nav-item" onclick="showSection('cadrage-scope')" data-section="cadrage-scope">
|
|
62
|
-
<span class="nav-icon">●</span> Perimetre fonctionnel
|
|
63
|
-
</a>
|
|
64
|
-
<a class="nav-item" onclick="showSection('cadrage-risks')" data-section="cadrage-risks">
|
|
65
|
-
<span class="nav-icon">●</span> Risques et hypotheses
|
|
66
|
-
</a>
|
|
67
|
-
<a class="nav-item" onclick="showSection('cadrage-success')" data-section="cadrage-success">
|
|
68
|
-
<span class="nav-icon">●</span> Criteres de reussite
|
|
69
|
-
</a>
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
<!-- Phase 2 : Decomposition -->
|
|
73
|
-
<div class="nav-group">
|
|
74
|
-
<div class="nav-group-title">2. Decomposition</div>
|
|
75
|
-
<a class="nav-item" onclick="showSection('decomp-modules')" data-section="decomp-modules">
|
|
76
|
-
<span class="nav-icon">●</span> Domaines fonctionnels
|
|
77
|
-
<span class="nav-badge" id="moduleCount">0</span>
|
|
78
|
-
</a>
|
|
79
|
-
<a class="nav-item" onclick="showSection('decomp-dependencies')" data-section="decomp-dependencies">
|
|
80
|
-
<span class="nav-icon">●</span> Dependances
|
|
81
|
-
</a>
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
|
-
<!-- Phase 3 : Specification par module -->
|
|
85
|
-
<div class="nav-group" id="modulesNav">
|
|
86
|
-
<div class="nav-group-title">3. Specification</div>
|
|
87
|
-
<!-- Populated dynamically per module -->
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
<!-- Phase 4 : Consolidation -->
|
|
91
|
-
<div class="nav-group">
|
|
92
|
-
<div class="nav-group-title">4. Consolidation</div>
|
|
93
|
-
<a class="nav-item" onclick="showSection('consol-datamodel')" data-section="consol-datamodel">
|
|
94
|
-
<span class="nav-icon">●</span> Modele de donnees
|
|
95
|
-
<span class="nav-badge" id="entityCount">0</span>
|
|
96
|
-
</a>
|
|
97
|
-
<a class="nav-item" onclick="showSection('consol-interactions')" data-section="consol-interactions">
|
|
98
|
-
<span class="nav-icon">●</span> Interactions
|
|
99
|
-
</a>
|
|
100
|
-
<a class="nav-item" onclick="showSection('consol-permissions')" data-section="consol-permissions">
|
|
101
|
-
<span class="nav-icon">●</span> Coherence des acces
|
|
102
|
-
</a>
|
|
103
|
-
<a class="nav-item" onclick="showSection('consol-flows')" data-section="consol-flows">
|
|
104
|
-
<span class="nav-icon">●</span> Parcours bout en bout
|
|
105
|
-
</a>
|
|
106
|
-
</div>
|
|
107
|
-
|
|
108
|
-
<!-- Phase 5 : Synthese -->
|
|
109
|
-
<div class="nav-group">
|
|
110
|
-
<div class="nav-group-title">5. Synthese</div>
|
|
111
|
-
<a class="nav-item" onclick="showSection('handoff-summary')" data-section="handoff-summary">
|
|
112
|
-
<span class="nav-icon">●</span> Vue d'ensemble
|
|
113
|
-
</a>
|
|
43
|
+
<!-- Dynamic Tree Navigation -->
|
|
44
|
+
<div id="sidebarNav">
|
|
45
|
+
<!-- Populated by buildNavTree() -->
|
|
114
46
|
</div>
|
|
115
47
|
</aside>
|
|
116
48
|
|
|
@@ -26,14 +26,15 @@ const FEATURE_DATA = {
|
|
|
26
26
|
scope: {
|
|
27
27
|
// CONVERT feature.json keys to HTML keys:
|
|
28
28
|
// mustHave → vital, shouldHave → important, couldHave → optional, outOfScope → excluded
|
|
29
|
+
// CRITICAL: Preserve per-item `module` assignment from coverageMatrix (NOT modules[0])
|
|
29
30
|
vital: (master.cadrage.globalScope?.mustHave || master.cadrage.coverageMatrix?.filter(i => i.category === "mustHave") || [])
|
|
30
|
-
.map(item => typeof item === 'string' ? { name: item, description: "" } : { name: item.item || item, description: item.notes || "" }),
|
|
31
|
+
.map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
|
|
31
32
|
important: (master.cadrage.globalScope?.shouldHave || master.cadrage.coverageMatrix?.filter(i => i.category === "shouldHave") || [])
|
|
32
|
-
.map(item => typeof item === 'string' ? { name: item, description: "" } : { name: item.item || item, description: item.notes || "" }),
|
|
33
|
+
.map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
|
|
33
34
|
optional: (master.cadrage.globalScope?.couldHave || master.cadrage.coverageMatrix?.filter(i => i.category === "couldHave") || [])
|
|
34
|
-
.map(item => typeof item === 'string' ? { name: item, description: "" } : { name: item.item || item, description: item.notes || "" }),
|
|
35
|
+
.map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
|
|
35
36
|
excluded: (master.cadrage.globalScope?.outOfScope || master.cadrage.coverageMatrix?.filter(i => i.category === "outOfScope") || [])
|
|
36
|
-
.map(item => typeof item === 'string' ? { name: item, description: "" } : { name: item.item || item, description: item.notes || "" })
|
|
37
|
+
.map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null })
|
|
37
38
|
},
|
|
38
39
|
risks: (master.cadrage.risks || []).map(r => ({
|
|
39
40
|
description: r.description,
|
|
@@ -123,8 +124,9 @@ const FEATURE_DATA = {
|
|
|
123
124
|
const EMBEDDED_ARTIFACTS = {
|
|
124
125
|
wireframes: {
|
|
125
126
|
// PER-MODULE keyed object (NOT a flat array)
|
|
126
|
-
// FOR EACH module: extract from specification.uiWireframes[]
|
|
127
|
-
|
|
127
|
+
// FOR EACH module: extract from specification.uiWireframes[] OR specification.wireframes[] (SAFETY NET)
|
|
128
|
+
// IMPORTANT: The agent may write wireframes under either key name — always check BOTH
|
|
129
|
+
[moduleCode]: (moduleFeature.specification.uiWireframes || moduleFeature.specification.wireframes || []).map(wf => ({
|
|
128
130
|
screen: wf.screen || wf.name || wf.id || "", // SAFETY NET: fallback name/id → screen
|
|
129
131
|
section: wf.section || "", // e.g. "list"
|
|
130
132
|
format: wf.mockupFormat || "ascii", // RENAME: mockupFormat → format
|
|
@@ -161,7 +163,7 @@ const EMBEDDED_ARTIFACTS = {
|
|
|
161
163
|
|
|
162
164
|
### Artifact Gathering
|
|
163
165
|
|
|
164
|
-
1. For EACH module: read `specification.uiWireframes[]
|
|
166
|
+
1. For EACH module: read `specification.uiWireframes[]` OR `specification.wireframes[]` (check BOTH keys — agent may use either), **rename fields** (`mockupFormat`→`format`, `mockup`→`content`), store under `wireframes[moduleCode]`
|
|
165
167
|
2. Read master's `consolidation.e2eFlows[]` and build e2eFlows array with diagram generation
|
|
166
168
|
3. Read master's `dependencyGraph` and build nodes/edges
|
|
167
169
|
4. Serialize as JSON with 2-space indentation
|
|
@@ -20,25 +20,17 @@ Build a JSON object following this **exact mapping** from feature.json to the HT
|
|
|
20
20
|
analysisMode: master.metadata.analysisMode || "interactive" // always "interactive"
|
|
21
21
|
},
|
|
22
22
|
cadrage: {
|
|
23
|
-
problem
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.flatMap(s => s.painPoints || []).join("\n"), // aggregate all painPoints
|
|
35
|
-
errors: ""
|
|
36
|
-
},
|
|
37
|
-
vision: {
|
|
38
|
-
changes: master.cadrage.toBe, // string → vision.changes
|
|
39
|
-
results: master.cadrage.acceptanceCriteria
|
|
40
|
-
.map(ac => ac.criterion).join("\n"), // AC → results (one per line)
|
|
41
|
-
successSign: ""
|
|
23
|
+
// CONTEXT SECTION — lean format (merged from problem/asIs/toBe)
|
|
24
|
+
// NOTE: 01-data-init.js has backward compat for old problem/current/vision format
|
|
25
|
+
context: {
|
|
26
|
+
problem: master.cadrage.problem || "", // flat string from feature.json
|
|
27
|
+
trigger: master.cadrage.trigger || "", // flat string from feature.json
|
|
28
|
+
currentSituation: master.cadrage.asIs || "", // flat string from feature.json
|
|
29
|
+
desiredSituation: master.cadrage.toBe || "", // flat string from feature.json
|
|
30
|
+
painPoints: (master.cadrage.stakeholders || [])
|
|
31
|
+
.flatMap(s => s.painPoints || []).join("\n"), // aggregate all painPoints
|
|
32
|
+
acceptanceCriteria: (master.cadrage.acceptanceCriteria || [])
|
|
33
|
+
.map(ac => ac.criterion).join("\n") // AC → newline-separated string
|
|
42
34
|
},
|
|
43
35
|
stakeholders: master.cadrage.stakeholders.map(s => ({
|
|
44
36
|
role: s.role,
|
|
@@ -64,22 +56,22 @@ Build a JSON object following this **exact mapping** from feature.json to the HT
|
|
|
64
56
|
impact: r.impact,
|
|
65
57
|
mitigation: r.mitigation || ""
|
|
66
58
|
})),
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
metrics: "",
|
|
72
|
-
timeline: "",
|
|
73
|
-
minimumConditions: ""
|
|
74
|
-
}
|
|
59
|
+
criteria: (master.cadrage.acceptanceCriteria || []).map(ac => ({
|
|
60
|
+
text: ac.criterion,
|
|
61
|
+
validated: ac.validated || false
|
|
62
|
+
}))
|
|
75
63
|
},
|
|
76
64
|
modules: master.modules.map(m => ({
|
|
77
65
|
code: m.code,
|
|
78
|
-
name: m.code,
|
|
66
|
+
name: m.name || m.code, // display name (MANDATORY, separate from code)
|
|
79
67
|
description: m.description || "",
|
|
80
68
|
featureType: m.featureType || "data-centric",
|
|
81
69
|
priority: m.priority || "must",
|
|
82
70
|
entities: m.entities || [],
|
|
71
|
+
anticipatedSections: m.anticipatedSections || [], // [{code, description, resources[]}]
|
|
72
|
+
dependencies: m.dependencies || [],
|
|
73
|
+
dependents: m.dependents || [],
|
|
74
|
+
estimatedComplexity: m.estimatedComplexity || "medium",
|
|
83
75
|
status: m.status || "handed-off"
|
|
84
76
|
})),
|
|
85
77
|
dependencies: (master.dependencyGraph?.edges || []).map(e => ({
|
|
@@ -44,7 +44,8 @@ Build EMBEDDED_ARTIFACTS with wireframes for completed modules only:
|
|
|
44
44
|
const EMBEDDED_ARTIFACTS = {
|
|
45
45
|
wireframes: {
|
|
46
46
|
// FOR EACH completed module: extract wireframes with RENAMED fields
|
|
47
|
-
|
|
47
|
+
// SAFETY NET: check BOTH key names (agent may use either)
|
|
48
|
+
[moduleCode]: (moduleFeature.specification.uiWireframes || moduleFeature.specification.wireframes || []).map(wf => ({
|
|
48
49
|
screen: wf.screen,
|
|
49
50
|
section: wf.section,
|
|
50
51
|
format: wf.mockupFormat || "ascii", // RENAME: mockupFormat → format
|
|
@@ -48,6 +48,45 @@ Compile all data collected in step-03a (data) and step-03b (UI) into the module
|
|
|
48
48
|
|
|
49
49
|
Generate the complete specification for this module. **Each subsection below includes a STRUCTURE CARD showing the EXACT JSON format. Follow them precisely.**
|
|
50
50
|
|
|
51
|
+
#### ENTITY ATTRIBUTE SCHEMA (MANDATORY — applies to ALL entities in section 6b of step-03a)
|
|
52
|
+
|
|
53
|
+
> **CRITICAL:** Entity attributes MUST use STRUCTURED properties, not free-text validation strings.
|
|
54
|
+
> The ralph-loop needs parseable types to generate correct C# code.
|
|
55
|
+
|
|
56
|
+
> **STRUCTURE CARD: analysis.entities[].attributes[]**
|
|
57
|
+
> ```json
|
|
58
|
+
> {
|
|
59
|
+
> "name": "occupancyRate",
|
|
60
|
+
> "description": "Employee occupancy percentage",
|
|
61
|
+
> "type": "decimal",
|
|
62
|
+
> "maxLength": null,
|
|
63
|
+
> "nullable": true,
|
|
64
|
+
> "required": false,
|
|
65
|
+
> "unique": false,
|
|
66
|
+
> "indexed": false,
|
|
67
|
+
> "defaultValue": "100",
|
|
68
|
+
> "validation": "min:0, max:100, step:0.01",
|
|
69
|
+
> "foreignKey": null
|
|
70
|
+
> }
|
|
71
|
+
> ```
|
|
72
|
+
> **MANDATORY fields:** `name`, `type`
|
|
73
|
+
> **type values:** `string`, `int`, `long`, `decimal`, `double`, `bool`, `DateTime`, `DateOnly`, `TimeOnly`, `Guid`, `enum:{EnumName}`, `byte[]`
|
|
74
|
+
> **For FK attributes:**
|
|
75
|
+
> ```json
|
|
76
|
+
> {
|
|
77
|
+
> "name": "departmentId",
|
|
78
|
+
> "description": "FK to Department",
|
|
79
|
+
> "type": "Guid",
|
|
80
|
+
> "required": true,
|
|
81
|
+
> "foreignKey": { "targetEntity": "Department", "targetField": "Id", "onDelete": "restrict" }
|
|
82
|
+
> }
|
|
83
|
+
> ```
|
|
84
|
+
> **FORBIDDEN:** Free-text-only validation like `"FK vers Employee"` or `"Max 100 caractères"` without corresponding structured properties.
|
|
85
|
+
>
|
|
86
|
+
> **BaseEntity inheritance:** All entities inherit `tenantId`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` from SmartStack.BaseEntity. Do NOT redeclare these. Document this once at the top of the entities section.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
51
90
|
#### 8a. Actors
|
|
52
91
|
|
|
53
92
|
Inherited from application roles → mapped to module permissions.
|
|
@@ -234,7 +273,7 @@ const navigationResources = specification.sections.flatMap(section =>
|
|
|
234
273
|
);
|
|
235
274
|
```
|
|
236
275
|
|
|
237
|
-
**Write to
|
|
276
|
+
**Write seedDataCore to specification:**
|
|
238
277
|
|
|
239
278
|
```
|
|
240
279
|
ba-writer.enrichSection({
|
|
@@ -254,6 +293,9 @@ ba-writer.enrichSection({
|
|
|
254
293
|
})
|
|
255
294
|
```
|
|
256
295
|
|
|
296
|
+
> **NOTE:** This step writes ONLY seedDataCore. The FULL specification write (actors, useCases, wireframes, sections, etc.) happens in step-03d section 11.
|
|
297
|
+
> ALL data from 8a-8l MUST be carried forward to step-03d. Do NOT discard in-memory data.
|
|
298
|
+
|
|
257
299
|
**Validation:**
|
|
258
300
|
|
|
259
301
|
- EVERY section in `specification.sections[]` MUST appear in `navigationSections`
|
|
@@ -280,8 +322,9 @@ BDD acceptance tests per UC.
|
|
|
280
322
|
> ]
|
|
281
323
|
> }
|
|
282
324
|
> ```
|
|
283
|
-
> **STRUCTURE:**
|
|
325
|
+
> **STRUCTURE:** MUST be an ARRAY of objects (NOT a single object). Each object has `feature` string + `scenarios[]` array. Each scenario has `given`, `when`, `then` as ARRAYS of strings.
|
|
284
326
|
> **FORBIDDEN:** Do NOT use flat arrays of `{uc, scenario, given, when, then}` where given/when/then are single strings.
|
|
327
|
+
> **FORBIDDEN:** Do NOT use a single object `{feature, scenarios}` — MUST be an ARRAY `[{feature, scenarios}]` even with one feature.
|
|
285
328
|
|
|
286
329
|
#### 8h. Validations
|
|
287
330
|
|
|
@@ -405,6 +448,16 @@ Before loading step-03d-validate, verify all 12 subsections (8a-8l) are populate
|
|
|
405
448
|
|
|
406
449
|
**IF any subsection is missing → STOP and request the missing data before proceeding.**
|
|
407
450
|
|
|
451
|
+
**SCHEMA UNIFORMITY CHECK (multi-module mode):**
|
|
452
|
+
IF this is NOT the first module in moduleOrder:
|
|
453
|
+
Compare THIS module's specification structure against the FIRST specified module:
|
|
454
|
+
- `gherkinScenarios` MUST be array (not object) in ALL modules
|
|
455
|
+
- `validations[].rules` MUST be array (not singular `rule`) in ALL modules
|
|
456
|
+
- `messages[]` MUST have `message` field in ALL modules
|
|
457
|
+
- `specification.apiEndpoints[]` MUST be present in ALL modules
|
|
458
|
+
- `specification.i18nKeys` MUST be present in ALL modules
|
|
459
|
+
IF any structural divergence detected → AUTO-FIX to match the canonical format before proceeding.
|
|
460
|
+
|
|
408
461
|
---
|
|
409
462
|
|
|
410
463
|
## NEXT STEP
|
|
@@ -34,21 +34,50 @@ Validate the module specification for completeness and consistency, write to fea
|
|
|
34
34
|
|
|
35
35
|
#### 9a. Completeness Checks
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
37
|
+
> **CRITICAL:** Checks MUST count ACTUAL elements in arrays, NOT declarative values.
|
|
38
|
+
> A check that reports PASS when the array is empty is a LIE and BLOCKS downstream quality.
|
|
39
|
+
|
|
40
|
+
| Section | Minimum | How to verify | Status |
|
|
41
|
+
|---------|---------|---------------|--------|
|
|
42
|
+
| actors | 2 | `specification.actors.length >= 2` | PASS/FAIL |
|
|
43
|
+
| useCases | 2 | `specification.useCases.length >= 2` | PASS/FAIL |
|
|
44
|
+
| functionalRequirements | 4 | `specification.functionalRequirements.length >= 4` | PASS/FAIL |
|
|
45
|
+
| permissionMatrix | 1 resource × 2 roles | `specification.permissionMatrix.permissions.length >= 1 && specification.permissionMatrix.roleAssignments.length >= 2` | PASS/FAIL |
|
|
46
|
+
| entities | 1 | `analysis.entities.length >= 1` | PASS/FAIL |
|
|
47
|
+
| entitySchemaFormat | attributes[] not fields[] (BLOCKING) | `analysis.entities.every(e => e.attributes?.length > 0)` | PASS/FAIL |
|
|
48
|
+
| entityAttributeTypes | ALL attributes have `type` field (BLOCKING) | `analysis.entities.every(e => e.attributes.every(a => a.type))` | PASS/FAIL |
|
|
49
|
+
| wireframes | 1 per section (BLOCKING) | `specification.wireframes.length >= specification.sections.length` (count REAL elements) | PASS/FAIL |
|
|
50
|
+
| wireframeSchema | All required fields present (BLOCKING) | `specification.wireframes.every(w => w.screen && w.section && (w.mockup \|\| w.content))` | PASS/FAIL |
|
|
51
|
+
| sections | 1 (BLOCKING) | `specification.sections.length >= 1` (EVERY module needs at least 1 section) | PASS/FAIL |
|
|
52
|
+
| gherkinScenarios | 1 array entry | `Array.isArray(specification.gherkinScenarios) && specification.gherkinScenarios.length >= 1` | PASS/FAIL |
|
|
53
|
+
| gherkinFormat | Array not object (BLOCKING) | `Array.isArray(specification.gherkinScenarios)` (NOT a single object) | PASS/FAIL |
|
|
54
|
+
| validations | 1 | `specification.validations.length >= 1` | PASS/FAIL |
|
|
55
|
+
| validationFormat | rules[] array (BLOCKING) | `specification.validations.every(v => Array.isArray(v.rules))` (NOT singular `rule`) | PASS/FAIL |
|
|
56
|
+
| messages | 4 | `specification.messages.length >= 4` | PASS/FAIL |
|
|
57
|
+
| messageFormat | `message` field present (BLOCKING) | `specification.messages.every(m => m.message)` | PASS/FAIL |
|
|
58
|
+
| lifeCycles | 1 (if entity has status) | `specification.lifeCycles.length >= 1` (if any entity has status/state field) | PASS/FAIL |
|
|
59
|
+
| seedDataCore | 7 arrays present with content | See detailed check below | PASS/FAIL (BLOCKING) |
|
|
60
|
+
| apiEndpoints | 1 | `specification.apiEndpoints.length >= 1` | PASS/FAIL |
|
|
61
|
+
| i18nKeys | present | `specification.i18nKeys !== undefined && specification.i18nKeys !== null` | PASS/FAIL |
|
|
62
|
+
| navigationIcons | non-null | `specification.seedDataCore.navigationModules.every(m => m.icon !== null)` | PASS/FAIL |
|
|
63
|
+
|
|
64
|
+
**seedDataCore detailed check (BLOCKING):**
|
|
65
|
+
```javascript
|
|
66
|
+
const sdc = specification.seedDataCore;
|
|
67
|
+
const checks = [
|
|
68
|
+
{ key: "navigationModules", actual: sdc.navigationModules?.length || 0, min: 1 },
|
|
69
|
+
{ key: "navigationSections", actual: sdc.navigationSections?.length || 0, min: 1 }, // EVERY module needs ≥1 section
|
|
70
|
+
{ key: "navigationResources", actual: sdc.navigationResources?.length || 0, min: 1 },
|
|
71
|
+
{ key: "navigationTranslations", actual: sdc.navigationTranslations?.length || 0, min: 2 }, // min fr+en
|
|
72
|
+
{ key: "permissions", actual: sdc.permissions?.length || 0, min: 1 },
|
|
73
|
+
{ key: "rolePermissions", actual: sdc.rolePermissions?.length || 0, min: 1 },
|
|
74
|
+
{ key: "permissionConstants", actual: sdc.permissionConstants?.length || 0, min: 1 }
|
|
75
|
+
];
|
|
76
|
+
const failures = checks.filter(c => c.actual < c.min);
|
|
77
|
+
IF failures.length > 0:
|
|
78
|
+
BLOCKING ERROR: "seedDataCore incomplete — empty arrays: {failures.map(f => f.key).join(', ')}"
|
|
79
|
+
→ Fix: Ensure specification.sections[] has ≥1 entry, then re-run 8f-bis transform
|
|
80
|
+
```
|
|
52
81
|
|
|
53
82
|
#### 9b. Consistency Checks
|
|
54
83
|
|
|
@@ -278,6 +307,44 @@ ba-writer.updateStatus({module_feature_id}, "specified")
|
|
|
278
307
|
ba-writer.updateModuleStatus({feature_id}, {currentModule.code}, "specified")
|
|
279
308
|
```
|
|
280
309
|
|
|
310
|
+
#### 11-POST-CHECK: Verify Written Data (BLOCKING)
|
|
311
|
+
|
|
312
|
+
> **CRITICAL — Data loss prevention.** After writing, READ BACK the module feature.json and verify the following arrays are **non-empty**:
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
// READ BACK the written feature.json
|
|
316
|
+
const written = ba-reader.read({module_feature_id});
|
|
317
|
+
|
|
318
|
+
// BLOCKING checks — if ANY fails, the write was incomplete
|
|
319
|
+
const checks = [
|
|
320
|
+
{ key: "specification.actors", actual: written.specification?.actors?.length, min: 2 },
|
|
321
|
+
{ key: "specification.useCases", actual: written.specification?.useCases?.length, min: 2 },
|
|
322
|
+
{ key: "specification.wireframes", actual: written.specification?.wireframes?.length, min: 1 },
|
|
323
|
+
{ key: "specification.sections", actual: written.specification?.sections?.length, min: 1 },
|
|
324
|
+
{ key: "specification.seedDataCore", actual: Object.keys(written.specification?.seedDataCore || {}).length, min: 7 },
|
|
325
|
+
{ key: "specification.lifeCycles", actual: written.specification?.lifeCycles?.length, min: 0 },
|
|
326
|
+
{ key: "specification.gherkinScenarios",actual: written.specification?.gherkinScenarios?.length,min: 1 },
|
|
327
|
+
{ key: "specification.apiEndpoints", actual: written.specification?.apiEndpoints?.length, min: 1 }
|
|
328
|
+
];
|
|
329
|
+
|
|
330
|
+
const failures = checks.filter(c => (c.actual || 0) < c.min);
|
|
331
|
+
|
|
332
|
+
IF failures.length > 0:
|
|
333
|
+
BLOCKING ERROR: "Feature.json write INCOMPLETE — missing data in: {failures.map(f => f.key).join(', ')}"
|
|
334
|
+
→ Re-execute section 11 write with ALL specification data
|
|
335
|
+
→ DO NOT proceed to next module until ALL checks pass
|
|
336
|
+
|
|
337
|
+
// SPECIAL CHECK: wireframes content verification
|
|
338
|
+
IF written.specification?.wireframes?.length > 0:
|
|
339
|
+
const emptyMockups = written.specification.wireframes.filter(wf => !wf.mockup && !wf.content);
|
|
340
|
+
IF emptyMockups.length > 0:
|
|
341
|
+
WARNING: "{emptyMockups.length} wireframes have empty mockup content — verify step-03b data"
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
> **WHY:** Step-03b generates wireframes in memory, step-03c only writes seedDataCore explicitly.
|
|
345
|
+
> If the comprehensive write in section 11 is skipped or partial, wireframes/sections are LOST.
|
|
346
|
+
> This POST-CHECK catches the data loss BEFORE advancing to the next module.
|
|
347
|
+
|
|
281
348
|
---
|
|
282
349
|
|
|
283
350
|
### 11-bis. Deploy Incremental Interactive HTML (MANDATORY)
|