@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.
Files changed (81) hide show
  1. package/dist/index.js +87 -41
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/project/claude-md/root.CLAUDE.md.template +1 -1
  5. package/templates/skills/ai-prompt/SKILL.md +64 -0
  6. package/templates/skills/ai-prompt/references/ai-agent-modes.md +89 -0
  7. package/templates/skills/ai-prompt/references/eval-framework.md +129 -0
  8. package/templates/skills/apex/SKILL.md +2 -2
  9. package/templates/skills/apex/references/checks/frontend-checks.sh +123 -11
  10. package/templates/skills/apex/references/checks/seed-checks.sh +81 -7
  11. package/templates/skills/apex/references/core-seed-data.md +27 -22
  12. package/templates/skills/apex/references/domain-events-pattern.md +45 -0
  13. package/templates/skills/apex/references/entity-hooks-pattern.md +68 -0
  14. package/templates/skills/apex/references/licensing-enforcement.md +52 -0
  15. package/templates/skills/apex/references/post-checks.md +18 -1
  16. package/templates/skills/apex/references/smartstack-api.md +116 -5
  17. package/templates/skills/apex/references/smartstack-frontend.md +1 -1
  18. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  19. package/templates/skills/apex/steps/step-00-init.md +1 -1
  20. package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
  21. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
  22. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  23. package/templates/skills/apex-verify/SKILL.md +110 -0
  24. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  25. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  26. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +96 -0
  27. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  28. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  29. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  30. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  31. package/templates/skills/application/references/contexts-cheatsheet.md +86 -0
  32. package/templates/skills/application/references/extensions-system.md +158 -0
  33. package/templates/skills/application/references/frontend-route-naming.md +7 -5
  34. package/templates/skills/application/references/frontend-verification.md +7 -5
  35. package/templates/skills/application/references/provider-template.md +4 -2
  36. package/templates/skills/application/references/smartstack-provider.md +118 -0
  37. package/templates/skills/application/references/themes-db-driven.md +484 -0
  38. package/templates/skills/application/templates-frontend.md +2 -2
  39. package/templates/skills/application/templates-seed.md +4 -2
  40. package/templates/skills/audit-route/references/routing-pattern.md +3 -1
  41. package/templates/skills/business-analyse/SKILL.md +3 -3
  42. package/templates/skills/business-analyse/_shared.md +37 -0
  43. package/templates/skills/business-analyse/react/components.md +30 -28
  44. package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
  45. package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
  46. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  47. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  48. package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
  49. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
  50. package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
  51. package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
  52. package/templates/skills/business-analyse/templates-react.md +15 -15
  53. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
  54. package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
  55. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
  56. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  57. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  58. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +6 -3
  59. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
  60. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
  61. package/templates/skills/business-analyse-html/references/data-build.md +2 -0
  62. package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
  63. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
  64. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  65. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  66. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  67. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  68. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  69. package/templates/skills/cli-app-sync/SKILL.md +105 -4
  70. package/templates/skills/cli-app-sync/references/comparison-map.md +13 -0
  71. package/templates/skills/cli-app-sync/references/diff-entities.md +162 -0
  72. package/templates/skills/dev-start/SKILL.md +7 -7
  73. package/templates/skills/documentation/templates.md +16 -16
  74. package/templates/skills/migrate/SKILL.md +312 -0
  75. package/templates/skills/migrate/references/v3.34-to-v3.46.md +289 -0
  76. package/templates/skills/sketch/SKILL.md +15 -153
  77. package/templates/skills/smoke-generation/SKILL.md +313 -0
  78. package/templates/skills/ui-components/SKILL.md +11 -1
  79. package/templates/skills/ui-components/patterns/data-table.md +1 -1
  80. package/templates/skills/ui-components/references/component-catalog.md +82 -0
  81. package/templates/skills/workflow/SKILL.md +70 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "4.75.0",
3
+ "version": "4.79.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -96,7 +96,7 @@ Application → Module → Section → Resource
96
96
  Permission: myapp.orders.{action}
