@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
package/package.json
CHANGED
|
@@ -96,7 +96,7 @@ Application → Module → Section → Resource
|
|
|
96
96
|
Permission: myapp.orders.{action}
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
> **Note**: Applications have
|
|
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 /
|
|
30
|
-
/
|
|
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 (
|
|
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
|
-
|
|
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 "
|
|
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-
|
|
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 "
|
|
209
|
-
echo "
|
|
210
|
-
echo "
|
|
211
|
-
echo "
|
|
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 "
|
|
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 — /
|
|
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*.*"[^"]*/(
|
|
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 " - '
|
|
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
|