@open-mercato/cli 0.6.7 → 0.6.8-canary.320.1.bf889f49e4

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 (335) hide show
  1. package/.turbo/turbo-build.log +77 -4
  2. package/AGENTS.md +6 -1
  3. package/build.mjs +68 -11
  4. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +32 -88
  5. package/dist/agentic/codex/enforcement-rules.md +2 -16
  6. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +19 -50
  7. package/dist/agentic/cursor/rules/entity-guard.mdc +9 -21
  8. package/dist/agentic/cursor/rules/generated-guard.mdc +3 -5
  9. package/dist/agentic/cursor/rules/open-mercato.mdc +3 -29
  10. package/dist/agentic/guides/ai-workflows.md +76 -0
  11. package/dist/agentic/guides/architecture.md +107 -0
  12. package/dist/agentic/guides/backend-ui.md +86 -0
  13. package/dist/agentic/guides/contracts.md +92 -0
  14. package/dist/agentic/guides/extensions.md +121 -0
  15. package/dist/agentic/guides/framework-extension-points.md +36 -0
  16. package/dist/agentic/guides/integrations.md +81 -0
  17. package/dist/agentic/guides/module-facts.json +116757 -3951
  18. package/dist/agentic/guides/module-system.md +12 -127
  19. package/dist/agentic/guides/modules/ai_assistant.md +227 -28
  20. package/dist/agentic/guides/modules/api_docs.md +58 -2
  21. package/dist/agentic/guides/modules/api_keys.md +92 -7
  22. package/dist/agentic/guides/modules/attachments.md +119 -14
  23. package/dist/agentic/guides/modules/audit_logs.md +131 -10
  24. package/dist/agentic/guides/modules/auth.md +276 -33
  25. package/dist/agentic/guides/modules/business_rules.md +149 -14
  26. package/dist/agentic/guides/modules/catalog.md +482 -43
  27. package/dist/agentic/guides/modules/channel_gmail.md +82 -3
  28. package/dist/agentic/guides/modules/channel_imap.md +82 -3
  29. package/dist/agentic/guides/modules/checkout.md +305 -27
  30. package/dist/agentic/guides/modules/communication_channels.md +315 -33
  31. package/dist/agentic/guides/modules/configs.md +104 -8
  32. package/dist/agentic/guides/modules/content.md +57 -2
  33. package/dist/agentic/guides/modules/currencies.md +152 -17
  34. package/dist/agentic/guides/modules/customer_accounts.md +387 -45
  35. package/dist/agentic/guides/modules/customers.md +746 -85
  36. package/dist/agentic/guides/modules/dashboards.md +155 -10
  37. package/dist/agentic/guides/modules/data_sync.md +149 -16
  38. package/dist/agentic/guides/modules/design_system.md +102 -0
  39. package/dist/agentic/guides/modules/dictionaries.md +107 -12
  40. package/dist/agentic/guides/modules/directory.md +148 -15
  41. package/dist/agentic/guides/modules/entities.md +145 -13
  42. package/dist/agentic/guides/modules/events.md +59 -2
  43. package/dist/agentic/guides/modules/feature_toggles.md +119 -9
  44. package/dist/agentic/guides/modules/gateway_stripe.md +92 -4
  45. package/dist/agentic/guides/modules/generators.md +45 -2
  46. package/dist/agentic/guides/modules/inbox_ops.md +207 -27
  47. package/dist/agentic/guides/modules/integrations.md +149 -16
  48. package/dist/agentic/guides/modules/messages.md +223 -24
  49. package/dist/agentic/guides/modules/notifications.md +96 -8
  50. package/dist/agentic/guides/modules/onboarding.md +77 -6
  51. package/dist/agentic/guides/modules/payment_gateways.md +168 -21
  52. package/dist/agentic/guides/modules/perspectives.md +77 -7
  53. package/dist/agentic/guides/modules/planner.md +136 -18
  54. package/dist/agentic/guides/modules/portal.md +93 -2
  55. package/dist/agentic/guides/modules/progress.md +104 -14
  56. package/dist/agentic/guides/modules/query_index.md +154 -15
  57. package/dist/agentic/guides/modules/record_locks.md +206 -21
  58. package/dist/agentic/guides/modules/resources.md +197 -30
  59. package/dist/agentic/guides/modules/sales.md +687 -84
  60. package/dist/agentic/guides/modules/scheduler.md +133 -14
  61. package/dist/agentic/guides/modules/search.md +170 -6
  62. package/dist/agentic/guides/modules/security.md +294 -35
  63. package/dist/agentic/guides/modules/shipping_carriers.md +134 -18
  64. package/dist/agentic/guides/modules/sso.md +151 -26
  65. package/dist/agentic/guides/modules/staff.md +372 -57
  66. package/dist/agentic/guides/modules/storage_s3.md +95 -5
  67. package/dist/agentic/guides/modules/sync_akeneo.md +85 -3
  68. package/dist/agentic/guides/modules/sync_excel.md +75 -6
  69. package/dist/agentic/guides/modules/system_status_overlays.md +60 -2
  70. package/dist/agentic/guides/modules/translations.md +104 -10
  71. package/dist/agentic/guides/modules/webhooks.md +203 -24
  72. package/dist/agentic/guides/modules/wms.md +469 -47
  73. package/dist/agentic/guides/modules/workflows.md +302 -43
  74. package/dist/agentic/guides/reference-module-facts.json +6942 -0
  75. package/dist/agentic/guides/reference-modules/example.md +462 -0
  76. package/dist/agentic/guides/spec-delivery.md +56 -0
  77. package/dist/agentic/guides/testing-debugging.md +80 -0
  78. package/dist/agentic/guides/upstream/AGENTS.md +300 -0
  79. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +334 -0
  80. package/dist/agentic/guides/upstream/manifest.json +8 -0
  81. package/dist/agentic/shared/AGENTS.md.template +120 -311
  82. package/dist/agentic/shared/ai/agentic.config.json +2 -0
  83. package/dist/agentic/shared/ai/harness/README.md +50 -0
  84. package/dist/agentic/shared/ai/harness/RELEASE.md +59 -0
  85. package/dist/agentic/shared/ai/harness/cases.json +20596 -0
  86. package/dist/agentic/shared/ai/harness/cases.schema.json +196 -0
  87. package/dist/agentic/shared/ai/harness/design-system-inventory.json +6326 -0
  88. package/dist/agentic/shared/ai/harness/fixtures/index.json +51 -0
  89. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +166 -0
  90. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +21 -0
  91. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +96 -0
  92. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +172 -0
  93. package/dist/agentic/shared/ai/harness/knowledge-change.schema.json +228 -0
  94. package/dist/agentic/shared/ai/harness/release-matrix.json +163 -0
  95. package/dist/agentic/shared/ai/harness/release-result.schema.json +219 -0
  96. package/dist/agentic/shared/ai/harness/result.schema.json +136 -0
  97. package/dist/agentic/shared/ai/harness/routing-response.schema.json +22 -0
  98. package/dist/agentic/shared/ai/harness/source-link-inventory.json +1706 -0
  99. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +73 -0
  100. package/dist/agentic/shared/ai/harness/validators.json +82 -0
  101. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +2121 -0
  102. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +1096 -0
  103. package/dist/agentic/shared/ai/harness/writable-spec-oracles.mjs +602 -0
  104. package/dist/agentic/shared/ai/lessons/_template.md +16 -0
  105. package/dist/agentic/shared/ai/lessons.md +26 -3
  106. package/dist/agentic/shared/ai/review-checklist.md +36 -0
  107. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +21 -0
  108. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +4 -0
  109. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +98 -0
  110. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +21 -241
  111. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +31 -0
  112. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +53 -0
  113. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +15 -0
  114. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +11 -0
  115. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +30 -0
  116. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +12 -0
  117. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +13 -0
  118. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +14 -0
  119. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +28 -0
  120. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +12 -0
  121. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +22 -0
  122. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +12 -0
  123. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +19 -0
  124. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +17 -601
  125. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +21 -0
  126. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +15 -0
  127. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +16 -0
  128. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +10 -0
  129. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +15 -310
  130. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +19 -0
  131. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +31 -0
  132. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +100 -0
  133. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +16 -0
  134. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/knowledge-change.md +133 -0
  135. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +17 -0
  136. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +25 -0
  137. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +11 -0
  138. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +13 -0
  139. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +15 -97
  140. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +18 -0
  141. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +20 -0
  142. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +33 -152
  143. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +33 -0
  144. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +40 -0
  145. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +27 -0
  146. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +24 -0
  147. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +25 -708
  148. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +16 -0
  149. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +25 -0
  150. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +15 -0
  151. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +12 -0
  152. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +34 -0
  153. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +4 -0
  154. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +11 -0
  155. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +31 -0
  156. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +39 -0
  157. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +43 -0
  158. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +11 -0
  159. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +20 -820
  160. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +32 -0
  161. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +172 -0
  162. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +14 -0
  163. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +83 -0
  164. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +40 -0
  165. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +11 -0
  166. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +22 -0
  167. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +13 -0
  168. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +41 -0
  169. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +19 -0
  170. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +44 -0
  171. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +39 -0
  172. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +20 -0
  173. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +57 -0
  174. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +11 -0
  175. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +486 -0
  176. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +20 -843
  177. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +42 -0
  178. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +27 -0
  179. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +13 -0
  180. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +43 -0
  181. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +11 -0
  182. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +15 -63
  183. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +15 -0
  184. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +17 -467
  185. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +29 -0
  186. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +14 -0
  187. package/dist/agentic/shared/ai/skills/tiers.json +143 -21
  188. package/dist/agentic/shared/ai/skills/tiers.schema.json +52 -40
  189. package/dist/agentic/shared/ai/specs/2026-08-06-reference-module-activation.md +248 -0
  190. package/dist/agentic/shared/ai/specs/README.md +4 -3
  191. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +226 -10
  192. package/dist/agentic/shared/ai/trackers/github.md +108 -4
  193. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +224 -0
  194. package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
  195. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +3812 -0
  196. package/dist/agentic/shared/scripts/execution-sandbox.mjs +329 -0
  197. package/dist/agentic/shared/scripts/framework-context.mjs +888 -0
  198. package/dist/agentic/shared/scripts/install-skills.mjs +1029 -0
  199. package/dist/agentic/shared/scripts/install-skills.sh +4 -694
  200. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +222 -0
  201. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1774 -0
  202. package/dist/agentic/shared/scripts/validate-knowledge-change.mjs +1172 -0
  203. package/dist/bin.js +32 -34
  204. package/dist/bin.js.map +2 -2
  205. package/dist/lib/__integration__/TC-INT-008.spec.js +88 -8
  206. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  207. package/dist/lib/agentic-init.js +13 -3
  208. package/dist/lib/agentic-init.js.map +2 -2
  209. package/dist/lib/agentic-setup.js +434 -186
  210. package/dist/lib/agentic-setup.js.map +2 -2
  211. package/dist/lib/cli-bootstrap-mode.js +32 -0
  212. package/dist/lib/cli-bootstrap-mode.js.map +7 -0
  213. package/dist/lib/dev-supervisor-manifest.js +105 -0
  214. package/dist/lib/dev-supervisor-manifest.js.map +7 -0
  215. package/dist/lib/generate-watch-events.js +15 -0
  216. package/dist/lib/generate-watch-events.js.map +2 -2
  217. package/dist/lib/generators/entity-ids.js +11 -24
  218. package/dist/lib/generators/entity-ids.js.map +2 -2
  219. package/dist/lib/generators/extensions/injection-widgets.js +20 -0
  220. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  221. package/dist/lib/generators/module-di.js +10 -23
  222. package/dist/lib/generators/module-di.js.map +2 -2
  223. package/dist/lib/generators/module-entities.js +10 -23
  224. package/dist/lib/generators/module-entities.js.map +2 -2
  225. package/dist/lib/generators/module-extension-facts.js +1882 -0
  226. package/dist/lib/generators/module-extension-facts.js.map +7 -0
  227. package/dist/lib/generators/module-fact-sources.js +70 -0
  228. package/dist/lib/generators/module-fact-sources.js.map +7 -0
  229. package/dist/lib/generators/module-facts-discovery.js +64 -7
  230. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  231. package/dist/lib/generators/module-facts.js +1913 -52
  232. package/dist/lib/generators/module-facts.js.map +3 -3
  233. package/dist/lib/generators/module-override-targets.js +712 -0
  234. package/dist/lib/generators/module-override-targets.js.map +7 -0
  235. package/dist/lib/generators/module-registry.js +405 -36
  236. package/dist/lib/generators/module-registry.js.map +2 -2
  237. package/dist/lib/generators/route-manifest-shards.js +170 -0
  238. package/dist/lib/generators/route-manifest-shards.js.map +7 -0
  239. package/dist/lib/in-process-generate-watcher.js +12 -3
  240. package/dist/lib/in-process-generate-watcher.js.map +2 -2
  241. package/dist/lib/load-env.js +24 -0
  242. package/dist/lib/load-env.js.map +7 -0
  243. package/dist/lib/queue-worker-supervisor.js.map +2 -2
  244. package/dist/lib/resolver.js +28 -3
  245. package/dist/lib/resolver.js.map +2 -2
  246. package/dist/lib/telemetry-init.js +358 -0
  247. package/dist/lib/telemetry-init.js.map +7 -0
  248. package/dist/lib/testing/integration.js +5 -2
  249. package/dist/lib/testing/integration.js.map +2 -2
  250. package/dist/lib/utils.js +15 -8
  251. package/dist/lib/utils.js.map +2 -2
  252. package/dist/mercato.js +35 -9
  253. package/dist/mercato.js.map +2 -2
  254. package/package.json +13 -11
  255. package/src/__tests__/mercato.test.ts +144 -0
  256. package/src/bin.ts +60 -42
  257. package/src/lib/__integration__/TC-INT-008.spec.ts +107 -9
  258. package/src/lib/__tests__/agentic-init.test.ts +41 -5
  259. package/src/lib/__tests__/agentic-setup.ownership.test.ts +386 -0
  260. package/src/lib/__tests__/cli-bootstrap-mode.test.ts +22 -0
  261. package/src/lib/__tests__/content-stable-writes.test.ts +74 -0
  262. package/src/lib/__tests__/dev-supervisor-manifest.test.ts +131 -0
  263. package/src/lib/__tests__/generate-watch-events.test.ts +76 -0
  264. package/src/lib/__tests__/in-process-generate-watcher.test.ts +52 -1
  265. package/src/lib/__tests__/injection-widget-id-validation.test.ts +184 -0
  266. package/src/lib/__tests__/load-env.test.ts +65 -0
  267. package/src/lib/__tests__/queue-worker-supervisor.test.ts +33 -0
  268. package/src/lib/__tests__/resolver.enterprise.test.ts +50 -0
  269. package/src/lib/__tests__/telemetry-init.test.ts +298 -0
  270. package/src/lib/agentic-init.ts +15 -3
  271. package/src/lib/agentic-setup.ts +573 -222
  272. package/src/lib/cli-bootstrap-mode.ts +31 -0
  273. package/src/lib/dev-supervisor-manifest.ts +142 -0
  274. package/src/lib/generate-watch-events.ts +14 -0
  275. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
  276. package/src/lib/generators/__tests__/entity-ids.test.ts +32 -0
  277. package/src/lib/generators/__tests__/helpers/override-target-diagnostic-fixtures.ts +150 -0
  278. package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
  279. package/src/lib/generators/__tests__/module-extension-facts.test.ts +603 -0
  280. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +139 -2
  281. package/src/lib/generators/__tests__/module-facts.computed-di-token.test.ts +88 -0
  282. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -0
  283. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +54 -10
  284. package/src/lib/generators/__tests__/module-facts.example-fact-coverage.test.ts +414 -0
  285. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +108 -0
  286. package/src/lib/generators/__tests__/module-facts.local-reference.test.ts +393 -0
  287. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
  288. package/src/lib/generators/__tests__/module-facts.search-entity-id.test.ts +151 -0
  289. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +160 -0
  290. package/src/lib/generators/__tests__/module-override-targets.diagnostic-wiring.test.ts +92 -0
  291. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
  292. package/src/lib/generators/__tests__/module-override-targets.test.ts +328 -0
  293. package/src/lib/generators/__tests__/module-override-targets.unknown-mode.test.ts +117 -0
  294. package/src/lib/generators/__tests__/module-subset.test.ts +266 -0
  295. package/src/lib/generators/__tests__/output-snapshots.test.ts +3 -3
  296. package/src/lib/generators/__tests__/route-manifest-shards.test.ts +62 -0
  297. package/src/lib/generators/entity-ids.ts +10 -26
  298. package/src/lib/generators/extensions/injection-widgets.ts +51 -0
  299. package/src/lib/generators/module-di.ts +9 -25
  300. package/src/lib/generators/module-entities.ts +9 -25
  301. package/src/lib/generators/module-extension-facts.ts +2472 -0
  302. package/src/lib/generators/module-fact-sources.ts +108 -0
  303. package/src/lib/generators/module-facts-discovery.ts +104 -7
  304. package/src/lib/generators/module-facts.ts +2747 -138
  305. package/src/lib/generators/module-override-targets.ts +907 -0
  306. package/src/lib/generators/module-registry.ts +448 -35
  307. package/src/lib/generators/route-manifest-shards.ts +146 -0
  308. package/src/lib/in-process-generate-watcher.ts +13 -4
  309. package/src/lib/load-env.ts +30 -0
  310. package/src/lib/queue-worker-supervisor.ts +7 -2
  311. package/src/lib/resolver.ts +34 -3
  312. package/src/lib/telemetry-init.ts +461 -0
  313. package/src/lib/testing/__tests__/integration.test.ts +4 -0
  314. package/src/lib/testing/integration.ts +5 -2
  315. package/src/lib/utils.ts +24 -16
  316. package/src/mercato.ts +46 -9
  317. package/dist/agentic/guides/cache.md +0 -45
  318. package/dist/agentic/guides/core.md +0 -453
  319. package/dist/agentic/guides/events.md +0 -117
  320. package/dist/agentic/guides/queue.md +0 -53
  321. package/dist/agentic/guides/search.md +0 -115
  322. package/dist/agentic/guides/shared.md +0 -108
  323. package/dist/agentic/guides/ui.md +0 -347
  324. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +0 -27
  325. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +0 -27
  326. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +0 -260
  327. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +0 -146
  328. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +0 -84
  329. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +0 -193
  330. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +0 -34
  331. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +0 -762
  332. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +0 -50
  333. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +0 -97
  334. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +0 -271
  335. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +0 -101