97
97
  ```
98
98
 
99
- > **Note**: Applications have an `ApplicationZone` property (enum: `Platform`, `Personal`, `Business`) used for UI layout grouping only. It does NOT appear in routes or permissions.
99
+ > **Note (v3.46+)**: The legacy `ApplicationZone` enum has been removed. Applications now have two boolean flags : `IsPersonal` (default `false`) — when `true`, the app belongs to the user's personal scope (e.g. myspace) and bypasses tenant catalog filtering / permission checks ; `IsOpen` (default `false`) when `true`, the app is accessible without permission checks (replaces the legacy hardcoded OPEN_APPS list in the frontend RouteGuard). Neither flag appears in routes or permission paths — they only steer access control.
100
100
 
101
101
  ### Permission Actions
102
102
  `access` | `read` | `create` | `update` | `delete` | `export` | `import` | `approve` | `reject` | `assign` | `execute`
@@ -58,6 +58,61 @@ Configured instance: Code, Name, SystemContext, DefaultModel, MonthlyBudgetLimit
58
58
  ### OutputSchema
59
59
  JSON Schema for validation: Code, Name, JsonSchema, DotNetType
60
60
 
61
+ ### AiAgent (R17 — v3.46+)
62
+ Multi-step agent with reasoning modes. See [references/ai-agent-modes.md](references/ai-agent-modes.md).
63
+
64
+ - **AiAgentMode** : `Sequential` (0), `ReAct` (1), `PlanAndExecute` (2), `SelfCorrection` (3)
65
+ - **AiAgentStepRole** : `Worker` (0), `Planner` (1), `Reasoner` (2), `Critic` (3), `Synthesizer` (4)
66
+ - **MaxIterations** (default 5, range 1-20) — protects against infinite loops
67
+ - **QualityThreshold** (default 0.8) — used by `SelfCorrection` mode
68
+ - **AiSkill.CacheTtlSeconds** + **AiSkillBlock.IsCacheable** — caching for idempotent outputs
69
+
70
+ Default to `Sequential` ; switch to `ReAct`/`PlanAndExecute`/`SelfCorrection` only when needed (cost/latency impact).
71
+
72
+ ### AiTool — function calling (R2 — v3.46+)
73
+
74
+ Server-side function the model can decide to invoke during a completion. Tools turn skills from one-shot prompts into agentic workflows that can read live data, call external APIs, and condition output on the result.
75
+
76
+ ```csharp
77
+ class AiTool : BaseEntity, IAuditableEntity
78
+ {
79
+ public string Name { get; } // Snake_case, presented to model: "search_database"
80
+ public string DisplayName { get; } // Admin UI label
81
+ public string Description { get; } // Drives model decision — short, action-oriented
82
+ public string ParametersSchemaJson { get; } // JSON Schema of tool arguments
83
+ public string HandlerKey { get; } // Stable key → IToolHandler dispatcher
84
+ public Guid ModuleId { get; } // Module owns the tool (permissions + admin grouping)
85
+ public bool IsActive { get; }
86
+ }
87
+ ```
88
+
89
+ - **Many-to-many** with `AiSkill` via `AiSkillTool` (kept as own entity for future per-attachment overrides : required flag, parameter aliasing, max-call budget).
90
+ - **HandlerKey** decouples definition from C# class name (handlers can be renamed without breaking the catalog).
91
+ - When `IsActive = false`, the orchestrator skips the tool even if a skill still references it.
92
+
93
+ **DO / DON'T:**
94
+ - DO author tool descriptions for the model — short, action-oriented, with preconditions
95
+ - DO snake_case the `Name` (LLM convention)
96
+ - DON'T expose long-running or heavy tools via function calling — prefer Workflow steps
97
+ - DON'T duplicate tool logic across skills — attach the same `AiTool` via `AiSkillTool`
98
+
99
+ ### AiAgentExecution — runtime tracking
100
+
101
+ Each execution of an `AiAgent` is recorded via `AiAgentExecution` with:
102
+ - `Status` : `Running` / `Completed` / `Failed` / `PartiallyCompleted` / `Cancelled`
103
+ - `ExecutedSteps` / `TotalSteps`
104
+ - `InputStateJson` / `FinalStateJson` (audit trail)
105
+ - **Usage tracking** : `TotalInputTokens`, `TotalOutputTokens`, `TotalCost` (decimal), `TotalExecutionTimeMs`
106
+ - `_stepExecutions` : per-step `AiAgentStepExecution` (with own status, tokens, cost)
107
+
108
+ Service methods : `IncrementExecutedSteps()`, `MarkCompleted(json)`, `MarkFailed(msg)`, `MarkPartiallyCompleted(json, msg)`, `MarkCancelled(msg)`, `AccumulateUsage(in, out, cost, ms)`, `AddStepExecution(step)`.
109
+
110
+ **Use** : surface in admin UI (run history, cost dashboard, debug trace). Each agent run produces one `AiAgentExecution` + N `AiAgentStepExecution`.
111
+
112
+ ### AI Evaluation Framework (R10)
113
+
114
+ For benchmarking skills against expected outputs, see [references/eval-framework.md](references/eval-framework.md). Pattern: `AiEvalDataset` (test cases) + `AiEvaluation` (one run) + `AiEvalResult` (per-item outcome). Provider/model agnostic — same dataset can compare 2 versions of the same skill code.
115
+
61
116
  ## Implementation
62
117
 
63
118
  Start with [steps/step-00-init.md](steps/step-00-init.md) to gather requirements, then proceed to [steps/step-01-implementation.md](steps/step-01-implementation.md) for:
@@ -95,7 +150,16 @@ Start with [steps/step-00-init.md](steps/step-00-init.md) to gather requirements
95
150
  | `Domain/AI/Prompts/Prompt.cs` | Prompt entity |
96
151
  | `Domain/AI/Schemas/OutputSchema.cs` | Schema validation |
97
152
  | `Domain/AI/AiProviderInstance.cs` | Configured instance |
153
+ | `Domain/AI/Agents/AiAgent.cs` | Multi-step agent (R17 — v3.46+) |
154
+ | `Domain/AI/Agents/AiAgentStep.cs` | Agent step with `Role` (Worker/Planner/Reasoner/Critic/Synthesizer) |
155
+ | `Domain/AI/Agents/AiAgentExecution.cs` | Run tracking (status, tokens, cost, step executions) |
156
+ | `Domain/AI/Skills/AiSkill.cs` | Reusable skill (`CacheTtlSeconds` for cache hint) |
157
+ | `Domain/AI/Tools/AiTool.cs` | Function-calling tool (R2 — v3.46+) |
158
+ | `Domain/AI/Tools/AiSkillTool.cs` | Many-to-many AiSkill ↔ AiTool |
159
+ | `Domain/AI/Evaluations/AiEvalDataset.cs` | Eval dataset (R10 — v3.46+) |
160
+ | `Domain/AI/Evaluations/AiEvaluation.cs` | Eval run with rollup stats |
98
161
  | `Application/Common/Interfaces/IAiCompletionService.cs` | Service interface |
162
+ | `Application/Common/Interfaces/IToolHandler.cs` | Tool handler interface (resolved via HandlerKey) |
99
163
  | `Infrastructure/Services/AI/AiCompletionService.cs` | Implementation |
100
164
  | `web/src/services/api/aiApi.ts` | Frontend API |
101
165
 
@@ -0,0 +1,89 @@
1
+ # AI Agent Modes & Step Roles (R17 — v3.46)
2
+
3
+ > **Reference for `ai-prompt` skill** — how to choose `AiAgentMode` and `AiAgentStepRole` when scaffolding multi-step AI agents.
4
+
5
+ ## When This Reference Applies
6
+
7
+ - You create an `AiAgent` with more than one step
8
+ - You need a non-Sequential reasoning mode (loop, plan-then-execute, self-correct)
9
+ - The user asks "how do I make the agent retry", "how do I make it plan first", "what is ReAct"
10
+
11
+ For single-prompt skills, the basic `Prompt + Block + OutputSchema` pattern is enough — no agent, no mode.
12
+
13
+ ---
14
+
15
+ ## `AiAgentMode` (4 values)
16
+
17
+ | Mode | Value | Use case | Latency | Cost |
18
+ |---|---|---|---|---|
19
+ | `Sequential` | 0 | Known DAG fan-out / fan-in (analyze → summarize → email) | Low | Low |
20
+ | `ReAct` | 1 | Reasoning + tool loop, plan unknown ahead of time | High | High |
21
+ | `PlanAndExecute` | 2 | Task that can be planned upfront then executed | Medium | Medium |
22
+ | `SelfCorrection` | 3 | Output that can be critiqued and replayed | Medium | Medium-High |
23
+
24
+ ### Recommended configuration
25
+
26
+ | Field | Default | Range |
27
+ |---|---|---|
28
+ | `MaxIterations` | 5 | [1..20] — protects against infinite loops |
29
+ | `QualityThreshold` | 0.8 | [0..1] — used by `SelfCorrection` to decide replay |
30
+
31
+ ---
32
+
33
+ ## `AiAgentStepRole` (5 values)
34
+
35
+ | Role | Value | Expected output schema |
36
+ |---|---|---|
37
+ | `Worker` | 0 | Standard skill output (used in `Sequential` mode) |
38
+ | `Planner` | 1 | `{ plan: [{ skillCode, label }] }` — produces the plan in `PlanAndExecute` |
39
+ | `Reasoner` | 2 | `{ action, input }` or `{ final_answer }` — used in `ReAct` |
40
+ | `Critic` | 3 | `{ score: 0..1, feedback?: string }` — used in `SelfCorrection` |
41
+ | `Synthesizer` | 4 | Final aggregated output (`PlanAndExecute` after parallel workers) |
42
+
43
+ ---
44
+
45
+ ## Cache hint (R-cache)
46
+
47
+ | Field | Type | Purpose |
48
+ |---|---|---|
49
+ | `AiSkill.CacheTtlSeconds` | `int?` | TTL for caching this skill's output (per input hash). Null = no cache. |
50
+ | `AiSkillBlock.IsCacheable` | `bool` | Marks an idempotent block whose output can be reused across executions. |
51
+
52
+ Only mark a skill / block cacheable when the output is **deterministic for a given input** — otherwise stale answers will be served.
53
+
54
+ ---
55
+
56
+ ## Choosing a mode (decision tree)
57
+
58
+ ```
59
+ Question: do I know the steps in advance?
60
+ Yes → Sequential
61
+ No → Question: can the LLM decide the steps once, then execute?
62
+ Yes → PlanAndExecute
63
+ No → Question: must I let the LLM iterate with tools?
64
+ Yes → ReAct
65
+ No → SelfCorrection (single attempt + critique loop)
66
+ ```
67
+
68
+ ---
69
+
70
+ ## DO / DON'T
71
+
72
+ | ✅ DO | ❌ DON'T |
73
+ |---|---|
74
+ | Start with `Sequential` — switch to other modes only when needed | Default to `ReAct` (expensive, hard to debug) |
75
+ | Cap `MaxIterations` at the smallest number that satisfies your case | Leave the default 5 for `ReAct` on long-running tasks (set 10-15) |
76
+ | Set `QualityThreshold` based on prompt evaluation results | Pick `0.8` blindly — measure on your eval dataset |
77
+ | Use `Critic` role with a structured output schema | Let the critic return free-form text |
78
+ | Cache only deterministic, idempotent skills | Cache anything that uses `now()` or randomness |
79
+
80
+ ---
81
+
82
+ ## Reference source files (read-only)
83
+
84
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Agents\AiAgent.cs`
85
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Agents\AiAgentMode.cs`
86
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Agents\AiAgentStep.cs`
87
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Agents\AiAgentStepRole.cs`
88
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Skills\AiSkill.cs` (CacheTtlSeconds)
89
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Skills\AiSkillBlock.cs` (IsCacheable)
@@ -0,0 +1,129 @@
1
+ # AI Evaluation Framework (R10 — v3.46+)
2
+
3
+ > **Reference for `ai-prompt` skill** — how to evaluate AI skills against expected outputs.
4
+
5
+ ## When This Reference Applies
6
+
7
+ - You are about to ship a new prompt or change an existing one
8
+ - You need to compare two versions of the same skill (`v1.0.0` vs `v1.1.0`)
9
+ - You want to benchmark the same prompt across providers/models (OpenAI vs Claude vs Gemini)
10
+ - The user asks "how do I test my prompt", "how do I prevent regression", "how do I A/B prompts"
11
+
12
+ For one-shot manual testing during development, the eval framework is overkill — call the skill directly. Use this framework when you want **reproducible, scored, auditable** runs.
13
+
14
+ ---
15
+
16
+ ## The 4 entities
17
+
18
+ ### `AiEvalDataset` — collection of test cases
19
+
20
+ ```csharp
21
+ class AiEvalDataset : BaseEntity, IAuditableEntity
22
+ {
23
+ public string Code { get; } // kebab-case stable id, e.g. "support-classification-v1"
24
+ public string Name { get; }
25
+ public string? Description { get; }
26
+ public IReadOnlyCollection<AiEvalDatasetItem> Items { get; }
27
+ }
28
+ ```
29
+
30
+ **Provider/model agnostic** : the same dataset can be replayed against multiple skills, providers and models to compare quality and cost.
31
+
32
+ ### `AiEvalDatasetItem` — one test case
33
+
34
+ ```csharp
35
+ class AiEvalDatasetItem
36
+ {
37
+ public Guid DatasetId { get; }
38
+ public string Label { get; } // Human-readable name for the case
39
+ public string VariablesJson { get; } // Inputs to feed into the skill
40
+ public string ExpectedOutputJson { get; } // Expected result (for grading)
41
+ }
42
+ ```
43
+
44
+ ### `AiEvaluation` — one run
45
+
46
+ ```csharp
47
+ class AiEvaluation : BaseEntity, IAuditableEntity
48
+ {
49
+ public Guid SkillId { get; } // Which skill was tested
50
+ public Guid DatasetId { get; } // Which dataset was replayed
51
+ public AiEvaluationStatus Status { get; } // Running / Completed / Failed
52
+ public DateTime StartedAt { get; }
53
+ public DateTime? CompletedAt { get; }
54
+ public int ProcessedItems { get; }
55
+ public int TotalItems { get; }
56
+ public int PassedItems { get; } // Items whose actual output matched expected
57
+ public double? AverageScore { get; } // [0..1], null while in progress
58
+ public IReadOnlyCollection<AiEvalResult> Results { get; }
59
+ }
60
+
61
+ enum AiEvaluationStatus { Running = 0, Completed = 1, Failed = 2 }
62
+ ```
63
+
64
+ Service methods : `RecordResult(AiEvalResult)`, `MarkCompleted()`, `MarkFailed(reason)`. The aggregate computes `AverageScore` from the per-item `Score` values when completed.
65
+
66
+ ### `AiEvalResult` — per-item outcome
67
+
68
+ Records actual output vs expected, the score (0..1), pass/fail flag, and any tokens/cost incurred.
69
+
70
+ ---
71
+
72
+ ## Workflow
73
+
74
+ ```
75
+ 1. Curate dataset
76
+ └─ AiEvalDataset.Create("support-classification-v1", "Support classification — 50 cases")
77
+ └─ AddItem("simple billing question", { "ticket": "..." }, { "category": "billing" })
78
+ └─ AddItem("urgent outage report", { "ticket": "..." }, { "category": "incident" })
79
+ └─ … 48 more items
80
+
81
+ 2. Run evaluation against current skill version
82
+ └─ AiEvaluation.Create(skillId, datasetId, totalItems: 50)
83
+ └─ For each item: invoke skill, grade output, RecordResult(actual, expected, score)
84
+ └─ MarkCompleted()
85
+ └─ Stored AverageScore = 0.86 (43/50 passed)
86
+
87
+ 3. Iterate — change prompt / blocks / model
88
+ └─ New AiEvaluation against same dataset
89
+ └─ Compare AverageScore : 0.86 → 0.92 (+0.06)
90
+
91
+ 4. Decide
92
+ └─ If new score significantly higher : promote prompt version
93
+ └─ If equal or lower : revert
94
+ ```
95
+
96
+ ---
97
+
98
+ ## DO / DON'T
99
+
100
+ | ✅ DO | ❌ DON'T |
101
+ |---|---|
102
+ | Build datasets from real production examples (anonymised) | Use synthetic / hallucinated test cases as the only ones |
103
+ | Stabilise dataset.Code — never rename, only add new datasets | Mutate dataset items after first run (compromises history) |
104
+ | Grade with structured comparison (JSON match, key fields, semantic distance) | Grade with substring search / regex |
105
+ | Track cost per evaluation — reject prompt changes that 3× cost for marginal score | Optimise for score alone |
106
+ | Run evals as part of the prompt-change PR review | Ship prompt changes without an eval baseline |
107
+ | Keep `AverageScore` as the single headline metric | Multiply scoring criteria — pick one and stick to it |
108
+
109
+ ---
110
+
111
+ ## Wiring into a release pipeline
112
+
113
+ A prompt change should run the relevant evaluation as part of CI:
114
+
115
+ 1. PR opens with a new prompt version (`v1.1.0`).
116
+ 2. CI invokes a Command that creates an `AiEvaluation(skillId, datasetId, totalItems)` against `v1.1.0`.
117
+ 3. CI compares `AverageScore` of `v1.1.0` vs the latest passing eval of `v1.0.0`.
118
+ 4. PR is annotated with the delta. Score regression > 5% → PR blocked.
119
+
120
+ ---
121
+
122
+ ## Reference source files (read-only)
123
+
124
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Evaluations\AiEvalDataset.cs`
125
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Evaluations\AiEvalDatasetItem.cs`
126
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Evaluations\AiEvaluation.cs`
127
+ - `D:\01 - projets\SmartStack.app\features\IA-Workflow\src\SmartStack.Domain\AI\Evaluations\AiEvalResult.cs`
128
+
129
+ See also [ai-agent-modes.md](ai-agent-modes.md) for `MaxIterations` / `QualityThreshold` (used by `SelfCorrection` mode in conjunction with eval scores).
@@ -26,8 +26,8 @@ Execute incremental SmartStack development using the APEX methodology. This skil
26
26
  /apex -e add status field to Project entity # Economy (no agents)
