@atlashub/smartstack-cli 4.74.0 → 4.76.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 (121) hide show
  1. package/dist/index.js +152 -31
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +14 -3
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/agents/ba-reader.md +17 -15
  7. package/templates/agents/ba-writer.md +49 -51
  8. package/templates/skills/apex/SKILL.md +2 -2
  9. package/templates/skills/apex/_shared.md +1 -1
  10. package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
  11. package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
  12. package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
  13. package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
  14. package/templates/skills/apex/references/core-seed-data.md +20 -18
  15. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
  16. package/templates/skills/apex/references/post-checks.md +23 -3
  17. package/templates/skills/apex/references/smartstack-api.md +4 -4
  18. package/templates/skills/apex/references/smartstack-frontend.md +54 -8
  19. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  20. package/templates/skills/apex/steps/step-00-init.md +75 -1
  21. package/templates/skills/apex/steps/step-03-execute.md +16 -4
  22. package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
  23. package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
  24. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
  25. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  26. package/templates/skills/apex-verify/SKILL.md +110 -0
  27. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  28. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  29. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
  30. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  31. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  32. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  33. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  34. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
  35. package/templates/skills/application/templates-frontend.md +2 -2
  36. package/templates/skills/business-analyse/SKILL.md +17 -3
  37. package/templates/skills/business-analyse/_shared.md +64 -0
  38. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
  39. package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
  40. package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
  41. package/templates/skills/business-analyse/questionnaire.md +86 -9
  42. package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
  43. package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
  44. package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
  45. package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
  46. package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
  47. package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
  48. package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
  49. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  50. package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
  51. package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
  52. package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
  53. package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
  54. package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
  55. package/templates/skills/business-analyse/references/portal-classification.md +52 -0
  56. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  57. package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
  58. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
  59. package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
  60. package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
  61. package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
  62. package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
  63. package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
  64. package/templates/skills/business-analyse-design/SKILL.md +10 -0
  65. package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
  66. package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
  67. package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
  68. package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
  69. package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
  70. package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
  71. package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
  72. package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
  73. package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
  74. package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
  75. package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
  76. package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
  77. package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
  78. package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
  79. package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
  80. package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
  81. package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
  82. package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
  83. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
  84. package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
  85. package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
  86. package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
  87. package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
  88. package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
  89. package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
  90. package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
  91. package/templates/skills/business-analyse-html/SKILL.md +10 -0
  92. package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
  93. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
  94. package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
  95. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  96. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  97. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +94 -36
  98. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
  99. package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
  100. package/templates/skills/business-analyse-html/html/src/template.html +2 -0
  101. package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
  102. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
  103. package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
  104. package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
  105. package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
  106. package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
  107. package/templates/skills/business-analyse-html/references/data-build.md +24 -1
  108. package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
  109. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
  110. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  111. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  112. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  113. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  114. package/templates/skills/business-analyse-review/SKILL.md +10 -0
  115. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  116. package/templates/skills/business-analyse-status/SKILL.md +8 -0
  117. package/templates/skills/dev-start/SKILL.md +143 -307
  118. package/templates/skills/efcore/SKILL.md +13 -0
  119. package/templates/skills/sketch/SKILL.md +15 -153
  120. package/templates/skills/ui-components/SKILL.md +1 -1
  121. package/templates/skills/ui-components/patterns/data-table.md +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlashub/smartstack-cli",
3
- "version": "4.74.0",
3
+ "version": "4.76.0",
4
4
  "description": "SmartStack Claude Code automation toolkit - GitFlow, EF Core migrations, prompts and more",