@@ -578,6 +578,66 @@ export { POST, PUT, DELETE }
578
578
  expect(apiRoutes).toContain('methods: ["GET", "POST", "PUT", "DELETE"]')
579
579
  })
580
580
 
581
+ it('partitions API and backend loader graphs while preserving legacy manifests', async () => {
582
+ scaffoldModule(tmpDir, 'customers', 'pkg', ['api/people/route.ts', 'backend/people/page.tsx'])
583
+ scaffoldModule(tmpDir, 'sales', 'pkg', ['api/orders/route.ts', 'backend/orders/page.tsx'])
584
+ touchFile(
585
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'customers', 'api', 'people', 'route.ts'),
586
+ `export const metadata = { path: '/customers/people' }\nexport function GET() { return null }\n`,
587
+ )
588
+ touchFile(
589
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'customers', 'backend', 'people', 'page.tsx'),
590
+ `export const metadata = { pageTitle: 'People' }\nexport default function Page() { return null }\n`,
591
+ )
592
+ touchFile(
593
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'sales', 'api', 'orders', 'route.ts'),
594
+ `export const metadata = { path: '/sales/orders' }\nexport function POST() { return null }\n`,
595
+ )
596
+ touchFile(
597
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'sales', 'backend', 'orders', 'page.tsx'),
598
+ `export const metadata = { pageTitle: 'Orders' }\nexport default function Page() { return null }\n`,
599
+ )
600
+
601
+ const resolver = createMockResolver(tmpDir, [
602
+ { id: 'customers', from: '@open-mercato/core' },
603
+ { id: 'sales', from: '@open-mercato/core' },
604
+ ])
605
+ await generateModuleRegistry({ resolver, quiet: true })
606
+
607
+ const outputDir = path.join(tmpDir, 'output', 'generated')
608
+ const generatedNames = fs.readdirSync(outputDir)
609
+ const apiShardNames = generatedNames.filter((name) => /^api-route-shard\..+\.generated\.ts$/.test(name))
610
+ const backendShardNames = generatedNames.filter((name) => /^backend-route-shard\..+\.generated\.ts$/.test(name))
611
+ const apiShards = apiShardNames.map((name) => fs.readFileSync(path.join(outputDir, name), 'utf8'))
612
+ const backendShards = backendShardNames.map((name) => fs.readFileSync(path.join(outputDir, name), 'utf8'))
613
+
614
+ expect(apiShardNames).toHaveLength(2)
615
+ expect(backendShardNames).toHaveLength(2)
616
+ expect(apiShards.filter((source) => source.includes('/customers/people'))).toHaveLength(1)
617
+ expect(apiShards.filter((source) => source.includes('/sales/orders'))).toHaveLength(1)
618
+ expect(backendShards.filter((source) => source.includes('/backend/people'))).toHaveLength(1)
619
+ expect(backendShards.filter((source) => source.includes('/backend/orders'))).toHaveLength(1)
620
+
621
+ const apiLoader = readGenerated(tmpDir, 'api-route-shards.generated.ts')!
622
+ const backendLoader = readGenerated(tmpDir, 'backend-route-shards.generated.ts')!
623
+ expect(apiLoader).not.toContain('@open-mercato/core/modules/customers/api')
624
+ expect(backendLoader).not.toContain('@open-mercato/core/modules/customers/backend')
625
+
626
+ const apiMetadata = readGenerated(tmpDir, 'api-route-metadata.generated.ts')!
627
+ const backendMetadata = readGenerated(tmpDir, 'backend-route-metadata.generated.ts')!
628
+ expect(apiMetadata).not.toContain('load: async')
629
+ expect(backendMetadata).not.toContain('load: async')
630
+ expect(readGenerated(tmpDir, 'api-routes.generated.ts')).toContain('/customers/people')
631
+ expect(readGenerated(tmpDir, 'backend-routes.generated.ts')).toContain('/backend/orders')
632
+
633
+ const customersOnlyResolver = createMockResolver(tmpDir, [
634
+ { id: 'customers', from: '@open-mercato/core' },
635
+ ])
636
+ await generateModuleRegistry({ resolver: customersOnlyResolver, quiet: true })
637
+ const remainingApiShards = fs.readdirSync(outputDir).filter((name) => /^api-route-shard\..+\.generated\.ts$/.test(name))
638
+ expect(remainingApiShards).toHaveLength(1)
639
+ })
640
+
581
641
  it('keeps all HTTP methods for route.ts files that destructure-export crud handlers', async () => {
582
642
  scaffoldModule(tmpDir, 'example', 'pkg', ['api/todos/route.ts'])
583
643
  touchFile(
@@ -702,6 +762,24 @@ export function GET() {
702
762
  expect(output).toContain('features:')
703
763
  })
704
764
 
765
+ it('normalizes singular app-owned integration exports without requiring plural properties', async () => {
766
+ touchFile(
767
+ path.join(tmpDir, 'app', 'src', 'modules', 'mailbox', 'integration.ts'),
768
+ "export const integration = { id: 'mailbox' }\nexport default integration\n",
769
+ )
770
+ const resolver = createMockResolver(tmpDir, [{ id: 'mailbox', from: '@app' }])
771
+ const result = await generateModuleRegistry({ resolver, quiet: true })
772
+
773
+ expect(result.errors).toEqual([])
774
+ for (const file of ['modules.generated.ts', 'modules.runtime.generated.ts']) {
775
+ const output = readGenerated(tmpDir, file)!
776
+ expect(output).toContain('as unknown as { integrations?:')
777
+ expect(output).toContain('.integration ? [')
778
+ expect(output).not.toMatch(/INTEGRATION_[A-Za-z0-9_]+\.integrations/)
779
+ expect(output).not.toMatch(/INTEGRATION_[A-Za-z0-9_]+\.bundles/)
780
+ }
781
+ })
782
+
705
783
  it('escapes generated module specifiers for lazy imports', async () => {
706
784
  touchFile(
707
785
  path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'safe_mod', 'subscribers', `o'hare.ts`),
@@ -1044,6 +1122,85 @@ export default async function handle(): Promise<void> {}
1044
1122
  expect(output).toContain("workers:")
1045
1123
  })
1046
1124
 
1125
+ it('writes a content-stable primitive dev supervisor manifest', async () => {
1126
+ scaffoldModule(tmpDir, 'events', 'pkg', ['workers/dispatch.ts'])
1127
+ scaffoldModule(tmpDir, 'scheduler', 'pkg', ['cli.ts'])
1128
+ touchFile(
1129
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'events', 'workers', 'dispatch.ts'),
1130
+ `export const metadata = { id: 'events:dispatch', queue: 'events', concurrency: 3 }\nexport default async function dispatch() {}\n`,
1131
+ )
1132
+ touchFile(
1133
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'scheduler', 'cli.ts'),
1134
+ `export default [{ command: 'start', run() {} }]\n`,
1135
+ )
1136
+ const resolver = createMockResolver(tmpDir, [
1137
+ { id: 'events', from: '@open-mercato/core' },
1138
+ { id: 'scheduler', from: '@open-mercato/core' },
1139
+ ])
1140
+
1141
+ const first = await generateModuleRegistryCli({ resolver, quiet: true })
1142
+ const manifestPath = path.join(tmpDir, 'output', 'generated', 'dev-supervisor.generated.json')
1143
+ expect(JSON.parse(fs.readFileSync(manifestPath, 'utf8'))).toEqual({
1144
+ version: 1,
1145
+ workers: [{
1146
+ id: 'events:dispatch',
1147
+ moduleId: 'events',
1148
+ queue: 'events',
1149
+ concurrency: 3,
1150
+ }],
1151
+ schedulerStartStatus: 'ok',
1152
+ requiresFullBootstrap: false,
1153
+ })
1154
+ expect(first.filesWritten).toContain(manifestPath)
1155
+
1156
+ const second = await generateModuleRegistryCli({ resolver, quiet: true })
1157
+ expect(second.filesUnchanged).toContain(manifestPath)
1158
+ expect(second.filesWritten).not.toContain(manifestPath)
1159
+ })
1160
+
1161
+ it('falls back to full bootstrap for worker overrides and computed scheduler commands', async () => {
1162
+ scaffoldModule(tmpDir, 'events', 'pkg', ['workers/dispatch.ts'])
1163
+ scaffoldModule(tmpDir, 'scheduler', 'pkg', ['cli.ts'])
1164
+ touchFile(
1165
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'events', 'workers', 'dispatch.ts'),
1166
+ `export const metadata = { id: 'events:dispatch', queue: 'events', concurrency: 3 }\nexport default async function dispatch() {}\n`,
1167
+ )
1168
+ touchFile(
1169
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'scheduler', 'cli.ts'),
1170
+ `const command = 'start'\nexport default [{ command, run() {} }]\n`,
1171
+ )
1172
+ const resolver = createMockResolver(tmpDir, [
1173
+ { id: 'events', from: '@open-mercato/core', devSupervisorRequiresFullBootstrap: true },
1174
+ { id: 'scheduler', from: '@open-mercato/core' },
1175
+ ])
1176
+
1177
+ await generateModuleRegistryCli({ resolver, quiet: true })
1178
+ const manifestPath = path.join(tmpDir, 'output', 'generated', 'dev-supervisor.generated.json')
1179
+ expect(JSON.parse(fs.readFileSync(manifestPath, 'utf8'))).toMatchObject({
1180
+ schedulerStartStatus: 'missing-command',
1181
+ requiresFullBootstrap: true,
1182
+ })
1183
+ })
1184
+
1185
+ it('falls back to full bootstrap when app code registers programmatic worker overrides', async () => {
1186
+ scaffoldModule(tmpDir, 'events', 'pkg', ['workers/dispatch.ts'])
1187
+ touchFile(
1188
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'events', 'workers', 'dispatch.ts'),
1189
+ `export const metadata = { id: 'events:dispatch', queue: 'events', concurrency: 3 }\nexport default async function dispatch() {}\n`,
1190
+ )
1191
+ touchFile(
1192
+ path.join(tmpDir, 'app', 'src', 'bootstrap-overrides.ts'),
1193
+ `import { applyWorkerOverrides as overrideWorkers } from '@open-mercato/shared/modules/overrides'\noverrideWorkers({ 'events:dispatch': null })\n`,
1194
+ )
1195
+ const resolver = createMockResolver(tmpDir, [
1196
+ { id: 'events', from: '@open-mercato/core' },
1197
+ ])
1198
+
1199
+ await generateModuleRegistryCli({ resolver, quiet: true })
1200
+ const manifestPath = path.join(tmpDir, 'output', 'generated', 'dev-supervisor.generated.json')
1201
+ expect(JSON.parse(fs.readFileSync(manifestPath, 'utf8')).requiresFullBootstrap).toBe(true)
1202
+ })
1203
+
1047
1204
  it('handles disabling a module that was previously enabled', async () => {
1048
1205
  scaffoldModule(tmpDir, 'keep_mod', 'pkg', [
1049
1206
  'subscribers/handler.ts',
@@ -1462,6 +1619,115 @@ describe('generateModuleRegistryApp with module subsets', () => {
1462
1619
  const bootstrapOutput = readGenerated(tmpDir, 'modules.bootstrap.generated.ts')
1463
1620
  expect(bootstrapOutput).not.toBeNull()
1464
1621
  expect(bootstrapOutput).toContain('export const modules: Module[] = [')
1622
+
1623
+ const i18nLoadersOutput = readGenerated(tmpDir, 'modules.i18n.loaders.generated.ts')
1624
+ expect(i18nLoadersOutput).toContain('export async function loadI18nModules')
1625
+ expect(i18nLoadersOutput).toContain('default:')
1626
+ expect(i18nLoadersOutput).toContain('return []')
1627
+ })
1628
+
1629
+ it('keeps the legacy i18n registry while emitting deterministic locale-specific shards', async () => {
1630
+ touchFile(
1631
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'localized', 'i18n', 'en.json'),
1632
+ JSON.stringify({ localized: { title: 'Core title' } }),
1633
+ )
1634
+ touchFile(
1635
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'localized', 'i18n', 'pl.json'),
1636
+ JSON.stringify({ localized: { title: 'Tytuł' } }),
1637
+ )
1638
+ touchFile(
1639
+ path.join(tmpDir, 'app', 'src', 'modules', 'localized', 'i18n', 'en.json'),
1640
+ JSON.stringify({ localized: { title: 'App title' } }),
1641
+ )
1642
+ touchFile(
1643
+ path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'localized', 'i18n', '.hardcoded-allowlist.json'),
1644
+ JSON.stringify({ allow: ['framework chrome'] }),
1645
+ )
1646
+
1647
+ const resolver = createMockResolver(tmpDir, [
1648
+ { id: 'localized', from: '@open-mercato/core' },
1649
+ ])
1650
+ const result = await generateModuleRegistryApp({ resolver, quiet: true })
1651
+
1652
+ expect(result.errors).toEqual([])
1653
+
1654
+ const legacyI18nOutput = readGenerated(tmpDir, 'modules.i18n.generated.ts')!
1655
+ expect(legacyI18nOutput).toContain('@open-mercato/core/modules/localized/i18n/en.json')
1656
+ expect(legacyI18nOutput).toContain('@/modules/localized/i18n/en.json')
1657
+ expect(legacyI18nOutput).toContain('@open-mercato/core/modules/localized/i18n/pl.json')
1658
+ expect(legacyI18nOutput).toContain('export const modules: Module[] = [')
1659
+
1660
+ const bootstrapOutput = readGenerated(tmpDir, 'modules.bootstrap.generated.ts')!
1661
+ expect(bootstrapOutput).not.toContain('/i18n/')
1662
+ expect(bootstrapOutput).not.toContain('translations:')
1663
+
1664
+ const loadersOutput = readGenerated(tmpDir, 'modules.i18n.loaders.generated.ts')!
1665
+ expect(loadersOutput).not.toContain('.json')
1666
+ expect(loadersOutput).toContain('case "en":')
1667
+ expect(loadersOutput).toContain('import("./modules.i18n.en.generated")')
1668
+ expect(loadersOutput).toContain('case "pl":')
1669
+ expect(loadersOutput).not.toContain('hardcoded-allowlist')
1670
+ expect(loadersOutput.indexOf('case "en":')).toBeLessThan(loadersOutput.indexOf('case "pl":'))
1671
+
1672
+ const enShard = readGenerated(tmpDir, 'modules.i18n.en.generated.ts')!
1673
+ expect(enShard).toContain('@open-mercato/core/modules/localized/i18n/en.json')
1674
+ expect(enShard).toContain('@/modules/localized/i18n/en.json')
1675
+ expect(enShard).not.toContain('/i18n/pl.json')
1676
+ expect(enShard).toContain('id: "localized"')
1677
+ expect(enShard).toContain('translations:')
1678
+
1679
+ const plShard = readGenerated(tmpDir, 'modules.i18n.pl.generated.ts')!
1680
+ expect(plShard).toContain('@open-mercato/core/modules/localized/i18n/pl.json')
1681
+ expect(plShard).not.toContain('/i18n/en.json')
1682
+ expect(plShard).not.toContain('@/modules/localized/i18n/pl.json')
1683
+ expect(readGenerated(tmpDir, 'modules.i18n._hardcoded-allowlist.generated.ts')).toBeNull()
1684
+
1685
+ const secondResult = await generateModuleRegistryApp({ resolver, quiet: true })
1686
+ expect(secondResult.filesWritten).toEqual([])
1687
+
1688
+ fs.rmSync(path.join(
1689
+ tmpDir,
1690
+ 'packages',
1691
+ 'core',
1692
+ 'src',
1693
+ 'modules',
1694
+ 'localized',
1695
+ 'i18n',
1696
+ 'pl.json',
1697
+ ))
1698
+ await generateModuleRegistryApp({ resolver, quiet: true })
1699
+ expect(readGenerated(tmpDir, 'modules.i18n.pl.generated.ts')).toBeNull()
1700
+ expect(fs.existsSync(path.join(
1701
+ tmpDir,
1702
+ 'output',
1703
+ 'generated',
1704
+ 'modules.i18n.pl.generated.checksum',
1705
+ ))).toBe(false)
1706
+ expect(readGenerated(tmpDir, 'modules.i18n.loaders.generated.ts')).not.toContain('case "pl":')
1707
+ })
1708
+
1709
+ it('uses standalone source-mirror translations in locale-specific shards', async () => {
1710
+ touchFile(
1711
+ path.join(tmpDir, 'node_modules', 'pkg', 'src', 'modules', 'customers', 'i18n', 'en.json'),
1712
+ JSON.stringify({ customers: { people: { title: 'People' } } }),
1713
+ )
1714
+ touchFile(
1715
+ path.join(tmpDir, 'node_modules', 'pkg', 'dist', 'modules', 'customers', 'index.js'),
1716
+ 'export const metadata = { id: "customers" }\n',
1717
+ )
1718
+
1719
+ const resolver = createStandaloneMockResolver(tmpDir, [
1720
+ { id: 'customers', from: '@open-mercato/core' },
1721
+ ])
1722
+ const result = await generateModuleRegistryApp({ resolver, quiet: true })
1723
+
1724
+ expect(result.errors).toEqual([])
1725
+ const shard = fs.readFileSync(
1726
+ path.join(tmpDir, '.mercato', 'generated', 'modules.i18n.en.generated.ts'),
1727
+ 'utf8',
1728
+ )
1729
+ expect(shard).toContain('@open-mercato/core/modules/customers/i18n/en.json')
1730
+ expect(shard).toContain('id: "customers"')
1465
1731
  })
1466
1732
 
1467
1733
  it('app output excludes CLI commands and keeps lazy workers/subscribers', async () => {
@@ -829,12 +829,12 @@ describe('generator output compatibility', () => {
829
829
 
830
830
  const content = readGenerated('bootstrap-registrations.generated.ts')
831
831
  expect(content).not.toBeNull()
832
- expect(content).toContain(`import { backendRoutes } from "./backend-routes.generated"`)
832
+ expect(content).toContain(`import { backendRouteFacades } from "./backend-route-shards.generated"`)
833
833
  expect(content).toContain(`import { frontendRoutes } from "./frontend-routes.generated"`)
834
834
  expect(content).toContain(
835
835
  `import { registerBackendRouteManifests, registerFrontendRouteManifests } from '@open-mercato/shared/modules/registry'`,
836
836
  )
837
- expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerBackendRouteManifests\(backendRoutes\)[\s\S]*\}/)
837
+ expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerBackendRouteManifests\(backendRouteFacades\)[\s\S]*\}/)
838
838
  expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerFrontendRouteManifests\(frontendRoutes\)[\s\S]*\}/)