27
27
  /apex -r # Resume previous
28
28
 
29
- # From /sketch (vague idea → precise command):
30
- /sketch une app RH avec employés et absences # → generates /apex command
29
+ # From /business-analyse-quick (vague idea → Mini-PRD):
30
+ /business-analyse-quick une app RH avec employés # → generates PRD for /apex -d
31
31
 
32
32
  # From /business-analyse-develop (automated multi-module):
33
33
  # /business-analyse-develop delegates to /apex -d automatically
@@ -191,28 +191,74 @@ if [ -n "$ALL_PAGES" ]; then
191
191
  fi
192
192
  fi
193
193
 
194
- # POST-CHECK C9: No hardcoded Tailwind colors in generated pages (WARNING)
194
+ # POST-CHECK C9: No hardcoded Tailwind colors in generated pages (BLOCKING)
195
+ # v3.46+ : tightened enforcement. Theme system via CSS variables is mandatory.
195
196
  ALL_PAGES=$(find src/pages/ src/components/ -name "*.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\." || true)
196
197
  if [ -n "$ALL_PAGES" ]; then
197
- HARDCODED=$(grep -Pn '(bg|text|border)-(?!\[)(red|blue|green|gray|white|black|slate|zinc|neutral|stone)-' $ALL_PAGES 2>/dev/null || true)
198
+ # Extended palette: catches Tailwind 22 named colors + white/black
199
+ # Excludes status `text-white`/`text-black` ONLY when used as foreground over a CSS-var background (rare)
200
+ HARDCODED=$(grep -Pn '(bg|text|border)-(?!\[)(red|blue|green|gray|white|black|slate|zinc|neutral|stone|amber|yellow|orange|purple|indigo|violet|cyan|pink|emerald|rose|sky|teal|lime|fuchsia)-' $ALL_PAGES 2>/dev/null || true)
198
201
  if [ -n "$HARDCODED" ]; then
