@tailor-platform/erp-kit 0.5.1 → 0.6.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 (251) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli.mjs +103 -23
  3. package/package.json +1 -1
  4. package/skills/erp-kit-app-5-impl-backend/SKILL.md +7 -4
  5. package/skills/erp-kit-app-7-impl-review/SKILL.md +1 -1
  6. package/skills/erp-kit-module-6-impl-review/SKILL.md +39 -17
  7. package/src/commands/generate-doc.ts +1 -1
  8. package/src/commands/lib/discovery.test.ts +13 -3
  9. package/src/commands/lib/discovery.ts +10 -2
  10. package/src/commands/lib/paths.ts +4 -2
  11. package/src/commands/lib/sync-check-tests.test.ts +84 -6
  12. package/src/commands/lib/sync-check-tests.ts +63 -3
  13. package/src/commands/sync-check.ts +7 -3
  14. package/src/generator/generate-app-code.ts +51 -16
  15. package/src/generator/generate-stubs.ts +4 -0
  16. package/src/generator/stub-templates.test.ts +11 -0
  17. package/src/generator/stub-templates.ts +22 -1
  18. package/src/modules/inventory/docs/features/inventory-adjustment.md +2 -1
  19. package/src/modules/inventory/docs/features/scrap-management.md +39 -1
  20. package/src/modules/manufacturing/README.md +63 -0
  21. package/src/modules/manufacturing/command/.gitkeep +0 -0
  22. package/src/modules/manufacturing/command/activateBillOfMaterial.generated.ts +6 -0
  23. package/src/modules/manufacturing/command/activateBillOfMaterial.test.ts +166 -0
  24. package/src/modules/manufacturing/command/activateBillOfMaterial.ts +173 -0
  25. package/src/modules/manufacturing/command/activateRouting.generated.ts +6 -0
  26. package/src/modules/manufacturing/command/activateRouting.test.ts +152 -0
  27. package/src/modules/manufacturing/command/activateRouting.ts +92 -0
  28. package/src/modules/manufacturing/command/activateWorkCenter.generated.ts +6 -0
  29. package/src/modules/manufacturing/command/activateWorkCenter.test.ts +135 -0
  30. package/src/modules/manufacturing/command/activateWorkCenter.ts +91 -0
  31. package/src/modules/manufacturing/command/cancelProductionOrder.generated.ts +6 -0
  32. package/src/modules/manufacturing/command/cancelProductionOrder.test.ts +151 -0
  33. package/src/modules/manufacturing/command/cancelProductionOrder.ts +114 -0
  34. package/src/modules/manufacturing/command/closeProductionOrder.generated.ts +6 -0
  35. package/src/modules/manufacturing/command/closeProductionOrder.test.ts +126 -0
  36. package/src/modules/manufacturing/command/closeProductionOrder.ts +87 -0
  37. package/src/modules/manufacturing/command/completeProductionOrder.generated.ts +6 -0
  38. package/src/modules/manufacturing/command/completeProductionOrder.test.ts +132 -0
  39. package/src/modules/manufacturing/command/completeProductionOrder.ts +97 -0
  40. package/src/modules/manufacturing/command/completeWorkOrder.generated.ts +6 -0
  41. package/src/modules/manufacturing/command/completeWorkOrder.test.ts +369 -0
  42. package/src/modules/manufacturing/command/completeWorkOrder.ts +212 -0
  43. package/src/modules/manufacturing/command/createBillOfMaterial.generated.ts +6 -0
  44. package/src/modules/manufacturing/command/createBillOfMaterial.test.ts +210 -0
  45. package/src/modules/manufacturing/command/createBillOfMaterial.ts +176 -0
  46. package/src/modules/manufacturing/command/createProductionOrder.generated.ts +6 -0
  47. package/src/modules/manufacturing/command/createProductionOrder.test.ts +160 -0
  48. package/src/modules/manufacturing/command/createProductionOrder.ts +129 -0
  49. package/src/modules/manufacturing/command/createRouting.generated.ts +6 -0
  50. package/src/modules/manufacturing/command/createRouting.test.ts +168 -0
  51. package/src/modules/manufacturing/command/createRouting.ts +128 -0
  52. package/src/modules/manufacturing/command/createWorkCenter.generated.ts +6 -0
  53. package/src/modules/manufacturing/command/createWorkCenter.test.ts +148 -0
  54. package/src/modules/manufacturing/command/createWorkCenter.ts +131 -0
  55. package/src/modules/manufacturing/command/deactivateBillOfMaterial.generated.ts +6 -0
  56. package/src/modules/manufacturing/command/deactivateBillOfMaterial.test.ts +103 -0
  57. package/src/modules/manufacturing/command/deactivateBillOfMaterial.ts +78 -0
  58. package/src/modules/manufacturing/command/deactivateRouting.generated.ts +6 -0
  59. package/src/modules/manufacturing/command/deactivateRouting.test.ts +112 -0
  60. package/src/modules/manufacturing/command/deactivateRouting.ts +76 -0
  61. package/src/modules/manufacturing/command/deactivateWorkCenter.generated.ts +6 -0
  62. package/src/modules/manufacturing/command/deactivateWorkCenter.test.ts +113 -0
  63. package/src/modules/manufacturing/command/deactivateWorkCenter.ts +85 -0
  64. package/src/modules/manufacturing/command/pauseWorkOrder.generated.ts +6 -0
  65. package/src/modules/manufacturing/command/pauseWorkOrder.test.ts +118 -0
  66. package/src/modules/manufacturing/command/pauseWorkOrder.ts +82 -0
  67. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.generated.ts +6 -0
  68. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.test.ts +183 -0
  69. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.ts +139 -0
  70. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.generated.ts +6 -0
  71. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.test.ts +120 -0
  72. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.ts +110 -0
  73. package/src/modules/manufacturing/command/releaseProductionOrder.generated.ts +6 -0
  74. package/src/modules/manufacturing/command/releaseProductionOrder.test.ts +220 -0
  75. package/src/modules/manufacturing/command/releaseProductionOrder.ts +450 -0
  76. package/src/modules/manufacturing/command/reopenProductionOrder.generated.ts +6 -0
  77. package/src/modules/manufacturing/command/reopenProductionOrder.test.ts +196 -0
  78. package/src/modules/manufacturing/command/reopenProductionOrder.ts +98 -0
  79. package/src/modules/manufacturing/command/reportWorkOrderProgress.generated.ts +6 -0
  80. package/src/modules/manufacturing/command/reportWorkOrderProgress.test.ts +204 -0
  81. package/src/modules/manufacturing/command/reportWorkOrderProgress.ts +129 -0
  82. package/src/modules/manufacturing/command/rescheduleProductionOrder.generated.ts +6 -0
  83. package/src/modules/manufacturing/command/rescheduleProductionOrder.test.ts +185 -0
  84. package/src/modules/manufacturing/command/rescheduleProductionOrder.ts +95 -0
  85. package/src/modules/manufacturing/command/resumeWorkOrder.generated.ts +6 -0
  86. package/src/modules/manufacturing/command/resumeWorkOrder.test.ts +122 -0
  87. package/src/modules/manufacturing/command/resumeWorkOrder.ts +94 -0
  88. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.generated.ts +6 -0
  89. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.test.ts +231 -0
  90. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.ts +137 -0
  91. package/src/modules/manufacturing/command/startWorkOrder.generated.ts +6 -0
  92. package/src/modules/manufacturing/command/startWorkOrder.test.ts +118 -0
  93. package/src/modules/manufacturing/command/startWorkOrder.ts +126 -0
  94. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.generated.ts +6 -0
  95. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.test.ts +153 -0
  96. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.ts +106 -0
  97. package/src/modules/manufacturing/command/unreleaseProductionOrder.generated.ts +6 -0
  98. package/src/modules/manufacturing/command/unreleaseProductionOrder.test.ts +140 -0
  99. package/src/modules/manufacturing/command/unreleaseProductionOrder.ts +131 -0
  100. package/src/modules/manufacturing/command/updateBillOfMaterial.generated.ts +6 -0
  101. package/src/modules/manufacturing/command/updateBillOfMaterial.test.ts +149 -0
  102. package/src/modules/manufacturing/command/updateBillOfMaterial.ts +174 -0
  103. package/src/modules/manufacturing/command/updateProductionOrder.generated.ts +6 -0
  104. package/src/modules/manufacturing/command/updateProductionOrder.test.ts +112 -0
  105. package/src/modules/manufacturing/command/updateProductionOrder.ts +145 -0
  106. package/src/modules/manufacturing/command/updateRouting.generated.ts +6 -0
  107. package/src/modules/manufacturing/command/updateRouting.test.ts +211 -0
  108. package/src/modules/manufacturing/command/updateRouting.ts +124 -0
  109. package/src/modules/manufacturing/command/updateWorkCenter.generated.ts +6 -0
  110. package/src/modules/manufacturing/command/updateWorkCenter.test.ts +152 -0
  111. package/src/modules/manufacturing/command/updateWorkCenter.ts +137 -0
  112. package/src/modules/manufacturing/db/.gitkeep +0 -0
  113. package/src/modules/manufacturing/db/billOfMaterial.ts +70 -0
  114. package/src/modules/manufacturing/db/billOfMaterialLine.ts +49 -0
  115. package/src/modules/manufacturing/db/costVarianceLine.ts +53 -0
  116. package/src/modules/manufacturing/db/manufacturingCostLine.ts +35 -0
  117. package/src/modules/manufacturing/db/manufacturingCostSettlementRecord.ts +39 -0
  118. package/src/modules/manufacturing/db/manufacturingCostSummary.ts +59 -0
  119. package/src/modules/manufacturing/db/productionOrder.ts +83 -0
  120. package/src/modules/manufacturing/db/productionOrderBomSnapshot.ts +44 -0
  121. package/src/modules/manufacturing/db/productionOrderCostBaseline.ts +44 -0
  122. package/src/modules/manufacturing/db/productionOrderMaterialRequirement.ts +57 -0
  123. package/src/modules/manufacturing/db/productionOrderRoutingSnapshot.ts +43 -0
  124. package/src/modules/manufacturing/db/routing.ts +63 -0
  125. package/src/modules/manufacturing/db/routingOperation.ts +57 -0
  126. package/src/modules/manufacturing/db/workCenter.ts +87 -0
  127. package/src/modules/manufacturing/db/workOrder.ts +65 -0
  128. package/src/modules/manufacturing/db/workOrderExecutionEvent.ts +54 -0
  129. package/src/modules/manufacturing/docs/commands/ActivateBillOfMaterial.md +50 -0
  130. package/src/modules/manufacturing/docs/commands/ActivateRouting.md +48 -0
  131. package/src/modules/manufacturing/docs/commands/ActivateWorkCenter.md +49 -0
  132. package/src/modules/manufacturing/docs/commands/CancelProductionOrder.md +48 -0
  133. package/src/modules/manufacturing/docs/commands/CloseProductionOrder.md +46 -0
  134. package/src/modules/manufacturing/docs/commands/CompleteProductionOrder.md +48 -0
  135. package/src/modules/manufacturing/docs/commands/CompleteWorkOrder.md +66 -0
  136. package/src/modules/manufacturing/docs/commands/CreateBillOfMaterial.md +54 -0
  137. package/src/modules/manufacturing/docs/commands/CreateProductionOrder.md +49 -0
  138. package/src/modules/manufacturing/docs/commands/CreateRouting.md +50 -0
  139. package/src/modules/manufacturing/docs/commands/CreateWorkCenter.md +51 -0
  140. package/src/modules/manufacturing/docs/commands/DeactivateBillOfMaterial.md +45 -0
  141. package/src/modules/manufacturing/docs/commands/DeactivateRouting.md +45 -0
  142. package/src/modules/manufacturing/docs/commands/DeactivateWorkCenter.md +45 -0
  143. package/src/modules/manufacturing/docs/commands/PauseWorkOrder.md +44 -0
  144. package/src/modules/manufacturing/docs/commands/RecordInventoryIssueOutcome.md +59 -0
  145. package/src/modules/manufacturing/docs/commands/RecordManufacturingCostSettlementAcknowledgment.md +49 -0
  146. package/src/modules/manufacturing/docs/commands/ReleaseProductionOrder.md +57 -0
  147. package/src/modules/manufacturing/docs/commands/ReopenProductionOrder.md +54 -0
  148. package/src/modules/manufacturing/docs/commands/ReportWorkOrderProgress.md +53 -0
  149. package/src/modules/manufacturing/docs/commands/RescheduleProductionOrder.md +45 -0
  150. package/src/modules/manufacturing/docs/commands/ResumeWorkOrder.md +44 -0
  151. package/src/modules/manufacturing/docs/commands/ReviewManufacturingCostSummary.md +52 -0
  152. package/src/modules/manufacturing/docs/commands/StartWorkOrder.md +46 -0
  153. package/src/modules/manufacturing/docs/commands/TechnicallyCompleteProductionOrder.md +51 -0
  154. package/src/modules/manufacturing/docs/commands/UnreleaseProductionOrder.md +46 -0
  155. package/src/modules/manufacturing/docs/commands/UpdateBillOfMaterial.md +48 -0
  156. package/src/modules/manufacturing/docs/commands/UpdateProductionOrder.md +48 -0
  157. package/src/modules/manufacturing/docs/commands/UpdateRouting.md +52 -0
  158. package/src/modules/manufacturing/docs/commands/UpdateWorkCenter.md +48 -0
  159. package/src/modules/manufacturing/docs/features/bill-of-material-management.md +83 -0
  160. package/src/modules/manufacturing/docs/features/manufacturing-cost-and-variance.md +191 -0
  161. package/src/modules/manufacturing/docs/features/production-order-lifecycle.md +103 -0
  162. package/src/modules/manufacturing/docs/features/routing-and-work-center-definition.md +63 -0
  163. package/src/modules/manufacturing/docs/features/work-order-execution.md +115 -0
  164. package/src/modules/manufacturing/docs/models/BillOfMaterial.md +60 -0
  165. package/src/modules/manufacturing/docs/models/ManufacturingCostSummary.md +66 -0
  166. package/src/modules/manufacturing/docs/models/ProductionOrder.md +76 -0
  167. package/src/modules/manufacturing/docs/models/Routing.md +58 -0
  168. package/src/modules/manufacturing/docs/models/WorkCenter.md +56 -0
  169. package/src/modules/manufacturing/docs/models/WorkOrder.md +63 -0
  170. package/src/modules/manufacturing/docs/queries/DetectBillOfMaterialCircularReference.md +39 -0
  171. package/src/modules/manufacturing/docs/queries/ExplodeBillOfMaterial.md +56 -0
  172. package/src/modules/manufacturing/docs/queries/GetBillOfMaterial.md +37 -0
  173. package/src/modules/manufacturing/docs/queries/GetManufacturingCostSummary.md +39 -0
  174. package/src/modules/manufacturing/docs/queries/GetProductionOrder.md +37 -0
  175. package/src/modules/manufacturing/docs/queries/GetRouting.md +39 -0
  176. package/src/modules/manufacturing/docs/queries/GetWorkCenter.md +35 -0
  177. package/src/modules/manufacturing/docs/queries/GetWorkOrder.md +38 -0
  178. package/src/modules/manufacturing/docs/queries/ListBillOfMaterialsByItem.md +42 -0
  179. package/src/modules/manufacturing/docs/queries/ListManufacturingCostSummariesByStatus.md +41 -0
  180. package/src/modules/manufacturing/docs/queries/ListProductionOrdersByStatus.md +41 -0
  181. package/src/modules/manufacturing/docs/queries/ListRoutingsByItem.md +42 -0
  182. package/src/modules/manufacturing/docs/queries/ListWorkCentersBySite.md +38 -0
  183. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByProductionOrder.md +39 -0
  184. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByWorkCenter.md +43 -0
  185. package/src/modules/manufacturing/executor/.gitkeep +0 -0
  186. package/src/modules/manufacturing/generated/enums.ts +113 -0
  187. package/src/modules/manufacturing/generated/kysely-tailordb.ts +247 -0
  188. package/src/modules/manufacturing/index.ts +2 -0
  189. package/src/modules/manufacturing/lib/_db_deps.ts +22 -0
  190. package/src/modules/manufacturing/lib/errors.generated.ts +592 -0
  191. package/src/modules/manufacturing/lib/permissions.generated.ts +35 -0
  192. package/src/modules/manufacturing/lib/types.ts +111 -0
  193. package/src/modules/manufacturing/module.ts +226 -0
  194. package/src/modules/manufacturing/permissions.ts +3 -0
  195. package/src/modules/manufacturing/query/.gitkeep +0 -0
  196. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.generated.ts +5 -0
  197. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.test.ts +115 -0
  198. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.ts +79 -0
  199. package/src/modules/manufacturing/query/explodeBillOfMaterial.generated.ts +5 -0
  200. package/src/modules/manufacturing/query/explodeBillOfMaterial.test.ts +445 -0
  201. package/src/modules/manufacturing/query/explodeBillOfMaterial.ts +306 -0
  202. package/src/modules/manufacturing/query/getBillOfMaterial.generated.ts +5 -0
  203. package/src/modules/manufacturing/query/getBillOfMaterial.test.ts +64 -0
  204. package/src/modules/manufacturing/query/getBillOfMaterial.ts +27 -0
  205. package/src/modules/manufacturing/query/getManufacturingCostSummary.generated.ts +5 -0
  206. package/src/modules/manufacturing/query/getManufacturingCostSummary.test.ts +147 -0
  207. package/src/modules/manufacturing/query/getManufacturingCostSummary.ts +46 -0
  208. package/src/modules/manufacturing/query/getProductionOrder.generated.ts +5 -0
  209. package/src/modules/manufacturing/query/getProductionOrder.test.ts +139 -0
  210. package/src/modules/manufacturing/query/getProductionOrder.ts +84 -0
  211. package/src/modules/manufacturing/query/getRouting.generated.ts +5 -0
  212. package/src/modules/manufacturing/query/getRouting.test.ts +71 -0
  213. package/src/modules/manufacturing/query/getRouting.ts +34 -0
  214. package/src/modules/manufacturing/query/getWorkCenter.generated.ts +5 -0
  215. package/src/modules/manufacturing/query/getWorkCenter.test.ts +37 -0
  216. package/src/modules/manufacturing/query/getWorkCenter.ts +21 -0
  217. package/src/modules/manufacturing/query/getWorkOrder.generated.ts +5 -0
  218. package/src/modules/manufacturing/query/getWorkOrder.test.ts +73 -0
  219. package/src/modules/manufacturing/query/getWorkOrder.ts +28 -0
  220. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.generated.ts +5 -0
  221. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.test.ts +107 -0
  222. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.ts +58 -0
  223. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.generated.ts +5 -0
  224. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.test.ts +96 -0
  225. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.ts +77 -0
  226. package/src/modules/manufacturing/query/listProductionOrdersByStatus.generated.ts +5 -0
  227. package/src/modules/manufacturing/query/listProductionOrdersByStatus.test.ts +121 -0
  228. package/src/modules/manufacturing/query/listProductionOrdersByStatus.ts +83 -0
  229. package/src/modules/manufacturing/query/listRoutingsByItem.generated.ts +5 -0
  230. package/src/modules/manufacturing/query/listRoutingsByItem.test.ts +110 -0
  231. package/src/modules/manufacturing/query/listRoutingsByItem.ts +54 -0
  232. package/src/modules/manufacturing/query/listWorkCentersBySite.generated.ts +5 -0
  233. package/src/modules/manufacturing/query/listWorkCentersBySite.test.ts +81 -0
  234. package/src/modules/manufacturing/query/listWorkCentersBySite.ts +70 -0
  235. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.generated.ts +5 -0
  236. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.test.ts +102 -0
  237. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.ts +53 -0
  238. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.generated.ts +5 -0
  239. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.test.ts +143 -0
  240. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.ts +56 -0
  241. package/src/modules/manufacturing/seed/index.ts +19 -0
  242. package/src/modules/manufacturing/tailor.config.ts +13 -0
  243. package/src/modules/manufacturing/tailor.d.ts +13 -0
  244. package/src/modules/manufacturing/testing/commandTestUtils.ts +29 -0
  245. package/src/modules/manufacturing/testing/fixtures.ts +402 -0
  246. package/templates/scaffold/app/backend/package.json +9 -2
  247. package/templates/scaffold/app/backend/src/tests/utils/graphql-client.ts +66 -0
  248. package/templates/scaffold/app/backend/src/tests/utils/setup.ts +21 -0
  249. package/templates/scaffold/app/backend/tsconfig.json +9 -2
  250. package/templates/scaffold/app/backend/vitest.config.ts +35 -0
  251. package/templates/scaffold/app/frontend/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @tailor-platform/erp-kit
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0cb94dc: - Add manufacturing module with BOM, work orders, and production tracking
8
+ - Add story test case sync-check and stub generation for `erp-kit test` command
9
+ - Update workspace creation instructions and deployment steps in skills
10
+
11
+ ### Patch Changes
12
+
13
+ - 0cb94dc: - Fix relative shared imports to use package self-reference for consistent module resolution
14
+ - Fix priority calibration in requirements review skill to improve accuracy
15
+ - Fix severity validation in module-2 requirements review skill
16
+ - Update module-4-plan-review skill and regenerate .agents/skills
17
+
3
18
  ## 0.5.1
