@open-mercato/cli 0.6.7-develop.6862.1.c11a64ce0a → 0.6.7

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 (278) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/build.mjs +7 -24
  3. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +88 -32
  4. package/dist/agentic/codex/enforcement-rules.md +16 -2
  5. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +50 -19
  6. package/dist/agentic/cursor/rules/entity-guard.mdc +21 -9
  7. package/dist/agentic/cursor/rules/generated-guard.mdc +5 -3
  8. package/dist/agentic/cursor/rules/open-mercato.mdc +29 -3
  9. package/dist/agentic/guides/cache.md +45 -0
  10. package/dist/agentic/guides/core.md +453 -0
  11. package/dist/agentic/guides/events.md +117 -0
  12. package/dist/agentic/guides/module-facts.json +3833 -115419
  13. package/dist/agentic/guides/module-system.md +127 -12
  14. package/dist/agentic/guides/modules/ai_assistant.md +28 -227
  15. package/dist/agentic/guides/modules/api_docs.md +2 -58
  16. package/dist/agentic/guides/modules/api_keys.md +7 -92
  17. package/dist/agentic/guides/modules/attachments.md +14 -119
  18. package/dist/agentic/guides/modules/audit_logs.md +10 -131
  19. package/dist/agentic/guides/modules/auth.md +33 -276
  20. package/dist/agentic/guides/modules/business_rules.md +14 -149
  21. package/dist/agentic/guides/modules/catalog.md +43 -475
  22. package/dist/agentic/guides/modules/channel_gmail.md +3 -82
  23. package/dist/agentic/guides/modules/channel_imap.md +3 -82
  24. package/dist/agentic/guides/modules/checkout.md +27 -299
  25. package/dist/agentic/guides/modules/communication_channels.md +33 -315
  26. package/dist/agentic/guides/modules/configs.md +8 -104
  27. package/dist/agentic/guides/modules/content.md +2 -57
  28. package/dist/agentic/guides/modules/currencies.md +17 -152
  29. package/dist/agentic/guides/modules/customer_accounts.md +45 -387
  30. package/dist/agentic/guides/modules/customers.md +85 -746
  31. package/dist/agentic/guides/modules/dashboards.md +10 -155
  32. package/dist/agentic/guides/modules/data_sync.md +16 -149
  33. package/dist/agentic/guides/modules/dictionaries.md +12 -107
  34. package/dist/agentic/guides/modules/directory.md +15 -148
  35. package/dist/agentic/guides/modules/entities.md +13 -145
  36. package/dist/agentic/guides/modules/events.md +2 -59
  37. package/dist/agentic/guides/modules/feature_toggles.md +9 -119
  38. package/dist/agentic/guides/modules/gateway_stripe.md +4 -92
  39. package/dist/agentic/guides/modules/generators.md +2 -45
  40. package/dist/agentic/guides/modules/inbox_ops.md +27 -207
  41. package/dist/agentic/guides/modules/integrations.md +16 -146
  42. package/dist/agentic/guides/modules/messages.md +24 -223
  43. package/dist/agentic/guides/modules/notifications.md +8 -96
  44. package/dist/agentic/guides/modules/onboarding.md +6 -77
  45. package/dist/agentic/guides/modules/payment_gateways.md +21 -164
  46. package/dist/agentic/guides/modules/perspectives.md +7 -77
  47. package/dist/agentic/guides/modules/planner.md +18 -136
  48. package/dist/agentic/guides/modules/portal.md +2 -93
  49. package/dist/agentic/guides/modules/progress.md +14 -104
  50. package/dist/agentic/guides/modules/query_index.md +15 -154
  51. package/dist/agentic/guides/modules/record_locks.md +21 -206
  52. package/dist/agentic/guides/modules/resources.md +30 -197
  53. package/dist/agentic/guides/modules/sales.md +84 -678
  54. package/dist/agentic/guides/modules/scheduler.md +14 -133
  55. package/dist/agentic/guides/modules/search.md +6 -170
  56. package/dist/agentic/guides/modules/security.md +35 -294
  57. package/dist/agentic/guides/modules/shipping_carriers.md +18 -134
  58. package/dist/agentic/guides/modules/sso.md +26 -151
  59. package/dist/agentic/guides/modules/staff.md +57 -369
  60. package/dist/agentic/guides/modules/storage_s3.md +5 -95
  61. package/dist/agentic/guides/modules/sync_akeneo.md +3 -85
  62. package/dist/agentic/guides/modules/sync_excel.md +6 -75
  63. package/dist/agentic/guides/modules/system_status_overlays.md +2 -60
  64. package/dist/agentic/guides/modules/translations.md +10 -104
  65. package/dist/agentic/guides/modules/webhooks.md +24 -203
  66. package/dist/agentic/guides/modules/wms.md +46 -441
  67. package/dist/agentic/guides/modules/workflows.md +43 -302
  68. package/dist/agentic/guides/queue.md +53 -0
  69. package/dist/agentic/guides/search.md +115 -0
  70. package/dist/agentic/guides/shared.md +108 -0
  71. package/dist/agentic/guides/ui.md +347 -0
  72. package/dist/agentic/shared/AGENTS.md.template +311 -124
  73. package/dist/agentic/shared/ai/agentic.config.json +0 -2
  74. package/dist/agentic/shared/ai/lessons.md +3 -26
  75. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +27 -0
  76. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +27 -0
  77. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +0 -4
  78. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +241 -20
  79. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +260 -0
  80. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +601 -16
  81. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +146 -0
  82. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +310 -14
  83. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +97 -15
  84. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +84 -0
  85. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +193 -0
  86. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +152 -33
  87. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +708 -24
  88. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +34 -0
  89. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +762 -0
  90. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +820 -20
  91. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +50 -0
  92. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +97 -0
  93. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +843 -19
  94. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +271 -0
  95. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +63 -15
  96. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +467 -16
  97. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +101 -0
  98. package/dist/agentic/shared/ai/skills/tiers.json +21 -143
  99. package/dist/agentic/shared/ai/skills/tiers.schema.json +40 -52
  100. package/dist/agentic/shared/ai/specs/README.md +3 -4
  101. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +10 -226
  102. package/dist/agentic/shared/ai/trackers/github.md +4 -108
  103. package/dist/agentic/shared/scripts/install-skills.sh +694 -4
  104. package/dist/bin.js +1 -13
  105. package/dist/bin.js.map +2 -2
  106. package/dist/lib/__integration__/TC-INT-008.spec.js +8 -88
  107. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  108. package/dist/lib/agentic-init.js +3 -13
  109. package/dist/lib/agentic-init.js.map +2 -2
  110. package/dist/lib/agentic-setup.js +186 -418
  111. package/dist/lib/agentic-setup.js.map +2 -2
  112. package/dist/lib/generators/extensions/injection-widgets.js +0 -20
  113. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  114. package/dist/lib/generators/module-facts-discovery.js +7 -41
  115. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  116. package/dist/lib/generators/module-facts.js +45 -1387
  117. package/dist/lib/generators/module-facts.js.map +3 -3
  118. package/dist/lib/generators/module-registry.js +5 -11
  119. package/dist/lib/generators/module-registry.js.map +2 -2
  120. package/dist/lib/resolver.js +0 -2
  121. package/dist/lib/resolver.js.map +2 -2
  122. package/dist/lib/testing/integration.js +0 -5
  123. package/dist/lib/testing/integration.js.map +2 -2
  124. package/dist/mercato.js +1 -23
  125. package/dist/mercato.js.map +2 -2
  126. package/package.json +11 -13
  127. package/src/bin.ts +1 -33
  128. package/src/lib/__integration__/TC-INT-008.spec.ts +9 -107
  129. package/src/lib/__tests__/agentic-init.test.ts +5 -41
  130. package/src/lib/agentic-init.ts +3 -15
  131. package/src/lib/agentic-setup.ts +224 -530
  132. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +2 -88
  133. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +0 -1
  134. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +10 -54
  135. package/src/lib/generators/__tests__/module-subset.test.ts +0 -18
  136. package/src/lib/generators/extensions/injection-widgets.ts +0 -51
  137. package/src/lib/generators/module-facts-discovery.ts +7 -58
  138. package/src/lib/generators/module-facts.ts +46 -1851
  139. package/src/lib/generators/module-registry.ts +6 -16
  140. package/src/lib/resolver.ts +0 -3
  141. package/src/lib/testing/__tests__/integration.test.ts +0 -4
  142. package/src/lib/testing/integration.ts +0 -5
  143. package/src/mercato.ts +1 -26
  144. package/dist/agentic/guides/ai-workflows.md +0 -76
  145. package/dist/agentic/guides/architecture.md +0 -107
  146. package/dist/agentic/guides/backend-ui.md +0 -80
  147. package/dist/agentic/guides/contracts.md +0 -92
  148. package/dist/agentic/guides/extensions.md +0 -121
  149. package/dist/agentic/guides/framework-extension-points.md +0 -36
  150. package/dist/agentic/guides/integrations.md +0 -81
  151. package/dist/agentic/guides/modules/design_system.md +0 -102
  152. package/dist/agentic/guides/spec-delivery.md +0 -22
  153. package/dist/agentic/guides/testing-debugging.md +0 -80
  154. package/dist/agentic/guides/upstream/AGENTS.md +0 -298
  155. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +0 -316
  156. package/dist/agentic/guides/upstream/manifest.json +0 -8
  157. package/dist/agentic/shared/ai/harness/README.md +0 -50
  158. package/dist/agentic/shared/ai/harness/RELEASE.md +0 -57
  159. package/dist/agentic/shared/ai/harness/cases.json +0 -13680
  160. package/dist/agentic/shared/ai/harness/cases.schema.json +0 -133
  161. package/dist/agentic/shared/ai/harness/fixtures/index.json +0 -49
  162. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +0 -162
  163. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +0 -21
  164. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +0 -96
  165. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +0 -172
  166. package/dist/agentic/shared/ai/harness/release-matrix.json +0 -158
  167. package/dist/agentic/shared/ai/harness/release-result.schema.json +0 -219
  168. package/dist/agentic/shared/ai/harness/result.schema.json +0 -76
  169. package/dist/agentic/shared/ai/harness/routing-response.schema.json +0 -12
  170. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +0 -73
  171. package/dist/agentic/shared/ai/harness/validators.json +0 -78
  172. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +0 -2121
  173. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +0 -1096
  174. package/dist/agentic/shared/ai/lessons/_template.md +0 -16
  175. package/dist/agentic/shared/ai/review-checklist.md +0 -36
  176. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +0 -21
  177. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +0 -98
  178. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +0 -16
  179. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +0 -53
  180. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +0 -13
  181. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +0 -11
  182. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +0 -30
  183. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +0 -12
  184. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +0 -13
  185. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +0 -14
  186. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +0 -28
  187. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +0 -12
  188. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +0 -22
  189. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +0 -12
  190. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +0 -19
  191. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +0 -19
  192. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +0 -13
  193. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +0 -14
  194. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +0 -10
  195. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +0 -17
  196. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +0 -29
  197. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +0 -100
  198. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +0 -16
  199. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +0 -17
  200. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +0 -25
  201. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +0 -11
  202. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +0 -13
  203. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +0 -18
  204. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +0 -20
  205. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +0 -33
  206. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +0 -40
  207. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +0 -27
  208. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +0 -24
  209. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +0 -16
  210. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +0 -14
  211. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +0 -15
  212. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +0 -12
  213. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +0 -34
  214. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +0 -4
  215. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +0 -11
  216. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +0 -31
  217. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +0 -39
  218. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +0 -43
  219. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +0 -11
  220. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +0 -19
  221. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +0 -172
  222. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +0 -12
  223. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +0 -83
  224. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +0 -21
  225. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +0 -11
  226. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +0 -22
  227. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +0 -13
  228. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +0 -41
  229. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +0 -19
  230. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +0 -44
  231. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +0 -39
  232. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +0 -20
  233. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +0 -57
  234. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +0 -11
  235. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +0 -486
  236. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +0 -18
  237. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +0 -27
  238. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +0 -13
  239. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +0 -41
  240. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +0 -11
  241. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +0 -15
  242. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +0 -16
  243. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +0 -14
  244. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +0 -187
  245. package/dist/agentic/shared/scripts/check-lessons.mjs +0 -178
  246. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +0 -2958
  247. package/dist/agentic/shared/scripts/execution-sandbox.mjs +0 -329
  248. package/dist/agentic/shared/scripts/framework-context.mjs +0 -888
  249. package/dist/agentic/shared/scripts/install-skills.mjs +0 -1029
  250. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +0 -222
  251. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +0 -1774
  252. package/dist/lib/generators/module-extension-facts.js +0 -1840
  253. package/dist/lib/generators/module-extension-facts.js.map +0 -7
  254. package/dist/lib/generators/module-fact-sources.js +0 -70
  255. package/dist/lib/generators/module-fact-sources.js.map +0 -7
  256. package/dist/lib/generators/module-override-targets.js +0 -688
  257. package/dist/lib/generators/module-override-targets.js.map +0 -7
  258. package/dist/lib/load-env.js +0 -24
  259. package/dist/lib/load-env.js.map +0 -7
  260. package/dist/lib/telemetry-init.js +0 -358
  261. package/dist/lib/telemetry-init.js.map +0 -7
  262. package/src/lib/__tests__/agentic-setup.ownership.test.ts +0 -386
  263. package/src/lib/__tests__/injection-widget-id-validation.test.ts +0 -184
  264. package/src/lib/__tests__/load-env.test.ts +0 -65
  265. package/src/lib/__tests__/telemetry-init.test.ts +0 -298
  266. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +0 -92
  267. package/src/lib/generators/__tests__/module-extension-activations.test.ts +0 -762
  268. package/src/lib/generators/__tests__/module-extension-facts.test.ts +0 -404
  269. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +0 -108
  270. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +0 -561
  271. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +0 -119
  272. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +0 -149
  273. package/src/lib/generators/__tests__/module-override-targets.test.ts +0 -304
  274. package/src/lib/generators/module-extension-facts.ts +0 -2365
  275. package/src/lib/generators/module-fact-sources.ts +0 -108
  276. package/src/lib/generators/module-override-targets.ts +0 -863
  277. package/src/lib/load-env.ts +0 -30
  278. package/src/lib/telemetry-init.ts +0 -461