199
- echo "WARNING: Pages MUST use CSS variables instead of hardcoded Tailwind colors"
200
- echo "SmartStack uses a theme system — hardcoded colors break dark mode and custom themes"
202
+ echo "BLOCKING (C9): Pages MUST use CSS variables instead of hardcoded Tailwind colors."
203
+ echo "SmartStack uses a theme system — hardcoded colors break dark mode and custom themes."
201
204
  echo ""
202
- echo "Fix mapping:"
205
+ echo "Fix mapping (canonical SmartStack tokens):"
203
206
  echo " bg-white → bg-[var(--bg-card)]"
204
207
  echo " bg-gray-50 → bg-[var(--bg-primary)]"
208
+ echo " bg-gray-100 → bg-[var(--bg-secondary)]"
209
+ echo " bg-gray-200 → bg-[var(--bg-tertiary)]"
205
210
  echo " text-gray-900 → text-[var(--text-primary)]"
206
- echo " text-gray-500 → text-[var(--text-secondary)]"
211
+ echo " text-gray-700 → text-[var(--text-secondary)]"
212
+ echo " text-gray-500 → text-[var(--text-tertiary)]"
213
+ echo " text-gray-400 → text-[var(--text-muted)]"
207
214
  echo " border-gray-200 → border-[var(--border-color)]"