5
5
  "author": {
6
6
  "name": "SmartStack",
@@ -12,9 +12,10 @@ You are a business analysis reader agent specialized in extracting and synthesiz
12
12
 
13
13
  Read index.json and thematic JSON files at both application-level and module-level, and provide structured answers, context summaries, or cross-feature insights. Support discovery, documentation, and skill orchestration.
14
14
 
15
- **File locations:**
16
- - Application-level: `docs/{app}/business-analyse/v{X.Y}/index.json` + thematic files
17
- - Module-level: `docs/{app}/{module}/business-analyse/v{X.Y}/index.json` + thematic files
15
+ **File locations (ba-006):**
16
+ - Project-level: `docs/{projectSlug}/v{X.Y}/index.json` + thematic files
17
+ - Application-level: `docs/{projectSlug}/v{X.Y}/{appKebab}/index.json` + thematic files
18
+ - Module-level: `docs/{projectSlug}/v{X.Y}/{appKebab}/{moduleKebab}/index.json` + thematic files
18
19
 
19
20
  ## Core Operations
20
21
 
@@ -27,12 +28,12 @@ Locate a feature by ID and return its latest version path (index.json).
27
28
  - Optional: scope ("application" | "module" | "any") — default: "any"
28
29
 
29
30
  **Process:**
30
- 1. Glob pattern: `docs/**/business-analyse/*/index.json`
31
+ 1. Glob pattern: `docs/*/v*/index.json` AND `docs/*/v*/*/*/index.json` (project + module levels)
31
32
  2. Read each index.json and check id field
32
33
  3. If scope filter provided, also check scope field
33
34
  4. If multiple versions exist, sort numerically by version
34
35
  5. Return highest matching version path
35
- 6. If not found, check legacy `.business-analyse/` format
36
+ 6. If not found, check legacy `docs/**/business-analyse/*/index.json` format
36
37
 
37
38
  **Output:**
38
39
  - Full path to index.json
@@ -49,7 +50,7 @@ Locate the master (application-level) index.json for a given app.
49
50
  - OR featureId: FEAT-NNN
50
51
 
51
52
  **Process:**
52
- 1. Glob: `docs/{app}/business-analyse/*/index.json`
53
+ 1. Glob: `docs/*/v*/{appKebab}/index.json` (where appKebab = toKebabCase(app))
53
54
  2. Read and verify scope = "application"
54
55
  3. Return latest version path
55
56
 
@@ -244,8 +245,8 @@ Get the highest version number for a feature or module.
244
245
 
245
246
  **Process:**
246
247
  1. Glob paths:
247
- - Single feature: `docs/**/business-analyse/v*/index.json` filtered by ID
248
- - Module: `docs/{app}/{module}/business-analyse/v*/index.json`
248
+ - Single feature: `docs/*/v*/index.json` AND `docs/*/v*/*/*/index.json` filtered by ID
249
+ - Module: `docs/*/v*/{appKebab}/{moduleKebab}/index.json`
249
250
  2. Extract version numbers from index.json files (v1.0, v1.5, v2.1, etc.)
250
251
  3. Sort numerically
251
252
  4. Return highest version path and number
@@ -322,14 +323,14 @@ Generate compact context for use by other skills and agents.
322
323
 
323
324
  ## Search Priority
324
325
 
325
- When searching for feature data:
326
+ When searching for feature data (ba-006 paths):
326
327
 
327
- 1. **Primary (application):** `docs/{app}/business-analyse/*/index.json` (scope: application)
328
- 2. **Primary (module):** `docs/{app}/{module}/business-analyse/*/index.json` (scope: module)
329
- 3. **Fallback:** `.business-analyse/` (legacy format)
330
- - Read: `00-context.md` + `3-functional-specification.md`
328
+ 1. **Primary (project/app):** `docs/*/v*/index.json` (scope: project or application)
329
+ 2. **Primary (app in multi-app):** `docs/*/v*/*/index.json` (scope: application within project)
330
+ 3. **Primary (module):** `docs/*/v*/*/*/index.json` (scope: module)
331
+ 4. **Fallback:** `docs/**/business-analyse/*/index.json` (legacy pre-ba-006 format)
331
332
  - Warn user about old format
332
- - Suggest migration to new format
333
+ - Suggest re-running /business-analyse to migrate
333
334
 
334
335
  ## Cross-Reference Resolution
335
336
 
@@ -347,9 +348,10 @@ When encountering ID references, resolve them by reading appropriate thematic fi
347
348
 
348
349
  ## Error Handling
349
350
 
351
+ - **EISDIR (directory path passed to Read):** NEVER use the Read tool on a directory path. If a path does not end with `.json` or a known file extension, use Glob to list its contents first, then Read specific files. This is the #1 source of silent failures.
350
352
  - If featureId not found: suggest similar IDs or search for feature by keyword
351
353
  - If legacy format detected: show warning and offer to display legacy content
352
- - If thematic file not found: list available thematic files in the feature version directory
354
+ - If thematic file not found: list available thematic files in the feature version directory (use Glob, NOT Read)
353
355
  - If version not found: list available versions
354
356
  - Return helpful suggestions for refinement
355
357
 
@@ -12,10 +12,10 @@ You are a business analysis writer agent specialized in managing granular JSON f
12
12
 
13
13
  Write and update granular JSON files for project-level (multi-app), application-level (master), and module-level documents. Handle progressive enrichment of features as they move through analysis phases, manage versioning for refactoring, and enforce schema consistency.
14
14
 
15
- **Directory structure (3-tier hierarchy):**
16
- - Project-level (multi-app only): `docs/business-analyse/v{X.Y}/index.json` + thematic files
17
- - Application-level: `docs/{app}/business-analyse/v{X.Y}/index.json` + thematic files
18
- - Module-level: `docs/{app}/{module}/business-analyse/v{X.Y}/index.json` + thematic files
15
+ **Directory structure (ba-006, 3-tier hierarchy):**
16
+ - Project-level (multi-app only): `docs/{projectSlug}/v{X.Y}/index.json` + thematic files
17
+ - Application-level: `docs/{projectSlug}/v{X.Y}/{appKebab}/index.json` + thematic files
18
+ - Module-level: `docs/{projectSlug}/v{X.Y}/{appKebab}/{moduleKebab}/index.json` + thematic files
19
19
 
20
20
  **Thematic files (v2 granular architecture):**
21
21
  - `index.json` — metadata, version, hash manifest, module registry (ALL scopes)
@@ -31,7 +31,7 @@ Write and update granular JSON files for project-level (multi-app), application-
31
31
  - `handoff.json` — complexity, file catalog, BR-to-code mapping (**MODULE ONLY**)
32
32
  - `review.json` — preserved review comments and change summary (ALL scopes)
33
33
 
34
- > **Backward compatibility:** If only 1 application, the project level is NOT created. The application-level index.json remains the master.
34
+ > **Single-app mode:** If only 1 application, the project level is NOT created. The application-level index.json at `docs/{projectSlug}/v{X.Y}/index.json` remains the master.
35
35
 
36
36
  ## Core Operations
37
37
 
@@ -48,10 +48,11 @@ Create initial index.json and empty thematic files with metadata and draft statu
48
48
  **Process:**
49
49
  1. Read `.business-analyse/config.json` to get lastFeatureId (or lastProjectId for scope = "project")
50
50
  2. Increment FEAT-NNN (or PROJ-NNN) identifier
51
- 3. Create directory structure based on scope:
52
- - If scope = "project": `docs/business-analyse/v1.0/`
53
- - If scope = "application": `docs/{app}/business-analyse/v1.0/`
54
- - If scope = "module": `docs/{app}/{module}/business-analyse/v1.0/`
51
+ 3. Create directory structure based on scope (ba-006 paths — NO `business-analyse/` intermediate directory):
52
+ - If scope = "project": `docs/{projectSlug}/v1.0/`
53
+ - If scope = "application": `docs/{projectSlug}/v{X.Y}/{appKebab}/`
54
+ - If scope = "module": `docs/{projectSlug}/v{X.Y}/{appKebab}/{moduleKebab}/`
55
+ **IMPORTANT:** Use Bash `mkdir -p` to create directories before writing files.
55
56
  4. Generate index.json with:
56
57
  - **`$schema`**: relative path to the deployed schema file (MANDATORY — see $schema rules below)
57
58
  - id: FEAT-NNN (from config)
@@ -74,7 +75,7 @@ Create initial index.json and empty thematic files with metadata and draft statu
74
75
  a. Read master index.json (via applicationRef FEAT-NNN)
75
76
  b. Find module in modules[] where code === moduleCode
76
77
  c. Set module.indexJsonPath to the relative path of the created index.json
77
- (relative from BA root, e.g. "modules/{moduleCode}/business-analyse/v{X.Y}/index.json")
78
+ (relative from project root, e.g. "{appKebab}/{moduleKebab}/index.json")
78
79
  d. Update master index.json with new module reference
79
80
  8. Return feature ID, path, and confirmation
80
81
 
@@ -100,9 +101,9 @@ Create a project-level index.json for multi-application analysis. Only used when
100
101
  **Process:**
101
102
  1. Read `.business-analyse/config.json` to get lastProjectId (default: 0)
102
103
  2. Increment PROJ-NNN identifier
103
- 3. Create directory: `docs/business-analyse/v1.0/`
104
+ 3. Create directory: `docs/{projectSlug}/v1.0/` (use Bash `mkdir -p`)
104
105
  4. Generate index.json with:
105
- - `$schema`: `"../schemas/project-schema.json"`
106
+ - `$schema`: `"schemas/project-schema.json"`
106
107
  - id: PROJ-NNN
107
108
  - version: "1.0"
108
109
  - status: "draft"
@@ -114,7 +115,7 @@ Create a project-level index.json for multi-application analysis. Only used when
114
115
  5. Create thematic files — **EXACTLY 3 files, NO OTHERS:** cadrage.json, validation.json, consolidation.json
115
116
  **FORBIDDEN at project level:** entities.json, rules.json, usecases.json, permissions.json, screens.json, handoff.json — these exist ONLY at module level.
116
117
  6. Update `.business-analyse/config.json` with new lastProjectId
117
- 7. Deploy schemas to `docs/business-analyse/schemas/` (including project-schema.json)
118
+ 7. Deploy schemas to `docs/{projectSlug}/v{X.Y}/schemas/` (including project-schema.json)
118
119
  8. Return project ID (PROJ-NNN) and path
119
120
 
120
121
  ### enrichApplicationRegistry
@@ -521,38 +522,31 @@ Perform these structural checks before every write:
521
522
  - OPTIONAL: `indexJsonPath`
522
523
  - FORBIDDEN: missing `dependencies`/`dependents`/`sortOrder`
523
524
 
524
- ## Directory Structure
525
+ ## Directory Structure (ba-006)
525
526
 
526
527
  ```
527
- docs/business-analyse/
528
- v1.0/
529
- index.json ← PROJECT metadata
528
+ docs/{projectSlug}/
529
+ v{X.Y}/
530
+ index.json ← PROJECT metadata (or APPLICATION if single-app)
530
531
  cadrage.json
531
532
  validation.json
532
533
  consolidation.json
533
- # NO entities/rules/usecases/permissions/screens/handoff MODULE ONLY
534
-
535
- docs/{app}/business-analyse/
536
- v1.0/
537
- index.json APPLICATION metadata
538
- cadrage.json
539
- validation.json
540
- consolidation.json
541
- navigation.json ← (created by business-analyse-design)
542
- # NO entities/rules/usecases/permissions/screens/handoff — MODULE ONLY
543
-
544
- docs/{app}/{module}/business-analyse/
545
- v1.0/
546
- index.json ← MODULE metadata
547
- entities.json
548
- rules.json
549
- usecases.json
550
- permissions.json
551
- screens.json
552
- validation.json
553
- handoff.json
534
+ schemas/ Deployed JSON schemas
535
+ {appKebab}/ ← Application directory (kebab-case)
536
+ index.json ← APPLICATION metadata
537
+ {moduleKebab}/ ← Module directory (kebab-case)
538
+ index.json MODULE metadata
539
+ entities.json
540
+ rules.json
541
+ usecases.json
542
+ permissions.json
543
+ screens.json
544
+ validation.json
545
+ handoff.json
554
546
  ```
555
547
 
548
+ > **No `business-analyse/` intermediate directory.** All data lives directly under `docs/{projectSlug}/v{X.Y}/`.
549
+
556
550
  Versions are stored as separate directories. Each directory contains index.json + thematic files.
557
551
 
558
552
  ## Hash Manifest (index.json fileHashes)
@@ -639,6 +633,7 @@ Before EVERY enrichSection() call for specification or handoff sections, validat
639
633
  7. **Timestamp management** - always set metadata.updatedAt to current ISO timestamp on write
640
634
  8. **Idempotency** - calling the same operation twice with same data should produce same result
641
635
  9. **Hash updates** - calculate and store MD5 hash for every thematic file write
636
+ 10. **Directory creation** - Before writing ANY file, verify parent directory exists. If not, create it with `mkdir -p` via the Bash tool. The Write tool does NOT create parent directories automatically. This is CRITICAL — skipping this step causes silent write failures.
642
637
 
643
638
  ## File Size Management (CRITICAL)
644
639
 
@@ -677,6 +672,7 @@ if (estimatedNewSize > 500 * 1024) { // 500KB
677
672
 
678
673
  ## Error Handling
679
674
 
675
+ - **EISDIR prevention:** Before any Read operation, verify the path points to a FILE (ends with `.json`), not a directory. Use Glob to discover files in a directory, then Read individual files. Never pass a directory path to the Read tool.
680
676
  - Return clear validation errors with line numbers
681
677
  - Suggest fixes for schema violations
682
678
  - Prevent writing invalid JSON
@@ -686,29 +682,31 @@ if (estimatedNewSize > 500 * 1024) { // 500KB
686
682
  ## $schema Reference Rules (MANDATORY)
687
683
 
688
684
  > **Every index.json MUST include a `$schema` field** pointing to the deployed schema file via relative path.
689
- > Schemas are deployed by step-00-init to: `docs/{app}/business-analyse/schemas/`
685
+ > Schemas are deployed by step-00-init to: `docs/{projectSlug}/v{X.Y}/schemas/`
690
686
 
691
- **$schema paths by scope:**
687
+ **$schema paths by scope (ba-006):**
692
688
 
693
689
  | Scope | index.json location | $schema value |
694
690
  |-------|----------------------|---------------|
695
- | project | `docs/business-analyse/v1.0/index.json` | `"../schemas/project-schema.json"` |
696
- | application | `docs/{app}/business-analyse/v1.0/index.json` | `"../schemas/application-schema.json"` |
697
- | module | `docs/{app}/{module}/business-analyse/v1.0/index.json` | `"../../../business-analyse/schemas/feature-schema.json"` |
691
+ | project | `docs/{projectSlug}/v{X.Y}/index.json` | `"schemas/project-schema.json"` |
692
+ | application (single-app) | `docs/{projectSlug}/v{X.Y}/index.json` | `"schemas/application-schema.json"` |
693
+ | application (multi-app) | `docs/{projectSlug}/v{X.Y}/{appKebab}/index.json` | `"../schemas/application-schema.json"` |
694
+ | module | `docs/{projectSlug}/v{X.Y}/{appKebab}/{moduleKebab}/index.json` | `"../../schemas/feature-schema.json"` |
698
695
 
699
696
  **Rules:**
700
697
  - `$schema` is ALWAYS the FIRST field in index.json (before `id`)
701
- - Path is RELATIVE from the index.json file to the schemas directory
702
- - For application scope: go up 1 level (v1.0/) → into schemas/
703
- - For module scope: go up 3 levels (v1.0/ → business-analyse/ → {module}/) → into business-analyse/schemas/
698
+ - Path is RELATIVE from the index.json file to the schemas directory at `docs/{projectSlug}/v{X.Y}/schemas/`
699
+ - For project/single-app: schemas/ is in the same directory
700
+ - For multi-app application: go up 1 level ({appKebab}/) → schemas/
701
+ - For module: go up 2 levels ({moduleKebab}/ → {appKebab}/) → schemas/
704
702
  - If schemas directory not found → WARNING (schemas may not have been deployed by step-00)
705
703
 
706
704
  ## Example index.json Structure
707
705
 
708
- **Module-level:**
706
+ **Module-level:** (`docs/{projectSlug}/v{X.Y}/{appKebab}/{moduleKebab}/index.json`)
709
707
  ```json
710
708
  {
711
- "$schema": "../../../business-analyse/schemas/feature-schema.json",
709
+ "$schema": "../../schemas/feature-schema.json",
712
710
  "id": "FEAT-001",
713
711
  "version": "1.0",
714
712
  "status": "draft",
@@ -743,7 +741,7 @@ if (estimatedNewSize > 500 * 1024) { // 500KB
743
741
  }
744
742
  ```
745
743
 
746
- **Application-level:**
744
+ **Application-level:** (`docs/{projectSlug}/v{X.Y}/{appKebab}/index.json` or `docs/{projectSlug}/v{X.Y}/index.json` for single-app)
747
745
  ```json
748
746
  {
749
747
  "$schema": "../schemas/application-schema.json",
@@ -778,10 +776,10 @@ if (estimatedNewSize > 500 * 1024) { // 500KB
778
776
  }
779
777
  ```
780
778
 
781
- **Project-level (multi-app only):**
779
+ **Project-level (multi-app only):** (`docs/{projectSlug}/v{X.Y}/index.json`)
782
780
  ```json
783
781
  {
784
- "$schema": "../schemas/project-schema.json",
782
+ "$schema": "schemas/project-schema.json",
785
783
  "id": "PROJ-001",
786
784
  "version": "1.0",
787
785
  "status": "draft",
@@ -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
@@ -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 `componentRegistry.generated.ts` with `PageRegistry.register()` calls manually following `smartstack-frontend.md` §1 (or legacy `applicationRoutes` array for pre-v3.7 projects) |
159
+ | `scaffold_routes` | Create `componentRegistry.generated.ts` manually. Keys MUST use DOT notation: `PageRegistry.register('{app_code}.{module_code}.{section_code}', lazy(...))`. Example: `PageRegistry.register('rh.employes.list', lazy(() => import('../pages/Rh/Employes/EmployeesListPage')));` — NEVER use hyphens as hierarchy separator. For client projects: `import { PageRegistry } from '@atlashub/smartstack';`. For SmartStack core: `import { PageRegistry } from '@/extensions/PageRegistry';`. See `frontend-route-wiring-app-tsx.md` §Client SDK |
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 |
@@ -2,7 +2,7 @@
2
2
  set -euo pipefail
3
3
 
4
4
  # POST-CHECK: Backend — Entity, Service & Controller Checks
5
- # V1-V2, C8, C12, C14, C28-C31, C54: Entity validation, API contracts, pagination, code generation
5
+ # V1-V2, C8, C12, C14, C28-C31, C54, C62: Entity validation, API contracts, pagination, code generation, tenant exceptions
6
6
 
7
7
  FAIL=false
8
8
 
@@ -10,9 +10,9 @@ FAIL=false
10
10
  CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
11
11
  if [ -n "$CTRL_FILES" ]; then
12
12
  for f in $CTRL_FILES; do
13
- HAS_WRITE=$(grep -cE "\[Http(Post|Put)\]" "$f")
13
+ HAS_WRITE=$(grep -cE "\[Http(Post|Put)\]" "$f" || true)
14
14
  if [ "$HAS_WRITE" -gt 0 ]; then
15
- DTOS=$(grep -oP '(?:Create|Update)\w+Dto' "$f" | sort -u)
15
+ DTOS=$(grep -oE '(Create|Update)[A-Za-z0-9_]+Dto' "$f" | sort -u || true)
16
16
  for DTO in $DTOS; do
17
17
  VALIDATOR_NAME=$(echo "$DTO" | sed 's/Dto$/Validator/')
18
18
  VALIDATOR_FILE=$(find src/ -path "*/Validators/*" -name "${VALIDATOR_NAME}.cs" 2>/dev/null)
@@ -60,7 +60,7 @@ if [ -n "$WEB_DIR" ]; then
60
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
61
  if [ -n "$LOOKUP_FILES" ]; then
62
62
  for f in $LOOKUP_FILES; do
63
- ENDPOINTS=$(grep -oP "apiEndpoint=['\"]([^'\"]+)['\"]" "$f" 2>/dev/null | grep -oP "['\"]([^'\"]+)['\"]" | tr -d "'" | tr -d '"' || true)
63
+ ENDPOINTS=$(grep -oE "apiEndpoint=['\"][^'\"]+['\"]" "$f" 2>/dev/null | sed "s/apiEndpoint=//;s/['\"]//g" || true)
64
64
  for ep in $ENDPOINTS; do
65
65
  ENTITY=$(echo "$ep" | sed 's|.*/||' | sed 's/.*/\u&/')
66
66
  CTRL=$(find src/ -path "*/Controllers/*${ENTITY}*Controller.cs" 2>/dev/null | head -1)
@@ -77,7 +77,7 @@ fi
77
77
  # POST-CHECK C12: GetAll methods must return PaginatedResult<T>
78
78
  SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
79
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)
80
+ BAD_RETURNS=$(grep -nE 'Task<\s*(List|IEnumerable|IList|ICollection|IReadOnlyList|IReadOnlyCollection)<.*GetAll' $SERVICE_FILES 2>/dev/null || true)
81
81
  if [ -n "$BAD_RETURNS" ]; then
82
82
  echo "WARNING: GetAll methods must return PaginatedResult<T>, not List/IEnumerable"
83
83
  echo "$BAD_RETURNS"
@@ -114,7 +114,7 @@ SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Se
114
114
  CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
115
115
  ALL_FILES="$SERVICE_FILES $CTRL_FILES"
116
116
  if [ -n "$(echo $ALL_FILES | tr -d ' ')" ]; then
117
- BAD_NAMES=$(grep -Pn 'PagedResult<|PaginatedResultDto<|PaginatedResponse<|PageResultDto<' $ALL_FILES 2>/dev/null || true)
117
+ BAD_NAMES=$(grep -nE 'PagedResult<|PaginatedResultDto<|PaginatedResponse<|PageResultDto<' $ALL_FILES 2>/dev/null || true)
118
118
  if [ -n "$BAD_NAMES" ]; then
119
119
  echo "WARNING: Pagination type must be PaginatedResult<T> — found non-canonical names"
120
120
  echo "$BAD_NAMES"
@@ -179,7 +179,7 @@ fi
179
179
  # POST-CHECK C54: No helper method calls inside .Select() on IQueryable (BLOCKING)
180
180
  SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
181
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)
182
+ BAD_SELECT=$(grep -nE '\.Select\([[:space:]]*[a-z]+[[:space:]]*=>[[:space:]]*[A-Z][A-Za-z0-9_]+\(' $SERVICE_FILES 2>/dev/null | grep -v '=>\s*new\s' || true)
183
183
  if [ -n "$BAD_SELECT" ]; then
184
184
  echo "BLOCKING: Helper method call inside .Select() on IQueryable — EF Core cannot translate to SQL"
185
185
  echo "$BAD_SELECT"
@@ -189,6 +189,20 @@ if [ -n "$SERVICE_FILES" ]; then
189
189
  fi
190
190
  fi
191
191
 
192
+ # POST-CHECK C62: Services must use TenantContextRequiredException, NOT InvalidOperationException for tenant (WARNING)
193
+ SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
194
+ if [ -n "$SERVICE_FILES" ]; then
195
+ for f in $SERVICE_FILES; do
196
+ BAD_TENANT=$(grep -nE 'throw new InvalidOperationException.*[Tt]enant' "$f" 2>/dev/null || true)
197
+ if [ -n "$BAD_TENANT" ]; then
198
+ echo "WARNING: Service uses InvalidOperationException for tenant check (returns 500 instead of 400): $f"
199
+ echo " $BAD_TENANT"
200
+ echo " Fix: Use 'throw new TenantContextRequiredException()' instead"
201
+ echo " TenantContextRequiredException returns 400 via GlobalExceptionHandlerMiddleware"
202
+ fi
203
+ done
204
+ fi
205
+
192
206
  if [ "$FAIL" = true ]; then
193
207
  exit 1
194
208
  fi
@@ -443,6 +443,32 @@ if [ -n "$APP_TSX" ]; then
443
443
  fi
444
444
  fi
445
445
 
446
+ # POST-CHECK C64: ListPages must have Create/New button (BLOCKING)
447
+ # Every ListPage must contain a navigate('create') or Link to="create" for the New button.
448
+ # Without it, users cannot access the create form (broken CRUD workflow).
449
+ # Note: C5 catches dead links (navigate exists but page missing). C64 catches missing buttons entirely.
450
+ LIST_PAGES=$(find web/ src/pages/ -name "*ListPage.tsx" 2>/dev/null | grep -v node_modules | grep -v "\.test\." | grep -v "\.spec\." || true)
451
+ if [ -n "$LIST_PAGES" ]; then
452
+ FAIL_C64=false
453
+ for LP in $LIST_PAGES; do
454
+ # Skip dashboard-like list pages (no CRUD expected)
455
+ if echo "$LP" | grep -qiP "dashboard|calendar|approve|balance"; then
456
+ continue
457
+ fi
458
+ HAS_CREATE_NAV=$(grep -P "navigate\(.*['\"\`]create['\"\`]|navigate\(.*['\"\`].*\/create['\"\`]|to=['\"\`]create['\"\`]|to=\{.*create.*\}" "$LP" 2>/dev/null || true)
459
+ if [ -z "$HAS_CREATE_NAV" ]; then
460
+ echo "BLOCKING: ListPage missing Create/New button: $LP"
461
+ echo " Every ListPage MUST have a 'New' button with navigate('create') in the page header."
462
+ echo " Without it, users cannot access the create form — CRUD workflow is broken."
463
+ echo " Fix: Add to page header: <button onClick={() => navigate('create')}>New</button>"
464
+ FAIL_C64=true
465
+ fi
466
+ done
467
+ if [ "$FAIL_C64" = true ]; then
468
+ FAIL=true
469
+ fi
470
+ fi
471
+
446
472
  if [ "$FAIL" = true ]; then
447
473
  exit 1
448
474
  fi
@@ -2,7 +2,7 @@
2
2
  set -euo pipefail
3
3
 
4
4
  # POST-CHECK: Infrastructure — Migration & Build
5
- # C13, C38-C43, C50-C51, C56: Database migrations, DI registration, routing uniqueness
5
+ # C13, C38-C43, C50-C51, C56, C60-C61: Database migrations, DI registration, routing uniqueness, MCP compliance
6
6
 
7
7
  FAIL=false
8
8
 
@@ -27,10 +27,10 @@ fi
27
27
  SNAPSHOT=$(find src/ -name "*ModelSnapshot.cs" -path "*/Migrations/*" 2>/dev/null | head -1)
28
28
  DBCONTEXT=$(find src/ -name "*DbContext.cs" -path "*/Persistence/*" ! -name "*DesignTime*" 2>/dev/null | head -1)
29
29
  if [ -n "$SNAPSHOT" ] && [ -n "$DBCONTEXT" ]; then
30
- DBSET_ENTITIES=$(grep -oP 'DbSet<(\w+)>' "$DBCONTEXT" 2>/dev/null | grep -oP '<\K\w+(?=>)' | sort -u || true)
30
+ DBSET_ENTITIES=$(grep -o 'DbSet<[A-Za-z0-9_]*>' "$DBCONTEXT" 2>/dev/null | sed 's/DbSet<//;s/>//' | sort -u || true)
31
31
  FAIL_C38=false
32
32
  for ENTITY in $DBSET_ENTITIES; do
33
- if echo "$ENTITY" | grep -qP '^(Navigation|Tenant|User|Role|Permission|AuditLog|ApplicationTracking)'; then
33
+ if echo "$ENTITY" | grep -qE '^(Navigation|Tenant|User|Role|Permission|AuditLog|ApplicationTracking)'; then
34
34
  continue
35
35
  fi
36
36
  if ! grep -q "Entity<$ENTITY>" "$SNAPSHOT" 2>/dev/null; then
@@ -103,7 +103,7 @@ DTO_FILES=$(find src/ -name "*Dto.cs" -path "*/DTOs/*" 2>/dev/null)
103
103
  if [ -n "$DTO_FILES" ]; then
104
104
  FAIL_C41=false
105
105
  for f in $DTO_FILES; do
106
- BAD_DATES=$(grep -Pn 'string\??\s+\w*[Dd]ate\w*\s*[{;,]' "$f" 2>/dev/null | grep -vi "Updated\|Created\|format\|pattern\|string\|parse" || true)
106
+ BAD_DATES=$(grep -nE 'string\??\s+[A-Za-z0-9_]*[Dd]ate[A-Za-z0-9_]*\s*[{;,]' "$f" 2>/dev/null | grep -vi "Updated\|Created\|format\|pattern\|string\|parse" || true)
107
107
  if [ -n "$BAD_DATES" ]; then
108
108
  echo "WARNING: DTO has string type for date field — must use DateOnly: $f"
109
109
  echo "$BAD_DATES"
@@ -114,7 +114,7 @@ if [ -n "$DTO_FILES" ]; then
114
114
  fi
115
115
 
116
116
  # POST-CHECK C42: Every module with entities must have a migration covering them (BLOCKING)
117
- ENTITY_FILES=$(find src/ -path "*/Domain/Entities/*" -name "*.cs" 2>/dev/null | grep -v test)
117
+ ENTITY_FILES=$(find src/ -path "*/Domain/Entities/*" -name "*.cs" 2>/dev/null | grep -v test || true)
118
118
  MIGRATION_DIR=$(find src/ -path "*/Migrations" -type d 2>/dev/null | head -1)
119
119
  if [ -n "$ENTITY_FILES" ] && [ -n "$MIGRATION_DIR" ]; then
120
120
  MIGRATION_FILES=$(find "$MIGRATION_DIR" -name "*.cs" ! -name "*ModelSnapshot*" ! -name "*DesignTime*" 2>/dev/null)
@@ -125,7 +125,7 @@ if [ -n "$ENTITY_FILES" ] && [ -n "$MIGRATION_DIR" ]; then
125
125
  FAIL_C42=false
126
126
  for EF in $ENTITY_FILES; do
127
127
  ENTITY_NAME=$(basename "$EF" .cs)
128
- if grep -qP '^\s*(public\s+)?(abstract|interface)\s' "$EF" 2>/dev/null; then continue; fi
128
+ if grep -qE '^\s*(public\s+)?(abstract|interface)\s' "$EF" 2>/dev/null; then continue; fi
129
129
  FOUND=$(grep -l "$ENTITY_NAME" $MIGRATION_FILES 2>/dev/null || true)
130
130
  if [ -z "$FOUND" ]; then
131
131
  echo "BLOCKING: Entity '$ENTITY_NAME' ($EF) not found in any migration file"
@@ -148,8 +148,8 @@ if [ -n "$CTRL_FILES" ]; then
148
148
  HAS_NAVROUTE=$(grep -c '\[NavRoute(' "$f" 2>/dev/null || true)
149
149
  HAS_ROUTE=$(grep -c '\[Route(' "$f" 2>/dev/null || true)
150
150
  if [ "$HAS_NAVROUTE" -gt 0 ] && [ "$HAS_ROUTE" -gt 0 ]; then
151
- NAVROUTE_VAL=$(grep -oP 'NavRoute\("([^"]+)"' "$f" 2>/dev/null | head -1)
152
- ROUTE_VAL=$(grep -oP 'Route\("([^"]+)"' "$f" 2>/dev/null | head -1)
151
+ NAVROUTE_VAL=$(grep -o 'NavRoute("[^"]*"' "$f" 2>/dev/null | head -1 || true)
152
+ ROUTE_VAL=$(grep -o 'Route("[^"]*"' "$f" 2>/dev/null | head -1 || true)
153
153
  echo "BLOCKING: Controller has BOTH [Route] and [NavRoute] — remove [Route]: $f"
154
154
  echo " Found: [$ROUTE_VAL] + [$NAVROUTE_VAL]"
155
155
  echo " In SmartStack, [NavRoute] resolves routes dynamically from the database."
@@ -166,7 +166,7 @@ fi
166
166
  # POST-CHECK C50: NavRoute Uniqueness — no duplicate NavRoute values (BLOCKING)
167
167
  CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
168
168
  if [ -n "$CTRL_FILES" ]; then
169
- NAVROUTES=$(grep -Pn '\[NavRoute\("([^"]+)"\)' $CTRL_FILES 2>/dev/null | sed 's/.*\[NavRoute("\([^"]*\)").*/\1/' | sort || true)
169
+ NAVROUTES=$(grep -n '\[NavRoute("' $CTRL_FILES 2>/dev/null | sed 's/.*\[NavRoute("\([^"]*\)".*/\1/' | sort || true)
170
170
  DUPLICATES=$(echo "$NAVROUTES" | uniq -d || true)
171
171
  if [ -n "$DUPLICATES" ]; then
172
172
  echo "BLOCKING: Duplicate NavRoute values detected:"
@@ -185,7 +185,7 @@ fi
185
185
  CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
186
186
  if [ -n "$CTRL_FILES" ]; then
187
187
  for f in $CTRL_FILES; do
188
- NAVROUTE=$(grep -oP '\[NavRoute\("\K[^"]+' "$f")
188
+ NAVROUTE=$(grep -o 'NavRoute("[^"]*"' "$f" 2>/dev/null | sed 's/NavRoute("//;s/"//' | head -1 || true)
189
189
  if [ -z "$NAVROUTE" ]; then continue; fi
190
190
 
191
191
  DOTS=$(echo "$NAVROUTE" | tr -cd '.' | wc -c)
@@ -250,6 +250,43 @@ if [ -n "${ENTITIES:-}" ] && [ -n "${SECTIONS:-}" ]; then
250
250
  fi
251
251
  fi
252
252
 
253
+ # POST-CHECK C60: Controllers must inject ISender _mediator, NOT direct business services (BLOCKING)
254
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
255
+ if [ -n "$CTRL_FILES" ]; then
256
+ FAIL_C60=false
257
+ for f in $CTRL_FILES; do
258
+ # Detect direct business service injection (I{Entity}Service pattern)
259
+ # Exclude infrastructure services: ICurrentTenantService, ICurrentUserService, IStringLocalizer
260
+ DIRECT_SVC=$(grep -E 'private readonly I[A-Za-z0-9_]+Service ' "$f" 2>/dev/null | grep -v 'ICurrentTenantService\|ICurrentUserService\|IStringLocalizer' || true)
261
+ if [ -n "$DIRECT_SVC" ] && ! grep -q 'ISender' "$f"; then
262
+ echo "BLOCKING: Controller injects business service directly instead of ISender _mediator: $f"
263
+ echo " Found: $(echo "$DIRECT_SVC" | head -1 | xargs)"
264
+ echo " SmartStack convention: Controllers use MediatR (ISender) for CQRS dispatch"
265
+ echo " Fix: Regenerate via mcp__smartstack__scaffold_extension(type: 'controller')"
266
+ FAIL_C60=true
267
+ fi
268
+ done
269
+ if [ "$FAIL_C60" = true ]; then
270
+ FAIL=true
271
+ fi
272
+ fi
273
+
274
+ # POST-CHECK C61: Controllers with [NavRoute] must have [Authorize] attribute (BLOCKING)
275
+ CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
276
+ if [ -n "$CTRL_FILES" ]; then
277
+ FAIL_C61=false
278
+ for f in $CTRL_FILES; do
279
+ if grep -q '\[NavRoute' "$f" && ! grep -q '\[.*Authorize\]' "$f"; then
280
+ echo "BLOCKING: Controller with [NavRoute] missing [Authorize]: $f"
281
+ echo " Fix: Add [Microsoft.AspNetCore.Authorization.Authorize] on class"
282
+ FAIL_C61=true
283
+ fi
284
+ done
285
+ if [ "$FAIL_C61" = true ]; then
286
+ FAIL=true
287
+ fi
288
+ fi
289
+
253
290
  if [ "$FAIL" = true ]; then
254
291
  exit 1
255
292
  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,50 @@ 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
+
534
574
  if [ "$FAIL" = true ]; then
535
575
  exit 1
536
576
  fi