@atlashub/smartstack-cli 4.32.0 → 4.34.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 (46) hide show
  1. package/.documentation/index.html +2 -2
  2. package/.documentation/init.html +358 -174
  3. package/dist/index.js +45 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/mcp-entry.mjs +271 -44
  6. package/dist/mcp-entry.mjs.map +1 -1
  7. package/package.json +1 -1
  8. package/templates/mcp-scaffolding/controller.cs.hbs +54 -128
  9. package/templates/project/README.md +19 -0
  10. package/templates/project/claude-md/api.CLAUDE.md.template +315 -0
  11. package/templates/project/claude-md/application.CLAUDE.md.template +181 -0
  12. package/templates/project/claude-md/domain.CLAUDE.md.template +125 -0
  13. package/templates/project/claude-md/infrastructure.CLAUDE.md.template +168 -0
  14. package/templates/project/claude-md/root.CLAUDE.md.template +339 -0
  15. package/templates/project/claude-md/web.CLAUDE.md.template +339 -0
  16. package/templates/skills/apex/SKILL.md +16 -10
  17. package/templates/skills/apex/_shared.md +1 -1
  18. package/templates/skills/apex/references/checks/architecture-checks.sh +154 -0
  19. package/templates/skills/apex/references/checks/backend-checks.sh +194 -0
  20. package/templates/skills/apex/references/checks/frontend-checks.sh +448 -0
  21. package/templates/skills/apex/references/checks/infrastructure-checks.sh +255 -0
  22. package/templates/skills/apex/references/checks/security-checks.sh +153 -0
  23. package/templates/skills/apex/references/checks/seed-checks.sh +536 -0
  24. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +49 -192
  25. package/templates/skills/apex/references/post-checks.md +124 -2156
  26. package/templates/skills/apex/references/smartstack-api.md +160 -957
  27. package/templates/skills/apex/references/smartstack-frontend.md +134 -1022
  28. package/templates/skills/apex/references/smartstack-layers.md +12 -6
  29. package/templates/skills/apex/steps/step-00-init.md +81 -238
  30. package/templates/skills/apex/steps/step-03-execute.md +25 -752
  31. package/templates/skills/apex/steps/step-03a-layer0-domain.md +118 -0
  32. package/templates/skills/apex/steps/step-03b-layer1-seed.md +91 -0
  33. package/templates/skills/apex/steps/step-03c-layer2-backend.md +240 -0
  34. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +300 -0
  35. package/templates/skills/apex/steps/step-03e-layer4-devdata.md +44 -0
  36. package/templates/skills/apex/steps/step-04-examine.md +70 -150
  37. package/templates/skills/application/references/frontend-i18n-and-output.md +2 -2
  38. package/templates/skills/application/references/frontend-route-naming.md +5 -1
  39. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +49 -198
  40. package/templates/skills/application/references/frontend-verification.md +11 -11
  41. package/templates/skills/application/steps/step-05-frontend.md +26 -15
  42. package/templates/skills/application/templates-frontend.md +4 -0
  43. package/templates/skills/cli-app-sync/SKILL.md +2 -2
  44. package/templates/skills/cli-app-sync/references/comparison-map.md +1 -1
  45. package/templates/skills/controller/references/controller-code-templates.md +70 -67
  46. package/templates/skills/controller/references/mcp-scaffold-workflow.md +5 -1
@@ -91,14 +91,19 @@ Execute incremental SmartStack development using the APEX methodology. This skil
91
91
  </entry_point>
92
92
 
93
93
  <step_files>
94
- **Progressive loading - only load current step:**
94
+ **Progressive loading only load current step. Step 03 dispatches to layer sub-steps.**
95
95
 
96
96
  | Step | File | Model | Purpose |
97
97
  |------|------|-------|---------|
98
98
  | 00 | `steps/step-00-init.md` | Sonnet | Parse flags, detect application, verify MCP, define hierarchy (4 levels), scope validation |
99
99
  | 01 | `steps/step-01-analyze.md` | Opus | Explore existing code (parallel Agent tool or sequential) |
100
100
  | 02 | `steps/step-02-plan.md` | Opus | Layer-by-layer plan with skill/MCP mapping |