208
- echo " bg-blue-600 bg-[var(--color-accent-500)]"
209
- echo " text-blue-600 text-[var(--color-accent-500)]"
210
- echo " text-red-500 text-[var(--error-text)]"
211
- echo " bg-green-500 bg-[var(--success-bg)]"
215
+ echo " border-gray-100 border-[var(--border-subtle)]"
216
+ echo " bg-blue-* bg-[var(--info-bg)] (semantic info)"
217
+ echo " bg-blue-600 bg-[var(--color-accent-600)] (action button)"
218
+ echo " text-blue-600 text-[var(--color-accent-600)]"
219
+ echo " text-red-* → text-[var(--error-text)]"
220
+ echo " bg-red-500/10 → bg-[var(--error-bg)]"
221
+ echo " text-yellow-* → text-[var(--warning-text)]"
222
+ echo " bg-amber-500/10 → bg-[var(--warning-bg)]"
223
+ echo " bg-green-* → bg-[var(--success-bg)]"
224
+ echo " text-green-* → text-[var(--success-text)]"
212
225
  echo ""
213
- echo "See references/smartstack-frontend.md section 4 for full variable reference"
226
+ echo "Status badges use the 4 status token sets : --success-*, --warning-*, --error-*, --info-*"
227
+ echo " Example: 'bg-[var(--info-bg)] text-[var(--info-text)] border border-[var(--info-border)]'"
228
+ echo ""
229
+ echo "Avoid Tailwind \`dark:\` prefix — SmartStack toggles a \`.dark\` class on <html> and CSS vars adapt automatically."
230
+ echo ""
231
+ echo "References:"
232
+ echo " - templates/skills/application/references/themes-db-driven.md (full token list)"
233
+ echo " - templates/skills/ui-components/style-guide.md (DO/DON'T)"
214
234
  echo ""