4
19
 
5
20
  ### Patch Changes
package/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
3
3
  import { arg, defineCommand, runMain } from "politty";
4
4
  import { z } from "zod";
5
5
  import chalk from "chalk";
6
- import fs, { existsSync, readFileSync, readdirSync } from "node:fs";
6
+ import fs, { existsSync, globSync, readFileSync, readdirSync } from "node:fs";
7
7
  import path, { basename, dirname, join, relative, resolve } from "node:path";
8
8
  import { execFile, execSync } from "node:child_process";
9
9
  import fg from "fast-glob";
@@ -452,11 +452,12 @@ const MODULE_PATHS = {
452
452
  }
453
453
  };
454
454
  const APP_PATHS = {
455
- storySegment: "story",
455
+ tests: { stories: "backend/src/tests/stories" },
456
456
  docs: {
457
457
  resolver: "docs/resolver",
458
458
  actors: "docs/actors",
459
459
  businessFlow: "docs/business-flow",
460
+ story: "story",
460
461
  screen: "docs/screen"
461
462
  }
462
463
  };
@@ -492,7 +493,14 @@ function appComposeCategories(root) {
492
493
  exclusions: [/\.test\.ts$/, /^index\.ts$/]
493
494
  }];
494
495
  }
495
- function testCaseCategories(root) {
496
+ function appTestCaseCategories(root) {
497
+ return {
498
+ name: "story-test-case",
499
+ docPattern: `${root}/*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
500
+ testDir: APP_PATHS.tests.stories
501
+ };
502
+ }
503
+ function moduleTestCaseCategories(root) {
496
504
  return [{
497
505
  name: "command-test-case",
498
506
  docPattern: `${root}/*/${MODULE_PATHS.docs.commands}/*.md`,
@@ -536,7 +544,7 @@ function buildCheckTargets(config) {
536
544
  glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/README.md`,
537
545
  schemaKey: "business-flow"
538
546
  }, {
539
- glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.storySegment}/*.md`,
547
+ glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
540
548
  schemaKey: "story"
541
549
  }, {
542
550
  glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.screen}/*.md`,
@@ -742,9 +750,9 @@ function parseItDescriptionsFromTest(content) {
742
750
  function toCamelCase(pascalCase) {
743
751
  return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
744
752
  }
745
- async function runTestCaseSyncCheck(root, cwd) {
753
+ async function runModuleTestCaseSyncCheck(root, cwd) {
746
754
  const errors = [];
747
- const categories = testCaseCategories(root);
755
+ const categories = moduleTestCaseCategories(root);
748
756
  for (const category of categories) {
749
757
  const docPaths = await fg(category.docPattern, { cwd });
750
758
  for (const docPath of docPaths) {
@@ -780,15 +788,57 @@ async function runTestCaseSyncCheck(root, cwd) {
780
788
  }
781
789
  return errors;
782
790
  }
791
+ function resolveStoryTestPath(docPath, testDir) {
792
+ const match = /^(.+)\/docs\/business-flow\/([^/]+)\/story\/([^/]+)\.md$/.exec(docPath);
793
+ if (!match) return null;
794
+ const [, appPath, flow, name] = match;
795
+ return `${appPath}/${testDir}/${flow}/${name}.test.ts`;
796
+ }
797
+ async function runAppTestCaseSyncCheck(appRoot, cwd) {
798
+ const errors = [];
799
+ const config = appTestCaseCategories(appRoot);
800
+ const docPaths = await fg(config.docPattern, { cwd });
801
+ for (const docPath of docPaths) {
802
+ const docFullPath = path.join(cwd, docPath);
803
+ const docTestCases = parseTestCasesFromDoc(fs.readFileSync(docFullPath, "utf-8"));
804
+ if (docTestCases.length === 0) continue;
805
+ const testPath = resolveStoryTestPath(docPath, config.testDir);
806
+ if (!testPath) continue;
807
+ const testFullPath = path.join(cwd, testPath);
808
+ if (!fs.existsSync(testFullPath)) continue;
809
+ const itDescriptions = parseItDescriptionsFromTest(fs.readFileSync(testFullPath, "utf-8"));
810
+ const docSet = new Set(docTestCases);
811
+ const testSet = new Set(itDescriptions);
812
+ for (const docCase of docSet) if (!testSet.has(docCase)) errors.push({
813
+ type: "missing-test-case",
814
+ category: config.name,
815
+ docPath,
816
+ sourcePath: testPath,
817
+ expectedBasename: docCase
818
+ });
819
+ for (const testCase of testSet) if (!docSet.has(testCase)) errors.push({
820
+ type: "extra-test-case",
821
+ category: config.name,
822
+ docPath,
823
+ sourcePath: testPath,
824
+ expectedBasename: testCase
825
+ });
826
+ }
827
+ return errors;
828
+ }
783
829
  //#endregion
784
830
  //#region src/commands/sync-check.ts
785
831
  async function runSyncCheck(config, cwd) {
786
832
  const sourceResult = await runSourceSyncCheck(config, cwd);
787
833
  if (config.modulesRoot) {
788
- const testCaseErrors = await runTestCaseSyncCheck(config.modulesRoot, cwd);
834
+ const testCaseErrors = await runModuleTestCaseSyncCheck(config.modulesRoot, cwd);
789
835
  sourceResult.errors.push(...testCaseErrors);
790
- sourceResult.exitCode = sourceResult.errors.length > 0 ? 1 : 0;
791
836
  }
837
+ if (config.appRoot) {
838
+ const storyErrors = await runAppTestCaseSyncCheck(config.appRoot, cwd);
839
+ sourceResult.errors.push(...storyErrors);
840
+ }
841
+ sourceResult.exitCode = sourceResult.errors.length > 0 ? 1 : 0;
792
842
  return sourceResult;
793
843
  }
794
844
  function formatSyncCheckReport(result) {
@@ -1092,10 +1142,22 @@ describe("${name}", () => {
1092
1142
  expect(resolver.default.name).toBe("${name}");
1093
1143
  });
1094
1144
  });
1145
+ `;
1146
+ } },
1147
+ storyTest: { render: (name, testCases) => {
1148
+ return `import { describe, expect, inject, it } from "vitest";
1149
+ import { createGraphQLClient } from "@/tests/utils/graphql-client";
1150
+
1151
+ describe("${name}", () => {
1152
+ const graphQLClient = createGraphQLClient(inject("url"), inject("token"));
1153
+
1154
+ ${testCases.map((tc) => ` it("${tc}", async () => {\n // TODO: implement\n });`).join("\n\n")}
1155
+ });
1095
1156
  `;
1096
1157
  } }
1097
1158
  };
1098
- function renderStub(type, name) {
1159
+ function renderStub(type, name, testCases) {
1160
+ if (type === "storyTest") return templates.storyTest.render(name, testCases);
1099
1161
  return templates[type].render(name);
1100
1162
  }
1101
1163
  //#endregion
@@ -1121,6 +1183,9 @@ function generateResolverStub(resolverName) {
1121
1183
  function generateResolverTestStub(resolverName) {
1122
1184
  return renderStub("resolverTest", resolverName);
1123
1185
  }
1186
+ function generateStoryTestStub(name, testCases) {
1187
+ return renderStub("storyTest", name, testCases);
1188
+ }
1124
1189
  //#endregion
1125
1190
  //#region src/generator/scaffold.ts
1126
1191
  function copyTemplateDir(srcDir, destDir, replacements, placeholderFiles) {
@@ -1314,7 +1379,7 @@ function resolveDocPath(type, name, modulePath) {
1314
1379
  if (type === "story") {
1315
1380
  const parts = name.split("/");
1316
1381
  if (parts.length !== 2) throw new Error(`Story name must be "<flow>/<story>" (e.g., "onboarding/admin--create-user")`);
1317
- return path.join(modulePath, APP_PATHS.docs.businessFlow, parts[0], APP_PATHS.storySegment, `${parts[1]}.md`);
1382
+ return path.join(modulePath, APP_PATHS.docs.businessFlow, parts[0], APP_PATHS.docs.story, `${parts[1]}.md`);
1318
1383
  }
1319
1384
  if (MODULE_DIR_MAP[type]) return path.join(modulePath, MODULE_DIR_MAP[type], `${name}.md`);
1320
1385
  if (APP_DIR_MAP[type]) return path.join(modulePath, APP_DIR_MAP[type], `${name}.md`);
@@ -1540,19 +1605,16 @@ function parseExceptionHandlingFromRaw(markdown) {
1540
1605
  function runGenerateAppCode(appPath) {
1541
1606
  const appName = path.basename(appPath);
1542
1607
  scaffoldAppBoilerplate(appPath, appName);
1608
+ generateResolverStubs(appPath, appName);
1609
+ generateStoryTestStubs(appPath, appName);
1610
+ return 0;
1611
+ }
1612
+ function generateResolverStubs(appPath, appName) {
1543
1613
  const docsDir = path.join(appPath, "docs", "resolver");
1544
- const resolverDir = path.join(appPath, "backend", "src", "resolvers");
1545
- if (!fs.existsSync(docsDir)) {
1546
- console.log(`No docs/resolver/ directory found — skipping resolver generation`);
1547
- console.log(`Generated boilerplate for ${appName}`);
1548
- return 0;
1549
- }
1614
+ if (!fs.existsSync(docsDir)) return;
1550
1615
  const mdFiles = fs.readdirSync(docsDir).filter((f) => f.endsWith(".md"));
1551
- if (mdFiles.length === 0) {
1552
- console.log(`No resolver docs found skipping resolver generation`);
1553
- console.log(`Generated boilerplate for ${appName}`);
1554
- return 0;
1555
- }
1616
+ if (mdFiles.length === 0) return;
1617
+ const resolverDir = path.join(appPath, "backend", "src", "resolvers");
1556
1618
  fs.mkdirSync(resolverDir, { recursive: true });
1557
1619
  let generated = 0;
1558
1620
  for (const file of mdFiles) {
@@ -1570,8 +1632,26 @@ function runGenerateAppCode(appPath) {
1570
1632
  console.log(` scaffolded ${path.relative(appPath, testFile)}`);
1571
1633
  }
1572
1634
  }
1573
- console.log(`Scaffolded ${generated} resolver(s) for ${appName}`);
1574
- return 0;
1635
+ if (generated > 0) console.log(`Scaffolded ${generated} resolver(s) for ${appName}`);
1636
+ }
1637
+ function generateStoryTestStubs(appPath, appName) {
1638
+ const storyDocs = globSync(path.join(appPath, "docs/business-flow/*/story/*.md"));
1639
+ let generated = 0;
1640
+ for (const storyDocPath of storyDocs) {
1641
+ const testCases = parseTestCasesFromDoc(fs.readFileSync(storyDocPath, "utf-8"));
1642
+ if (testCases.length === 0) continue;
1643
+ const match = /\/docs\/business-flow\/([^/]+)\/story\/([^/]+)\.md$/.exec(storyDocPath);
1644
+ if (!match) continue;
1645
+ const [, flow, storyName] = match;
1646
+ const testDir = path.join(appPath, APP_PATHS.tests.stories, flow);
1647
+ const testFile = path.join(testDir, `${storyName}.test.ts`);
1648
+ if (fs.existsSync(testFile)) continue;
1649
+ fs.mkdirSync(testDir, { recursive: true });
1650
+ fs.writeFileSync(testFile, generateStoryTestStub(storyName, testCases));
1651
+ console.log(` scaffolded ${path.relative(appPath, testFile)}`);
1652
+ generated++;
1653
+ }
1654
+ if (generated > 0) console.log(`Scaffolded ${generated} story test(s) for ${appName}`);
1575
1655
  }
1576
1656
  //#endregion
1577
1657
  //#region src/shared/uuidv5.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/erp-kit",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Opinionated ERP toolkit for building business applications on Tailor Platform",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -90,7 +90,10 @@ For each resolver, check whether its spec (`docs/resolver/*.md`) documents error
90
90
 
91
91
  Do not directly mutate module-owned tables via Kysely — always use module commands.
92
92
 
93
+ After resolvers are done, implement the story integration test stubs generated in Phase 1b (`backend/src/tests/stories/`). Each stub has `it()` descriptions from the story doc's `## Test Cases`. Use the scaffolded GraphQL client and `inject("url")` / `inject("token")` to call resolvers. Run `pnpm test:integration` and `pnpm erp-kit app sync-check` to verify.
94
+
93
95
  > **Parallelize if possible:** Dispatch one agent per resolver. Each agent writes only to its own files:
96
+ >
94
97
  > - `src/resolvers/<resolverName>.ts`
95
98
  > - `src/executors/<resolverName>.ts`
96
99
  >
@@ -107,10 +110,10 @@ Do not directly mutate module-owned tables via Kysely — always use module comm
107
110
 
108
111
  Frontend implementation requires a deployed backend (for GraphQL schema generation). Prompt the user to:
109
112
 
110
- 1. Create a workspace and configure `.env` (including `TAILOR_PLATFORM_WORKSPACE_ID`)
111
- 2. Run `pnpm deploy` and `pnpm generate`
112
- 3. Run `pnpm seed` if seed data was created
113
- 4. Retrieve OAuth2 client ID via `tailor-sdk oauth2client get default --json` for frontend `.env`
113
+ 1. Create a workspace via `tailor-sdk workspace create --name <NAME> --region <us-west|asia-northeast>` and configure `.env` (including `TAILOR_PLATFORM_WORKSPACE_ID`)
114
+ 2. Run `pnpm run deploy` and `pnpm run generate`
115
+ 3. Run `pnpm run seed` if seed data was created
116
+ 4. Retrieve OAuth2 client ID via `tailor-sdk oauth2client get default --json --env-file-if-exists .env` for frontend `.env`
114
117
 
115
118
  Wait for the user to confirm deployment is complete before proceeding.
116
119
 
@@ -63,7 +63,7 @@ cd <APP_ROOT>/frontend && pnpm lint && pnpm typecheck && pnpm build
63
63
  erp-kit app sync-check -p <APP_ROOT>/..
64
64
  ```
65
65
 
66
- Report pass/fail for each check. `sync-check` detects missing resolver implementations and orphaned docs, so groups A-C agents can skip file-existence checks and focus on content parity.
66
+ Report pass/fail for each check. `sync-check` detects missing resolver implementations, orphaned docs, and story doc ↔ integration test case mismatches, so groups A-C agents can skip file-existence checks and focus on content parity.
67
67
 
68
68
  ## Step 3: Aggregate Results
69
69
 
@@ -31,35 +31,57 @@ Define shared context for all agents:
31
31
 
32
32
  Verify at least `MODEL_DOCS`, `COMMAND_DOCS`, or `QUERY_DOCS` is non-empty. If no docs exist, stop with: "No docs found for module <MODULE_NAME>."
33
33
 
34
- ## Step 2: Dispatch Agents (parallelize if possible)
34
+ ## Step 2: Dispatch Agents (parallelize ALL agents in a single message)
35
35
 
36
- Launch 3 Agent tool calls in parallel one per domain (model, command, query).
37
- Each agent receives: MODULE_NAME, all relevant doc/code/test file paths for its domain.
38
- Each agent runs all parity perspectives (doc→code, error, doc→test) internally and returns: structured JSON per [references/impl-parity-report-format.md](references/impl-parity-report-format.md).
39
- If a doc or code directory is empty, the agent reports "no files found" and skips.
36
+ Split each parity check into its own agent, then further split by file batches when there are many files. Target **~5 agents per domain** (model, command, query) for maximum parallelism. Launch **ALL agents in a single message** — do NOT wait for one domain to finish before starting another.
40
37
 
41
- | Agent | Domain | Prompt Templates | Inputs |
42
- | ----- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
43
- | 1 | Model | [references/model-doc-code-parity.md](references/model-doc-code-parity.md) | MODEL_DOCS, MODEL_CODE |
44
- | 2 | Command | [references/command-doc-code-parity.md](references/command-doc-code-parity.md), [references/command-error-implementation-parity.md](references/command-error-implementation-parity.md), [references/command-doc-test-parity.md](references/command-doc-test-parity.md) | COMMAND_DOCS, COMMAND_CODE, COMMAND_TEST_CODE, ERROR_DEFS |
45
- | 3 | Query | [references/query-doc-code-parity.md](references/query-doc-code-parity.md), [references/query-error-implementation-parity.md](references/query-error-implementation-parity.md), [references/query-doc-test-parity.md](references/query-doc-test-parity.md) | QUERY_DOCS, QUERY_CODE, QUERY_TEST_CODE, ERROR_DEFS |
38
+ If a doc or code directory is empty, skip that agent entirely.
39
+
40
+ ### Splitting Strategy
41
+
42
+ 1. **Each parity check type = separate agent** (never combine different check types in one agent)
43
+ 2. **File batching**: If a check has more than 5 doc files, split docs into batches of 3-5 files each. Each batch becomes its own agent running the same check type on a subset of files.
44
+ 3. **Corresponding code/test files**: When batching by doc files, include only the matching code and test files for those specific docs (match by filename stem, e.g., `docs/commands/createFoo.md` → `command/createFoo.ts` + `command/createFoo.test.ts`).
45
+
46
+ ### Agent Table
47
+
48
+ | Check Type | Domain | Prompt Template | Inputs per agent |
49
+ | ---------- | ------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
50
+ | M-1 | Model | [references/model-doc-code-parity.md](references/model-doc-code-parity.md) | MODEL_DOCS (batch), MODEL_CODE (matching) |
51
+ | C-1 | Command | [references/command-doc-code-parity.md](references/command-doc-code-parity.md) | COMMAND_DOCS (batch), COMMAND_CODE (matching) |
52
+ | C-2 | Command | [references/command-error-implementation-parity.md](references/command-error-implementation-parity.md) | COMMAND_DOCS (batch), COMMAND_CODE (matching), ERROR_DEFS |
53
+ | C-3 | Command | [references/command-doc-test-parity.md](references/command-doc-test-parity.md) | COMMAND_DOCS (batch), COMMAND_TEST_CODE (matching) |
54
+ | Q-1 | Query | [references/query-doc-code-parity.md](references/query-doc-code-parity.md) | QUERY_DOCS (batch), QUERY_CODE (matching) |
55
+ | Q-2 | Query | [references/query-error-implementation-parity.md](references/query-error-implementation-parity.md) | QUERY_DOCS (batch), QUERY_CODE (matching), ERROR_DEFS |
56
+ | Q-3 | Query | [references/query-doc-test-parity.md](references/query-doc-test-parity.md) | QUERY_DOCS (batch), QUERY_TEST_CODE (matching) |
57
+
58
+ ### Batching Example
59
+
60
+ If a module has 8 command docs: split into 3 batches (3+3+2). Each batch spawns 3 agents (C-1, C-2, C-3) → 9 command agents total. If it has 4 command docs: no batching needed, 3 agents (C-1, C-2, C-3).
61
+
62
+ For model docs: if 6 models → 2 batches (3+3) → 2 agents (M-1 × 2). If 3 models → 1 agent.
63
+
64
+ ### Agent Dispatch
46
65
 
47
66
  For each agent:
48
67
 
49
- 1. Read ALL prompt template files listed for that agent
68
+ 1. Read the prompt template file for that check type
50
69
  2. Replace `{{MODULE_NAME}}` with the resolved module name
51
- 3. Replace `{{MODEL_DOCS}}`, `{{COMMAND_DOCS}}`, `{{QUERY_DOCS}}`, `{{MODEL_CODE}}`, `{{COMMAND_CODE}}`, `{{QUERY_CODE}}`, `{{COMMAND_TEST_CODE}}`, `{{QUERY_TEST_CODE}}`, `{{ERROR_DEFS}}` with the actual file paths
52
- 4. Combine the templates into a single prompt and dispatch the agent
70
+ 3. Replace template placeholders (`{{MODEL_DOCS}}`, `{{COMMAND_DOCS}}`, `{{QUERY_DOCS}}`, `{{MODEL_CODE}}`, `{{COMMAND_CODE}}`, `{{QUERY_CODE}}`, `{{COMMAND_TEST_CODE}}`, `{{QUERY_TEST_CODE}}`, `{{ERROR_DEFS}}`) with the actual file paths **for this batch only**
71
+ 4. Dispatch the agent with the filled prompt
72
+
73
+ **IMPORTANT**: Launch ALL agents across ALL domains in a single parallel message. Do not serialize by domain.
53
74
 
54
75
  ## Step 3: Aggregate Results
55
76
 
56
77
  After ALL agents return:
57
78
 
58
79
  1. Collect the JSON results from each agent
59
- 2. Merge all `gaps[]` arrays into a single list
60
- 3. Merge all `inconsistencies[]` arrays into a single list
61
- 4. Deduplicate: if two gaps share the same `source + target + check`, keep only one
62
- 5. Calculate totals across all summaries
80
+ 2. **Merge batches**: If a check type was split across multiple batches (e.g., C-1 batch 1 + C-1 batch 2), merge their `gaps[]` and `inconsistencies[]` into a single result per check type
81
+ 3. Merge all `gaps[]` arrays across all check types into a single list
82
+ 4. Merge all `inconsistencies[]` arrays into a single list
83
+ 5. Deduplicate: if two gaps share the same `source + target + check`, keep only one
84
+ 6. Calculate totals across all summaries
63
85
 
64
86
  ## Step 4: Severity Validation
65
87
 
@@ -48,7 +48,7 @@ export function resolveDocPath(
48
48
  modulePath,
49
49
  APP_PATHS.docs.businessFlow,
50
50
  parts[0],
51
- APP_PATHS.storySegment,
51
+ APP_PATHS.docs.story,
52
52
  `${parts[1]}.md`,
53
53
  );
54
54
  }
@@ -2,7 +2,8 @@ import { describe, it, expect } from "vitest";
2
2
  import {
3
3
  moduleCategories,
4
4
  appComposeCategories,
5
- testCaseCategories,
5
+ moduleTestCaseCategories,
6
+ appTestCaseCategories,
6
7
  buildCheckTargets,
7
8
  } from "./discovery";
8
9
 
@@ -35,15 +36,24 @@ describe("appComposeCategories", () => {
35
36
  });
36
37
  });
37
38
 
38
- describe("testCaseCategories", () => {
39
+ describe("moduleTestCaseCategories", () => {
39
40
  it("produces command and query test case categories", () => {
40
- const cats = testCaseCategories("modules");
41
+ const cats = moduleTestCaseCategories("modules");
41
42
  expect(cats).toHaveLength(2);
42
43
  expect(cats[0].testDir).toBe("command");
43
44
  expect(cats[1].testDir).toBe("query");
44
45
  });
45
46
  });
46
47
 
48
+ describe("appTestCaseCategories", () => {
49
+ it("produces story test case category with correct pattern", () => {
50
+ const cats = appTestCaseCategories("apps");
51
+ expect(cats.name).toBe("story-test-case");
52
+ expect(cats.docPattern).toBe("apps/*/docs/business-flow/*/story/*.md");
53
+ expect(cats.testDir).toBe("backend/src/tests/stories");
54
+ });
55
+ });
56
+
47
57
  describe("buildCheckTargets", () => {
48
58
  it("builds module check targets", () => {
49
59
  const targets = buildCheckTargets({ modulesRoot: "modules" });
@@ -52,7 +52,15 @@ export function appComposeCategories(root: string): CategoryConfig[] {
52
52
  ];
53
53
  }
54
54
 
55
- export function testCaseCategories(root: string): TestCaseCategoryConfig[] {
55
+ export function appTestCaseCategories(root: string): TestCaseCategoryConfig {
56
+ return {
57
+ name: "story-test-case",
58
+ docPattern: `${root}/*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
59
+ testDir: APP_PATHS.tests.stories,
60
+ };
61
+ }
62
+
63
+ export function moduleTestCaseCategories(root: string): TestCaseCategoryConfig[] {
56
64
  return [
57
65
  {
58
66
  name: "command-test-case",
@@ -94,7 +102,7 @@ export function buildCheckTargets(config: {
94
102
  schemaKey: "business-flow",
95
103
  },
96
104
  {
97
- glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.storySegment}/*.md`,
105
+ glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
98
106
  schemaKey: "story",
99
107
  },
100
108
  { glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.screen}/*.md`, schemaKey: "screen" },
@@ -11,12 +11,14 @@ export const MODULE_PATHS = {
11
11
  } as const;
12
12
 
13
13
  export const APP_PATHS = {
14
- // Subdirectory name within docs/business-flow/<flow>/story/
15
- storySegment: "story",
14
+ tests: {
15
+ stories: "backend/src/tests/stories",
16
+ },
16
17
  docs: {
17
18
  resolver: "docs/resolver",
18
19
  actors: "docs/actors",
19
20
  businessFlow: "docs/business-flow",
21
+ story: "story",
20
22
  screen: "docs/screen",
21
23
  },
22
24
  } as const;
@@ -3,7 +3,7 @@ import os from "node:os";
3
3
  import fs from "node:fs";
4
4
  import { describe, it, expect, beforeEach, afterEach } from "vitest";
5
5
  import { parseTestCasesFromDoc, parseItDescriptionsFromTest } from "../parse-doc-test-cases";
6
- import { runTestCaseSyncCheck } from "./sync-check-tests";
6
+ import { runModuleTestCaseSyncCheck, runAppTestCaseSyncCheck } from "./sync-check-tests";
7
7
 
8
8
  describe("parseTestCasesFromDoc", () => {
9
9
  it("extracts test cases from ## Test Cases section", () => {
@@ -67,7 +67,7 @@ describe("createProduct", () => {
67
67
  });
68
68
  });
69
69
 
70
- describe("runTestCaseSyncCheck", () => {
70
+ describe("runModuleTestCaseSyncCheck", () => {
71
71
  let tmpDir: string;
72
72
 
73
73
  beforeEach(() => {
@@ -94,7 +94,7 @@ describe("runTestCaseSyncCheck", () => {
94
94
  'describe("createOrder", () => { it("creates order", async () => {}); });',
95
95
  );
96
96
 
97
- const errors = await runTestCaseSyncCheck("modules", tmpDir);
97
+ const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
98
98
  expect(errors).toContainEqual(
99
99
  expect.objectContaining({
100
100
  type: "missing-test-case",
@@ -120,7 +120,7 @@ describe("runTestCaseSyncCheck", () => {
120
120
  'describe("createOrder", () => { it("creates order", async () => {}); it("extra case", async () => {}); });',
121
121
  );
122
122
 
123
- const errors = await runTestCaseSyncCheck("modules", tmpDir);
123
+ const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
124
124
  expect(errors).toContainEqual(
125
125
  expect.objectContaining({
126
126
  type: "extra-test-case",
@@ -146,7 +146,7 @@ describe("runTestCaseSyncCheck", () => {
146
146
  'describe("createOrder", () => { it("creates order", async () => {}); it("validates input", async () => {}); });',
147
147
  );
148
148
 
149
- const errors = await runTestCaseSyncCheck("modules", tmpDir);
149
+ const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
150
150
  const tcErrors = errors.filter(
151
151
  (e) => e.type === "missing-test-case" || e.type === "extra-test-case",
152
152
  );
@@ -169,10 +169,88 @@ describe("runTestCaseSyncCheck", () => {
169
169
  'describe("createOrder", () => { it("creates order", async () => {}); });',
170
170
  );
171
171
 
172
- const errors = await runTestCaseSyncCheck("modules", tmpDir);
172
+ const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
173
173
  const tcErrors = errors.filter(
174
174
  (e) => e.type === "missing-test-case" || e.type === "extra-test-case",
175
175
  );
176
176
  expect(tcErrors).toHaveLength(0);
177
177
  });
178
178
  });
179
+
180
+ describe("runAppTestCaseSyncCheck", () => {
181
+ let tmpDir: string;
182
+
183
+ beforeEach(() => {
184
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sync-check-story-"));
185
+ });
186
+
187
+ afterEach(() => {
188
+ fs.rmSync(tmpDir, { recursive: true, force: true });
189
+ });
190
+
191
+ function setupStoryFixture(storyName: string, storyContent: string, testContent: string) {
192
+ const flow = "item-lifecycle";
193
+ const storyDir = path.join(tmpDir, "apps", "my-app", "docs", "business-flow", flow, "story");
194
+ const testDir = path.join(tmpDir, "apps", "my-app", "backend", "src", "tests", "stories", flow);
195
+ fs.mkdirSync(storyDir, { recursive: true });
196
+ fs.mkdirSync(testDir, { recursive: true });
197
+ fs.writeFileSync(path.join(storyDir, `${storyName}.md`), storyContent);
198
+ fs.writeFileSync(path.join(testDir, `${storyName}.test.ts`), testContent);
199
+ }
200
+
201
+ it("reports missing-test-case when story doc has case not in test", async () => {
202
+ setupStoryFixture(
203
+ "admin--create-item",
204
+ "# Create Item\n\n## Test Cases\n\n- creates item\n- validates input\n",
205
+ 'describe("admin--create-item", () => { it("creates item", async () => {}); });',
206
+ );
207
+
208
+ const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
209
+ expect(errors).toContainEqual(
210
+ expect.objectContaining({
211
+ type: "missing-test-case",
212
+ category: "story-test-case",
213
+ expectedBasename: "validates input",
214
+ }),
215
+ );
216
+ });
217
+
218
+ it("reports extra-test-case when test has case not in story doc", async () => {
219
+ setupStoryFixture(
220
+ "admin--create-item",
221
+ "# Create Item\n\n## Test Cases\n\n- creates item\n",
222
+ 'describe("admin--create-item", () => { it("creates item", async () => {}); it("extra case", async () => {}); });',
223
+ );
224
+
225
+ const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
226
+ expect(errors).toContainEqual(
227
+ expect.objectContaining({
228
+ type: "extra-test-case",
229
+ category: "story-test-case",
230
+ expectedBasename: "extra case",
231
+ }),
232
+ );
233
+ });
234
+
235
+ it("returns no errors when story doc and test match", async () => {
236
+ setupStoryFixture(
237
+ "admin--create-item",
238
+ "# Create Item\n\n## Test Cases\n\n- creates item\n- validates input\n",
239
+ 'describe("admin--create-item", () => { it("creates item", async () => {}); it("validates input", async () => {}); });',
240
+ );
241
+
242
+ const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
243
+ expect(errors).toHaveLength(0);
244
+ });
245
+
246
+ it("skips when story doc has no Test Cases section", async () => {
247
+ setupStoryFixture(
248
+ "admin--view-items",
249
+ "# View Items\n\n## Scenario Patterns\n\n- Browse items\n",
250
+ 'describe("admin--view-items", () => { it("some test", async () => {}); });',
251
+ );
252
+
253
+ const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
254
+ expect(errors).toHaveLength(0);
255
+ });
256
+ });