101
- | 03 | `steps/step-03-execute.md` | Opus | Orchestrate execution via skills and MCP |
101
+ | 03 | `steps/step-03-execute.md` | Opus | **Orchestrator** dispatches to layer sub-steps below |
102
+ | 03a | `steps/step-03a-layer0-domain.md` | Opus | Layer 0: Domain entities, EF configs, migration |
103
+ | 03b | `steps/step-03b-layer1-seed.md` | Opus | Layer 1: Seed data (navigation, permissions, roles) |
104
+ | 03c | `steps/step-03c-layer2-backend.md` | Opus | Layer 2: Services, controllers, backend tests |
105
+ | 03d | `steps/step-03d-layer3-frontend.md` | Opus | Layer 3: Pages, i18n, routes, frontend tests |
106
+ | 03e | `steps/step-03e-layer4-devdata.md` | Opus | Layer 4: Development test data (optional) |
102
107
  | 04 | `steps/step-04-examine.md` | Opus | eXamine: MCP validation, build, POST-CHECKs, acceptance criteria |
103
108
  | 05 | `steps/step-05-deep-review.md` | Opus | Deep Review: adversarial code review (if -x) |
104
109
  | 06 | `steps/step-06-resolve.md` | Opus | Fix BLOCKING findings (if any) |
@@ -126,15 +131,16 @@ Execute incremental SmartStack development using the APEX methodology. This skil
126
131
 
127
132
  | File | Purpose | Loaded by | Stays in context for |
128
133
  |------|---------|-----------|---------------------|
129
- | `references/smartstack-api.md` | BaseEntity, interfaces, entity/config/controller patterns | step-01 | step-03 L0-L2 (released after L2) |
130
- | `references/smartstack-layers.md` | Layer rules, skill/MCP mapping, planning templates, delegate fast path | step-02 | step-03 L0-L2 (released after L2) |
131
- | `references/core-seed-data.md` | Comprehensive seed data templates (navigation, permissions, roles) | step-03 Layer 1 | released after Layer 1 |
132
- | `references/smartstack-frontend.md` | Frontend patterns, EntityLookup, i18n (sections 1-6) | step-03 Layer 3 (deferred) | step-04 |
133
- | `references/smartstack-frontend-compliance.md` | Documentation, form testing, compliance gates (sections 7-9) | step-03 Layer 3 (deferred) | step-04 |
134
+ | `references/smartstack-api.md` | BaseEntity, interfaces, entity/config/controller patterns | step-01 | step-03a/03b/03c (released after L2) |
135
+ | `references/smartstack-layers.md` | Layer rules, skill/MCP mapping, planning templates, delegate fast path | step-02 | step-03a/03b/03c (released after L2) |
136
+ | `references/core-seed-data.md` | Comprehensive seed data templates (navigation, permissions, roles) | step-03b (Layer 1) | released after Layer 1 |
137
+ | `references/smartstack-frontend.md` | Frontend patterns, EntityLookup, i18n (sections 1-6) | step-03d (Layer 3, deferred) | step-04 |
138
+ | `references/smartstack-frontend-compliance.md` | Documentation, form testing, compliance gates (sections 7-9) | step-03d (Layer 3, deferred) | step-04 |
134
139
  | `references/challenge-questions.md` | Hierarchy rules, challenge questions, delegate mode skip | step-00 | — |
135
140
  | `references/error-classification.md` | Build error diagnosis categories A-F | step-03 (build failure), step-04 | — |
136
- | `references/parallel-execution.md` | Agent tool launch patterns, task coordination, decision matrix | step-01, step-03 (if NOT economy_mode) | — |
137
- | `references/post-checks.md` | Security + convention + architecture bash checks (MCP tools run first) | step-04 | — |
141
+ | `references/parallel-execution.md` | Agent tool launch patterns, task coordination, decision matrix | step-01, step-03c/03d (if NOT economy_mode) | — |
142
+ | `references/post-checks.md` | Compact checklist indexes checks in `references/checks/*.sh` | step-04 | — |
143
+ | `references/checks/*.sh` | Bash check scripts (security, backend, frontend, seed, architecture, infrastructure) | step-04 (executed via bash) | — |
138
144
 