215
235
  echo "$HARDCODED"
236
+ FAIL=true
237
+ fi
238
+ fi
239
+
240
+ # POST-CHECK C9b: Avoid Tailwind dark: prefix — SmartStack uses .dark class on root (BLOCKING)
241
+ ALL_PAGES=$(find src/pages/ src/components/ -name "*.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\." || true)
242
+ if [ -n "$ALL_PAGES" ]; then
243
+ DARK_PREFIX=$(grep -PnH '\bdark:(bg|text|border)-' $ALL_PAGES 2>/dev/null || true)
244
+ if [ -n "$DARK_PREFIX" ]; then
245
+ echo "BLOCKING (C9b): Tailwind 'dark:' prefix detected — incompatible with SmartStack theme system."
246
+ echo "SmartStack toggles a '.dark' class on <html> ; CSS vars (var(--bg-*), var(--text-*)) adapt automatically."
247
+ echo "Fix: remove 'dark:' prefix and rely on CSS vars instead."
248
+ echo "$DARK_PREFIX"
249
+ FAIL=true
250
+ fi
251
+ fi
252
+
253
+ # POST-CHECK C9c: No hex colors in className (BLOCKING)
254
+ ALL_PAGES=$(find src/pages/ src/components/ -name "*.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\." || true)
255
+ if [ -n "$ALL_PAGES" ]; then
256
+ HEX_CN=$(grep -PnH 'className=[^>]*\[(#[0-9a-fA-F]{3,8})\]' $ALL_PAGES 2>/dev/null || true)
257
+ if [ -n "$HEX_CN" ]; then
258
+ echo "BLOCKING (C9c): Hex colors in className detected — must use CSS vars."
259
+ echo "Fix: replace #xxxxxx by var(--xxx) from the SmartStack token set."
260
+ echo "$HEX_CN"
261
+ FAIL=true
216
262
  fi
217
263
  fi
218
264
 
@@ -324,6 +370,46 @@ if [ -n "$TSX_PAGES" ] && [ -z "$DOC_DATA" ]; then
324
370
  echo "The DocToggleButton in page headers will link to this documentation"
325
371
  fi
326
372
 
373
+ # POST-CHECK C28: i18n namespace consistency across the 4 languages (BLOCKING)
374
+ # Each language directory must have the same set of JSON namespaces.
375
+ # Reference language is the one with the most files (usually fr or en).
376
+ I18N_BASE=$(find src/i18n/locales -maxdepth 1 -type d 2>/dev/null | grep -E "/(fr|en|it|de)$" || true)
377
+ if [ -n "$I18N_BASE" ]; then
378
+ # Build sorted file lists per language (basename only, .json suffix)
379
+ FR_FILES=$(find src/i18n/locales/fr -maxdepth 1 -name "*.json" -exec basename {} \; 2>/dev/null | sort -u || true)
380
+ EN_FILES=$(find src/i18n/locales/en -maxdepth 1 -name "*.json" -exec basename {} \; 2>/dev/null | sort -u || true)
381
+ IT_FILES=$(find src/i18n/locales/it -maxdepth 1 -name "*.json" -exec basename {} \; 2>/dev/null | sort -u || true)
382
+ DE_FILES=$(find src/i18n/locales/de -maxdepth 1 -name "*.json" -exec basename {} \; 2>/dev/null | sort -u || true)
383
+
384
+ # Pick the largest set as the canonical reference (excludes *_tmp.json which is intentionally orphan)
385
+ REF_FILES=$(echo -e "$FR_FILES\n$EN_FILES\n$IT_FILES\n$DE_FILES" | grep -v "_tmp\." | sort -u)
386
+
387
+ if [ -n "$REF_FILES" ]; then
388
+ FAIL_C28=false
389
+ for LANG in fr en it de; do
390
+ LANG_FILES=$(find src/i18n/locales/$LANG -maxdepth 1 -name "*.json" -exec basename {} \; 2>/dev/null | grep -v "_tmp\." | sort -u || true)
391
+ MISSING=$(comm -23 <(echo "$REF_FILES") <(echo "$LANG_FILES") || true)
392
+ if [ -n "$MISSING" ]; then
393
+ echo "BLOCKING (C28): i18n language '$LANG' is missing namespaces present in other languages:"
394
+ echo "$MISSING" | sed 's/^/ - /'
395
+ echo " Fix: create the missing JSON file(s) in src/i18n/locales/$LANG/ — even a stub {} is better than the runtime crash on a missing namespace."
396
+ FAIL_C28=true
397
+ fi
398
+ done
399
+
400
+ # Detect orphan _tmp.json files (intentional but should be cleaned up)
401
+ ORPHAN=$(find src/i18n/locales -name "*_tmp.json" 2>/dev/null || true)
402
+ if [ -n "$ORPHAN" ]; then
403
+ echo "WARNING (C28): orphan *_tmp.json file(s) detected — clean up after migration:"
404
+ echo "$ORPHAN" | sed 's/^/ - /'
405
+ fi
406
+
407
+ if [ "$FAIL_C28" = true ]; then
408
+ FAIL=true
409
+ fi
410
+ fi
411
+ fi
412
+
327
413
  # POST-CHECK C36: Frontend navigate() calls must have matching route definitions (CRITICAL)
328
414
  PAGE_FILES=$(find web/ -name "*.tsx" -path "*/pages/*" ! -name "*.test.tsx" 2>/dev/null || true)
329
415
  if [ -n "$PAGE_FILES" ]; then