839
839
  })
840
840
 
@@ -846,6 +846,6 @@ describe('generator output compatibility', () => {
846
846
 
847
847
  const content = readGenerated('bootstrap-registrations.generated.ts')
848
848
  expect(content).not.toBeNull()
849
- expect(content).toContain(`registerBackendRouteManifests(backendRoutes)`)
849
+ expect(content).toContain(`registerBackendRouteManifests(backendRouteFacades)`)
850
850
  })
851
851
  })
@@ -0,0 +1,62 @@
1
+ import {
2
+ getRouteManifestShardKey,
3
+ renderRouteManifestShardOutputs,
4
+ renderRouteMetadataOutput,
5
+ } from '../route-manifest-shards'
6
+
7
+ describe('route manifest shards', () => {
8
+ it('groups static request prefixes and preserves dynamic-prefix fallbacks', () => {
9
+ expect(getRouteManifestShardKey('api', '/customers/[id]')).toBe('customers')
10
+ expect(getRouteManifestShardKey('backend', '/backend/sales/orders')).toBe('sales')
11
+ expect(getRouteManifestShardKey('backend', '/backend/[tenant]/settings')).toBe('__dynamic')
12
+ expect(getRouteManifestShardKey('api', '/')).toBe('__root')
13
+ })
14
+
15
+ it('renders a small loader graph with exact and dynamic shards', () => {
16
+ const outputs = renderRouteManifestShardOutputs('api', [
17
+ { path: '/customers', declaration: `{ moduleId: 'customers' } as any` },
18
+ { path: '/sales/orders', declaration: `{ moduleId: 'sales' } as any` },
19
+ { path: '/[module]/health', declaration: `{ moduleId: 'dynamic' } as any` },
20
+ ])
21
+ const loader = outputs.find((entry) => entry.fileName === 'api-route-shards.generated.ts')?.content ?? ''
22
+
23
+ expect(outputs.filter((entry) => entry.fileName.includes('route-shard.'))).toHaveLength(3)
24
+ expect(loader).toContain(`case "customers":`)
25
+ expect(loader).toContain(`case "sales":`)
26
+ expect(loader).toContain(`const dynamicShard = await loadShard("__dynamic")`)
27
+ expect(loader).toContain(`export const apiRouteFacades`)
28
+ expect(loader).toContain(`loadApiRouteManifestByOrdinal(ordinal)`)
29
+ expect(loader).not.toContain("moduleId: 'customers'")
30
+ })
31
+
32
+ it('retains global route order when exact and dynamic entries interleave', () => {
33
+ const outputs = renderRouteManifestShardOutputs('api', [
34
+ { path: '/customers/new', declaration: `{ path: '/customers/new' } as any` },
35
+ { path: '/[module]/health', declaration: `{ path: '/[module]/health' } as any` },
36
+ { path: '/customers/[id]', declaration: `{ path: '/customers/[id]' } as any` },
37
+ { path: '/', declaration: `{ path: '/' } as any` },
38
+ ])
39
+ const customerShard = outputs.find((entry) => entry.content.includes(`/customers/new`))?.content ?? ''
40
+ const dynamicShard = outputs.find((entry) => entry.content.includes(`/[module]/health`))?.content ?? ''
41
+ const loader = outputs.find((entry) => entry.fileName === 'api-route-shards.generated.ts')?.content ?? ''
42
+
43
+ expect(customerShard).toContain('export const routeOrders = [0, 2]')
44
+ expect(dynamicShard).toContain('export const routeOrders = [1]')
45
+ expect(loader).toContain(`case "__root":`)
46
+ expect(loader).toContain(`.sort((a, b) => a.order - b.order)`)
47
+ expect(loader).toContain(`const ROUTE_SHARD_KEYS = ["customers","__dynamic","customers","__root"]`)
48
+ })
49
+
50
+ it('renders metadata without load functions while retaining metadata imports', () => {
51
+ const output = renderRouteMetadataOutput('backend', [{
52
+ path: '/backend/settings',
53
+ declaration: `{ moduleId: 'settings', ...resolvePageRouteMetadata('/backend/settings', META.metadata) }`,
54
+ imports: [`import * as META from '@app/settings/meta'`],
55
+ }])
56
+
57
+ expect(output.fileName).toBe('backend-route-metadata.generated.ts')
58
+ expect(output.content).toContain(`import * as META from '@app/settings/meta'`)
59
+ expect(output.content).toContain(`Omit<BackendRouteManifestEntry, 'load'>`)
60
+ expect(output.content).not.toContain('load: async')
61
+ })
62
+ })
@@ -5,17 +5,13 @@ import { VariableDeclarationKind } from 'ts-morph'
5
5
  import type { PackageResolver, ModuleEntry } from '../resolver'