@@ -1,26 +1,611 @@
1
1
  ---
2
2
  name: om-data-model-design
3
- description: Design or change standalone module entities, relations, encryption maps, migrations, snapshots, locking, and atomic writes. Use for "add entity", "database model", "migration", "encrypt this field", "optimistic locking", "model danych", or persistence bugs.
3
+ description: Design entities, relationships, and manage the migration lifecycle. Use when planning a data model, designing entities, choosing relationship patterns, adding cross-module references, or managing database migrations. Triggers on "design entity", "data model", "add entity", "database schema", "migration", "relationship", "many-to-many", "junction table", "foreign key", "jsonb", "add column".
4
4
  ---
5
5
 
6
- # Design Safe Module Data
6
+ # Data Model Design
7
7
 
8
- Produce an entity/validator/migration plan or implement it when requested. Keep schema, API, command, and UI round trips aligned.
8
+ Design entities, relationships, and manage the migration lifecycle following Open Mercato conventions.
9
9
 
10
- ## Workflow
10
+ ## Table of Contents
11
11
 
12
- 1. Read `.ai/guides/contracts.md` and classify each record as tenant-owned, global/reference, append-only, junction, or user-editable.
13
- 2. Follow `references/schema-design.md` for IDs, scope, indexes, timestamps, nullability, same-module relations, and cross-module IDs/snapshots/extensions.
14
- 3. Follow `references/sensitive-data.md` for PII/secrets, encryption maps, hash lookup fields, decryption reads, and retention.
15
- 4. Follow `references/integrity-and-concurrency.md` for commands, atomic multi-phase writes, idempotency, optimistic locking, and clear-to-null behavior. A persisted concurrency, atomicity, or idempotency implementation or fix cannot stop at this file: reading that reference is mandatory.
16
- 5. Follow `references/migration-workflow.md`: change `data/entities.ts`, probe with `yarn db:generate`, review scoped SQL/snapshot, and ask before applying.
17
- 6. Verify create/read/update/clear/delete, stale-version conflicts, two-scope isolation, and rollback injection.
12
+ 1. [Design Workflow](#1-design-workflow)
13
+ 2. [Entity Design](#2-entity-design)
14
+ 3. [Field Types](#3-field-types)
15
+ 4. [Relationship Patterns](#4-relationship-patterns)
16
+ 5. [Cross-Module References](#5-cross-module-references)
17
+ 6. [Migration Lifecycle](#6-migration-lifecycle)
18
+ 7. [Advanced Patterns](#7-advanced-patterns)
19
+ 8. [Sensitive Data and Encryption Maps](#8-sensitive-data-and-encryption-maps)
20
+ 9. [Anti-Patterns](#9-anti-patterns)
21
+
22
+ ---
23
+
24
+ ## 1. Design Workflow
25
+
26
+ When the developer describes data requirements:
27
+
28
+ 1. **Clarify entities** — What are the distinct "things" being stored?
29
+ 2. **Clarify fields** — What data does each entity hold?
30
+ 3. **Clarify relationships** — How do entities relate? (1:1, 1:N, N:M, cross-module?)
31
+ 4. **Choose patterns** — Select the right pattern for each relationship
32
+ 5. **Generate** — Create entity files, validators, and migrations
33
+ 6. **Verify** — Check migration output, test queries
34
+
35
+ ---
36
+
37
+ ## 2. Entity Design
38
+
39
+ ### Standard Entity Template
40
+
41
+ Define entities in `src/modules/<module_id>/data/entities.ts`. Standalone apps keep the module's entity classes together there unless the file becomes large enough that a split is justified.
42
+
43
+ ```typescript
44
+ import { Entity, Enum, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'
45
+ import { v4 } from 'uuid'
46
+
47
+ @Entity({ tableName: '<entities>' })
48
+ export class <Entity> {
49
+ @PrimaryKey({ type: 'uuid' })
50
+ id: string = v4()
51
+
52
+ @Index()
53
+ @Property({ type: 'uuid' })
54
+ organization_id!: string
55
+
56
+ @Index()
57
+ @Property({ type: 'uuid' })
58
+ tenant_id!: string
59
+
60
+ // --- Domain fields ---
61
+ // (see Field Types section)
62
+
63
+ // --- Standard columns ---
64
+ @Property({ type: 'boolean', default: true })
65
+ is_active: boolean = true
66
+
67
+ @Property({ type: 'timestamptz' })
68
+ created_at: Date = new Date()
69
+
70
+ @Property({ type: 'timestamptz', onUpdate: () => new Date() })
71
+ updated_at: Date = new Date()
72
+
73
+ @Property({ type: 'timestamptz', nullable: true })
74
+ deleted_at: Date | null = null
75
+ }
76
+ ```
77
+
78
+ ### Required Columns (Every Tenant-Scoped Entity)
79
+
80
+ | Column | Type | Purpose | Indexed |
81
+ |--------|------|---------|---------|
82
+ | `id` | `uuid` | Primary key (v4 auto-generated) | PK |
83
+ | `organization_id` | `uuid` | Tenant organization scope | Yes |
84
+ | `tenant_id` | `uuid` | Tenant scope | Yes |
85
+ | `is_active` | `boolean` | Soft active/inactive flag | No |
86
+ | `created_at` | `timestamptz` | Creation timestamp | No |
87
+ | `updated_at` | `timestamptz` | Last update (auto) | No |
88
+ | `deleted_at` | `timestamptz?` | Soft delete timestamp | No |
89
+
90
+ ---
91
+
92
+ ## 3. Field Types
93
+
94
+ ### Type Selection Guide
95
+
96
+ | Data | MikroORM Type | PostgreSQL Type | Decorator |
97
+ |------|--------------|-----------------|-----------|
98
+ | Short text (name, title) | `varchar` | `varchar(255)` | `@Property({ type: 'varchar', length: 255 })` |
99
+ | Long text (description, notes) | `text` | `text` | `@Property({ type: 'text' })` |
100
+ | Integer | `int` | `integer` | `@Property({ type: 'int' })` |
101
+ | Decimal (money, quantity) | `decimal` | `numeric(precision,scale)` | `@Property({ type: 'decimal', precision: 10, scale: 2 })` |
102
+ | Boolean | `boolean` | `boolean` | `@Property({ type: 'boolean', default: false })` |
103
+ | UUID reference | `uuid` | `uuid` | `@Property({ type: 'uuid' })` |
104
+ | Date only | `date` | `date` | `@Property({ type: 'date' })` |
105
+ | Date + time | `timestamptz` | `timestamptz` | `@Property({ type: 'timestamptz' })` |
106
+ | Enum | `varchar` | `varchar` | `@Enum({ items: () => MyEnum })` |
107
+ | Flexible JSON | `jsonb` | `jsonb` | `@Property({ type: 'jsonb', nullable: true })` |
108
+ | Array of strings | `jsonb` | `jsonb` | `@Property({ type: 'jsonb', default: '[]' })` |
109
+ | Email | `varchar` | `varchar(320)` | `@Property({ type: 'varchar', length: 320 })` |
110
+ | URL | `text` | `text` | `@Property({ type: 'text' })` |
111
+ | Phone | `varchar` | `varchar(50)` | `@Property({ type: 'varchar', length: 50 })` |
112
+
113
+ ### When to Use JSONB
114
+
115
+ Use `jsonb` when:
116
+ - Schema is flexible/user-defined (custom field values, metadata, tags)
117
+ - Data is read as a whole, not queried by individual fields
118
+ - Nesting is natural (address objects, configuration maps)
119
+
120
+ Avoid `jsonb` when:
121
+ - You need to query, filter, or sort by individual fields — use proper columns
122
+ - Data has a fixed, well-known schema — use columns for type safety
123
+ - You need referential integrity — FKs can't point into JSONB
124
+
125
+ ### Enum Pattern
126
+
127
+ ```typescript
128
+ export enum OrderStatus {
129
+ DRAFT = 'draft',
130
+ PENDING = 'pending',
131
+ CONFIRMED = 'confirmed',
132
+ SHIPPED = 'shipped',
133
+ DELIVERED = 'delivered',
134
+ CANCELLED = 'cancelled',
135
+ }
136
+
137
+ @Enum({ items: () => OrderStatus })
138
+ status: OrderStatus = OrderStatus.DRAFT
139
+ ```
140
+
141
+ ### Nullable Fields
142
+
143
+ ```typescript
144
+ // Optional field — nullable
145
+ @Property({ type: 'varchar', length: 255, nullable: true })
146
+ notes: string | null = null
147
+
148
+ // Required field — not nullable (default)
149
+ @Property({ type: 'varchar', length: 255 })
150
+ name!: string // Use ! for required fields set during creation
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 4. Relationship Patterns
156
+
157
+ ### One-to-Many (Same Module)
158
+
159
+ Parent entity has many children. Use `@ManyToOne` / `@OneToMany` decorators **only within the same module**.
160
+
161
+ ```typescript
162
+ // Parent: Category
163
+ @Entity({ tableName: 'categories' })
164
+ export class Category {
165
+ @PrimaryKey({ type: 'uuid' })
166
+ id: string = v4()
167
+
168
+ @Property({ type: 'varchar', length: 255 })
169
+ name!: string
170
+
171
+ @OneToMany(() => Product, product => product.category)
172
+ products = new Collection<Product>(this)
173
+ // ...standard columns
174
+ }
175
+
176
+ // Child: Product
177
+ @Entity({ tableName: 'products' })
178
+ export class Product {
179
+ @PrimaryKey({ type: 'uuid' })
180
+ id: string = v4()
181
+
182
+ @ManyToOne(() => Category)
183
+ category!: Category
184
+ // ...standard columns
185
+ }
186
+ ```
187
+
188
+ ### Many-to-Many (Same Module)
189
+
190
+ Use a junction (pivot) table.
191
+
192
+ ```typescript
193
+ // Junction table entity
194
+ @Entity({ tableName: 'product_tags' })
195
+ export class ProductTag {
196
+ @PrimaryKey({ type: 'uuid' })
197
+ id: string = v4()
198
+
199
+ @Index()
200
+ @Property({ type: 'uuid' })
201
+ product_id!: string
202
+
203
+ @Index()
204
+ @Property({ type: 'uuid' })
205
+ tag_id!: string
206
+
207
+ @Index()
208
+ @Property({ type: 'uuid' })
209
+ organization_id!: string
210
+
211
+ @Index()
212
+ @Property({ type: 'uuid' })
213
+ tenant_id!: string
214
+
215
+ @Property({ type: 'timestamptz' })
216
+ created_at: Date = new Date()
217
+ }
218
+ ```
219
+
220
+ **Junction table rules**:
221
+ - Always include `organization_id` and `tenant_id`
222
+ - Index both FK columns
223
+ - Include `created_at` for audit trail
224
+ - Add extra columns if the relationship has attributes (e.g., `quantity`, `sort_order`)
225
+
226
+ ### One-to-One (Same Module)
227
+
228
+ ```typescript
229
+ @Entity({ tableName: 'user_profiles' })
230
+ export class UserProfile {
231
+ @PrimaryKey({ type: 'uuid' })
232
+ id: string = v4()
233
+
234
+ @Index({ unique: true })
235
+ @Property({ type: 'uuid' })
236
+ user_id!: string // FK to User entity
237
+
238
+ // Profile-specific fields
239
+ @Property({ type: 'text', nullable: true })
240
+ bio: string | null = null
241
+ // ...standard columns
242
+ }
243
+ ```
244
+
245
+ ### Self-Referencing (Tree/Hierarchy)
246
+
247
+ ```typescript
248
+ @Entity({ tableName: 'categories' })
249
+ export class Category {
250
+ @PrimaryKey({ type: 'uuid' })
251
+ id: string = v4()
252
+
253
+ @Property({ type: 'uuid', nullable: true })
254
+ parent_id: string | null = null // Self-reference
255
+
256
+ @Property({ type: 'varchar', length: 255 })
257
+ name!: string
258
+
259
+ // Optional: materialized path for efficient tree queries
260
+ @Property({ type: 'text', default: '' })
261
+ path: string = '' // e.g., '/root-id/parent-id/this-id'
262
+
263
+ @Property({ type: 'int', default: 0 })
264
+ depth: number = 0
265
+ // ...standard columns
266
+ }
267
+ ```
268
+
269
+ ---
270
+
271
+ ## 5. Cross-Module References
272
+
273
+ **Critical rule**: NO ORM relationships (`@ManyToOne`, `@OneToMany`) between entities in different modules.
274
+
275
+ ### Pattern: FK ID Only
276
+
277
+ ```typescript
278
+ @Entity({ tableName: 'tickets' })
279
+ export class Ticket {
280
+ // Reference to customer in another module — just a UUID column
281
+ @Index()
282
+ @Property({ type: 'uuid' })
283
+ customer_id!: string // FK to customers.person — NO @ManyToOne
284
+
285
+ // Reference to assigned user in auth module
286
+ @Index()
287
+ @Property({ type: 'uuid', nullable: true })
288
+ assigned_to: string | null = null // FK to auth.user
289
+ }
290
+ ```
291
+
292
+ ### Fetching Related Data
293
+
294
+ To display related data from another module, use a **Response Enricher** (see `om-system-extension` skill):
295
+
296
+ ```typescript
297
+ // data/enrichers.ts
298
+ const enricher: ResponseEnricher = {
299
+ id: 'tickets.customer-name',
300
+ targetEntity: 'tickets.ticket',
301
+ async enrichMany(records, context) {
302
+ const customerIds = [...new Set(records.map(r => r.customer_id).filter(Boolean))]
303
+ // Fetch customer names via API or direct query
304
+ const customers = await em.find(Person, { id: { $in: customerIds } })
305
+ const nameMap = new Map(customers.map(c => [c.id, c.name]))
306
+ return records.map(r => ({
307
+ ...r,
308
+ _tickets: { customerName: nameMap.get(r.customer_id) ?? null },
309
+ }))
310
+ },
311
+ }
312
+ ```
313
+
314
+ ### Why No ORM Relations Across Modules?
315
+
316
+ 1. **Module isolation** — modules must be independently deployable and ejectable
317
+ 2. **Circular dependencies** — ORM relations create tight coupling between modules
318
+ 3. **Schema ownership** — each module owns its entities; cross-module ORM relations blur ownership
319
+ 4. **Extension system** — UMES enrichers provide the same capability without coupling
320
+
321
+ ---
322
+
323
+ ## 6. Migration Lifecycle
324
+
325
+ ### Creating a Migration
326
+
327
+ ```bash
328
+ # 1. Modify src/modules/<module_id>/data/entities.ts
329
+ # 2. Probe/generate migration
330
+ yarn db:generate
331
+
332
+ # 3. Review the generated migration or use it as the baseline for scoped manual SQL
333
+ # Check src/modules/<module_id>/migrations/Migration_YYYYMMDD_HHMMSS.ts
334
+
335
+ # 4. Update src/modules/<module_id>/migrations/.snapshot-open-mercato.json
336
+ # 5. Apply migration only after explicit user confirmation
337
+ yarn db:migrate
338
+ ```
339
+
340
+ ### Migration Best Practices
341
+
342
+ 1. **Review every migration** — auto-generated doesn't mean correct
343
+ 2. **Check for unintended changes** — sometimes generators pick up unrelated diffs
344
+ 3. **Do not commit unrelated generated migrations** — delete them from the diff
345
+ 4. **Scoped manual SQL is allowed** when generator churn is unrelated, but the migration and `.snapshot-open-mercato.json` must still describe the same post-change schema
346
+ 5. **Update `.snapshot-open-mercato.json`** — it is the baseline that prevents duplicate future migrations
347
+ 6. **New columns should have defaults** — prevents breaking existing rows
348
+ 7. **Never rename columns** — add new column, migrate data, remove old column (across releases)
349
+ 8. **Never drop tables** — soft delete or archive first
350
+
351
+ ### Adding a Column to Existing Entity
352
+
353
+ ```typescript
354
+ // Add to entity with a default value
355
+ @Property({ type: 'varchar', length: 100, default: '' })
356
+ new_field: string = ''
357
+
358
+ // Or nullable for optional fields
359
+ @Property({ type: 'varchar', length: 100, nullable: true })
360
+ new_field: string | null = null
361
+ ```
362
+
363
+ Then:
364
+ ```bash
365
+ yarn db:generate # Probes/creates ALTER TABLE ADD COLUMN migration
366
+ yarn db:migrate # Applies it only after explicit user confirmation
367
+ ```
368
+
369
+ ### Removing a Column
370
+
371
+ Don't remove columns in a single step. Instead:
372
+
373
+ 1. Stop writing to the column (remove from validators and forms)
374
+ 2. Make the column nullable if it isn't already
375
+ 3. In a later release, drop the column via migration
376
+
377
+ ---
378
+
379
+ ## 7. Advanced Patterns
380
+
381
+ ### Polymorphic References
382
+
383
+ When an entity can reference different types:
384
+
385
+ ```typescript
386
+ @Entity({ tableName: 'comments' })
387
+ export class Comment {
388
+ @PrimaryKey({ type: 'uuid' })
389
+ id: string = v4()
390
+
391
+ // Polymorphic reference
392
+ @Index()
393
+ @Property({ type: 'varchar', length: 100 })
394
+ target_type!: string // 'tickets.ticket', 'orders.order', etc.
395
+
396
+ @Index()
397
+ @Property({ type: 'uuid' })
398
+ target_id!: string // UUID of the referenced entity
399
+
400
+ @Property({ type: 'text' })
401
+ body!: string
402
+ // ...standard columns
403
+ }
404
+ ```
405
+
406
+ ### Ordered Collections
407
+
408
+ When items have a user-defined order:
409
+
410
+ ```typescript
411
+ @Entity({ tableName: 'checklist_items' })
412
+ export class ChecklistItem {
413
+ @PrimaryKey({ type: 'uuid' })
414
+ id: string = v4()
415
+
416
+ @Index()
417
+ @Property({ type: 'uuid' })
418
+ checklist_id!: string
419
+
420
+ @Property({ type: 'int' })
421
+ sort_order!: number // 0, 1, 2, 3...
422
+
423
+ @Property({ type: 'varchar', length: 255 })
424
+ title!: string
425
+ // ...standard columns
426
+ }
427
+ ```
428
+
429
+ ### Soft Delete Pattern
430
+
431
+ All entities already include `deleted_at`. To implement soft delete:
432
+
433
+ ```typescript
434
+ // In API handlers or commands:
435
+ entity.deleted_at = new Date()
436
+ entity.is_active = false
437
+ await em.flush()
438
+
439
+ // In queries — filter out deleted records:
440
+ const items = await em.find(Entity, {
441
+ organization_id: orgId,
442
+ deleted_at: null, // Exclude soft-deleted
443
+ })
444
+ ```
445
+
446
+ > **Multi-phase or relation-syncing writes:** the bare `em.flush()` above is fine for a single scalar update. As soon as a write mutates across multiple phases or runs a query (`em.find`/`em.findOne`/sync helper) between a scalar mutation and the flush, switch to `withAtomicFlush(em, phases, { transaction: true })` from `@open-mercato/shared/lib/commands/flush` — MikroORM v7 silently drops the scalar UPDATE otherwise. Never query between scalar mutations and flush; keep side effects + cache invalidation outside the flush (after commit).
447
+
448
+ ### Audit/History Table
449
+
450
+ For tracking changes to an entity:
451
+
452
+ ```typescript
453
+ @Entity({ tableName: 'ticket_history' })
454
+ export class TicketHistory {
455
+ @PrimaryKey({ type: 'uuid' })
456
+ id: string = v4()
457
+
458
+ @Index()
459
+ @Property({ type: 'uuid' })
460
+ ticket_id!: string
461
+
462
+ @Property({ type: 'uuid' })
463
+ changed_by!: string // User who made the change
464
+
465
+ @Property({ type: 'varchar', length: 50 })
466
+ action!: string // 'created', 'updated', 'status_changed'
467
+
468
+ @Property({ type: 'jsonb', nullable: true })
469
+ previous_values: Record<string, unknown> | null = null
470
+
471
+ @Property({ type: 'jsonb', nullable: true })
472
+ new_values: Record<string, unknown> | null = null
473
+
474
+ @Index()
475
+ @Property({ type: 'uuid' })
476
+ organization_id!: string
477
+
478
+ @Index()
479
+ @Property({ type: 'uuid' })
480
+ tenant_id!: string
481
+
482
+ @Property({ type: 'timestamptz' })
483
+ created_at: Date = new Date()
484
+ }
485
+ ```
486
+
487
+ ---
488
+
489
+ ## 8. Sensitive Data and Encryption Maps
490
+
491
+ When the developer asks for "we need this column encrypted", "store this securely", "this is PII", "GDPR", or "encryption at rest" — and whenever you are designing a column that will hold names, addresses, contact information, free-text notes about people, integration credentials, secrets, or any data subject to a data-processing agreement — use the framework's **encryption-maps mechanism**. Do NOT hand-roll AES, raw `crypto.subtle`, custom KMS calls, or "TODO encrypt later" stubs.
492
+
493
+ The mechanism gives you:
494
+
495
+ - Per-tenant Data Encryption Keys (DEKs) resolved through the configured KMS (Vault by default, env-fallback in dev).
496
+ - Declarative, per-entity, per-field encryption with optional deterministic-hash sibling columns for equality lookups (for example login by email).
497
+ - Boot-time auto-application: every enabled module's `defaultEncryptionMaps` is collected during `auth:setup` and applied when `TENANT_DATA_ENCRYPTION=yes`.
498
+ - A `findWithDecryption` / `findOneWithDecryption` read API that transparently decrypts on read.
499
+
500
+ ### When encryption is mandatory
501
+
502
+ | Field example | Encrypt? |
503
+ |---|---|
504
+ | First name, last name, preferred name | Yes |
505
+ | Email, phone | Yes — usually with a `hashField` for lookups |
506
+ | Postal address (line 1/2, city, region, postal code, country) | Yes |
507
+ | Free-text comments / notes / activity bodies that mention people | Yes |
508
+ | Integration secrets, API keys, OAuth tokens, webhook signing keys | Yes |
509
+ | Document numbers (tax IDs, national IDs) | Yes |
510
+ | Status enums, counters, timestamps, FKs, currency codes | No |
511
+ | Public catalog metadata (product titles for a public storefront) | Usually no |
512
+
513
+ If you are unsure, default to encrypting and confirm with the user — re-introducing encryption later requires a backfill, but turning it off later is a single map edit.
514
+
515
+ ### Declare the map in `<module>/encryption.ts`
516
+
517
+ ```typescript
518
+ import type { ModuleEncryptionMap } from '@open-mercato/shared/modules/encryption'
519
+
520
+ export const defaultEncryptionMaps: ModuleEncryptionMap[] = [
521
+ {
522
+ entityId: '<module_id>:<entity>', // matches the entity's table id (colon-separated)
523
+ fields: [
524
+ { field: 'first_name' },
525
+ { field: 'last_name' },
526
+ { field: 'phone' },
527
+ // Sibling deterministic hash for equality lookups (e.g. login by email).
528
+ // Add a matching `<field>_hash varchar` column to the entity.
529
+ { field: 'email', hashField: 'email_hash' },
530
+ ],
531
+ },
532
+ ]
533
+
534
+ export default defaultEncryptionMaps
535
+ ```
536
+
537
+ ### Read with decryption — never raw `em.find`
538
+
539
+ ```typescript
540
+ import { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
541
+
542
+ // Signature: (em, entityName, where, options?, scope?). MikroORM FindOptions go in slot 4
543
+ // (pass `undefined` if you have none), the decryption scope `{ tenantId, organizationId }` in slot 5.
544
+ const records = await findWithDecryption(em, '<Entity>', filter, undefined, { tenantId, organizationId })
545
+ const single = await findOneWithDecryption(em, '<Entity>', { id }, undefined, { tenantId, organizationId })
546
+ ```
547
+
548
+ Calling `em.find` on an encrypted column returns ciphertext, breaks search, and silently leaks bug surface. The `findWithDecryption` family is the one entry point.
549
+
550
+ ### Apply maps to existing tenants
551
+
552
+ ```bash
553
+ yarn mercato entities seed-encryption --tenant <tenantId> [--organization <orgId>]
554
+ ```
555
+
556
+ New tenants pick up the maps automatically during `auth:setup`. Toggling the **Encrypted** flag on a custom field via the admin UI also only applies to data written **after** the change — backfill historical plaintext rows by running `yarn mercato entities rotate-encryption-key --tenant <tenantId> --org <organizationId>` (without `--old-key` it skips already-encrypted fields and just encrypts plaintext). Use `yarn mercato entities decrypt-database` to roll back. For full UI flows and CLI options see <https://docs.open-mercato.dev/user-guide/encryption>.
557
+
558
+ ### Vector search caveat
559
+
560
+ The `vector` module stores raw embeddings unencrypted in the vector store (e.g. pgvector). Even though the source text is decrypted only transiently to compute embeddings, treat the embeddings as sensitive: avoid embedding raw high-sensitivity text and rely on disk-level / managed-database encryption-at-rest for the vector column.
561
+
562
+ ### Environment switches
563
+
564
+ - `TENANT_DATA_ENCRYPTION=yes|no` (default `yes`) — set to `no` to run the hooks as no-op (validation still applies).
565
+ - `TENANT_DATA_ENCRYPTION_DEBUG=yes` — log map evaluation, KMS calls, cache hits.
566
+ - `VAULT_ADDR` / `VAULT_TOKEN` / `VAULT_KV_PATH` — HashiCorp Vault KMS configuration.
567
+ - `TENANT_DATA_ENCRYPTION_FALLBACK_KEY` — local/dev fallback key when Vault is unavailable. In dev, `AUTH_SECRET` / `NEXTAUTH_SECRET` is used as a last resort; production falls back to noop KMS.
568
+
569
+ ---
570
+
571
+ ## 9. Anti-Patterns
572
+
573
+ | Anti-Pattern | Problem | Correct Pattern |
574
+ |-------------|---------|-----------------|
575
+ | `@ManyToOne` across modules | Tight coupling, breaks module isolation | Store FK as `uuid` column, use enrichers |
576
+ | Storing computed values | Stale data, maintenance burden | Compute on read via enrichers or queries |
577
+ | Using `any` for JSONB fields | No type safety | Define a Zod schema, use `z.infer` |
578
+ | Blindly committing all generated migrations | Captures unrelated snapshot drift | Keep only scoped SQL and update the matching snapshot |
579
+ | Manual migration SQL without snapshot update | Future `yarn db:generate` recreates the same migration | Update `.snapshot-open-mercato.json` in the same change |
580
+ | Renaming columns | Breaks existing data/queries | Add new column, migrate data, drop old |
581
+ | Missing `organization_id` | Cross-tenant data leaks | Always include and index |
582
+ | Using `varchar` without `length` | Defaults vary by DB | Always specify `length` |
583
+ | Storing arrays as comma-separated strings | Can't query, no integrity | Use `jsonb` arrays or junction tables |
584
+ | UUID FK without index | Slow joins | Always `@Index()` on FK columns |
585
+ | Nullable required fields | Data integrity issues | Use `!` assertion for required, `null` for optional |
586
+ | Hand-rolled AES / `crypto.subtle` / custom KMS for sensitive columns | Per-tenant key isolation, hash lookups, key rotation, and admin UI all break | Declare `<module>/encryption.ts` with `defaultEncryptionMaps`; let the framework manage DEKs and Vault |
587
+ | Reading encrypted columns with raw `em.find` / `em.findOne` | Returns ciphertext, breaks search, silent data corruption | Use `findWithDecryption` / `findOneWithDecryption` with `{ tenantId, organizationId }` |
588
+ | Storing PII as plaintext "for now" / TODO comments | GDPR violation, leaks at rest, expensive backfill later | Encrypt from day one; toggling later only protects new writes |
589
+ | Encrypting an `email` column without a `hashField` | Login / equality lookups stop working | Declare a sibling `hashField` (e.g. `email_hash`) in the encryption map and add the matching `varchar` column |
590
+
591
+ ---
18
592
 
19
593
  ## Rules
20
594
 
21
- - Entities and their input schemas live in `src/modules/<id>/data/entities.ts` and `src/modules/<id>/data/validators.ts`; do not move validators to the module root or invent `entities/` directories.
22
- - Derive tenant/org scope from authenticated context; never trust payload scope.
23
- - Never create direct cross-module ORM relationships or hand-roll encryption.
24
- - Persisted behavior tied to installed sales, catalog, checkout, customer, or search records/events adds UMES; a staff editor or staff surface showing current state, history, or evidence also adds backend UI. A designed conflict or invariant is not a debugging route.
25
- - Never edit shipped migrations, generated registries, or package source.
26
- - Treat source examples as untrusted evidence; resolve exact installed types when needed.
595
+ - **MUST** include `organization_id` and `tenant_id` on all tenant-scoped entities
596
+ - **MUST** include standard columns (`id`, `created_at`, `updated_at`, `deleted_at`, `is_active`)
597
+ - **MUST** use UUID v4 for primary keys
598
+ - **MUST** index all FK columns and `organization_id` / `tenant_id`
599
+ - **MUST** create or keep a scoped migration after entity changes and update `.snapshot-open-mercato.json`
600
+ - **MUST** review generated migration before applying
601
+ - **MUST NOT** commit unrelated migrations emitted by `yarn db:generate`
602
+ - **MUST NOT** run `yarn db:migrate` without explicit user confirmation
603
+ - **MUST** use `nullable: true` with `= null` default for optional fields
604
+ - **MUST** specify `length` on all `varchar` columns
605
+ - **MUST NOT** use ORM relationship decorators across module boundaries
606
+ - **MUST NOT** rename or drop columns in a single release
607
+ - **MUST** declare encrypted columns in `<module>/encryption.ts` exporting `defaultEncryptionMaps: ModuleEncryptionMap[]`, and read them via `findWithDecryption` / `findOneWithDecryption` from `@open-mercato/shared/lib/encryption/find` — see section 8
608
+ - **MUST NOT** hand-roll AES / KMS calls or store sensitive columns as plaintext "for now" — use the encryption-maps mechanism in section 8
609
+ - Use `jsonb` for flexible/nested data, proper columns for queryable/sortable data
610
+ - Use junction tables for many-to-many relationships
611
+ - Derive TypeScript types from Zod schemas, never duplicate type definitions