@@ -443,6 +529,32 @@ if [ -n "$APP_TSX" ]; then
443
529
  fi
444
530
  fi
445
531
 
532
+ # POST-CHECK C64: ListPages must have Create/New button (BLOCKING)
533
+ # Every ListPage must contain a navigate('create') or Link to="create" for the New button.
534
+ # Without it, users cannot access the create form (broken CRUD workflow).
535
+ # Note: C5 catches dead links (navigate exists but page missing). C64 catches missing buttons entirely.
536
+ LIST_PAGES=$(find web/ src/pages/ -name "*ListPage.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\." | grep -v "\.spec\." || true)
537
+ if [ -n "$LIST_PAGES" ]; then
538
+ FAIL_C64=false
539
+ for LP in $LIST_PAGES; do
540
+ # Skip dashboard-like list pages (no CRUD expected)
541
+ if echo "$LP" | grep -qiP "dashboard|calendar|approve|balance"; then
542
+ continue
543
+ fi
544
+ HAS_CREATE_NAV=$(grep -P "navigate\(.*['\"\`]create['\"\`]|navigate\(.*['\"\`].*\/create['\"\`]|to=['\"\`]create['\"\`]|to=\{.*create.*\}" "$LP" 2>/dev/null || true)
545
+ if [ -z "$HAS_CREATE_NAV" ]; then
546
+ echo "BLOCKING: ListPage missing Create/New button: $LP"
547
+ echo " Every ListPage MUST have a 'New' button with navigate('create') in the page header."
548
+ echo " Without it, users cannot access the create form — CRUD workflow is broken."
549
+ echo " Fix: Add to page header: <button onClick={() => navigate('create')}>New</button>"
550
+ FAIL_C64=true
551
+ fi
552
+ done
553
+ if [ "$FAIL_C64" = true ]; then
554
+ FAIL=true
555
+ fi
556
+ fi
557
+
446
558
  if [ "$FAIL" = true ]; then
447
559
  exit 1
448
560
  fi
@@ -41,9 +41,6 @@ if [ -n "$APP_TSX" ] && [ -n "$SEED_ROUTES" ]; then
41
41
  SEED_NORM=$(echo "$SEED_SUFFIX" | tr '[:upper:]' '[:lower:]' | tr -d '-')
42
42
  MATCH_FOUND=false
43
43
  for FE_PATH in $FRONTEND_PATHS; do
44
- if echo "$FE_PATH" | grep -qP '/list$'; then
45
- echo "WARNING: Frontend route ends with /list — should use index route instead: $FE_PATH"
46
- fi
47
44
  FE_BASE=$(echo "$FE_PATH" | sed 's|/list$||;s|/new$||;s|/:id.*||;s|/create$||')
48
45
  FE_NORM=$(echo "$FE_BASE" | tr '[:upper:]' '[:lower:]' | tr -d '-')
49
46
  if [ "$SEED_NORM" = "$FE_NORM" ]; then
@@ -174,14 +171,13 @@ if [ -n "$SECTION_SEED_FILES" ] && [ -n "$PERM_FILE" ]; then
174
171
  done
175
172
  fi
176
173
 
177
- # POST-CHECK C21: FORBIDDEN route patterns — /list and /detail/:id (WARNING)
174
+ # POST-CHECK C21: FORBIDDEN route patterns — /detail/:id (WARNING)
178
175
  SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" -o -name "*NavigationSectionSeedData.cs" 2>/dev/null)
179
176
  if [ -n "$SEED_NAV_FILES" ]; then
180
- BAD_ROUTES=$(grep -Pn 'Route\s*=\s*.*"[^"]*/(list|detail)["/]' $SEED_NAV_FILES 2>/dev/null | grep -v '//.*Route' || true)
177
+ BAD_ROUTES=$(grep -Pn 'Route\s*=\s*.*"[^"]*/(detail)["/]' $SEED_NAV_FILES 2>/dev/null | grep -v '//.*Route' || true)
181
178
  if [ -n "$BAD_ROUTES" ]; then
182
179
  echo "WARNING: FORBIDDEN route pattern in seed data"
183
- echo " - 'list' section route = module route (NO /list suffix)"
184
- echo " - 'detail' section route = module route + /:id (NOT /detail/:id)"
180
+ echo " - 'detail' is an implicit route do NOT use /detail/:id as section route"
185
181
  echo "$BAD_ROUTES"
186
182
  fi
187
183
  fi
@@ -531,6 +527,84 @@ if [ "$HAS_GLOBAL_CONVERTER" = false ]; then
531
527
  fi
532
528
  fi
533
529
 