6
6
  import { MODULE_CODE_EXTENSIONS } from './scanner'
7
7
  import {
8
- calculateChecksum,
9
- readChecksumRecord,
10
- writeChecksumRecord,
11
- ensureDir,
12
8
  toVar,
13
9
  toSnake,
14
10
  rimrafDir,
15
- logGenerationResult,
16
- writeIfChanged,
17
11
  type GeneratorResult,
18
12
  createGeneratorResult,
13
+ writeGeneratedFile,
14
+ writeIfChanged,
19
15
  } from '../utils'
20
16
  import { addAutoGeneratedComment, createGeneratedSourceFile, elementAccess, getSourceText, identifier, returnStatement } from './ast'
21
17
 
@@ -438,26 +434,14 @@ export async function generateEntityIds(options: EntityIdsOptions): Promise<Gene
438
434
  })
439
435
  const consolidatedSrc = getSourceText(consolidatedSourceFile)
440
436
 
441
- // Check if content has changed
442
- const newChecksum = calculateChecksum(consolidatedSrc)
443
- let shouldWrite = true
444
-
445
- const existingRecord = readChecksumRecord(checksumFile)
446
- if (existingRecord && existingRecord.content === newChecksum) {
447
- shouldWrite = false
448
- }
449
-
450
- if (shouldWrite) {
451
- ensureDir(outFile)
452
- fs.writeFileSync(outFile, consolidatedSrc)
453
- writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })
454
- result.filesWritten.push(outFile)
455
- if (!quiet) {
456
- logGenerationResult(path.relative(process.cwd(), outFile), true)
457
- }
458
- } else {
459
- result.filesUnchanged.push(outFile)
460
- }
437
+ writeGeneratedFile({
438
+ outFile,
439
+ checksumFile,
440
+ content: consolidatedSrc,
441
+ structureChecksum: '',
442
+ result,
443
+ quiet,
444
+ })
461
445
 
