@tailor-platform/erp-kit 0.5.0 → 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 (262) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli.mjs +139 -35
  3. package/package.json +1 -1
  4. package/skills/erp-kit-app-5-impl-backend/SKILL.md +10 -5
  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/init-module.test.ts +17 -3
  9. package/src/commands/init-module.ts +0 -12
  10. package/src/commands/lib/discovery.test.ts +13 -3
  11. package/src/commands/lib/discovery.ts +10 -2
  12. package/src/commands/lib/paths.ts +4 -2
  13. package/src/commands/lib/sync-check-tests.test.ts +84 -6
  14. package/src/commands/lib/sync-check-tests.ts +63 -3
  15. package/src/commands/sync-check.ts +7 -3
  16. package/src/generator/generate-app-code.ts +51 -16
  17. package/src/generator/generate-code-boilerplate.test.ts +9 -1
  18. package/src/generator/generate-stubs.ts +4 -0
  19. package/src/generator/scaffold.ts +6 -2
  20. package/src/generator/stub-templates.test.ts +11 -0
  21. package/src/generator/stub-templates.ts +22 -1
  22. package/src/mdschema.ts +39 -3
  23. package/src/modules/inventory/docs/features/inventory-adjustment.md +2 -1
  24. package/src/modules/inventory/docs/features/scrap-management.md +39 -1
  25. package/src/modules/manufacturing/README.md +63 -0
  26. package/src/modules/manufacturing/command/activateBillOfMaterial.generated.ts +6 -0
  27. package/src/modules/manufacturing/command/activateBillOfMaterial.test.ts +166 -0
  28. package/src/modules/manufacturing/command/activateBillOfMaterial.ts +173 -0
  29. package/src/modules/manufacturing/command/activateRouting.generated.ts +6 -0
  30. package/src/modules/manufacturing/command/activateRouting.test.ts +152 -0
  31. package/src/modules/manufacturing/command/activateRouting.ts +92 -0
  32. package/src/modules/manufacturing/command/activateWorkCenter.generated.ts +6 -0
  33. package/src/modules/manufacturing/command/activateWorkCenter.test.ts +135 -0
  34. package/src/modules/manufacturing/command/activateWorkCenter.ts +91 -0
  35. package/src/modules/manufacturing/command/cancelProductionOrder.generated.ts +6 -0
  36. package/src/modules/manufacturing/command/cancelProductionOrder.test.ts +151 -0
  37. package/src/modules/manufacturing/command/cancelProductionOrder.ts +114 -0
  38. package/src/modules/manufacturing/command/closeProductionOrder.generated.ts +6 -0
  39. package/src/modules/manufacturing/command/closeProductionOrder.test.ts +126 -0
  40. package/src/modules/manufacturing/command/closeProductionOrder.ts +87 -0
  41. package/src/modules/manufacturing/command/completeProductionOrder.generated.ts +6 -0
  42. package/src/modules/manufacturing/command/completeProductionOrder.test.ts +132 -0
  43. package/src/modules/manufacturing/command/completeProductionOrder.ts +97 -0
  44. package/src/modules/manufacturing/command/completeWorkOrder.generated.ts +6 -0
  45. package/src/modules/manufacturing/command/completeWorkOrder.test.ts +369 -0
  46. package/src/modules/manufacturing/command/completeWorkOrder.ts +212 -0
  47. package/src/modules/manufacturing/command/createBillOfMaterial.generated.ts +6 -0
  48. package/src/modules/manufacturing/command/createBillOfMaterial.test.ts +210 -0
  49. package/src/modules/manufacturing/command/createBillOfMaterial.ts +176 -0
  50. package/src/modules/manufacturing/command/createProductionOrder.generated.ts +6 -0
  51. package/src/modules/manufacturing/command/createProductionOrder.test.ts +160 -0
  52. package/src/modules/manufacturing/command/createProductionOrder.ts +129 -0
  53. package/src/modules/manufacturing/command/createRouting.generated.ts +6 -0
  54. package/src/modules/manufacturing/command/createRouting.test.ts +168 -0
  55. package/src/modules/manufacturing/command/createRouting.ts +128 -0
  56. package/src/modules/manufacturing/command/createWorkCenter.generated.ts +6 -0
  57. package/src/modules/manufacturing/command/createWorkCenter.test.ts +148 -0
  58. package/src/modules/manufacturing/command/createWorkCenter.ts +131 -0
  59. package/src/modules/manufacturing/command/deactivateBillOfMaterial.generated.ts +6 -0
  60. package/src/modules/manufacturing/command/deactivateBillOfMaterial.test.ts +103 -0
  61. package/src/modules/manufacturing/command/deactivateBillOfMaterial.ts +78 -0
  62. package/src/modules/manufacturing/command/deactivateRouting.generated.ts +6 -0
  63. package/src/modules/manufacturing/command/deactivateRouting.test.ts +112 -0
  64. package/src/modules/manufacturing/command/deactivateRouting.ts +76 -0
  65. package/src/modules/manufacturing/command/deactivateWorkCenter.generated.ts +6 -0
  66. package/src/modules/manufacturing/command/deactivateWorkCenter.test.ts +113 -0
  67. package/src/modules/manufacturing/command/deactivateWorkCenter.ts +85 -0
  68. package/src/modules/manufacturing/command/pauseWorkOrder.generated.ts +6 -0
  69. package/src/modules/manufacturing/command/pauseWorkOrder.test.ts +118 -0
  70. package/src/modules/manufacturing/command/pauseWorkOrder.ts +82 -0
  71. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.generated.ts +6 -0
  72. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.test.ts +183 -0
  73. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.ts +139 -0
  74. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.generated.ts +6 -0
  75. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.test.ts +120 -0
  76. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.ts +110 -0
  77. package/src/modules/manufacturing/command/releaseProductionOrder.generated.ts +6 -0
  78. package/src/modules/manufacturing/command/releaseProductionOrder.test.ts +220 -0
  79. package/src/modules/manufacturing/command/releaseProductionOrder.ts +450 -0
  80. package/src/modules/manufacturing/command/reopenProductionOrder.generated.ts +6 -0
  81. package/src/modules/manufacturing/command/reopenProductionOrder.test.ts +196 -0
  82. package/src/modules/manufacturing/command/reopenProductionOrder.ts +98 -0
  83. package/src/modules/manufacturing/command/reportWorkOrderProgress.generated.ts +6 -0
  84. package/src/modules/manufacturing/command/reportWorkOrderProgress.test.ts +204 -0
  85. package/src/modules/manufacturing/command/reportWorkOrderProgress.ts +129 -0
  86. package/src/modules/manufacturing/command/rescheduleProductionOrder.generated.ts +6 -0
  87. package/src/modules/manufacturing/command/rescheduleProductionOrder.test.ts +185 -0
  88. package/src/modules/manufacturing/command/rescheduleProductionOrder.ts +95 -0
  89. package/src/modules/manufacturing/command/resumeWorkOrder.generated.ts +6 -0
  90. package/src/modules/manufacturing/command/resumeWorkOrder.test.ts +122 -0
  91. package/src/modules/manufacturing/command/resumeWorkOrder.ts +94 -0
  92. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.generated.ts +6 -0
  93. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.test.ts +231 -0
  94. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.ts +137 -0
  95. package/src/modules/manufacturing/command/startWorkOrder.generated.ts +6 -0
  96. package/src/modules/manufacturing/command/startWorkOrder.test.ts +118 -0
  97. package/src/modules/manufacturing/command/startWorkOrder.ts +126 -0
  98. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.generated.ts +6 -0
  99. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.test.ts +153 -0
  100. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.ts +106 -0
  101. package/src/modules/manufacturing/command/unreleaseProductionOrder.generated.ts +6 -0
  102. package/src/modules/manufacturing/command/unreleaseProductionOrder.test.ts +140 -0
  103. package/src/modules/manufacturing/command/unreleaseProductionOrder.ts +131 -0
  104. package/src/modules/manufacturing/command/updateBillOfMaterial.generated.ts +6 -0
  105. package/src/modules/manufacturing/command/updateBillOfMaterial.test.ts +149 -0
  106. package/src/modules/manufacturing/command/updateBillOfMaterial.ts +174 -0
  107. package/src/modules/manufacturing/command/updateProductionOrder.generated.ts +6 -0
  108. package/src/modules/manufacturing/command/updateProductionOrder.test.ts +112 -0
  109. package/src/modules/manufacturing/command/updateProductionOrder.ts +145 -0
  110. package/src/modules/manufacturing/command/updateRouting.generated.ts +6 -0
  111. package/src/modules/manufacturing/command/updateRouting.test.ts +211 -0
  112. package/src/modules/manufacturing/command/updateRouting.ts +124 -0
  113. package/src/modules/manufacturing/command/updateWorkCenter.generated.ts +6 -0
  114. package/src/modules/manufacturing/command/updateWorkCenter.test.ts +152 -0
  115. package/src/modules/manufacturing/command/updateWorkCenter.ts +137 -0
  116. package/src/modules/manufacturing/db/.gitkeep +0 -0
  117. package/src/modules/manufacturing/db/billOfMaterial.ts +70 -0
  118. package/src/modules/manufacturing/db/billOfMaterialLine.ts +49 -0
  119. package/src/modules/manufacturing/db/costVarianceLine.ts +53 -0
  120. package/src/modules/manufacturing/db/manufacturingCostLine.ts +35 -0
  121. package/src/modules/manufacturing/db/manufacturingCostSettlementRecord.ts +39 -0
  122. package/src/modules/manufacturing/db/manufacturingCostSummary.ts +59 -0
  123. package/src/modules/manufacturing/db/productionOrder.ts +83 -0
  124. package/src/modules/manufacturing/db/productionOrderBomSnapshot.ts +44 -0
  125. package/src/modules/manufacturing/db/productionOrderCostBaseline.ts +44 -0
  126. package/src/modules/manufacturing/db/productionOrderMaterialRequirement.ts +57 -0
  127. package/src/modules/manufacturing/db/productionOrderRoutingSnapshot.ts +43 -0
  128. package/src/modules/manufacturing/db/routing.ts +63 -0
  129. package/src/modules/manufacturing/db/routingOperation.ts +57 -0
  130. package/src/modules/manufacturing/db/workCenter.ts +87 -0
  131. package/src/modules/manufacturing/db/workOrder.ts +65 -0
  132. package/src/modules/manufacturing/db/workOrderExecutionEvent.ts +54 -0
  133. package/src/modules/manufacturing/docs/commands/ActivateBillOfMaterial.md +50 -0
  134. package/src/modules/manufacturing/docs/commands/ActivateRouting.md +48 -0
  135. package/src/modules/manufacturing/docs/commands/ActivateWorkCenter.md +49 -0
  136. package/src/modules/manufacturing/docs/commands/CancelProductionOrder.md +48 -0
  137. package/src/modules/manufacturing/docs/commands/CloseProductionOrder.md +46 -0
  138. package/src/modules/manufacturing/docs/commands/CompleteProductionOrder.md +48 -0
  139. package/src/modules/manufacturing/docs/commands/CompleteWorkOrder.md +66 -0
  140. package/src/modules/manufacturing/docs/commands/CreateBillOfMaterial.md +54 -0
  141. package/src/modules/manufacturing/docs/commands/CreateProductionOrder.md +49 -0
  142. package/src/modules/manufacturing/docs/commands/CreateRouting.md +50 -0
  143. package/src/modules/manufacturing/docs/commands/CreateWorkCenter.md +51 -0
  144. package/src/modules/manufacturing/docs/commands/DeactivateBillOfMaterial.md +45 -0
  145. package/src/modules/manufacturing/docs/commands/DeactivateRouting.md +45 -0
  146. package/src/modules/manufacturing/docs/commands/DeactivateWorkCenter.md +45 -0
  147. package/src/modules/manufacturing/docs/commands/PauseWorkOrder.md +44 -0
  148. package/src/modules/manufacturing/docs/commands/RecordInventoryIssueOutcome.md +59 -0
  149. package/src/modules/manufacturing/docs/commands/RecordManufacturingCostSettlementAcknowledgment.md +49 -0
  150. package/src/modules/manufacturing/docs/commands/ReleaseProductionOrder.md +57 -0
  151. package/src/modules/manufacturing/docs/commands/ReopenProductionOrder.md +54 -0
  152. package/src/modules/manufacturing/docs/commands/ReportWorkOrderProgress.md +53 -0
  153. package/src/modules/manufacturing/docs/commands/RescheduleProductionOrder.md +45 -0
  154. package/src/modules/manufacturing/docs/commands/ResumeWorkOrder.md +44 -0
  155. package/src/modules/manufacturing/docs/commands/ReviewManufacturingCostSummary.md +52 -0
  156. package/src/modules/manufacturing/docs/commands/StartWorkOrder.md +46 -0
  157. package/src/modules/manufacturing/docs/commands/TechnicallyCompleteProductionOrder.md +51 -0
  158. package/src/modules/manufacturing/docs/commands/UnreleaseProductionOrder.md +46 -0
  159. package/src/modules/manufacturing/docs/commands/UpdateBillOfMaterial.md +48 -0
  160. package/src/modules/manufacturing/docs/commands/UpdateProductionOrder.md +48 -0
  161. package/src/modules/manufacturing/docs/commands/UpdateRouting.md +52 -0
  162. package/src/modules/manufacturing/docs/commands/UpdateWorkCenter.md +48 -0
  163. package/src/modules/manufacturing/docs/features/bill-of-material-management.md +83 -0
  164. package/src/modules/manufacturing/docs/features/manufacturing-cost-and-variance.md +191 -0
  165. package/src/modules/manufacturing/docs/features/production-order-lifecycle.md +103 -0
  166. package/src/modules/manufacturing/docs/features/routing-and-work-center-definition.md +63 -0
  167. package/src/modules/manufacturing/docs/features/work-order-execution.md +115 -0
  168. package/src/modules/manufacturing/docs/models/BillOfMaterial.md +60 -0
  169. package/src/modules/manufacturing/docs/models/ManufacturingCostSummary.md +66 -0
  170. package/src/modules/manufacturing/docs/models/ProductionOrder.md +76 -0
  171. package/src/modules/manufacturing/docs/models/Routing.md +58 -0
  172. package/src/modules/manufacturing/docs/models/WorkCenter.md +56 -0
  173. package/src/modules/manufacturing/docs/models/WorkOrder.md +63 -0
  174. package/src/modules/manufacturing/docs/queries/DetectBillOfMaterialCircularReference.md +39 -0
  175. package/src/modules/manufacturing/docs/queries/ExplodeBillOfMaterial.md +56 -0
  176. package/src/modules/manufacturing/docs/queries/GetBillOfMaterial.md +37 -0
  177. package/src/modules/manufacturing/docs/queries/GetManufacturingCostSummary.md +39 -0
  178. package/src/modules/manufacturing/docs/queries/GetProductionOrder.md +37 -0
  179. package/src/modules/manufacturing/docs/queries/GetRouting.md +39 -0
  180. package/src/modules/manufacturing/docs/queries/GetWorkCenter.md +35 -0
  181. package/src/modules/manufacturing/docs/queries/GetWorkOrder.md +38 -0
  182. package/src/modules/manufacturing/docs/queries/ListBillOfMaterialsByItem.md +42 -0
  183. package/src/modules/manufacturing/docs/queries/ListManufacturingCostSummariesByStatus.md +41 -0
  184. package/src/modules/manufacturing/docs/queries/ListProductionOrdersByStatus.md +41 -0
  185. package/src/modules/manufacturing/docs/queries/ListRoutingsByItem.md +42 -0
  186. package/src/modules/manufacturing/docs/queries/ListWorkCentersBySite.md +38 -0
  187. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByProductionOrder.md +39 -0
  188. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByWorkCenter.md +43 -0
  189. package/src/modules/manufacturing/executor/.gitkeep +0 -0
  190. package/src/modules/manufacturing/generated/enums.ts +113 -0
  191. package/src/modules/manufacturing/generated/kysely-tailordb.ts +247 -0
  192. package/src/modules/manufacturing/index.ts +2 -0
  193. package/src/modules/manufacturing/lib/_db_deps.ts +22 -0
  194. package/src/modules/manufacturing/lib/errors.generated.ts +592 -0
  195. package/src/modules/manufacturing/lib/permissions.generated.ts +35 -0
  196. package/src/modules/manufacturing/lib/types.ts +111 -0
  197. package/src/modules/manufacturing/module.ts +226 -0
  198. package/src/modules/manufacturing/permissions.ts +3 -0
  199. package/src/modules/manufacturing/query/.gitkeep +0 -0
  200. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.generated.ts +5 -0
  201. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.test.ts +115 -0
  202. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.ts +79 -0
  203. package/src/modules/manufacturing/query/explodeBillOfMaterial.generated.ts +5 -0
  204. package/src/modules/manufacturing/query/explodeBillOfMaterial.test.ts +445 -0
  205. package/src/modules/manufacturing/query/explodeBillOfMaterial.ts +306 -0
  206. package/src/modules/manufacturing/query/getBillOfMaterial.generated.ts +5 -0
  207. package/src/modules/manufacturing/query/getBillOfMaterial.test.ts +64 -0
  208. package/src/modules/manufacturing/query/getBillOfMaterial.ts +27 -0
  209. package/src/modules/manufacturing/query/getManufacturingCostSummary.generated.ts +5 -0
  210. package/src/modules/manufacturing/query/getManufacturingCostSummary.test.ts +147 -0
  211. package/src/modules/manufacturing/query/getManufacturingCostSummary.ts +46 -0
  212. package/src/modules/manufacturing/query/getProductionOrder.generated.ts +5 -0
  213. package/src/modules/manufacturing/query/getProductionOrder.test.ts +139 -0
  214. package/src/modules/manufacturing/query/getProductionOrder.ts +84 -0
  215. package/src/modules/manufacturing/query/getRouting.generated.ts +5 -0
  216. package/src/modules/manufacturing/query/getRouting.test.ts +71 -0
  217. package/src/modules/manufacturing/query/getRouting.ts +34 -0
  218. package/src/modules/manufacturing/query/getWorkCenter.generated.ts +5 -0
  219. package/src/modules/manufacturing/query/getWorkCenter.test.ts +37 -0
  220. package/src/modules/manufacturing/query/getWorkCenter.ts +21 -0
  221. package/src/modules/manufacturing/query/getWorkOrder.generated.ts +5 -0
  222. package/src/modules/manufacturing/query/getWorkOrder.test.ts +73 -0
  223. package/src/modules/manufacturing/query/getWorkOrder.ts +28 -0
  224. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.generated.ts +5 -0
  225. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.test.ts +107 -0
  226. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.ts +58 -0
  227. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.generated.ts +5 -0
  228. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.test.ts +96 -0
  229. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.ts +77 -0
  230. package/src/modules/manufacturing/query/listProductionOrdersByStatus.generated.ts +5 -0
  231. package/src/modules/manufacturing/query/listProductionOrdersByStatus.test.ts +121 -0
  232. package/src/modules/manufacturing/query/listProductionOrdersByStatus.ts +83 -0
  233. package/src/modules/manufacturing/query/listRoutingsByItem.generated.ts +5 -0
  234. package/src/modules/manufacturing/query/listRoutingsByItem.test.ts +110 -0
  235. package/src/modules/manufacturing/query/listRoutingsByItem.ts +54 -0
  236. package/src/modules/manufacturing/query/listWorkCentersBySite.generated.ts +5 -0
  237. package/src/modules/manufacturing/query/listWorkCentersBySite.test.ts +81 -0
  238. package/src/modules/manufacturing/query/listWorkCentersBySite.ts +70 -0
  239. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.generated.ts +5 -0
  240. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.test.ts +102 -0
  241. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.ts +53 -0
  242. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.generated.ts +5 -0
  243. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.test.ts +143 -0
  244. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.ts +56 -0
  245. package/src/modules/manufacturing/seed/index.ts +19 -0
  246. package/src/modules/manufacturing/tailor.config.ts +13 -0
  247. package/src/modules/manufacturing/tailor.d.ts +13 -0
  248. package/src/modules/manufacturing/testing/commandTestUtils.ts +29 -0
  249. package/src/modules/manufacturing/testing/fixtures.ts +402 -0
  250. package/templates/scaffold/app/backend/package.json +9 -2
  251. package/templates/scaffold/app/backend/src/tests/utils/graphql-client.ts +66 -0
  252. package/templates/scaffold/app/backend/src/tests/utils/setup.ts +21 -0
  253. package/templates/scaffold/app/backend/tsconfig.json +9 -2
  254. package/templates/scaffold/app/backend/vitest.config.ts +35 -0
  255. package/templates/scaffold/app/frontend/package.json +2 -2
  256. package/templates/scaffold/module/__dot__gitignore +3 -0
  257. package/templates/scaffold/module/eslint.config.js +31 -0
  258. package/templates/scaffold/module/generated/kysely-tailordb.ts +3 -0
  259. package/templates/scaffold/module/lib/types.ts +1 -6
  260. package/templates/scaffold/module/package.json +26 -0
  261. package/templates/scaffold/module/tsconfig.json +16 -0
  262. /package/{templates/scaffold/module/generated → src/modules/manufacturing/command}/.gitkeep +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
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
+
18
+ ## 0.5.1
19
+
20
+ ### Patch Changes
21
+
22
+ - fb40e4d: - Fix mdschema binary resolution to use platform-specific native binaries, enabling Windows support
23
+ - Fix init commands to be idempotent when directory already exists instead of erroring
24
+ - Add package.json and dev tooling to module template
25
+ - Update app skill to use erp-kit app generate seed
26
+
3
27
  ## 0.5.0