530
+ # POST-CHECK C63: Reserved section codes must NOT be seeded as visible menu items (BLOCKING)
531
+ # Reserved codes: detail, create, edit, *-detail
532
+ # These are internal route targets resolved by DynamicRouter convention, NOT sidebar menu items.
533
+ SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" \( -name "*NavigationSeedData.cs" -o -name "*NavigationModuleSeedData.cs" \) 2>/dev/null)
534
+ if [ -n "$SEED_NAV_FILES" ]; then
535
+ for f in $SEED_NAV_FILES; do
536
+ for RESERVED in detail create edit; do
537
+ # Pattern A: SectionData record — new("detail", ...)
538
+ MATCHES=$(grep -Pn "new\s*\(\s*\"${RESERVED}\"" "$f" 2>/dev/null || true)
539
+ if [ -n "$MATCHES" ]; then
540
+ echo "BLOCKING: Reserved section code '${RESERVED}' seeded as menu item in $f"
541
+ echo "$MATCHES"
542
+ echo " Reserved codes (detail, create, edit) are internal route targets, NOT sidebar menu items."
543
+ echo " Fix: Remove this section entry. DynamicRouter resolves /:id, /create, /:id/edit by convention."
544
+ FAIL=true
545
+ fi
546
+ # Pattern B: NavigationSectionSeedEntry — Code = "detail"
547
+ MATCHES=$(grep -Pn "Code\s*=\s*\"${RESERVED}\"" "$f" 2>/dev/null || true)
548
+ if [ -n "$MATCHES" ]; then
549
+ # Check if IsActive = false is nearby (acceptable)
550
+ HAS_INACTIVE=$(grep -A 10 "Code\s*=\s*\"${RESERVED}\"" "$f" | grep -P "IsActive\s*=\s*false" 2>/dev/null || true)
551
+ if [ -z "$HAS_INACTIVE" ]; then
552
+ echo "BLOCKING: Reserved section code '${RESERVED}' seeded with IsActive=true in $f"
553
+ echo "$MATCHES"
554
+ echo " Fix: Remove this section, or set IsActive = false."
555
+ FAIL=true
556
+ fi
557
+ fi
558
+ done
559
+ # Check for *-detail codes (e.g., department-detail)
560
+ DETAIL_SUFFIX=$(grep -Pn "new\s*\(\s*\"[a-z]+-detail\"" "$f" 2>/dev/null || true)
561
+ if [ -z "$DETAIL_SUFFIX" ]; then
562
+ DETAIL_SUFFIX=$(grep -Pn "Code\s*=\s*\"[a-z]+-detail\"" "$f" 2>/dev/null || true)
563
+ fi
564
+ if [ -n "$DETAIL_SUFFIX" ]; then
565
+ echo "BLOCKING: Section code with '-detail' suffix seeded as menu item in $f"
566
+ echo "$DETAIL_SUFFIX"
567
+ echo " Detail routes are the /:id route of their parent section."
568
+ echo " Fix: Remove this section entry. Use parent section's /:id route instead."
569
+ FAIL=true
570
+ fi
571
+ done
572
+ fi
573
+
574
+ # POST-CHECK C66: ApplicationZone enum removed in v3.46 (BLOCKING)
575
+ # Detects any leftover reference to the removed ApplicationZone enum or Zone column.
576
+ SEED_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*.cs" 2>/dev/null)
577
+ DOMAIN_FILES=$(find src/ -path "*/Domain/Navigation/*" -name "*.cs" 2>/dev/null)
578
+ INFRA_FILES=$(find src/ -path "*/Infrastructure/Persistence/Configurations/Navigation/*" -name "*.cs" 2>/dev/null)
579
+ APP_FILES=$(find src/ -path "*/Application/Navigation/*" -name "*.cs" 2>/dev/null)
580
+ ALL_NAV_FILES="$SEED_FILES $DOMAIN_FILES $INFRA_FILES $APP_FILES"
581
+ if [ -n "$ALL_NAV_FILES" ]; then
582
+ BAD_ZONE=$(grep -Pn 'ApplicationZone\.\w+|public\s+ApplicationZone\s+Zone|HasColumnName\("Zone"\)|Zone\s*=\s*ApplicationZone' $ALL_NAV_FILES 2>/dev/null || true)
583
+ if [ -n "$BAD_ZONE" ]; then
584
+ echo "BLOCKING (C66): ApplicationZone enum has been removed in SmartStack v3.46."
585
+ echo "Replace by IsPersonal (bool, true => myspace scope) and/or IsOpen (bool, true => bypass permissions)."
586
+ echo "$BAD_ZONE"
587
+ echo "Fix: NavigationApplication.Create(code, label, ..., isOpen: false, isPersonal: false)"
588
+ echo "Reference: templates/skills/apex/references/core-seed-data.md (v3.46+ section)"
589
+ FAIL=true
590
+ fi
591
+ fi
592
+
593
+ # POST-CHECK C67: Legacy domain-specific layouts removed in v3.46 (BLOCKING)
594
+ # AdminLayout / BusinessLayout / UserLayout / HRLayout / SalesLayout no longer exist.
595
+ TSX_FILES=$(find web/ -name "*.tsx" -not -path "*/node_modules/*" 2>/dev/null; find src/ -name "*.tsx" -not -path "*/node_modules/*" 2>/dev/null)
596
+ if [ -n "$TSX_FILES" ]; then
597
+ BAD_LAYOUT=$(grep -PnH '<\s*(AdminLayout|BusinessLayout|UserLayout|HRLayout|SalesLayout)\b|from\s+["'\''][^"'\'']*\/(AdminLayout|BusinessLayout|UserLayout|HRLayout|SalesLayout)["'\'']' $TSX_FILES 2>/dev/null || true)
598
+ if [ -n "$BAD_LAYOUT" ]; then
599
+ echo "BLOCKING (C67): Legacy layouts (AdminLayout/BusinessLayout/UserLayout/HRLayout/SalesLayout) have been removed in v3.46."
600
+ echo "Use AppLayout — the sole shell for authenticated application routes."
601
+ echo "$BAD_LAYOUT"
602
+ echo "Fix: Replace by <AppLayout /> (no domain-specific shell anymore)."
603
+ echo "Reference: templates/skills/application/references/frontend-route-naming.md"
604
+ FAIL=true
605
+ fi
606
+ fi
607
+
534
608
  if [ "$FAIL" = true ]; then
535
609
  exit 1
536
610
  fi