462
446
  // Write per-group outputs
463
447
  const groups = Object.keys(grouped) as GroupKey[]
@@ -183,9 +183,52 @@ function resolveScannedWidgetPath(roots: ModuleRoots, fromApp: boolean, relPath:
183
183
  return path.join(base, 'widgets', 'injection', ...relPath.split('/'))
184
184
  }
185
185
 
186
+ /**
187
+ * A widget's `metadata.id` and the injection-table entry that mounts it are two hand-typed string
188
+ * literals in two different files, and nothing links them. A typo in either fails silently: the spot
189
+ * resolves to no widget and nothing is logged, so it only surfaces as "why isn't my widget
190
+ * rendering" during manual QA.
191
+ *
192
+ * Reports table entries whose `widgetId` no scanned widget declares. Diagnostics only — generated
193
+ * output is unchanged and the build is not failed, since a table entry may legitimately point at a
194
+ * widget supplied by a module that is not enabled in this app.
195
+ *
196
+ * Deliberately one-directional. The mirror check ("a widget nothing references is never mounted")
197
+ * cannot be done reliably yet: `extractInjectionTableWidgetIds` only reads statically-analyzable
198
+ * object literals, so a table assembled by composition or computation contributes **no** visible
199
+ * ids — `example` exports `cond ? { ...a, ...b } : a` and `translations` exports
200
+ * `buildInjectionTable()`, and both would have every one of their widgets reported as unreferenced.
201
+ * Under-reporting is harmless in the direction implemented here (a reference the extractor never saw
202
+ * cannot produce a warning) but fatal in the other. Teaching the extractor to resolve spreads,
203
+ * conditionals and call expressions is the prerequisite for adding it.
204
+ */
205
+ function reportUnresolvableWidgetReferences(
206
+ widgetEntries: Map<string, InjectionWidgetEntry>,
207
+ tableWidgetRefs: Map<string, Set<string>>,
208
+ ): void {
209
+ const declaredIds = new Set<string>()
210
+ for (const entry of widgetEntries.values()) {
211
+ if (entry.widgetId) declaredIds.add(entry.widgetId)
212
+ }
213
+
214
+ for (const [widgetId, modules] of Array.from(tableWidgetRefs.entries()).sort(([a], [b]) => a.localeCompare(b))) {
215
+ if (declaredIds.has(widgetId)) continue
216
+ const owners = Array.from(modules).sort((left, right) => left.localeCompare(right)).join(', ')
217
+ console.warn(
218
+ `[generate] ⚠ Injection table in "${owners}" references widget id "${widgetId}", but no scanned ` +
219
+ 'widget declares it — that spot will render nothing. Check the id against the widget\'s ' +
220
+ 'metadata.id, and that the widget lives in a file the scanner reads ' +
221
+ '(widgets/injection/**/widget.ts|tsx — note that widget.client.tsx alone is not scanned).',
222
+ )
223
+ }
224
+ }
225
+
186
226
  export function createInjectionWidgetsExtension(): GeneratorExtension {
187
227
  const widgetEntries = new Map<string, InjectionWidgetEntry>()
188
228
  const tableEntries: InjectionTableEntry[] = []
229
+ // widgetId -> module ids whose injection table references it. Collected across every module so a
230
+ // table may legitimately reference a widget another module owns.
231
+ const tableWidgetRefs = new Map<string, Set<string>>()
189
232
 
190
233
  return {
191
234
  id: 'registry.injection-widgets',
@@ -217,6 +260,12 @@ export function createInjectionWidgetsExtension(): GeneratorExtension {
217
260
  moduleWidgetEntries[0].widgetId = tableWidgetIds[0]
218
261
  }
219
262
 
263
+ for (const widgetId of tableWidgetIds) {
264
+ const modules = tableWidgetRefs.get(widgetId) ?? new Set<string>()
265
+ modules.add(ctx.moduleId)
266
+ tableWidgetRefs.set(widgetId, modules)
267
+ }
268
+
220
269
  for (const entry of moduleWidgetEntries) {
221
270
  const existing = widgetEntries.get(entry.key)
222
271
  if (!existing || (existing.source !== 'app' && entry.source === 'app')) {
@@ -233,6 +282,8 @@ export function createInjectionWidgetsExtension(): GeneratorExtension {
233
282
  }
234
283
  },
235
284
  generateOutput() {
285
+ reportUnresolvableWidgetReferences(widgetEntries, tableWidgetRefs)
286
+
236
287
  const widgetDecls = Array.from(widgetEntries.entries())
237
288
  .sort(([left], [right]) => left.localeCompare(right))
238
289
  .map(([, entry]) =>
@@ -4,14 +4,10 @@ import { StructureKind, VariableDeclarationKind } from 'ts-morph'
4
4
  import type { PackageResolver } from '../resolver'
5
5
  import { MODULE_CODE_EXTENSIONS } from './scanner'
6
6
  import {
7
- calculateChecksum,
8
- readChecksumRecord,
9
- writeChecksumRecord,
10
- ensureDir,
11
7
  toVar,
12
- logGenerationResult,
13
8
  type GeneratorResult,
14
9
  createGeneratorResult,
10
+ writeGeneratedFile,
15
11
  } from '../utils'
16
12
  import {
17
13
  addAutoGeneratedComment,
@@ -96,26 +92,14 @@ export async function generateModuleDi(options: ModuleDiOptions): Promise<Genera
96
92
  })
97
93
  const output = getSourceText(sourceFile)
98
94
 
99
- // Check if content has changed
100
- const newChecksum = calculateChecksum(output)
101
- let shouldWrite = true
102
-
103
- const existingRecord = readChecksumRecord(checksumFile)
104
- if (existingRecord && existingRecord.content === newChecksum) {
105
- shouldWrite = false
106
- }
107
-
108
- if (shouldWrite) {
109
- ensureDir(outFile)
110
- fs.writeFileSync(outFile, output)
111
- writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })
112
- result.filesWritten.push(outFile)
113
- if (!quiet) {
114
- logGenerationResult(path.relative(process.cwd(), outFile), true)
115
- }
116
- } else {
117
- result.filesUnchanged.push(outFile)
118
- }
95
+ writeGeneratedFile({
96
+ outFile,
97
+ checksumFile,
98
+ content: output,
99
+ structureChecksum: '',
100
+ result,
101
+ quiet,
102
+ })
119
103
 
120
104
  return result
121
105
  }
@@ -4,14 +4,10 @@ import { StructureKind, VariableDeclarationKind } from 'ts-morph'
4
4
  import type { PackageResolver } from '../resolver'
5
5
  import { MODULE_CODE_EXTENSIONS, stripModuleCodeExtension } from './scanner'
6
6
  import {
7
- calculateChecksum,
8
- readChecksumRecord,
9
- writeChecksumRecord,
10
- ensureDir,
11
7
  toVar,
12
- logGenerationResult,
13
8
  type GeneratorResult,
14
9
  createGeneratorResult,
10
+ writeGeneratedFile,
15
11
  } from '../utils'
16
12
  import {
17
13
  addAutoGeneratedComment,
@@ -206,26 +202,14 @@ export async function generateModuleEntities(options: ModuleEntitiesOptions): Pr
206
202
  })
207
203
  const output = getSourceText(sourceFile)
208
204
 
209
- // Check if content has changed
210
- const newChecksum = calculateChecksum(output)
211
- let shouldWrite = true
212
-
213
- const existingRecord = readChecksumRecord(checksumFile)
214
- if (existingRecord && existingRecord.content === newChecksum) {
215
- shouldWrite = false
216
- }
217
-
218
- if (shouldWrite) {
219
- ensureDir(outFile)
220
- fs.writeFileSync(outFile, output)
221
- writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })
222
- result.filesWritten.push(outFile)
223
- if (!quiet) {
224
- logGenerationResult(path.relative(process.cwd(), outFile), true)
225
- }
226
- } else {
227
- result.filesUnchanged.push(outFile)
228
- }
205
+ writeGeneratedFile({
206
+ outFile,
207
+ checksumFile,
208
+ content: output,
209
+ structureChecksum: '',
210
+ result,
211
+ quiet,
212
+ })
229
213
 
230
214
  return result
231
215
  }