4
28
 
5
29
  ### Minor 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";
@@ -364,12 +364,35 @@ function runLicenseCheck(configPath) {
364
364
  //#endregion
365
365
  //#region src/mdschema.ts
366
366
  const require = createRequire(import.meta.url);
367
+ const PLATFORMS = {
368
+ darwin: {
369
+ arm64: "@jackchuka/mdschema-darwin-arm64",
370
+ x64: "@jackchuka/mdschema-darwin-x64"
371
+ },
372
+ linux: {
373
+ arm64: "@jackchuka/mdschema-linux-arm64",
374
+ x64: "@jackchuka/mdschema-linux-x64"
375
+ },
376
+ win32: {
377
+ arm64: "@jackchuka/mdschema-windows-arm64",
378
+ x64: "@jackchuka/mdschema-windows-x64"
379
+ }
380
+ };
367
381
  function getMdschemaBin() {
368
382
  const pkgPath = require.resolve("@jackchuka/mdschema/package.json");
369
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
370
- const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.mdschema;
383
+ const pkgDir = path.dirname(pkgPath);
384
+ const mdschemaRequire = createRequire(pkgPath);
385
+ const ext = process.platform === "win32" ? ".exe" : "";
386
+ const pkg = PLATFORMS[process.platform]?.[process.arch];
387
+ if (pkg) try {
388
+ return mdschemaRequire.resolve(`${pkg}/bin/mdschema${ext}`);
389
+ } catch {}
390
+ const localBin = path.join(pkgDir, "bin", `mdschema${ext}`);
391
+ if (fs.existsSync(localBin)) return localBin;
392
+ const pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
393
+ const bin = typeof pkgJson.bin === "string" ? pkgJson.bin : pkgJson.bin?.mdschema;
371
394
  if (!bin) throw new Error("Could not resolve mdschema binary from package.json bin field");
372
- return path.join(path.dirname(pkgPath), bin);
395
+ return path.join(pkgDir, bin);
373
396
  }
374
397
  function runMdschema(args, cwd) {
375
398
  return new Promise((resolve) => {
@@ -429,11 +452,12 @@ const MODULE_PATHS = {
429
452
  }
430
453
  };
431
454
  const APP_PATHS = {
432
- storySegment: "story",
455
+ tests: { stories: "backend/src/tests/stories" },
433
456
  docs: {
434
457
  resolver: "docs/resolver",
435
458
  actors: "docs/actors",
436
459
  businessFlow: "docs/business-flow",
460
+ story: "story",
437
461
  screen: "docs/screen"
438
462
  }
439
463
  };
@@ -469,7 +493,14 @@ function appComposeCategories(root) {
469
493
  exclusions: [/\.test\.ts$/, /^index\.ts$/]
470
494
  }];
471
495
  }
472
- 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) {
473
504
  return [{
474
505
  name: "command-test-case",
475
506
  docPattern: `${root}/*/${MODULE_PATHS.docs.commands}/*.md`,
@@ -513,7 +544,7 @@ function buildCheckTargets(config) {
513
544
  glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/README.md`,
514
545
  schemaKey: "business-flow"
515
546
  }, {
516
- 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`,
517
548
  schemaKey: "story"
518
549
  }, {
519
550
  glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.screen}/*.md`,
@@ -552,10 +583,6 @@ async function runCheck(config, cwd) {
552
583
  //#region src/commands/init-module.ts
553
584
  function runInitModule(name, dir) {
554
585
  const moduleDir = path.resolve(dir, name);
555
- if (fs.existsSync(moduleDir)) {
556
- console.error(`Directory already exists: ${moduleDir}`);
557
- return 1;
558
- }
559
586
  fs.mkdirSync(moduleDir, { recursive: true });
560
587
  return 0;
561
588
  }
@@ -578,10 +605,6 @@ async function runInitModuleWithReadme(name, dir, cwd) {
578
605
  }
579
606
  function runInitApp(name, dir) {
580
607
  const appDir = path.resolve(dir, name);
581
- if (fs.existsSync(appDir)) {
582
- console.error(`Directory already exists: ${appDir}`);
583
- return 1;
584
- }
585
608
  fs.mkdirSync(appDir, { recursive: true });
586
609
  return 0;
587
610
  }
@@ -727,9 +750,9 @@ function parseItDescriptionsFromTest(content) {
727
750
  function toCamelCase(pascalCase) {
728
751
  return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
729
752
  }
730
- async function runTestCaseSyncCheck(root, cwd) {
753
+ async function runModuleTestCaseSyncCheck(root, cwd) {
731
754
  const errors = [];
732
- const categories = testCaseCategories(root);
755
+ const categories = moduleTestCaseCategories(root);
733
756
  for (const category of categories) {
734
757
  const docPaths = await fg(category.docPattern, { cwd });
735
758
  for (const docPath of docPaths) {
@@ -765,15 +788,57 @@ async function runTestCaseSyncCheck(root, cwd) {
765
788
  }
766
789
  return errors;
767
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
+ }
768
829
  //#endregion
769
830
  //#region src/commands/sync-check.ts
770
831
  async function runSyncCheck(config, cwd) {
771
832
  const sourceResult = await runSourceSyncCheck(config, cwd);
772
833
  if (config.modulesRoot) {
773
- const testCaseErrors = await runTestCaseSyncCheck(config.modulesRoot, cwd);
834
+ const testCaseErrors = await runModuleTestCaseSyncCheck(config.modulesRoot, cwd);
774
835
  sourceResult.errors.push(...testCaseErrors);
775
- sourceResult.exitCode = sourceResult.errors.length > 0 ? 1 : 0;
776
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;
777
842
  return sourceResult;
778
843
  }
779
844
  function formatSyncCheckReport(result) {
@@ -1077,10 +1142,22 @@ describe("${name}", () => {
1077
1142
  expect(resolver.default.name).toBe("${name}");
1078
1143
  });
1079
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
+ });
1080
1156
  `;
1081
1157
  } }
1082
1158
  };
1083
- function renderStub(type, name) {
1159
+ function renderStub(type, name, testCases) {
1160
+ if (type === "storyTest") return templates.storyTest.render(name, testCases);
1084
1161
  return templates[type].render(name);
1085
1162
  }
1086
1163
  //#endregion
@@ -1106,6 +1183,9 @@ function generateResolverStub(resolverName) {
1106
1183
  function generateResolverTestStub(resolverName) {
1107
1184
  return renderStub("resolverTest", resolverName);
1108
1185
  }
1186
+ function generateStoryTestStub(name, testCases) {
1187
+ return renderStub("storyTest", name, testCases);
1188
+ }
1109
1189
  //#endregion
1110
1190
  //#region src/generator/scaffold.ts
1111
1191
  function copyTemplateDir(srcDir, destDir, replacements, placeholderFiles) {
@@ -1129,7 +1209,16 @@ function copyTemplateDir(srcDir, destDir, replacements, placeholderFiles) {
1129
1209
  }
1130
1210
  }
1131
1211
  function scaffoldModuleBoilerplate(moduleDir, moduleName) {
1132
- copyTemplateDir(path.join(PACKAGE_ROOT, "templates", "scaffold", "module"), moduleDir, { "template-module": moduleName }, new Set(["permissions.ts", "tailor.config.ts"]));
1212
+ const templateDir = path.join(PACKAGE_ROOT, "templates", "scaffold", "module");
1213
+ const erpKitVersion = readErpKitVersion();
1214
+ copyTemplateDir(templateDir, moduleDir, {
1215
+ "template-module": moduleName,
1216
+ "\"workspace:*\"": `"${erpKitVersion}"`
1217
+ }, new Set([
1218
+ "package.json",
1219
+ "permissions.ts",
1220
+ "tailor.config.ts"
1221
+ ]));
1133
1222
  }
1134
1223
  function scaffoldAppBoilerplate(appDir, appName) {
1135
1224
  const templateDir = path.join(PACKAGE_ROOT, "templates", "scaffold", "app");
@@ -1290,7 +1379,7 @@ function resolveDocPath(type, name, modulePath) {
1290
1379
  if (type === "story") {
1291
1380
  const parts = name.split("/");
1292
1381
  if (parts.length !== 2) throw new Error(`Story name must be "<flow>/<story>" (e.g., "onboarding/admin--create-user")`);
1293
- 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`);
1294
1383
  }
1295
1384
  if (MODULE_DIR_MAP[type]) return path.join(modulePath, MODULE_DIR_MAP[type], `${name}.md`);
1296
1385
  if (APP_DIR_MAP[type]) return path.join(modulePath, APP_DIR_MAP[type], `${name}.md`);
@@ -1516,19 +1605,16 @@ function parseExceptionHandlingFromRaw(markdown) {
1516
1605
  function runGenerateAppCode(appPath) {
1517
1606
  const appName = path.basename(appPath);
1518
1607
  scaffoldAppBoilerplate(appPath, appName);
1608
+ generateResolverStubs(appPath, appName);
1609
+ generateStoryTestStubs(appPath, appName);
1610
+ return 0;
1611
+ }
1612
+ function generateResolverStubs(appPath, appName) {
1519
1613
  const docsDir = path.join(appPath, "docs", "resolver");
1520
- const resolverDir = path.join(appPath, "backend", "src", "resolvers");
1521
- if (!fs.existsSync(docsDir)) {
1522
- console.log(`No docs/resolver/ directory found — skipping resolver generation`);
1523
- console.log(`Generated boilerplate for ${appName}`);
1524
- return 0;
1525
- }
1614
+ if (!fs.existsSync(docsDir)) return;
1526
1615
  const mdFiles = fs.readdirSync(docsDir).filter((f) => f.endsWith(".md"));
1527
- if (mdFiles.length === 0) {
1528
- console.log(`No resolver docs found skipping resolver generation`);
1529
- console.log(`Generated boilerplate for ${appName}`);
1530
- return 0;
1531
- }
1616
+ if (mdFiles.length === 0) return;
1617
+ const resolverDir = path.join(appPath, "backend", "src", "resolvers");
1532
1618
  fs.mkdirSync(resolverDir, { recursive: true });
1533
1619
  let generated = 0;
1534
1620
  for (const file of mdFiles) {
@@ -1546,8 +1632,26 @@ function runGenerateAppCode(appPath) {
1546
1632
  console.log(` scaffolded ${path.relative(appPath, testFile)}`);
1547
1633
  }
1548
1634
  }
1549
- console.log(`Scaffolded ${generated} resolver(s) for ${appName}`);
1550
- 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}`);
1551
1655
  }
1552
1656
  //#endregion
1553
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.0",
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
  >
@@ -99,16 +102,18 @@ Do not directly mutate module-owned tables via Kysely — always use module comm
99
102
  ### Phase 5: Generated Files
100
103
 
101
104
  - **Kysely types** (`src/generated/kysely-tailordb.ts`) — Auto-generated by `pnpm generate` after deployment. Before deployment, create a minimal placeholder so the codebase typechecks.
102
- - **Seed data** (`seed/`) — `exec.mjs` runner + `data/*.jsonl` (records) and `data/*.schema.ts` (validation). Create seed records for initial data (permissions, roles, test users, etc.). Seeded users must have concrete permission keys required by the wired modules.
105
+ - **Seed data** (`seed/`) — `exec.mjs` runner + `data/*.jsonl` (records) and `data/*.schema.ts` (validation).
106
+ 1. **Module seed** — Run `pnpm erp-kit app generate seed -p <app-path>` to generate module-provided defaults (currencies, units, UoM categories, exchange rates). This writes JSONL files to `seed/data/`, skipping files that already exist.
107
+ 2. **App-specific seed** — Manually create seed records for permissions, roles, test users, and domain-specific data. Seeded users must have concrete permission keys required by the wired modules.
103
108
 
104
109
  ### Phase 6: Deploy Backend & Next Steps
105
110
 
106
111
  Frontend implementation requires a deployed backend (for GraphQL schema generation). Prompt the user to:
107
112
 
108
- 1. Create a workspace and configure `.env` (including `TAILOR_PLATFORM_WORKSPACE_ID`)
109
- 2. Run `pnpm deploy` and `pnpm generate`
110
- 3. Run `pnpm seed` if seed data was created
111
- 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`
112
117
 
113
118
  Wait for the user to confirm deployment is complete before proceeding.
114
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
  }
@@ -14,12 +14,19 @@ describe("runInitModule", () => {
14
14
  });
15
15
 
16
16
  it("creates module directory", () => {
17
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "init-module-test-"));
18
+ const exitCode = runInitModule("my-module", tmpDir);
19
+ expect(exitCode).toBe(0);
20
+ expect(fs.existsSync(path.join(tmpDir, "my-module"))).toBe(true);
21
+ });
22
+
23
+ it("succeeds if module directory already exists", () => {
17
24
  tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "init-module-test-"));
18
25
  const moduleDir = path.join(tmpDir, "my-module");
19
26
  fs.mkdirSync(moduleDir, { recursive: true });
20
27
 
21
28
  const exitCode = runInitModule("my-module", tmpDir);
22
- expect(exitCode).toBe(1); // should fail — dir exists
29
+ expect(exitCode).toBe(0);
23
30
  });
24
31
  });
25
32
 
@@ -32,12 +39,19 @@ describe("runInitApp", () => {
32
39
  }
33
40
  });
34
41
 
35
- it("errors if app directory already exists", () => {
42
+ it("creates app directory", () => {
43
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "init-app-test-"));
44
+ const exitCode = runInitApp("my-app", tmpDir);
45
+ expect(exitCode).toBe(0);
46
+ expect(fs.existsSync(path.join(tmpDir, "my-app"))).toBe(true);
47
+ });
48
+
49
+ it("succeeds if app directory already exists", () => {
36
50
  tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "init-app-test-"));
37
51
  const appDir = path.join(tmpDir, "my-app");
38
52
  fs.mkdirSync(appDir, { recursive: true });
39
53
 
40
54
  const exitCode = runInitApp("my-app", tmpDir);
41
- expect(exitCode).toBe(1);
55
+ expect(exitCode).toBe(0);
42
56
  });
43
57
  });
@@ -5,12 +5,6 @@ import { MODULE_SCHEMAS, APP_COMPOSE_SCHEMAS } from "../schemas";
5
5
 
6
6
  export function runInitModule(name: string, dir: string): number {
7
7
  const moduleDir = path.resolve(dir, name);
8
-
9
- if (fs.existsSync(moduleDir)) {
10
- console.error(`Directory already exists: ${moduleDir}`);
11
- return 1;
12
- }
13
-
14
8
  fs.mkdirSync(moduleDir, { recursive: true });
15
9
  return 0;
16
10
  }
@@ -40,12 +34,6 @@ export async function runInitModuleWithReadme(
40
34
 
41
35
  export function runInitApp(name: string, dir: string): number {
42
36
  const appDir = path.resolve(dir, name);
43
-
44
- if (fs.existsSync(appDir)) {
45
- console.error(`Directory already exists: ${appDir}`);
46
- return 1;
47
- }
48
-
49
37
  fs.mkdirSync(appDir, { recursive: true });
50
38
  return 0;
51
39
  }
@@ -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;