139
145
  **Context propagation rule:** Files loaded in step N remain in conversation context for step N+1. Steps mark "do NOT re-read" to avoid duplicate reads.
140
146
  </reference_files>
@@ -148,7 +154,7 @@ Execute incremental SmartStack development using the APEX methodology. This skil
148
154
  - **Layer order** - Layer 0 (domain+infra+migration) → Layer 1 (seed data) → Layer 2 (backend+tests) → Layer 3 (frontend+tests) → Layer 4 (devdata)
149
155
  - **Parallel Agent tool** - Parallel execution for scan (step-01) and within Layer 2/3 (step-03) for multi-entity, unless economy_mode
150
156
  - **Tests inline** - Backend tests run after Layer 2, frontend tests run after Layer 3 (max 3 fix iterations each). Step-07 = final sweep (security + coverage).
151
- - **Exception: seed data** — The templates in core-seed-data.md and person-extension-pattern.md are generated directly because no MCP tool covers seed data creation. This is a documented exception to the "orchestrate, never generate" rule.
157
+ - **Exception: seed data** — The templates in core-seed-data.md and person-extension-pattern.md are generated directly because no MCP tool covers seed data creation yet. This is a documented exception to the "orchestrate, never generate" rule. <!-- TODO: Remove exception when MCP scaffold_seed_data (B1) is ready -->
152
158
  - **Frontend pages: ALWAYS via Skill("ui-components")** — economy_mode affects parallelization only, NOT whether /ui-components is called. NEVER generate .tsx pages directly, even in delegate or economy mode.
153
159
  - **Save outputs** if `{save_mode}` = true
154
160
  - **Commits per layer** - Atomic commits after each execution layer
@@ -156,7 +156,7 @@ Write back to {delegate_prd_path}
156
156
  | `scaffold_extension` | Create files manually following `smartstack-api.md` entity/service/controller patterns |
157
157
  | `suggest_migration` | Name format: `{context}_v{version}_{sequence}_{Description}` (see existing migrations for version) |
158
158
  | `generate_permissions` | Write `HasData()` code manually following `core-seed-data.md` permission section |
159
- | `scaffold_routes` | Create `applicationRoutes` array manually following `smartstack-frontend.md` §1 |
159
+ | `scaffold_routes` | Create `componentRegistry.generated.ts` with `PageRegistry.register()` calls manually following `smartstack-frontend.md` §1 (or legacy `applicationRoutes` array for pre-v3.7 projects) |
160
160
  | `validate_frontend_routes` | Run POST-CHECK bash scripts from `post-checks.md` |
161
161
  | `validate_security` | Run security POST-CHECKs S1-S6 from `post-checks.md` |
162
162
  | `check_migrations` | Run `dotnet ef migrations has-pending-model-changes` manually |
