@atlashub/smartstack-cli 4.75.0 → 4.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +87 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/project/claude-md/root.CLAUDE.md.template +1 -1
- package/templates/skills/ai-prompt/SKILL.md +64 -0
- package/templates/skills/ai-prompt/references/ai-agent-modes.md +89 -0
- package/templates/skills/ai-prompt/references/eval-framework.md +129 -0
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/references/checks/frontend-checks.sh +123 -11
- package/templates/skills/apex/references/checks/seed-checks.sh +81 -7
- package/templates/skills/apex/references/core-seed-data.md +27 -22
- package/templates/skills/apex/references/domain-events-pattern.md +45 -0
- package/templates/skills/apex/references/entity-hooks-pattern.md +68 -0
- package/templates/skills/apex/references/licensing-enforcement.md +52 -0
- package/templates/skills/apex/references/post-checks.md +18 -1
- package/templates/skills/apex/references/smartstack-api.md +116 -5
- package/templates/skills/apex/references/smartstack-frontend.md +1 -1
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +1 -1
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
- package/templates/skills/apex/steps/step-04-examine.md +163 -0
- package/templates/skills/apex-verify/SKILL.md +110 -0
- package/templates/skills/apex-verify/references/audit-rules.md +50 -0
- package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
- package/templates/skills/apex-verify/steps/step-01-nav-audit.md +96 -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/contexts-cheatsheet.md +86 -0
- package/templates/skills/application/references/extensions-system.md +158 -0
- package/templates/skills/application/references/frontend-route-naming.md +7 -5
- package/templates/skills/application/references/frontend-verification.md +7 -5
- package/templates/skills/application/references/provider-template.md +4 -2
- package/templates/skills/application/references/smartstack-provider.md +118 -0
- package/templates/skills/application/references/themes-db-driven.md +484 -0
- package/templates/skills/application/templates-frontend.md +2 -2
- package/templates/skills/application/templates-seed.md +4 -2
- package/templates/skills/audit-route/references/routing-pattern.md +3 -1
- package/templates/skills/business-analyse/SKILL.md +3 -3
- package/templates/skills/business-analyse/_shared.md +37 -0
- package/templates/skills/business-analyse/react/components.md +30 -28
- package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
- package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
- package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
- package/templates/skills/business-analyse/templates-react.md +15 -15
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +6 -3
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
- package/templates/skills/business-analyse-html/references/data-build.md +2 -0
- package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
- package/templates/skills/business-analyse-quick/SKILL.md +807 -0
- package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
- package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
- package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
- package/templates/skills/cli-app-sync/SKILL.md +105 -4
- package/templates/skills/cli-app-sync/references/comparison-map.md +13 -0
- package/templates/skills/cli-app-sync/references/diff-entities.md +162 -0
- package/templates/skills/dev-start/SKILL.md +7 -7
- package/templates/skills/documentation/templates.md +16 -16
- package/templates/skills/migrate/SKILL.md +312 -0
- package/templates/skills/migrate/references/v3.34-to-v3.46.md +289 -0
- package/templates/skills/sketch/SKILL.md +15 -153
- package/templates/skills/smoke-generation/SKILL.md +313 -0
- package/templates/skills/ui-components/SKILL.md +11 -1
- package/templates/skills/ui-components/patterns/data-table.md +1 -1
- package/templates/skills/ui-components/references/component-catalog.md +82 -0
- package/templates/skills/workflow/SKILL.md +70 -1
|
@@ -34,19 +34,20 @@ import {
|
|
|
34
34
|
import type { FeatureJson, FeatureStatus } from '@/types/docs';
|
|
35
35
|
import { loadFeature, listVersions } from '@/services/docs';
|
|
36
36
|
|
|
37
|
-
// Status Badge Component (
|
|
37
|
+
// Status Badge Component (uses SmartStack theme CSS vars — adapts to dark mode and custom themes)
|
|
38
|
+
// Maps the 8 BA statuses to the 4 canonical status tokens (info/warning/success) + neutral + accent.
|
|
38
39
|
function StatusBadge({ status }: { status: FeatureStatus }) {
|
|
39
40
|
const config: Record<FeatureStatus, { color: string; label: string }> = {
|
|
40
|
-
'draft':
|
|
41
|
-
'framed':
|
|
42
|
-
'analysed':
|
|
43
|
-
'decomposed':
|
|
44
|
-
'specified':
|
|
45
|
-
'consolidated': { color: 'bg-
|
|
46
|
-
'approved':
|
|
47
|
-
'handed-off':
|
|
41
|
+
'draft': { color: 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]', label: 'Draft' },
|
|
42
|
+
'framed': { color: 'bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)]', label: 'Framed' },
|
|
43
|
+
'analysed': { color: 'bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)]', label: 'Analysed' },
|
|
44
|
+
'decomposed': { color: 'bg-[var(--accent-bg)] text-[var(--accent-text)] border border-[var(--accent-border)]', label: 'Decomposed' },
|
|
45
|
+
'specified': { color: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border border-[var(--warning-border)]', label: 'Specified' },
|
|
46
|
+
'consolidated': { color: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border border-[var(--warning-border)]', label: 'Consolidated' },
|
|
47
|
+
'approved': { color: 'bg-[var(--success-bg)] text-[var(--success-text)] border border-[var(--success-border)]', label: 'Approved' },
|
|
48
|
+
'handed-off': { color: 'bg-[var(--accent-bg)] text-[var(--accent-text)] border border-[var(--accent-border)]', label: 'Handed Off' }
|
|
48
49
|
};
|
|
49
|
-
const { color, label } = config[status] || { color: 'bg-
|
|
50
|
+
const { color, label } = config[status] || { color: 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]', label: status };
|
|
50
51
|
return (
|
|
51
52
|
<span className={`px-2 py-0.5 rounded text-xs font-medium ${color}`}>
|
|
52
53
|
{label}
|
|
@@ -54,15 +55,15 @@ function StatusBadge({ status }: { status: FeatureStatus }) {
|
|
|
54
55
|
);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
// Priority Badge Component
|
|
58
|
+
// Priority Badge Component (MoSCoW → status tokens)
|
|
58
59
|
function PriorityBadge({ priority }: { priority: string }) {
|
|
59
60
|
const colors: Record<string, string> = {
|
|
60
|
-
Must:
|
|
61
|
-
Should: 'bg-
|
|
62
|
-
Could:
|
|
61
|
+
Must: 'bg-[var(--error-bg)] text-[var(--error-text)] border border-[var(--error-border)]',
|
|
62
|
+
Should: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border border-[var(--warning-border)]',
|
|
63
|
+
Could: 'bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)]'
|
|
63
64
|
};
|
|
64
65
|
return (
|
|
65
|
-
<span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[priority] || 'bg-
|
|
66
|
+
<span className={`px-2 py-0.5 rounded text-xs font-medium ${colors[priority] || 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]'}`}>
|
|
66
67
|
{priority}
|
|
67
68
|
</span>
|
|
68
69
|
);
|
|
@@ -240,8 +241,8 @@ export function BusinessAnalyseViewer() {
|
|
|
240
241
|
{feature.discovery.risks.map((risk) => (
|
|
241
242
|
<div key={risk.id} className="flex items-start gap-2 p-2 rounded bg-[var(--bg-secondary)] mb-1">
|
|
242
243
|
<AlertTriangle className={`w-4 h-4 flex-shrink-0 mt-0.5 ${
|
|
243
|
-
risk.severity === 'high' ? 'text-
|
|
244
|
-
risk.severity === 'medium' ? 'text-
|
|
244
|
+
risk.severity === 'high' ? 'text-[var(--error-text)]' :
|
|
245
|
+
risk.severity === 'medium' ? 'text-[var(--warning-text)]' : 'text-[var(--info-text)]'
|
|
245
246
|
}`} />
|
|
246
247
|
<div>
|
|
247
248
|
<span className="text-sm font-medium">{risk.description}</span>
|
|
@@ -341,7 +342,7 @@ export function BusinessAnalyseViewer() {
|
|
|
341
342
|
</ol>
|
|
342
343
|
</div>
|
|
343
344
|
<div className="flex items-center gap-2 text-xs">
|
|
344
|
-
<Shield className="w-3 h-3 text-
|
|
345
|
+
<Shield className="w-3 h-3 text-[var(--warning-text)]" />
|
|
345
346
|
<code className="bg-[var(--bg-secondary)] px-2 py-0.5 rounded">{uc.permission}</code>
|
|
346
347
|
{uc.linkedRules.length > 0 && (
|
|
347
348
|
<span className="text-[var(--text-secondary)]">
|
|
@@ -393,11 +394,12 @@ export function BusinessAnalyseViewer() {
|
|
|
393
394
|
</thead>
|
|
394
395
|
<tbody>
|
|
395
396
|
{feature.specification.apiEndpoints.map((ep, idx) => {
|
|
397
|
+
// HTTP methods → status tokens (theme-aware)
|
|
396
398
|
const methodColors: Record<string, string> = {
|
|
397
|
-
GET:
|
|
398
|
-
POST:
|
|
399
|
-
PUT:
|
|
400
|
-
DELETE: 'bg-
|
|
399
|
+
GET: 'bg-[var(--success-bg)] text-[var(--success-text)] border border-[var(--success-border)]',
|
|
400
|
+
POST: 'bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)]',
|
|
401
|
+
PUT: 'bg-[var(--warning-bg)] text-[var(--warning-text)] border border-[var(--warning-border)]',
|
|
402
|
+
DELETE: 'bg-[var(--error-bg)] text-[var(--error-text)] border border-[var(--error-border)]'
|
|
401
403
|
};
|
|
402
404
|
return (
|
|
403
405
|
<tr key={`${ep.method}-${ep.path}`} className={idx % 2 === 1 ? 'bg-[var(--bg-secondary)]/50' : ''}>
|
|
@@ -431,7 +433,7 @@ export function BusinessAnalyseViewer() {
|
|
|
431
433
|
</h3>
|
|
432
434
|
<div className="flex gap-1">
|
|
433
435
|
{wf.permissionsRequired?.map((perm) => (
|
|
434
|
-
<code key={perm} className="px-2 py-0.5 rounded bg-
|
|
436
|
+
<code key={perm} className="px-2 py-0.5 rounded bg-[var(--warning-bg)] text-[var(--warning-text)] border border-[var(--warning-border)] text-xs">
|
|
435
437
|
{perm}
|
|
436
438
|
</code>
|
|
437
439
|
))}
|
|
@@ -466,8 +468,8 @@ export function BusinessAnalyseViewer() {
|
|
|
466
468
|
<div className="space-y-2">
|
|
467
469
|
{feature.suggestions.map((s) => (
|
|
468
470
|
<div key={s.code} className={`flex items-center justify-between p-3 rounded-lg border ${
|
|
469
|
-
s.accepted === true ? 'border-
|
|
470
|
-
s.accepted === false ? 'border-
|
|
471
|
+
s.accepted === true ? 'border-[var(--success-border)] bg-[var(--success-bg)]' :
|
|
472
|
+
s.accepted === false ? 'border-[var(--error-border)] bg-[var(--error-bg)] opacity-50' :
|
|
471
473
|
'border-[var(--border-color)]'
|
|
472
474
|
}`}>
|
|
473
475
|
<div>
|
|
@@ -476,9 +478,9 @@ export function BusinessAnalyseViewer() {
|
|
|
476
478
|
<p className="text-xs text-[var(--text-secondary)]">{s.reason}</p>
|
|
477
479
|
</div>
|
|
478
480
|
<span className={`px-2 py-0.5 rounded text-xs ${
|
|
479
|
-
s.accepted === true ? 'bg-
|
|
480
|
-
s.accepted === false ? 'bg-
|
|
481
|
-
'bg-
|
|
481
|
+
s.accepted === true ? 'bg-[var(--success-bg)] text-[var(--success-text)] border border-[var(--success-border)]' :
|
|
482
|
+
s.accepted === false ? 'bg-[var(--error-bg)] text-[var(--error-text)] border border-[var(--error-border)]' :
|
|
483
|
+
'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]'
|
|
482
484
|
}`}>
|
|
483
485
|
{s.accepted === true ? 'Accepted' : s.accepted === false ? 'Declined' : 'Pending'}
|
|
484
486
|
</span>
|
|
@@ -38,6 +38,8 @@ Omit flags when `false` (default). Include explicitly when `true`.
|
|
|
38
38
|
|
|
39
39
|
## Business Rules Schema Format
|
|
40
40
|
|
|
41
|
+
> **Source of truth:** `schemas/sections/analysis-schema.json` property `businessRules`.
|
|
42
|
+
|
|
41
43
|
```json
|
|
42
44
|
{
|
|
43
45
|
"id": "BR-VAL-EMPLOYEES-001",
|
|
@@ -45,9 +47,10 @@ Omit flags when `false` (default). Include explicitly when `true`.
|
|
|
45
47
|
"category": "validation",
|
|
46
48
|
"sectionCode": "list",
|
|
47
49
|
"statement": "La date d'embauche ne peut pas être dans le futur",
|
|
48
|
-
"
|
|
50
|
+
"severity": "blocking",
|
|
49
51
|
"entities": ["Employee"],
|
|
50
|
-
"
|
|
52
|
+
"examples": [{ "input": "hireDate = 2027-01-01", "expected": "Erreur : date dans le futur" }],
|
|
53
|
+
"domainSpecific": false
|
|
51
54
|
}
|
|
52
55
|
```
|
|
53
56
|
|
|
@@ -63,10 +66,15 @@ Categories: `validation`, `calculation`, `workflow`, `security`, `data`, `notifi
|
|
|
63
66
|
```json
|
|
64
67
|
{
|
|
65
68
|
"id": "BR-CALC-INV-003",
|
|
69
|
+
"name": "Calcul reste à payer",
|
|
66
70
|
"category": "calculation",
|
|
71
|
+
"sectionCode": "detail",
|
|
67
72
|
"statement": "Le reste à payer = total - montant payé",
|
|
73
|
+
"severity": "blocking",
|
|
68
74
|
"formula": "remainingAmount = total - paidAmount",
|
|
69
|
-
"entities": ["Invoice"]
|
|
75
|
+
"entities": ["Invoice"],
|
|
76
|
+
"examples": [{ "input": "total=1000, paidAmount=300", "expected": "remainingAmount = 700" }],
|
|
77
|
+
"domainSpecific": false
|
|
70
78
|
}
|
|
71
79
|
```
|
|
72
80
|
|
|
@@ -129,6 +129,70 @@ for (const mod of modules) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
// ── Business Rules Schema Conformity (C16-C21) ──────────────────────
|
|
134
|
+
|
|
135
|
+
// Quality gate: BR schema field conformity (C16 — BLOCKING)
|
|
136
|
+
for (const br of brs) {
|
|
137
|
+
if (!br.id) errors.push(mod.code + ": BR missing 'id' field");
|
|
138
|
+
else if (!/^BR-(VAL|CALC|WF|SEC|DATA|NOTIF)-[A-Z]{2,4}-\d{3}$/.test(br.id))
|
|
139
|
+
errors.push(mod.code + ": BR '" + br.id + "' does not match ID pattern BR-{CAT}-{MOD}-{NNN}");
|
|
140
|
+
if (!br.name) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'name'");
|
|
141
|
+
if (!br.category) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'category'");
|
|
142
|
+
if (!br.statement) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'statement'");
|
|
143
|
+
if (!br.severity) errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'severity'");
|
|
144
|
+
if (!br.entities || br.entities.length === 0)
|
|
145
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'entities[]'");
|
|
146
|
+
// Reject deprecated field names (common drift from sub-agent schema ignorance)
|
|
147
|
+
if (br.code && !br.id) errors.push(mod.code + ": BR uses 'code' instead of canonical 'id'");
|
|
148
|
+
if (br.label && !br.name) errors.push(mod.code + ": BR uses 'label' instead of canonical 'name'");
|
|
149
|
+
if (br.description && !br.statement) errors.push(mod.code + ": BR uses 'description' instead of canonical 'statement'");
|
|
150
|
+
if (br.rule && !br.statement) errors.push(mod.code + ": BR uses 'rule' instead of canonical 'statement'");
|
|
151
|
+
if (br.type && !br.category) errors.push(mod.code + ": BR uses 'type' instead of canonical 'category'");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Quality gate: BR examples format (C17 — BLOCKING)
|
|
155
|
+
for (const br of brs) {
|
|
156
|
+
if (!br.examples || br.examples.length === 0)
|
|
157
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' missing 'examples[]' (min 1 required)");
|
|
158
|
+
if (br.example && !br.examples)
|
|
159
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' uses deprecated 'example' string — use 'examples[]' array of {input, expected}");
|
|
160
|
+
if (br.examples) {
|
|
161
|
+
for (const ex of br.examples) {
|
|
162
|
+
if (typeof ex === 'string')
|
|
163
|
+
errors.push(mod.code + ": BR '" + (br.id||'?') + "' has string in examples[] — must be {input, expected}");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Quality gate: BR count minimum (C18 — BLOCKING if < 4)
|
|
169
|
+
if (brs.length > 0 && brs.length < 4) {
|
|
170
|
+
errors.push(mod.code + ": only " + brs.length + " business rules (minimum: 4)");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Quality gate: Domain-specific ratio (C19 — WARNING if < 2)
|
|
174
|
+
const domainBRs = brs.filter(br => br.domainSpecific === true);
|
|
175
|
+
if (brs.length >= 4 && domainBRs.length < 2) {
|
|
176
|
+
warnings.push(mod.code + ": only " + domainBRs.length + " domain-specific rules out of " + brs.length +
|
|
177
|
+
" (minimum: 2 — see _shared.md rule 3)");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Quality gate: domainSpecific flag presence (C20 — WARNING)
|
|
181
|
+
const brsMissingFlag = brs.filter(br => typeof br.domainSpecific === 'undefined');
|
|
182
|
+
if (brsMissingFlag.length > 0) {
|
|
183
|
+
warnings.push(mod.code + ": " + brsMissingFlag.length + "/" + brs.length +
|
|
184
|
+
" BRs missing 'domainSpecific' flag");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Quality gate: Structured conditions for conditional rules (C21 — WARNING)
|
|
188
|
+
const conditionalBRs = brs.filter(br =>
|
|
189
|
+
br.category === 'workflow' || br.category === 'security' ||
|
|
190
|
+
(br.statement && /\b(IF|Si |si |Quand |quand |Lorsqu)/i.test(br.statement)));
|
|
191
|
+
const brsWithConditions = conditionalBRs.filter(br => br.conditions && br.conditions.length > 0);
|
|
192
|
+
if (conditionalBRs.length > 2 && brsWithConditions.length < 2) {
|
|
193
|
+
warnings.push(mod.code + ": " + brsWithConditions.length + "/" + conditionalBRs.length +
|
|
194
|
+
" conditional BRs have structured conditions[] (recommended: >= 2)");
|
|
195
|
+
}
|
|
132
196
|
}
|
|
133
197
|
|
|
134
198
|
if (errors.length > 0) {
|
|
@@ -83,9 +83,10 @@
|
|
|
83
83
|
"category": "validation",
|
|
84
84
|
"sectionCode": "list",
|
|
85
85
|
"statement": "La date ne peut pas être dans le futur",
|
|
86
|
-
"
|
|
86
|
+
"severity": "blocking",
|
|
87
87
|
"entities": ["Employee"],
|
|
88
|
-
"
|
|
88
|
+
"examples": [{ "input": "date = 2027-01-01", "expected": "Erreur : date dans le futur" }],
|
|
89
|
+
"domainSpecific": false
|
|
89
90
|
}
|
|
90
91
|
]
|
|
91
92
|
}
|
|
@@ -95,7 +96,10 @@
|
|
|
95
96
|
|---------------|------------------------|
|
|
96
97
|
| `rules` (root) | `businessRules` |
|
|
97
98
|
| `statement` | `description` |
|
|
98
|
-
| `id` | `name` (as identifier) |
|
|
99
|
+
| `id` | `name` (as identifier), `code` |
|
|
100
|
+
| `examples` (array of `{input, expected}`) | `example` (singular string) |
|
|
101
|
+
| `category` | `type` |
|
|
102
|
+
| `name` | `label` |
|
|
99
103
|
|
|
100
104
|
Categories: `validation`, `calculation`, `workflow`, `security`, `data`, `notification`
|
|
101
105
|
|
|
@@ -61,7 +61,7 @@ Ensures each module has ALL required components before being marked as "specifie
|
|
|
61
61
|
| 5.1 | Sections count | ≥2 | YES | Modules need list + form minimum |
|
|
62
62
|
| 5.2 | Wireframes count | 1 per section | YES | EVERY section MUST have wireframe |
|
|
63
63
|
| 5.3 | Navigation entries | ≥1 | YES | Module must be accessible in menu |
|
|
64
|
-
| 5.4 | Navigation route patterns | No `/
|
|
64
|
+
| 5.4 | Navigation route patterns | No `/detail/:id` suffix | YES | ALL sections use uniform route: module route + /{section-code}. list route MUST end with `/list`. FORBIDDEN: `/module/detail/:id` (detail is implicit) |
|
|
65
65
|
|
|
66
66
|
#### SECTION 6: I18N & Messages (3 checks)
|
|
67
67
|
|
|
@@ -169,20 +169,20 @@ const checklist = {
|
|
|
169
169
|
},
|
|
170
170
|
|
|
171
171
|
navigationRoutePatterns: {
|
|
172
|
-
check: "Navigation routes do NOT contain
|
|
172
|
+
check: "Navigation routes do NOT contain forbidden patterns (/detail/:id)",
|
|
173
173
|
status: (() => {
|
|
174
174
|
const allRoutes = [
|
|
175
175
|
...(specification.seedDataCore?.navigationSections || []).map(s => s.route),
|
|
176
176
|
...(specification.sections || []).map(s => s.route),
|
|
177
177
|
...(specification.navigation?.entries || []).map(e => e.route)
|
|
178
178
|
].filter(Boolean);
|
|
179
|
-
const forbidden = allRoutes.filter(r => r.match(/\/
|
|
179
|
+
const forbidden = allRoutes.filter(r => r.match(/\/detail\/:id$/));
|
|
180
180
|
return forbidden.length === 0 ? "PASS" : "FAIL";
|
|
181
181
|
})(),
|
|
182
182
|
blocking: true,
|
|
183
|
-
details: "FORBIDDEN: /module/
|
|
184
|
-
"
|
|
185
|
-
"
|
|
183
|
+
details: "FORBIDDEN: /module/detail/:id (detail is implicit, not a section). " +
|
|
184
|
+
"ALL sections use uniform route: module route + /{section-code}. " +
|
|
185
|
+
"list route = module route + /list. " +
|
|
186
186
|
"Other sections (dashboard, approve) add /{section} normally."
|
|
187
187
|
},
|
|
188
188
|
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"type": "object",
|
|
25
25
|
"required": ["id", "name", "category", "statement"],
|
|
26
26
|
"properties": {
|
|
27
|
-
"id": { "type": "string", "pattern": "^BR-(VAL|CALC|WF|SEC|DATA)-[A-Z]{2,4}-\\d{3}$", "description": "Module-prefixed BR ID (e.g., BR-VAL-RM-001)" },
|
|
27
|
+
"id": { "type": "string", "pattern": "^BR-(VAL|CALC|WF|SEC|DATA|NOTIF)-[A-Z]{2,4}-\\d{3}$", "description": "Module-prefixed BR ID (e.g., BR-VAL-RM-001)" },
|
|
28
28
|
"name": { "type": "string" },
|
|
29
|
-
"category": { "type": "string", "enum": ["validation", "calculation", "workflow", "security", "data"] },
|
|
29
|
+
"category": { "type": "string", "enum": ["validation", "calculation", "workflow", "security", "data", "notification"] },
|
|
30
30
|
"statement": { "type": "string", "description": "IF ... THEN ... ELSE ..." },
|
|
31
31
|
"priority": { "type": "string", "enum": ["must", "should", "could"] },
|
|
32
32
|
"conditions": {
|
|
@@ -48,15 +48,26 @@
|
|
|
48
48
|
},
|
|
49
49
|
"examples": {
|
|
50
50
|
"type": "array",
|
|
51
|
+
"description": "Test-ready examples: each example = a test case with concrete values. Minimum 2 per rule (happy_path + error). These feed Gherkin generation and automated tests.",
|
|
51
52
|
"items": {
|
|
52
53
|
"type": "object",
|
|
53
54
|
"required": ["input", "expected"],
|
|
54
55
|
"properties": {
|
|
55
|
-
"input": { "type": "string", "description": "
|
|
56
|
-
"expected": { "type": "string", "description": "
|
|
56
|
+
"input": { "type": "string", "description": "LEGACY fallback. Prefer given/when/then format." },
|
|
57
|
+
"expected": { "type": "string", "description": "LEGACY fallback. Prefer given/when/then format." },
|
|
58
|
+
"scenario": { "type": "string", "enum": ["happy_path", "error", "boundary", "calculation", "security"], "description": "Test scenario type" },
|
|
59
|
+
"description": { "type": "string", "description": "Human-readable test case description" },
|
|
60
|
+
"given": { "type": "object", "description": "Initial state: Entity.field → concrete value. Ex: {\"Employee.code\": \"EMP-001\", \"existingCodes\": [\"EMP-001\"]}", "additionalProperties": true },
|
|
61
|
+
"when": { "type": "string", "description": "Triggering action. Ex: 'create', 'update', 'submit', 'calculate_remaining'" },
|
|
62
|
+
"then": { "type": "object", "description": "Expected outcome: {result: 'success'|'error', Entity.field: value, message: '...', field: '...'}", "additionalProperties": true }
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
},
|
|
66
|
+
"entities": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": { "type": "string" },
|
|
69
|
+
"description": "Entity names this rule applies to"
|
|
70
|
+
},
|
|
60
71
|
"testability": { "type": "string" },
|
|
61
72
|
"formula": { "type": "string", "description": "BR-CALC calculation formulas" },
|
|
62
73
|
"sectionCode": { "type": "string", "description": "Section code the rule applies to" },
|
|
@@ -31,6 +31,26 @@ function resolveModuleDir(appCode, moduleCode) {
|
|
|
31
31
|
> **Module directory (ba-006):** `mod.dir = resolveModuleDir(applicationCode, module.code)`
|
|
32
32
|
> Example: `docs/projet-rh/v1.0/human-resources/employees/`
|
|
33
33
|
|
|
34
|
+
## ENFORCEMENT LANGUE (OBLIGATOIRE)
|
|
35
|
+
|
|
36
|
+
Toutes les valeurs textuelles générées dans les fichiers JSON de ce module DOIVENT être rédigées en `{language}` :
|
|
37
|
+
|
|
38
|
+
| Champ JSON | Langue | Exemple FR | Exemple EN |
|
|
39
|
+
|------------|--------|-----------|-----------|
|
|
40
|
+
| entity.description | `{language}` | "Représente un employé" | "Represents an employee" |
|
|
41
|
+
| attribute.description | `{language}` | "Code unique de l'employé" | "Unique employee code" |
|
|
42
|
+
| rule.statement | `{language}` | "Le code doit être unique" | "Code must be unique" |
|
|
43
|
+
| rule.examples[].input | `{language}` | "code='EMP-001', doublon" | "code='EMP-001', duplicate" |
|
|
44
|
+
| rule.examples[].expected | `{language}` | "Erreur : code déjà existant" | "Error: code already exists" |
|
|
45
|
+
| usecase.name | `{language}` | "Créer un employé" | "Create employee" |
|
|
46
|
+
| usecase.mainScenario[] | `{language}` | "L'utilisateur ouvre la page" | "User opens the page" |
|
|
47
|
+
| usecase.preconditions[] | `{language}` | "L'utilisateur est connecté" | "User is logged in" |
|
|
48
|
+
| permission.description | `{language}` | "Consulter la liste" | "View the list" |
|
|
49
|
+
|
|
50
|
+
**Identifiants techniques** (PascalCase) restent en anglais : `Employee`, `AbsenceBalance`, `status`, `BR-VAL-EMP-001`.
|
|
51
|
+
|
|
52
|
+
**VIOLATION = contenu rejeté à la consolidation (step-04).**
|
|
53
|
+
|
|
34
54
|
## Prerequisites
|
|
35
55
|
|
|
36
56
|
- Step 02 (structure) completed
|
|
@@ -82,6 +102,11 @@ Display(`Scope locked: ${expectedApps} app(s), ${confirmedModules.length} module
|
|
|
82
102
|
| - INTERDICTION de spawner des ba-writer pour des modules NON encore |
|
|
83
103
|
| valides par le client |
|
|
84
104
|
| |
|
|
105
|
+
| 6. JAMAIS "accelerer" en batchant des modules dans des sub-agents. |
|
|
106
|
+
| Sub-agents (Agent/Snipper) n'ont PAS acces au schema JSON canonique. |
|
|
107
|
+
| PREUVE: audit ba-012 → 4 dialectes JSON, conformite 95% → 35%. |
|
|
108
|
+
| Si > 10 modules: maintenir le sequentiel, le retravail coute plus. |
|
|
109
|
+
| |
|
|
85
110
|
+==============================================================================+
|
|
86
111
|
|
|
87
112
|
## Sequential Execution
|
|
@@ -349,9 +374,12 @@ IF module has workflow states OR approval patterns:
|
|
|
349
374
|
- BR-VAL-ABS-008: "Medical certificate required for absences > 3 days" [domainSpecific]
|
|
350
375
|
- BR-SEC-ABS-001: "Manager cannot approve their own absences" [domainSpecific]
|
|
351
376
|
|
|
352
|
-
ELSE (non-workflow module):
|
|
377
|
+
ELSE (non-workflow module with >= 2 entities):
|
|
378
|
+
SHOULD WebSearch: "{module_domain} data validation rules best practices"
|
|
353
379
|
→ Use entity patterns from A-bis for validation rules
|
|
354
380
|
→ Load archetype rules from module-completeness-challenge.md
|
|
381
|
+
→ For each archetype rule relevant to this module type, ADD to proposal
|
|
382
|
+
→ Mark rules from research with domainSpecific: true
|
|
355
383
|
```
|
|
356
384
|
|
|
357
385
|
### C-bis. Client Validation — Business Rules (OBLIGATOIRE)
|
|
@@ -363,13 +391,63 @@ ELSE (non-workflow module):
|
|
|
363
391
|
- Patterns domaine (A-bis) : validations standards, calculs attendus
|
|
364
392
|
- Cadrage : errorFlows, processus décrits
|
|
365
393
|
|
|
394
|
+
> **Schema:** ALL fields defined in `schemas/sections/analysis-schema.json` property `businessRules`. Use EXACT canonical field names (`id`, `name`, `category`, `statement`, `severity`, `entities`, `examples`, `sectionCode`, `domainSpecific`).
|
|
395
|
+
|
|
366
396
|
**ENRICHISSEMENT OBLIGATOIRE pour chaque règle :**
|
|
367
397
|
|
|
368
398
|
Pour chaque BR proposée, inclure ces champs :
|
|
369
399
|
- `severity` : TOUJOURS spécifier (blocking/warning/info)
|
|
370
400
|
- `sectionCode` : TOUJOURS spécifier (section où la règle s'applique)
|
|
371
|
-
- `examples[]` : OBLIGATOIRE —
|
|
372
|
-
|
|
401
|
+
- `examples[]` : OBLIGATOIRE — format **test-ready** `{scenario, description, given, when, then}`
|
|
402
|
+
Chaque exemple est un **jeu de test** avec des valeurs concrètes (pas de prose).
|
|
403
|
+
Minimum **2 exemples** par règle : `happy_path` + `error` (ou `calculation` + `boundary`).
|
|
404
|
+
|
|
405
|
+
**Format par catégorie :**
|
|
406
|
+
- `validation` :
|
|
407
|
+
```json
|
|
408
|
+
{ "scenario": "happy_path", "description": "Code unique accepté",
|
|
409
|
+
"given": { "Employee.code": "EMP-00042", "existingCodes": [] },
|
|
410
|
+
"when": "create",
|
|
411
|
+
"then": { "result": "success" } }
|
|
412
|
+
{ "scenario": "error", "description": "Code dupliqué rejeté",
|
|
413
|
+
"given": { "Employee.code": "EMP-00001", "existingCodes": ["EMP-00001"] },
|
|
414
|
+
"when": "create",
|
|
415
|
+
"then": { "result": "error", "message": "Code déjà existant", "field": "code" } }
|
|
416
|
+
```
|
|
417
|
+
- `calculation` :
|
|
418
|
+
```json
|
|
419
|
+
{ "scenario": "calculation", "description": "Solde avec report",
|
|
420
|
+
"given": { "AbsenceBalance.entitled": 25, "AbsenceBalance.carriedForward": 3, "AbsenceBalance.taken": 12 },
|
|
421
|
+
"when": "calculate_remaining",
|
|
422
|
+
"then": { "AbsenceBalance.remaining": 16, "formula": "25 + 3 - 12 = 16" } }
|
|
423
|
+
{ "scenario": "boundary", "description": "Solde à zéro",
|
|
424
|
+
"given": { "AbsenceBalance.entitled": 10, "AbsenceBalance.carriedForward": 0, "AbsenceBalance.taken": 10 },
|
|
425
|
+
"when": "calculate_remaining",
|
|
426
|
+
"then": { "AbsenceBalance.remaining": 0 } }
|
|
427
|
+
```
|
|
428
|
+
- `workflow` :
|
|
429
|
+
```json
|
|
430
|
+
{ "scenario": "happy_path", "description": "Transition Draft→Submitted",
|
|
431
|
+
"given": { "Absence.status": "Draft" },
|
|
432
|
+
"when": "submit",
|
|
433
|
+
"then": { "Absence.status": "Submitted", "result": "success" } }
|
|
434
|
+
{ "scenario": "error", "description": "Transition interdite Approved→Draft",
|
|
435
|
+
"given": { "Absence.status": "Approved" },
|
|
436
|
+
"when": "submit",
|
|
437
|
+
"then": { "result": "error", "message": "Transition non autorisée" } }
|
|
438
|
+
```
|
|
439
|
+
- `security` :
|
|
440
|
+
```json
|
|
441
|
+
{ "scenario": "happy_path", "description": "Admin accède à toutes les fiches",
|
|
442
|
+
"given": { "user.role": "RH Admin", "Employee.departmentId": "any" },
|
|
443
|
+
"when": "read",
|
|
444
|
+
"then": { "result": "success", "scope": "all" } }
|
|
445
|
+
{ "scenario": "error", "description": "Employé accède uniquement à sa fiche",
|
|
446
|
+
"given": { "user.role": "Employé", "target.employeeId": "other-user" },
|
|
447
|
+
"when": "read",
|
|
448
|
+
"then": { "result": "error", "message": "Accès limité à votre fiche" } }
|
|
449
|
+
```
|
|
450
|
+
**Fallback** : `{input, expected}` accepté mais marqué WARNING "format non test-ready".
|
|
373
451
|
- `conditions[]` : OBLIGATOIRE pour les règles conditionnelles (IF/THEN) — structuré `{entity, field, operator, value}`
|
|
374
452
|
Ex: `[{ entity: "Absence", field: "type", operator: "==", value: "Maladie" }, { entity: "Absence", field: "duration", operator: ">", value: 3 }]`
|
|
375
453
|
- `consequences[]` : OBLIGATOIRE pour les règles avec effets de bord
|
|
@@ -378,9 +456,20 @@ ELSE (non-workflow module):
|
|
|
378
456
|
- `domainSpecific: true` si la règle vient de la recherche domaine (C-pre)
|
|
379
457
|
|
|
380
458
|
**Minimums par module métier (hors Portal/config) :**
|
|
381
|
-
-
|
|
459
|
+
- 100% des règles avec `examples[]` non vide (minimum 2 par règle : happy_path + error)
|
|
460
|
+
- >= 4 règles avec exemples test-ready `{scenario, given, when, then}`
|
|
382
461
|
- >= 2 règles avec `conditions[]` structurés
|
|
383
462
|
- >= 1 règle avec `consequences[]`
|
|
463
|
+
- Toute BR-CALC avec un exemple `calculation` contenant des valeurs numériques chiffrées
|
|
464
|
+
|
|
465
|
+
**Self-check AVANT présentation client :**
|
|
466
|
+
```
|
|
467
|
+
domainCount = rules.filter(r => r.domainSpecific === true).length
|
|
468
|
+
IF domainCount < 2 AND module is NOT config/lookup/Portal:
|
|
469
|
+
SELF-CHECK: "Only {domainCount} domain-specific rules."
|
|
470
|
+
→ Re-examine module-completeness-challenge.md archetypes for this module type
|
|
471
|
+
→ Add at least 2 domain-specific rules before presenting to client
|
|
472
|
+
```
|
|
384
473
|
|
|
385
474
|
2. **Présenter le LOT complet** (markdown PUIS AskUserQuestion) :
|
|
386
475
|
```
|
|
@@ -398,6 +487,60 @@ ELSE (non-workflow module):
|
|
|
398
487
|
- Q3.8 si les relations inter-champs sont complexes
|
|
399
488
|
- Q3.9 si les données sensibles ne sont pas identifiées
|
|
400
489
|
|
|
490
|
+
4. **POST-VALIDATION : Qualité test-ready des exemples (OBLIGATOIRE)**
|
|
491
|
+
|
|
492
|
+
Après validation client des règles métier, vérifier la qualité des exemples AVANT écriture :
|
|
493
|
+
|
|
494
|
+
```javascript
|
|
495
|
+
// 1. Règles sans aucun exemple
|
|
496
|
+
const rulesWithoutExamples = module.rules.filter(r =>
|
|
497
|
+
!r.examples || r.examples.length === 0
|
|
498
|
+
);
|
|
499
|
+
if (rulesWithoutExamples.length > 0) {
|
|
500
|
+
// Auto-générer des exemples test-ready à partir du statement et des conditions
|
|
501
|
+
for (const rule of rulesWithoutExamples) {
|
|
502
|
+
rule.examples = generateTestReadyExamples(rule);
|
|
503
|
+
// Génère minimum: 1 happy_path + 1 error avec given/when/then
|
|
504
|
+
}
|
|
505
|
+
Display(`${rulesWithoutExamples.length} règle(s) complétée(s) avec exemples test-ready auto-générés.`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// 2. Règles avec exemples en prose (ancien format {input, expected} sans given/when/then)
|
|
509
|
+
const proseExamples = module.rules.filter(r =>
|
|
510
|
+
r.examples?.length > 0 && !r.examples.some(e => e.given && e.when && e.then)
|
|
511
|
+
);
|
|
512
|
+
if (proseExamples.length > 0) {
|
|
513
|
+
// Convertir les exemples prose en format test-ready
|
|
514
|
+
for (const rule of proseExamples) {
|
|
515
|
+
rule.examples = rule.examples.map(e => convertToTestReady(e, rule));
|
|
516
|
+
}
|
|
517
|
+
Display(`${proseExamples.length} règle(s) converties du format prose au format test-ready.`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// 3. BR-CALC sans exemple chiffré
|
|
521
|
+
const calcWithoutNumbers = module.rules.filter(r =>
|
|
522
|
+
r.category === 'calculation' &&
|
|
523
|
+
!r.examples?.some(e => e.scenario === 'calculation' || (e.then && Object.values(e.then).some(v => typeof v === 'number')))
|
|
524
|
+
);
|
|
525
|
+
if (calcWithoutNumbers.length > 0) {
|
|
526
|
+
Display(`⚠ ${calcWithoutNumbers.length} BR-CALC sans exemple chiffré — ajouter des valeurs numériques.`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// 4. Vérification minimale
|
|
530
|
+
const testReady = module.rules.filter(r =>
|
|
531
|
+
r.examples?.some(e => e.given && e.when && e.then)
|
|
532
|
+
);
|
|
533
|
+
if (testReady.length < 4 && module.featureType !== 'portal') {
|
|
534
|
+
Display(`⚠ ${testReady.length}/4 règles au format test-ready {given, when, then}. Enrichir.`);
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**Minimum requis par module métier (hors Portal/config) :**
|
|
539
|
+
- 100% des règles avec `examples[]` non vide (minimum 2 par règle)
|
|
540
|
+
- >= 4 règles avec exemples test-ready `{scenario, given, when, then}`
|
|
541
|
+
- Toute BR-CALC avec un exemple `calculation` contenant des valeurs numériques
|
|
542
|
+
- Zéro exemple en prose vague (tout doit être en format `given/when/then`)
|
|
543
|
+
|
|
401
544
|
INTERDICTION de passer à D (Use Cases) tant que le client n'a pas validé les règles.
|
|
402
545
|
|
|
403
546
|
### D. Use Cases
|
|
@@ -767,6 +910,21 @@ For EACH apiEndpoint in module:
|
|
|
767
910
|
|
|
768
911
|
**criture :** Stocker dans `usecases.json` sous la cl `apiEndpoints[]` (enrichi).
|
|
769
912
|
|
|
913
|
+
### H-pre. Schema Refresh (OBLIGATOIRE avant chaque ecriture)
|
|
914
|
+
|
|
915
|
+
AVANT d'ecrire les JSON pour le module courant :
|
|
916
|
+
→ **Relire** `references/03-json-schemas.md` section "Business Rules Schema Format"
|
|
917
|
+
→ Verifier que CHAQUE rule dans le buffer utilise les champs canoniques :
|
|
918
|
+
- `id` (pas `code`), `name` (pas `label`), `statement` (pas `description`/`rule`),
|
|
919
|
+
`category` (pas `type`), `severity` (blocking/warning/info, pas Error/error),
|
|
920
|
+
`examples[]` (array d'`{input, expected}`, pas string), `entities[]`, `sectionCode`
|
|
921
|
+
→ SI un champ deprecated est detecte : corriger AVANT ecriture
|
|
922
|
+
→ Ce refresh prend ~30 secondes et previent 100% des derives de schema
|
|
923
|
+
|
|
924
|
+
**POURQUOI ce refresh :** Apres 5+ modules, le contexte est compacte et les exemples
|
|
925
|
+
JSON du debut de conversation sont perdus. Ce refresh garantit que chaque module
|
|
926
|
+
est ecrit avec le format canonique, meme si le contexte a ete compresse.
|
|
927
|
+
|
|
770
928
|
### H. Write & Advance (SEULEMENT apres validation complete)
|
|
771
929
|
|
|
772
930
|
> **GATE:** ba-writer ne peut etre lance QUE si le client a valide :
|