@@ -0,0 +1,154 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # POST-CHECK: Architecture — Clean Architecture Layer Isolation
5
+ # A1-A8: Layer boundary enforcement, DTO usage, handoff compliance
6
+
7
+ FAIL=false
8
+
9
+ # POST-CHECK A1: Domain must not import other layers (BLOCKING)
10
+ DOMAIN_FILES=$(find src/ -path "*/Domain/*" -name "*.cs" 2>/dev/null)
11
+ if [ -n "$DOMAIN_FILES" ]; then
12
+ BAD_IMPORTS=$(grep -Pn 'using\s+[\w.]*\.(Application|Infrastructure|Api)[\w.]*;' $DOMAIN_FILES 2>/dev/null || true)
13
+ if [ -n "$BAD_IMPORTS" ]; then
14
+ echo "BLOCKING: Domain layer imports Application/Infrastructure/Api — violates Clean Architecture"
15
+ echo "Domain is the core, it must not depend on any other layer"
16
+ echo "$BAD_IMPORTS"
17
+ echo "Fix: Move shared types to Domain or remove the dependency"
18
+ FAIL=true
19
+ fi
20
+ fi
21
+
22
+ # POST-CHECK A2: Application must not import Infrastructure or Api (BLOCKING)
23
+ APP_FILES=$(find src/ -path "*/Application/*" -name "*.cs" 2>/dev/null)
24
+ if [ -n "$APP_FILES" ]; then
25
+ BAD_IMPORTS=$(grep -Pn 'using\s+[\w.]*\.(Infrastructure|Api)[\w.]*;' $APP_FILES 2>/dev/null || true)
26
+ if [ -n "$BAD_IMPORTS" ]; then
27
+ echo "BLOCKING: Application layer imports Infrastructure/Api — violates Clean Architecture"
28
+ echo "Application defines interfaces, Infrastructure implements them"
29
+ echo "$BAD_IMPORTS"
30
+ echo "Fix: Define an interface in Application and implement it in Infrastructure"
31
+ FAIL=true
32
+ fi
33
+ fi
34
+
35
+ # POST-CHECK A3: Controllers must not inject DbContext (BLOCKING)
36
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
37
+ if [ -n "$CTRL_FILES" ]; then
38
+ BAD_DBCONTEXT=$(grep -Pn 'private\s+readonly\s+\w*DbContext|DbContext\s+\w+[,)]' $CTRL_FILES 2>/dev/null || true)
39
+ if [ -n "$BAD_DBCONTEXT" ]; then
40
+ echo "BLOCKING: Controller injects DbContext directly — violates Clean Architecture"
41
+ echo "Controllers must use Application services, not access the database directly"
42
+ echo "$BAD_DBCONTEXT"
43
+ echo "Fix: Create an Application service with the required business logic and inject it instead"
44
+ FAIL=true
45
+ fi
46
+ fi
47
+
48
+ # POST-CHECK A4: API must return DTOs, not Domain entities (WARNING)
49
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
50
+ ENTITY_FILES=$(find src/ -path "*/Domain/Entities/*" -name "*.cs" 2>/dev/null)
51
+ if [ -n "$CTRL_FILES" ] && [ -n "$ENTITY_FILES" ]; then
52
+ ENTITY_NAMES=$(grep -ohP 'public\s+class\s+(\w+)\s*:' $ENTITY_FILES 2>/dev/null | grep -oP '\w+(?=\s*:)' | grep -v '^public$' | sort -u || true)
53
+ for ENTITY in $ENTITY_NAMES; do
54
+ BAD_RETURN=$(grep -Pn "ActionResult<$ENTITY>|ActionResult<IEnumerable<$ENTITY>>|ActionResult<List<$ENTITY>>" $CTRL_FILES 2>/dev/null || true)
55
+ if [ -n "$BAD_RETURN" ]; then
56
+ echo "WARNING: Controller returns Domain entity '$ENTITY' instead of a DTO"
57
+ echo "$BAD_RETURN"
58
+ echo "Fix: Return ${ENTITY}ResponseDto instead of $ENTITY"
59
+ fi
60
+ done
61
+ fi
62
+
63
+ # POST-CHECK A5: Service interfaces in Application, implementations in Infrastructure (WARNING)
64
+ APP_SERVICES=$(find src/ -path "*/Application/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
65
+ if [ -n "$APP_SERVICES" ]; then
66
+ for f in $APP_SERVICES; do
67
+ if grep -q 'public class.*Service' "$f" 2>/dev/null; then
68
+ echo "WARNING: Service implementation found in Application layer: $f"
69
+ echo "Fix: Move implementation to Infrastructure/Services/. Application should only contain interfaces."
70
+ fi
71
+ done
72
+ fi
73
+
74
+ DOMAIN_INTERFACES=$(find src/ -path "*/Domain/*" -name "I*Service.cs" 2>/dev/null)
75
+ API_INTERFACES=$(find src/ -path "*/Api/*" -name "I*Service.cs" 2>/dev/null)
76
+ for f in $DOMAIN_INTERFACES $API_INTERFACES; do
77
+ if [ -n "$f" ] && grep -q 'public interface.*Service' "$f" 2>/dev/null; then
78
+ echo "WARNING: Service interface found outside Application layer: $f"
79
+ echo "Fix: Move to Application/Interfaces/"
80
+ fi
81
+ done
82
+
83
+ # POST-CHECK A6: No EF Core attributes in Domain entities (BLOCKING)
84
+ DOMAIN_FILES=$(find src/ -path "*/Domain/*" -name "*.cs" 2>/dev/null)
85
+ if [ -n "$DOMAIN_FILES" ]; then
86
+ BAD_EF=$(grep -Pn '\[Table\(|\[Column\(|\[Index\(|using\s+Microsoft\.EntityFrameworkCore' $DOMAIN_FILES 2>/dev/null || true)
87
+ if [ -n "$BAD_EF" ]; then
88
+ echo "BLOCKING: EF Core attributes or using directives found in Domain layer"
89
+ echo "Domain entities must be persistence-ignorant — EF configuration belongs in Infrastructure"
90
+ echo "$BAD_EF"
91
+ echo "Fix: Move [Table], [Column], [Index] to IEntityTypeConfiguration<T> in Infrastructure/Persistence/Configurations/"
92
+ FAIL=true
93
+ fi
94
+ fi
95
+
96
+ # POST-CHECK A7: No direct repository usage in controllers (WARNING)
97
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
98
+ if [ -n "$CTRL_FILES" ]; then
99
+ BAD_REPO=$(grep -Pn 'IRepository<|IGenericRepository<|private\s+readonly\s+IRepository|private\s+readonly\s+IGenericRepository' $CTRL_FILES 2>/dev/null || true)
100
+ if [ -n "$BAD_REPO" ]; then
101
+ echo "WARNING: Controller injects repository directly — should use Application services"
102
+ echo "$BAD_REPO"
103
+ echo "Fix: Controllers should depend on Application services (I*Service), not repositories"
104
+ fi
105
+ fi
106
+
107
+ # POST-CHECK A8: API endpoints must match handoff apiEndpointSummary (BLOCKING)
108
+ PRD_FILE=".ralph/prd.json"
109
+ if [ ! -f "$PRD_FILE" ]; then
110
+ if [ -f ".ralph/modules-queue.json" ]; then
111
+ PRD_FILE=$(cat .ralph/modules-queue.json | grep -o '"prdFile":"[^"]*"' | tail -1 | cut -d'"' -f4)
112
+ fi
113
+ fi
114
+
115
+ if [ -f "$PRD_FILE" ]; then
116
+ OPERATIONS=$(cat "$PRD_FILE" | grep -o '"operation"\s*:\s*"[^"]*"' | cut -d'"' -f4 2>/dev/null || true)
117
+
118
+ if [ -n "$OPERATIONS" ]; then
119
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
120
+ MISSING_OPS=""
121
+ TOTAL_OPS=0
122
+ FOUND_OPS=0
123
+
124
+ for op in $OPERATIONS; do
125
+ TOTAL_OPS=$((TOTAL_OPS + 1))
126
+ FOUND=false
127
+ if [ -n "$CTRL_FILES" ]; then
128
+ for f in $CTRL_FILES; do
129
+ if grep -q "$op" "$f" 2>/dev/null; then
130
+ FOUND=true
131
+ break
132
+ fi
133
+ done
134
+ fi
135
+ if [ "$FOUND" = true ]; then
136
+ FOUND_OPS=$((FOUND_OPS + 1))
137
+ else
138
+ MISSING_OPS="$MISSING_OPS $op"
139
+ fi
140
+ done
141
+
142
+ if [ -n "$MISSING_OPS" ]; then
143
+ echo "BLOCKING: API endpoints missing from controllers (handoff contract violation)"
144
+ echo "Found: $FOUND_OPS/$TOTAL_OPS operations"
145
+ echo "Missing operations:$MISSING_OPS"
146
+ echo "Fix: Implement missing endpoints in the appropriate Controller"
147
+ FAIL=true
148
+ fi
149
+ fi
150
+ fi
151
+
152
+ if [ "$FAIL" = true ]; then
153
+ exit 1
154
+ fi
@@ -0,0 +1,194 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # POST-CHECK: Backend — Entity, Service & Controller Checks
5
+ # V1-V2, C8, C12, C14, C28-C31, C54: Entity validation, API contracts, pagination, code generation
6
+
7
+ FAIL=false
8
+
9
+ # POST-CHECK V1: Controllers with POST/PUT must have corresponding Validators (BLOCKING)
10
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
11
+ if [ -n "$CTRL_FILES" ]; then
12
+ for f in $CTRL_FILES; do
13
+ HAS_WRITE=$(grep -cE "\[Http(Post|Put)\]" "$f")
14
+ if [ "$HAS_WRITE" -gt 0 ]; then
15
+ DTOS=$(grep -oP '(?:Create|Update)\w+Dto' "$f" | sort -u)
16
+ for DTO in $DTOS; do
17
+ VALIDATOR_NAME=$(echo "$DTO" | sed 's/Dto$/Validator/')
18
+ VALIDATOR_FILE=$(find src/ -path "*/Validators/*" -name "${VALIDATOR_NAME}.cs" 2>/dev/null)
19
+ if [ -z "$VALIDATOR_FILE" ]; then
20
+ echo "BLOCKING: Controller $f uses $DTO but no ${VALIDATOR_NAME}.cs found"
21
+ echo "Fix: Create Validator with FluentValidation rules from business rules"
22
+ FAIL=true
23
+ fi
24
+ done
25
+ fi
26
+ done
27
+ fi
28
+
29
+ # POST-CHECK V2: Validators must be registered in DI (WARNING)
30
+ VALIDATOR_FILES=$(find src/ -path "*/Validators/*" -name "*Validator.cs" 2>/dev/null)
31
+ if [ -n "$VALIDATOR_FILES" ]; then
32
+ DI_FILE=$(find src/ -name "DependencyInjection.cs" -o -name "ServiceCollectionExtensions.cs" 2>/dev/null | head -1)
33
+ if [ -n "$DI_FILE" ]; then
34
+ for f in $VALIDATOR_FILES; do
35
+ VALIDATOR_NAME=$(basename "$f" .cs)
36
+ if ! grep -q "$VALIDATOR_NAME" "$DI_FILE"; then
37
+ echo "WARNING: Validator $VALIDATOR_NAME not registered in DI: $DI_FILE"
38
+ fi
39
+ done
40
+ fi
41
+ fi
42
+
43
+ # POST-CHECK C8: Backend APIs must support search parameter for EntityLookup (BLOCKING)
44
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
45
+ if [ -n "$CTRL_FILES" ]; then
46
+ for f in $CTRL_FILES; do
47
+ if grep -q "\[HttpGet\]" "$f" && grep -q "GetAll" "$f"; then
48
+ if ! grep -q "search" "$f"; then
49
+ echo "BLOCKING: Controller missing search parameter on GetAll: $f"
50
+ echo "GetAll endpoints must accept ?search= to enable EntityLookup on frontend"
51
+ echo "Fix: Add [FromQuery] string? search parameter to GetAll action"
52
+ FAIL=true
53
+ fi
54
+ fi
55
+ done
56
+ fi
57
+
58
+ WEB_DIR=$(find . -name "vitest.config.ts" -o -name "vite.config.ts" 2>/dev/null | head -1 | xargs dirname 2>/dev/null)
59
+ if [ -n "$WEB_DIR" ]; then
60
+ LOOKUP_FILES=$(grep -rl "EntityLookup" "$WEB_DIR/src/pages/" "$WEB_DIR/src/components/" 2>/dev/null | grep -v node_modules | grep -v "\.test\." || true)
61
+ if [ -n "$LOOKUP_FILES" ]; then
62
+ for f in $LOOKUP_FILES; do
63
+ ENDPOINTS=$(grep -oP "apiEndpoint=['\"]([^'\"]+)['\"]" "$f" 2>/dev/null | grep -oP "['\"]([^'\"]+)['\"]" | tr -d "'" | tr -d '"' || true)
64
+ for ep in $ENDPOINTS; do
65
+ ENTITY=$(echo "$ep" | sed 's|.*/||' | sed 's/.*/\u&/')
66
+ CTRL=$(find src/ -path "*/Controllers/*${ENTITY}*Controller.cs" 2>/dev/null | head -1)
67
+ if [ -n "$CTRL" ] && ! grep -q "search" "$CTRL"; then
68
+ echo "BLOCKING: EntityLookup in $f calls $ep, but controller $CTRL does not support ?search="
69
+ echo "Fix: Add [FromQuery] string? search parameter to GetAll in $CTRL"
70
+ FAIL=true
71
+ fi
72
+ done
73
+ done
74
+ fi
75
+ fi
76
+
77
+ # POST-CHECK C12: GetAll methods must return PaginatedResult<T>
78
+ SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
79
+ if [ -n "$SERVICE_FILES" ]; then
80
+ BAD_RETURNS=$(grep -Pn '(Task<\s*(?:List|IEnumerable|IList|ICollection|IReadOnlyList|IReadOnlyCollection)<).*GetAll' $SERVICE_FILES 2>/dev/null)
81
+ if [ -n "$BAD_RETURNS" ]; then
82
+ echo "WARNING: GetAll methods must return PaginatedResult<T>, not List/IEnumerable"
83
+ echo "$BAD_RETURNS"
84
+ echo "Fix: Change return type to Task<PaginatedResult<{Entity}ResponseDto>>"
85
+ fi
86
+ fi
87
+
88
+ # POST-CHECK C14: Entities must implement IAuditableEntity + Validators must have Create/Update pairs
89
+ ENTITY_FILES=$(find src/ -path "*/Domain/Entities/*" -name "*.cs" 2>/dev/null)
90
+ if [ -n "$ENTITY_FILES" ]; then
91
+ for f in $ENTITY_FILES; do
92
+ if grep -q "ITenantEntity" "$f" && ! grep -q "IAuditableEntity" "$f"; then
93
+ echo "WARNING: Entity implements ITenantEntity but NOT IAuditableEntity: $f"
94
+ echo "Pattern: public class Entity : BaseEntity, ITenantEntity, IAuditableEntity"
95
+ fi
96
+ done
97
+ fi
98
+
99
+ CREATE_VALIDATORS=$(find src/ -path "*/Validators/*" -name "Create*Validator.cs" 2>/dev/null)
100
+ if [ -n "$CREATE_VALIDATORS" ]; then
101
+ for f in $CREATE_VALIDATORS; do
102
+ VALIDATOR_DIR=$(dirname "$f")
103
+ ENTITY_NAME=$(basename "$f" | sed 's/^Create\(.*\)Validator\.cs$/\1/')
104
+ if [ ! -f "$VALIDATOR_DIR/Update${ENTITY_NAME}Validator.cs" ]; then
105
+ echo "WARNING: Create${ENTITY_NAME}Validator exists but Update${ENTITY_NAME}Validator is missing"
106
+ echo " Found: $f"
107
+ echo " Expected: $VALIDATOR_DIR/Update${ENTITY_NAME}Validator.cs"
108
+ fi
109
+ done
110
+ fi
111
+
112
+ # POST-CHECK C28: Pagination type must be PaginatedResult<T> — no aliases (WARNING)
113
+ SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
114
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
115
+ ALL_FILES="$SERVICE_FILES $CTRL_FILES"
116
+ if [ -n "$(echo $ALL_FILES | tr -d ' ')" ]; then
117
+ BAD_NAMES=$(grep -Pn 'PagedResult<|PaginatedResultDto<|PaginatedResponse<|PageResultDto<' $ALL_FILES 2>/dev/null || true)
118
+ if [ -n "$BAD_NAMES" ]; then
119
+ echo "WARNING: Pagination type must be PaginatedResult<T> — found non-canonical names"
120
+ echo "$BAD_NAMES"
121
+ echo "FORBIDDEN type names: PagedResult, PaginatedResultDto, PaginatedResponse, PageResultDto"
122
+ echo "Fix: Use PaginatedResult<T> from SmartStack.Application.Common.Models everywhere"
123
+ fi
124
+ fi
125
+
126
+ # POST-CHECK C29: Code generation — ICodeGenerator must be registered for auto-generated entities (BLOCKING)
127
+ FEATURE_FILES=$(find docs/ -name "feature.json" 2>/dev/null)
128
+ DI_FILE=$(find src/ -name "DependencyInjection.cs" -path "*/Infrastructure/*" 2>/dev/null | head -1)
129
+ if [ -n "$FEATURE_FILES" ] && [ -n "$DI_FILE" ]; then
130
+ for FEATURE in $FEATURE_FILES; do
131
+ ENTITIES_WITH_CODE=$(python3 -c "
132
+ import json, sys
133
+ try:
134
+ with open('$FEATURE') as f:
135
+ data = json.load(f)
136
+ for e in data.get('analysis', {}).get('entities', []):
137
+ cp = e.get('codePattern', {})
138
+ if cp.get('strategy', 'manual') != 'manual':
139
+ print(e['name'])
140
+ except: pass
141
+ " 2>/dev/null || true)
142
+ for ENTITY in $ENTITIES_WITH_CODE; do
143
+ if ! grep -q "ICodeGenerator<$ENTITY>" "$DI_FILE" 2>/dev/null; then
144
+ echo "BLOCKING: Entity $ENTITY has auto-generated code pattern but ICodeGenerator<$ENTITY> is not registered in DI"
145
+ echo "Fix: Add CodeGenerator<$ENTITY> registration in DependencyInjection.cs — see references/code-generation.md"
146
+ FAIL=true
147
+ fi
148
+ done
149
+ done
150
+ fi
151
+
152
+ # POST-CHECK C30: Code regex must support hyphens (BLOCKING)
153
+ VALIDATOR_FILES=$(find src/ -path "*/Validators/*" -name "*Validator.cs" 2>/dev/null)
154
+ if [ -n "$VALIDATOR_FILES" ]; then
155
+ OLD_REGEX=$(grep -rn '\^\\[a-z0-9_\\]+\$' $VALIDATOR_FILES 2>/dev/null | grep -v '\-' || true)
156
+ if [ -n "$OLD_REGEX" ]; then
157
+ echo "BLOCKING: Code validator uses old regex without hyphen support"
158
+ echo "$OLD_REGEX"
159
+ echo "Fix: Update regex to ^[a-z0-9_-]+$ to support auto-generated codes with hyphens"
160
+ FAIL=true
161
+ fi
162
+ fi
163
+
164
+ # POST-CHECK C31: CreateDto must NOT have Code field when service uses ICodeGenerator (WARNING)
165
+ SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
166
+ if [ -n "$SERVICE_FILES" ]; then
167
+ for f in $SERVICE_FILES; do
168
+ if grep -q "ICodeGenerator" "$f"; then
169
+ ENTITY=$(basename "$f" | sed 's/Service\.cs$//')
170
+ DTO_FILE=$(find src/ -path "*/DTOs/*" -name "Create${ENTITY}Dto.cs" 2>/dev/null | head -1)
171
+ if [ -n "$DTO_FILE" ] && grep -q "public string Code" "$DTO_FILE"; then
172
+ echo "WARNING: Create${ENTITY}Dto has Code field but service uses ICodeGenerator (code is auto-generated)"
173
+ echo "Fix: Remove Code from Create${ENTITY}Dto — it is auto-generated by ICodeGenerator<${ENTITY}>"
174
+ fi
175
+ fi
176
+ done
177
+ fi
178
+
179
+ # POST-CHECK C54: No helper method calls inside .Select() on IQueryable (BLOCKING)
180
+ SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
181
+ if [ -n "$SERVICE_FILES" ]; then
182
+ BAD_SELECT=$(grep -Pn '\.Select\(\s*\w+\s*=>\s*(?!new\s)[A-Z]\w+\(|\.Select\(\s*(?!x\s*=>)[A-Z]\w+\s*\)' $SERVICE_FILES 2>/dev/null || true)
183
+ if [ -n "$BAD_SELECT" ]; then
184
+ echo "BLOCKING: Helper method call inside .Select() on IQueryable — EF Core cannot translate to SQL"
185
+ echo "$BAD_SELECT"
186
+ echo "Fix: Use inline DTO construction: .Select(x => new ResponseDto(x.Id, x.Name, x.FK.Code))"
187
+ echo " Helper methods (MapToDto, ToDto) are only safe AFTER materialization (ToListAsync, FirstAsync)"
188
+ FAIL=true
189
+ fi
190
+ fi
191
+
192
+ if [ "$FAIL" = true ]; then
193
+ exit 1
194
+ fi