create-ailk 0.23.0 → 0.25.1

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 (604) hide show
  1. package/README.md +68 -33
  2. package/component-catalog.json +3 -3
  3. package/component-catalog.md +3 -3
  4. package/dist/add-module.js +10 -3
  5. package/dist/add-route.d.ts +12 -1
  6. package/dist/add-route.js +38 -4
  7. package/dist/cli.js +12 -7
  8. package/dist/derive-architecture.d.ts +3 -4
  9. package/dist/derive-architecture.js +75 -22
  10. package/dist/lib/design-checks.d.ts +9 -4
  11. package/dist/lib/design-checks.js +13 -7
  12. package/dist/lib/extract-module-bundle.js +29 -2
  13. package/dist/lib/fetch-template-snapshot.d.ts +1 -1
  14. package/dist/lib/fetch-template-snapshot.js +7 -3
  15. package/dist/lib/import-graph.d.ts +1 -1
  16. package/dist/lib/import-graph.js +4 -4
  17. package/dist/lib/module-license-gate.d.ts +12 -1
  18. package/dist/lib/module-license-gate.js +34 -12
  19. package/dist/lib/scaffold-render-sweep.d.ts +33 -0
  20. package/dist/lib/scaffold-render-sweep.js +45 -0
  21. package/dist/module-architecture.d.ts +20 -3
  22. package/dist/module-architecture.js +122 -538
  23. package/dist/paid-sections.d.ts +25 -1
  24. package/dist/paid-sections.js +88 -3
  25. package/dist/surfaces.js +10 -2
  26. package/dist/sync-routes.d.ts +30 -4
  27. package/dist/sync-routes.js +271 -18
  28. package/dist/template-record.d.ts +14 -4
  29. package/dist/template-record.js +23 -5
  30. package/dist/topology.d.ts +16 -3
  31. package/dist/topology.js +31 -19
  32. package/dist/update.d.ts +10 -1
  33. package/dist/update.js +540 -16
  34. package/package.json +3 -3
  35. package/release-scope.json +1 -1
  36. package/templates/.claude/cloudparity-repo-hooks.json +5 -0
  37. package/templates/.claude/hooks/template-guard.sh +137 -0
  38. package/templates/.claude/rules/extending.md +106 -0
  39. package/templates/.claude/rules/loop-contract.md +2 -2
  40. package/templates/.claude/settings.json +11 -0
  41. package/templates/.env.example +9 -5
  42. package/templates/.github/workflows/ci.yml +7 -4
  43. package/templates/CLAUDE.md +15 -3
  44. package/templates/CONVENTIONS.md +8 -10
  45. package/templates/README.md +14 -13
  46. package/templates/apps/api/.env.example +9 -2
  47. package/templates/apps/api/CLAUDE.md +122 -82
  48. package/templates/apps/api/jest.config.cjs +15 -5
  49. package/templates/apps/api/package.json +16 -20
  50. package/templates/apps/api/src/__tests__/trusted-origins.server.test.ts +2 -2
  51. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +4 -4
  52. package/templates/apps/api/src/index.ts +1 -1
  53. package/templates/apps/api/src/lib/__mocks__/prisma.ts +7 -4
  54. package/templates/apps/api/src/modules/commerce.ts +10 -0
  55. package/templates/apps/api/src/modules/content-write.ts +10 -0
  56. package/templates/apps/api/src/modules/multi-tenant.ts +10 -0
  57. package/templates/apps/api/src/modules/scheduling.ts +10 -0
  58. package/templates/apps/api/src/modules/signal-collectors.ts +10 -0
  59. package/templates/apps/api/src/modules/workspace.ts +10 -0
  60. package/templates/apps/api/src/server.ts +117 -1857
  61. package/templates/apps/api/vercel.json +2 -2
  62. package/templates/apps/mcp/CLAUDE.md +77 -24
  63. package/templates/apps/mcp/__tests__/app-entry.test.ts +4 -254
  64. package/templates/apps/mcp/__tests__/boot-file.test.ts +92 -0
  65. package/templates/apps/mcp/jest.config.cjs +6 -1
  66. package/templates/apps/mcp/package.json +5 -12
  67. package/templates/apps/mcp/src/app.ts +8 -7
  68. package/templates/apps/mcp/src/index.ts +6 -34
  69. package/templates/apps/mcp/src/lib/paid-tools.generated.ts +10 -0
  70. package/templates/apps/mcp/src/lib/topology.generated.ts +2 -2
  71. package/templates/apps/mcp/src/site.ts +12 -0
  72. package/templates/apps/web/.env.example +1 -1
  73. package/templates/apps/web/CLAUDE.md +21 -5
  74. package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
  75. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +1 -1
  76. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +14 -4
  77. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +1 -1
  78. package/templates/apps/web/app/[locale]/contact/intake-script.ts +1 -1
  79. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +22 -1
  80. package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +28 -7
  81. package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +43 -0
  82. package/templates/apps/web/app/[locale]/pricing/page.tsx +7 -3
  83. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +222 -0
  84. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +10 -3
  85. package/templates/apps/web/app/api/revalidate/route.ts +3 -3
  86. package/templates/apps/web/auth.ts +2 -2
  87. package/templates/apps/web/jest.config.cjs +1 -0
  88. package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +7 -0
  89. package/templates/apps/web/lib/auth-email.ts +1 -1
  90. package/templates/apps/web/lib/magnet-routes.ts +3 -1
  91. package/templates/apps/web/lib/newsletter.actions.ts +9 -6
  92. package/templates/apps/web/lib/relay.ts +2 -2
  93. package/templates/apps/web/lib/release-scope.ts +47 -0
  94. package/templates/apps/web/lib/server-api.ts +1 -1
  95. package/templates/apps/web/src/lib/commerce-claim.ts +1 -1
  96. package/templates/customizations.yaml +37 -0
  97. package/templates/database/prisma.config.ts +10 -14
  98. package/templates/database/seed-test.ts +2 -2
  99. package/templates/database/seed.ts +32 -5
  100. package/templates/package.json +12 -6
  101. package/templates/pnpm-workspace.yaml +0 -1
  102. package/templates/project.yaml +16 -8
  103. package/templates/tools/jest/reporters.cjs +65 -0
  104. package/templates/tools/lint-architecture.ts +757 -7
  105. package/templates/tsconfig.json +1 -2
  106. package/versions.json +48 -35
  107. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +0 -409
  108. package/templates/apps/api/src/__tests__/factories/agent-caller.ts +0 -102
  109. package/templates/apps/api/src/__tests__/factories/content-fixture.ts +0 -138
  110. package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +0 -441
  111. package/templates/apps/api/src/__tests__/factories/index.ts +0 -70
  112. package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +0 -82
  113. package/templates/apps/api/src/__tests__/leads-forwarded-rate-limit.test.ts +0 -135
  114. package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +0 -126
  115. package/templates/apps/api/src/__tests__/stripe-boot.test.ts +0 -132
  116. package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +0 -362
  117. package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +0 -184
  118. package/templates/apps/api/src/bin/abandon-sweep.ts +0 -42
  119. package/templates/apps/api/src/bin/rate-limit-probe.ts +0 -53
  120. package/templates/apps/api/src/bin/spokes.ts +0 -198
  121. package/templates/apps/api/src/bin/stripe-connections-rekey.ts +0 -45
  122. package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +0 -196
  123. package/templates/apps/api/src/config/__tests__/index.test.ts +0 -233
  124. package/templates/apps/api/src/config/__tests__/modules.test.ts +0 -76
  125. package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +0 -185
  126. package/templates/apps/api/src/config/__tests__/trusted-origins.test.ts +0 -238
  127. package/templates/apps/api/src/config/index.ts +0 -596
  128. package/templates/apps/api/src/config/modules.ts +0 -83
  129. package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +0 -581
  130. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +0 -143
  131. package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +0 -237
  132. package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +0 -353
  133. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +0 -201
  134. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +0 -224
  135. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +0 -120
  136. package/templates/apps/api/src/lib/__tests__/errors.test.ts +0 -94
  137. package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +0 -144
  138. package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +0 -8
  139. package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +0 -149
  140. package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +0 -116
  141. package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +0 -209
  142. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +0 -257
  143. package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +0 -96
  144. package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +0 -125
  145. package/templates/apps/api/src/lib/__tests__/prisma.test.ts +0 -165
  146. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +0 -317
  147. package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +0 -129
  148. package/templates/apps/api/src/lib/__tests__/site.test.ts +0 -292
  149. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +0 -89
  150. package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +0 -202
  151. package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +0 -217
  152. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -82
  153. package/templates/apps/api/src/lib/agent-store.ts +0 -613
  154. package/templates/apps/api/src/lib/auth-email-budget.ts +0 -161
  155. package/templates/apps/api/src/lib/consent-store.ts +0 -363
  156. package/templates/apps/api/src/lib/content-adapter.ts +0 -112
  157. package/templates/apps/api/src/lib/deliverable-asset-store.ts +0 -96
  158. package/templates/apps/api/src/lib/error-serializer.ts +0 -164
  159. package/templates/apps/api/src/lib/errors.ts +0 -95
  160. package/templates/apps/api/src/lib/file-fallback.ts +0 -87
  161. package/templates/apps/api/src/lib/flow-completion-store.ts +0 -136
  162. package/templates/apps/api/src/lib/hub-sign-in-store.ts +0 -455
  163. package/templates/apps/api/src/lib/lead-capture-store.ts +0 -122
  164. package/templates/apps/api/src/lib/lead-delivery.ts +0 -52
  165. package/templates/apps/api/src/lib/license-key-store.ts +0 -45
  166. package/templates/apps/api/src/lib/locales.ts +0 -201
  167. package/templates/apps/api/src/lib/outbound-http.ts +0 -127
  168. package/templates/apps/api/src/lib/prisma.ts +0 -21
  169. package/templates/apps/api/src/lib/proposal-paid-store.ts +0 -66
  170. package/templates/apps/api/src/lib/rate-limit-probe.ts +0 -165
  171. package/templates/apps/api/src/lib/rate-limit-store.ts +0 -142
  172. package/templates/apps/api/src/lib/require-commerce-claim.ts +0 -250
  173. package/templates/apps/api/src/lib/require-paid-tier.ts +0 -53
  174. package/templates/apps/api/src/lib/site.ts +0 -254
  175. package/templates/apps/api/src/lib/slice-load.ts +0 -90
  176. package/templates/apps/api/src/lib/spoke-store.ts +0 -180
  177. package/templates/apps/api/src/lib/stripe.ts +0 -318
  178. package/templates/apps/api/src/lib/webhook-idempotency.ts +0 -156
  179. package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +0 -111
  180. package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +0 -172
  181. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +0 -119
  182. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +0 -138
  183. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-trusted-origins.test.ts +0 -142
  184. package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +0 -155
  185. package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +0 -139
  186. package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +0 -562
  187. package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +0 -76
  188. package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +0 -220
  189. package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +0 -97
  190. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +0 -557
  191. package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +0 -72
  192. package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +0 -140
  193. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +0 -721
  194. package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +0 -220
  195. package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +0 -430
  196. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +0 -1409
  197. package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +0 -174
  198. package/templates/apps/api/src/middleware/auth.ts +0 -864
  199. package/templates/apps/api/src/middleware/default-tenant.ts +0 -166
  200. package/templates/apps/api/src/middleware/rate-limit.ts +0 -983
  201. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +0 -264
  202. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +0 -182
  203. package/templates/apps/api/src/openapi/index.ts +0 -18
  204. package/templates/apps/api/src/openapi/spec.ts +0 -1815
  205. package/templates/apps/api/src/openapi/surface-drift.ts +0 -195
  206. package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +0 -175
  207. package/templates/apps/api/src/routes/aeo/index.ts +0 -107
  208. package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +0 -196
  209. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +0 -249
  210. package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +0 -532
  211. package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +0 -313
  212. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +0 -413
  213. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +0 -609
  214. package/templates/apps/api/src/routes/auth/hub-sign-in.ts +0 -242
  215. package/templates/apps/api/src/routes/auth/tokens.ts +0 -211
  216. package/templates/apps/api/src/routes/auth/transactional-email.ts +0 -301
  217. package/templates/apps/api/src/routes/auth/user.ts +0 -305
  218. package/templates/apps/api/src/routes/billing/plan-checkout.ts +0 -325
  219. package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +0 -178
  220. package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +0 -1300
  221. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +0 -448
  222. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -1742
  223. package/templates/apps/api/src/routes/checkout/index.ts +0 -34
  224. package/templates/apps/api/src/routes/checkout/session.ts +0 -171
  225. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -540
  226. package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +0 -386
  227. package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +0 -160
  228. package/templates/apps/api/src/routes/consent/account.ts +0 -268
  229. package/templates/apps/api/src/routes/consent/cookie.ts +0 -90
  230. package/templates/apps/api/src/routes/consent/index.ts +0 -26
  231. package/templates/apps/api/src/routes/consent/unsubscribe.ts +0 -127
  232. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +0 -853
  233. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +0 -230
  234. package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +0 -44
  235. package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +0 -118
  236. package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +0 -731
  237. package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +0 -753
  238. package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +0 -51
  239. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +0 -450
  240. package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +0 -762
  241. package/templates/apps/api/src/routes/content/get-page.ts +0 -268
  242. package/templates/apps/api/src/routes/content/index.ts +0 -49
  243. package/templates/apps/api/src/routes/content/list-pages.ts +0 -95
  244. package/templates/apps/api/src/routes/content/list-types.ts +0 -19
  245. package/templates/apps/api/src/routes/content/offerings.ts +0 -184
  246. package/templates/apps/api/src/routes/content/pricing.ts +0 -176
  247. package/templates/apps/api/src/routes/content/rate-limit.ts +0 -22
  248. package/templates/apps/api/src/routes/content/search.ts +0 -129
  249. package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
  250. package/templates/apps/api/src/routes/content/slug.ts +0 -9
  251. package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +0 -88
  252. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +0 -171
  253. package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +0 -515
  254. package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +0 -392
  255. package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +0 -165
  256. package/templates/apps/api/src/routes/leads/index.ts +0 -573
  257. package/templates/apps/api/src/routes/leads/list.ts +0 -104
  258. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +0 -626
  259. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +0 -1268
  260. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +0 -847
  261. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +0 -480
  262. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -465
  263. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +0 -872
  264. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +0 -1022
  265. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +0 -484
  266. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +0 -535
  267. package/templates/apps/api/src/routes/project-listings/index.ts +0 -164
  268. package/templates/apps/api/src/routes/project-listings/me.ts +0 -87
  269. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +0 -224
  270. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +0 -102
  271. package/templates/apps/api/src/routes/project-listings/public.ts +0 -98
  272. package/templates/apps/api/src/routes/project-listings/respond.ts +0 -48
  273. package/templates/apps/api/src/routes/project-listings/resume-token.ts +0 -27
  274. package/templates/apps/api/src/routes/project-listings/start.ts +0 -140
  275. package/templates/apps/api/src/routes/project-listings/submit.ts +0 -130
  276. package/templates/apps/api/src/routes/project-listings/verify-email.ts +0 -150
  277. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +0 -686
  278. package/templates/apps/api/src/routes/requests/confirm.ts +0 -601
  279. package/templates/apps/api/src/routes/requests/index.ts +0 -54
  280. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +0 -306
  281. package/templates/apps/api/src/routes/test/auth/session.ts +0 -143
  282. package/templates/apps/api/src/routes/webhooks/README.md +0 -398
  283. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +0 -649
  284. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +0 -749
  285. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -448
  286. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -264
  287. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +0 -159
  288. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +0 -158
  289. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +0 -814
  290. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -301
  291. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-proposal-paid.test.ts +0 -369
  292. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -215
  293. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +0 -301
  294. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +0 -808
  295. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -1155
  296. package/templates/apps/api/src/routes/webhooks/index.ts +0 -141
  297. package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +0 -384
  298. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -2026
  299. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -528
  300. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +0 -132
  301. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +0 -1028
  302. package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +0 -313
  303. package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +0 -503
  304. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +0 -65
  305. package/templates/apps/api/src/services/__tests__/consent.test.ts +0 -299
  306. package/templates/apps/api/src/services/__tests__/default-tenant.test.ts +0 -147
  307. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +0 -867
  308. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +0 -379
  309. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +0 -368
  310. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +0 -142
  311. package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +0 -854
  312. package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +0 -234
  313. package/templates/apps/api/src/services/__tests__/lead-accept-user.test.ts +0 -251
  314. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +0 -2660
  315. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +0 -166
  316. package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +0 -389
  317. package/templates/apps/api/src/services/__tests__/person.test.ts +0 -1176
  318. package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -444
  319. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -154
  320. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +0 -137
  321. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +0 -531
  322. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +0 -372
  323. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +0 -348
  324. package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +0 -660
  325. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +0 -941
  326. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +0 -588
  327. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +0 -78
  328. package/templates/apps/api/src/services/abandon-email.ts +0 -313
  329. package/templates/apps/api/src/services/aeo-score.ts +0 -325
  330. package/templates/apps/api/src/services/answer-distributions.ts +0 -138
  331. package/templates/apps/api/src/services/audit-log.ts +0 -961
  332. package/templates/apps/api/src/services/checkout-buyer.ts +0 -168
  333. package/templates/apps/api/src/services/consent.ts +0 -834
  334. package/templates/apps/api/src/services/default-tenant.ts +0 -416
  335. package/templates/apps/api/src/services/deliverable-fulfillment.ts +0 -755
  336. package/templates/apps/api/src/services/dunning-policy.ts +0 -294
  337. package/templates/apps/api/src/services/enrichment-shim.ts +0 -53
  338. package/templates/apps/api/src/services/entitlements.ts +0 -338
  339. package/templates/apps/api/src/services/hub-sign-in.ts +0 -472
  340. package/templates/apps/api/src/services/lead-accept-user.ts +0 -126
  341. package/templates/apps/api/src/services/lead-promotion.ts +0 -160
  342. package/templates/apps/api/src/services/lead-routing.ts +0 -1372
  343. package/templates/apps/api/src/services/leads-inbox.ts +0 -312
  344. package/templates/apps/api/src/services/listing-config.ts +0 -178
  345. package/templates/apps/api/src/services/magnet-completion.ts +0 -242
  346. package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +0 -295
  347. package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +0 -268
  348. package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +0 -868
  349. package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +0 -416
  350. package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +0 -396
  351. package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -681
  352. package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
  353. package/templates/apps/api/src/services/offerings/catalog.ts +0 -308
  354. package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -385
  355. package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
  356. package/templates/apps/api/src/services/offerings/connection.ts +0 -1003
  357. package/templates/apps/api/src/services/offerings/index.ts +0 -614
  358. package/templates/apps/api/src/services/offerings/source.ts +0 -140
  359. package/templates/apps/api/src/services/offerings/store.ts +0 -150
  360. package/templates/apps/api/src/services/owner-workspaces.ts +0 -132
  361. package/templates/apps/api/src/services/person.ts +0 -1188
  362. package/templates/apps/api/src/services/plan-offerings.ts +0 -125
  363. package/templates/apps/api/src/services/project-listing-resume-email.ts +0 -326
  364. package/templates/apps/api/src/services/project-listings.ts +0 -2073
  365. package/templates/apps/api/src/services/public-site-resolver.ts +0 -396
  366. package/templates/apps/api/src/services/request-confirm.ts +0 -698
  367. package/templates/apps/api/src/services/revalidation.ts +0 -235
  368. package/templates/apps/api/src/services/slack-notify.ts +0 -291
  369. package/templates/apps/api/src/services/usage-aggregation.ts +0 -516
  370. package/templates/apps/api/src/services/usage-credits.ts +0 -340
  371. package/templates/apps/api/src/services/verified-email.ts +0 -71
  372. package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +0 -52
  373. package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +0 -46
  374. package/templates/apps/mcp/__tests__/abuse/guard.test.ts +0 -198
  375. package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +0 -36
  376. package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +0 -54
  377. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +0 -116
  378. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +0 -299
  379. package/templates/apps/mcp/__tests__/boot.test.ts +0 -280
  380. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +0 -359
  381. package/templates/apps/mcp/__tests__/config/modules.test.ts +0 -78
  382. package/templates/apps/mcp/__tests__/get_business_hours.test.ts +0 -82
  383. package/templates/apps/mcp/__tests__/get_faq.test.ts +0 -80
  384. package/templates/apps/mcp/__tests__/get_locations.test.ts +0 -65
  385. package/templates/apps/mcp/__tests__/get_offerings.test.ts +0 -557
  386. package/templates/apps/mcp/__tests__/get_pricing.test.ts +0 -524
  387. package/templates/apps/mcp/__tests__/get_site_info.test.ts +0 -81
  388. package/templates/apps/mcp/__tests__/http-transport.test.ts +0 -1151
  389. package/templates/apps/mcp/__tests__/index.test.ts +0 -195
  390. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +0 -191
  391. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +0 -531
  392. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +0 -65
  393. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -187
  394. package/templates/apps/mcp/__tests__/lib/content-root.test.ts +0 -93
  395. package/templates/apps/mcp/__tests__/lib/errors.test.ts +0 -75
  396. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +0 -165
  397. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
  398. package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -228
  399. package/templates/apps/mcp/__tests__/locale.test.ts +0 -135
  400. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +0 -175
  401. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +0 -101
  402. package/templates/apps/mcp/__tests__/read_content.test.ts +0 -474
  403. package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -225
  404. package/templates/apps/mcp/__tests__/search_content.test.ts +0 -256
  405. package/templates/apps/mcp/__tests__/server.test.ts +0 -388
  406. package/templates/apps/mcp/__tests__/start_purchase.test.ts +0 -448
  407. package/templates/apps/mcp/__tests__/submit_lead.test.ts +0 -307
  408. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -223
  409. package/templates/apps/mcp/__tests__/tools/create_project.test.ts +0 -243
  410. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +0 -451
  411. package/templates/apps/mcp/src/abuse/daily-cap.ts +0 -47
  412. package/templates/apps/mcp/src/abuse/email-validator.ts +0 -25
  413. package/templates/apps/mcp/src/abuse/honeypot.ts +0 -21
  414. package/templates/apps/mcp/src/abuse/index.ts +0 -342
  415. package/templates/apps/mcp/src/abuse/user-agent-deny.ts +0 -58
  416. package/templates/apps/mcp/src/boot.ts +0 -167
  417. package/templates/apps/mcp/src/config/modules.ts +0 -43
  418. package/templates/apps/mcp/src/http-transport.ts +0 -526
  419. package/templates/apps/mcp/src/lib/audit-single-page.ts +0 -308
  420. package/templates/apps/mcp/src/lib/commerce-claim.ts +0 -263
  421. package/templates/apps/mcp/src/lib/content-adapter.ts +0 -142
  422. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -139
  423. package/templates/apps/mcp/src/lib/errors.ts +0 -61
  424. package/templates/apps/mcp/src/lib/license-key-store.ts +0 -45
  425. package/templates/apps/mcp/src/lib/locales.ts +0 -171
  426. package/templates/apps/mcp/src/lib/prisma.ts +0 -39
  427. package/templates/apps/mcp/src/lib/public-workspace.ts +0 -133
  428. package/templates/apps/mcp/src/lib/resolve-locale.ts +0 -30
  429. package/templates/apps/mcp/src/lib/shown-offerings.ts +0 -105
  430. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +0 -59
  431. package/templates/apps/mcp/src/request-context.ts +0 -52
  432. package/templates/apps/mcp/src/server.ts +0 -343
  433. package/templates/apps/mcp/src/serverless-app.ts +0 -88
  434. package/templates/apps/mcp/src/tools/audit.ts +0 -127
  435. package/templates/apps/mcp/src/tools/capture_lead.ts +0 -64
  436. package/templates/apps/mcp/src/tools/create_project.ts +0 -134
  437. package/templates/apps/mcp/src/tools/get_business_hours.ts +0 -68
  438. package/templates/apps/mcp/src/tools/get_faq.ts +0 -71
  439. package/templates/apps/mcp/src/tools/get_locations.ts +0 -88
  440. package/templates/apps/mcp/src/tools/get_offerings.ts +0 -96
  441. package/templates/apps/mcp/src/tools/get_pricing.ts +0 -94
  442. package/templates/apps/mcp/src/tools/get_site_info.ts +0 -86
  443. package/templates/apps/mcp/src/tools/index.ts +0 -137
  444. package/templates/apps/mcp/src/tools/list_pages.ts +0 -74
  445. package/templates/apps/mcp/src/tools/person_notes.ts +0 -25
  446. package/templates/apps/mcp/src/tools/proposals.ts +0 -27
  447. package/templates/apps/mcp/src/tools/purchase.ts +0 -205
  448. package/templates/apps/mcp/src/tools/read_content.ts +0 -75
  449. package/templates/apps/mcp/src/tools/request_callback.ts +0 -59
  450. package/templates/apps/mcp/src/tools/search_content.ts +0 -67
  451. package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -57
  452. package/templates/apps/mcp/src/workspace-server.ts +0 -224
  453. package/templates/database/CHANGELOG.md +0 -961
  454. package/templates/database/LICENSE +0 -202
  455. package/templates/database/NOTICE +0 -2
  456. package/templates/database/README.md +0 -243
  457. package/templates/database/__tests__/agents.db.test.ts +0 -320
  458. package/templates/database/__tests__/appointment.db.test.ts +0 -106
  459. package/templates/database/__tests__/artifact-content-store.db.test.ts +0 -316
  460. package/templates/database/__tests__/composite-fence.db.test.ts +0 -206
  461. package/templates/database/__tests__/consent-ledger.db.test.ts +0 -345
  462. package/templates/database/__tests__/consent-source-checkout-migration.test.ts +0 -79
  463. package/templates/database/__tests__/fence-fan-out.db.test.ts +0 -715
  464. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +0 -79
  465. package/templates/database/__tests__/fixtures/person-backfill.json +0 -264
  466. package/templates/database/__tests__/fixtures/person-contact-backfill.json +0 -182
  467. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +0 -100
  468. package/templates/database/__tests__/flow-completion-migration.test.ts +0 -115
  469. package/templates/database/__tests__/flow-completion.db.test.ts +0 -136
  470. package/templates/database/__tests__/flow-workspace-key.db.test.ts +0 -263
  471. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +0 -392
  472. package/templates/database/__tests__/flows-presets.test.ts +0 -173
  473. package/templates/database/__tests__/in-page-magnets-migration.test.ts +0 -161
  474. package/templates/database/__tests__/lead-idempotency-key.db.test.ts +0 -92
  475. package/templates/database/__tests__/member-area.db.test.ts +0 -257
  476. package/templates/database/__tests__/member-contract-migration.test.ts +0 -85
  477. package/templates/database/__tests__/member-contract.db.test.ts +0 -413
  478. package/templates/database/__tests__/offerings-checkout-session.db.test.ts +0 -270
  479. package/templates/database/__tests__/offerings-migration.test.ts +0 -197
  480. package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +0 -86
  481. package/templates/database/__tests__/offerings.db.test.ts +0 -303
  482. package/templates/database/__tests__/outbound-message-action.db.test.ts +0 -80
  483. package/templates/database/__tests__/person-contact.db.test.ts +0 -563
  484. package/templates/database/__tests__/person-notes.db.test.ts +0 -249
  485. package/templates/database/__tests__/person.db.test.ts +0 -586
  486. package/templates/database/__tests__/piece-kind-migration.test.ts +0 -67
  487. package/templates/database/__tests__/piece-kind.db.test.ts +0 -208
  488. package/templates/database/__tests__/prefixed-ids.db.test.ts +0 -222
  489. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +0 -111
  490. package/templates/database/__tests__/seed.test.ts +0 -240
  491. package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
  492. package/templates/database/__tests__/tenancy.test.ts +0 -141
  493. package/templates/database/__tests__/user-cascade.test.ts +0 -140
  494. package/templates/database/_gitignore +0 -8
  495. package/templates/database/auth/.gitkeep +0 -0
  496. package/templates/database/auth/schema.prisma +0 -641
  497. package/templates/database/config/.gitkeep +0 -0
  498. package/templates/database/config/schema.prisma +0 -441
  499. package/templates/database/content/.gitkeep +0 -0
  500. package/templates/database/content/schema.prisma +0 -339
  501. package/templates/database/eslint.config.mjs +0 -69
  502. package/templates/database/flows/listing-diagnostic.ts +0 -182
  503. package/templates/database/flows/presets.ts +0 -315
  504. package/templates/database/inbox/.gitkeep +0 -0
  505. package/templates/database/inbox/README.md +0 -3
  506. package/templates/database/inbox/__tests__/schema.test.ts +0 -44
  507. package/templates/database/inbox/schema.prisma +0 -1447
  508. package/templates/database/jest.config.cjs +0 -36
  509. package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +0 -328
  510. package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +0 -100
  511. package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +0 -28
  512. package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +0 -25
  513. package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +0 -42
  514. package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +0 -67
  515. package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +0 -21
  516. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +0 -201
  517. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +0 -67
  518. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +0 -46
  519. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +0 -25
  520. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +0 -49
  521. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +0 -197
  522. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +0 -32
  523. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +0 -22
  524. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +0 -151
  525. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +0 -223
  526. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +0 -22
  527. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +0 -32
  528. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +0 -40
  529. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +0 -4
  530. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +0 -2
  531. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +0 -2
  532. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +0 -29
  533. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +0 -6
  534. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +0 -39
  535. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +0 -24
  536. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +0 -3
  537. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +0 -33
  538. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +0 -30
  539. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +0 -47
  540. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +0 -16
  541. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +0 -47
  542. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +0 -10
  543. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +0 -26
  544. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +0 -90
  545. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +0 -174
  546. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +0 -14
  547. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +0 -25
  548. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +0 -32
  549. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +0 -71
  550. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +0 -30
  551. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +0 -49
  552. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +0 -123
  553. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +0 -15
  554. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +0 -158
  555. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +0 -40
  556. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +0 -67
  557. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +0 -70
  558. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +0 -181
  559. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +0 -172
  560. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +0 -41
  561. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +0 -185
  562. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +0 -70
  563. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +0 -57
  564. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +0 -68
  565. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +0 -1067
  566. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +0 -210
  567. package/templates/database/migrations/20261003120000_spoke_registry/migration.sql +0 -80
  568. package/templates/database/migrations/20261003130000_scheduling_connection/migration.sql +0 -49
  569. package/templates/database/migrations/20261005120000_agents/migration.sql +0 -84
  570. package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +0 -35
  571. package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +0 -20
  572. package/templates/database/migrations/20261009120000_agent_grants/migration.sql +0 -33
  573. package/templates/database/migrations/20261010120000_person/migration.sql +0 -106
  574. package/templates/database/migrations/20261010121000_person_backfill/migration.sql +0 -132
  575. package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +0 -20
  576. package/templates/database/migrations/20261011120000_member_area/migration.sql +0 -158
  577. package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +0 -21
  578. package/templates/database/migrations/20261012120000_member_contract/migration.sql +0 -192
  579. package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +0 -20
  580. package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +0 -122
  581. package/templates/database/migrations/20261014120000_person_contact/migration.sql +0 -231
  582. package/templates/database/migrations/20261015120000_person_notes/migration.sql +0 -77
  583. package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +0 -43
  584. package/templates/database/migrations/20261016120000_piece_kind/migration.sql +0 -95
  585. package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +0 -30
  586. package/templates/database/migrations/20261018120000_flow_completion/migration.sql +0 -52
  587. package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +0 -27
  588. package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +0 -146
  589. package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +0 -77
  590. package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +0 -45
  591. package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +0 -47
  592. package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +0 -16
  593. package/templates/database/migrations/migration_lock.toml +0 -3
  594. package/templates/database/ops/.gitkeep +0 -0
  595. package/templates/database/ops/schema.prisma +0 -43
  596. package/templates/database/package.json +0 -89
  597. package/templates/database/schema.prisma +0 -71
  598. package/templates/database/scripts/db-generate-locked.sh +0 -80
  599. package/templates/database/scripts/person-backfill.sql +0 -132
  600. package/templates/database/tenancy.ts +0 -183
  601. package/templates/database/tsconfig.build.json +0 -16
  602. package/templates/database/tsconfig.flows.json +0 -15
  603. package/templates/database/tsconfig.json +0 -16
  604. package/templates/database/tsconfig.tenancy.json +0 -14
@@ -1,181 +0,0 @@
1
- -- #6155 — FlowDefinition and FlowSession gain a workspace isolation key.
2
- --
3
- -- The ownership brief (docs/internal/decisions/workspace-owns-data-surfaces-render.md)
4
- -- and AD-2 (docs/internal/decisions/flow-engine-admin-surface.md): a workspace
5
- -- owns data, and the flow engine's tenant-scope fence grows to cover it.
6
- -- `siteId` stays a legacy alias for the public path (unchanged); `workspaceId`
7
- -- is the isolation key every authenticated read/write will go through
8
- -- (apps/api/src/lib/tenant-db.ts's new `flows`/`flowSessions` accessors).
9
- --
10
- -- NULLABLE, no default (spec D3) — a row whose `siteId` matches no workspace
11
- -- is left UNSET rather than guessed, and stays invisible to every accessor
12
- -- rather than visible to the wrong tenant (the same "fails empty, never
13
- -- wide" posture apps/api/src/services/leads-inbox.ts already takes).
14
- --
15
- -- RE-RUNNABLE (NFR3): `ADD COLUMN IF NOT EXISTS` / `CREATE INDEX IF NOT
16
- -- EXISTS` cover the column and index. Postgres has no
17
- -- `ADD CONSTRAINT IF NOT EXISTS`, so each format CHECK is wrapped in its own
18
- -- existence-guarded `DO $$` block — the stated exception to NFR3's "in the
19
- -- house style of 20260918120000_asset_metadata_columns" note: that
20
- -- precedent (and 20260713192429_prefixed_uuid_entity_ids's own CHECK
21
- -- additions) rely on `prisma migrate`'s per-migration transaction and
22
- -- ledger, not on idempotent DDL — the guard below is this migration's own
23
- -- addition.
24
- --
25
- -- Rollback: DROP INDEX "flow_definitions_workspace_id_idx",
26
- -- "flow_sessions_workspace_id_idx"; ALTER TABLE "flow_definitions" DROP
27
- -- CONSTRAINT "flow_definitions_workspace_id_format", DROP COLUMN
28
- -- "workspace_id"; ALTER TABLE "flow_sessions" DROP CONSTRAINT
29
- -- "flow_sessions_workspace_id_format", DROP COLUMN "workspace_id". Safe
30
- -- unconditionally at this pin — nothing reads the column until #6156+.
31
-
32
- -- The census the post-assertions compare against. ON COMMIT DROP: `prisma
33
- -- migrate` runs each migration in one transaction, so this disappears with it.
34
- CREATE TEMP TABLE "_6155_flow_census" ON COMMIT DROP AS
35
- SELECT (SELECT count(*) FROM "flow_definitions") AS d,
36
- (SELECT count(*) FROM "flow_sessions") AS s;
37
-
38
- -- AlterTable
39
- ALTER TABLE "flow_definitions" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
40
- ALTER TABLE "flow_sessions" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
41
-
42
- -- Format CHECKs — NULL-permitting, in the exact shape of the CHECKs
43
- -- 20260713192429_prefixed_uuid_entity_ids/migration.sql:170-197 already
44
- -- applies to every other `workspace_id` column. Existence-guarded because
45
- -- Postgres has no `ADD CONSTRAINT IF NOT EXISTS`.
46
- DO $$ BEGIN
47
- IF NOT EXISTS (
48
- SELECT 1 FROM pg_constraint WHERE conname = 'flow_definitions_workspace_id_format'
49
- ) THEN
50
- ALTER TABLE "flow_definitions" ADD CONSTRAINT "flow_definitions_workspace_id_format"
51
- CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
52
- END IF;
53
- END $$;
54
-
55
- DO $$ BEGIN
56
- IF NOT EXISTS (
57
- SELECT 1 FROM pg_constraint WHERE conname = 'flow_sessions_workspace_id_format'
58
- ) THEN
59
- ALTER TABLE "flow_sessions" ADD CONSTRAINT "flow_sessions_workspace_id_format"
60
- CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
61
- END IF;
62
- END $$;
63
-
64
- -- CreateIndex
65
- CREATE INDEX IF NOT EXISTS "flow_definitions_workspace_id_idx" ON "flow_definitions"("workspace_id");
66
- CREATE INDEX IF NOT EXISTS "flow_sessions_workspace_id_idx" ON "flow_sessions"("workspace_id");
67
-
68
- -- ── Backfill (spec D2) ──────────────────────────────────────────────────────
69
- -- Both spellings of the alias, one statement per table. The literal alias
70
- -- "default" resolves to the seeded sentinel workspace
71
- -- (database/tenancy.ts DEFAULT_WORKSPACE_ID); every other alias resolves
72
- -- only by exact workspaces.id equality. An inner join to "workspaces" means a
73
- -- row whose alias resolves to nothing is simply not updated and stays NULL
74
- -- — the correct, stated behavior (D3), not an error.
75
- --
76
- -- The sentinel id is written as a literal because SQL cannot import
77
- -- database/tenancy.ts; database/__tests__/tenancy.test.ts pins the same
78
- -- literal against the exported constant so the two cannot drift.
79
- UPDATE "flow_definitions" f
80
- SET "workspace_id" = w."id"
81
- FROM "workspaces" w
82
- WHERE f."workspace_id" IS NULL
83
- AND w."id" = CASE WHEN f."site_id" = 'default'
84
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
85
- ELSE f."site_id" END;
86
-
87
- UPDATE "flow_sessions" f
88
- SET "workspace_id" = w."id"
89
- FROM "workspaces" w
90
- WHERE f."workspace_id" IS NULL
91
- AND w."id" = CASE WHEN f."site_id" = 'default'
92
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
93
- ELSE f."site_id" END;
94
-
95
- -- ── POST-ASSERTIONS ─────────────────────────────────────────────────────────
96
- -- AC-1's own enforcement: nothing lost, nothing guessed, nothing missed that
97
- -- was resolvable. The fourth check is a NOTICE, not an exception — an
98
- -- unpinned/unseeded deployment leaving rows NULL is the expected, documented
99
- -- case (spec § 1.3), never an error this migration should refuse to run over.
100
- DO $$
101
- DECLARE
102
- before_d bigint;
103
- before_s bigint;
104
- after_d bigint;
105
- after_s bigint;
106
- guessed bigint;
107
- missed bigint;
108
- null_d bigint;
109
- null_s bigint;
110
- BEGIN
111
- SELECT d, s INTO before_d, before_s FROM "_6155_flow_census";
112
- SELECT count(*) INTO after_d FROM "flow_definitions";
113
- SELECT count(*) INTO after_s FROM "flow_sessions";
114
-
115
- -- 1. Nothing lost: row counts unchanged on both tables.
116
- IF before_d <> after_d OR before_s <> after_s THEN
117
- RAISE EXCEPTION
118
- '#6155 post-assertion: row count changed across a purely additive migration (flow_definitions % -> %, flow_sessions % -> %)',
119
- before_d, after_d, before_s, after_s;
120
- END IF;
121
-
122
- -- 2. Nothing guessed: no row's workspace_id was set to a value absent
123
- -- from "workspaces" (the inner-join backfill above cannot produce this
124
- -- on its own, but a re-run against a database missing the sentinel row
125
- -- must never leave a dangling reference).
126
- SELECT count(*) INTO guessed FROM "flow_definitions" f
127
- WHERE f."workspace_id" IS NOT NULL
128
- AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = f."workspace_id");
129
- IF guessed <> 0 THEN
130
- RAISE EXCEPTION
131
- '#6155 post-assertion: % flow_definitions row(s) carry a workspace_id absent from workspaces',
132
- guessed;
133
- END IF;
134
- SELECT count(*) INTO guessed FROM "flow_sessions" f
135
- WHERE f."workspace_id" IS NOT NULL
136
- AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = f."workspace_id");
137
- IF guessed <> 0 THEN
138
- RAISE EXCEPTION
139
- '#6155 post-assertion: % flow_sessions row(s) carry a workspace_id absent from workspaces',
140
- guessed;
141
- END IF;
142
-
143
- -- 3. Nothing missed: no row whose site_id names a workspace that actually
144
- -- exists came out of the backfill with workspace_id still NULL.
145
- SELECT count(*) INTO missed FROM "flow_definitions" f
146
- WHERE f."workspace_id" IS NULL
147
- AND EXISTS (
148
- SELECT 1 FROM "workspaces" w
149
- WHERE w."id" = CASE WHEN f."site_id" = 'default'
150
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
151
- ELSE f."site_id" END
152
- );
153
- IF missed <> 0 THEN
154
- RAISE EXCEPTION
155
- '#6155 post-assertion: % flow_definitions row(s) had a resolvable site_id but came out of the backfill NULL',
156
- missed;
157
- END IF;
158
- SELECT count(*) INTO missed FROM "flow_sessions" f
159
- WHERE f."workspace_id" IS NULL
160
- AND EXISTS (
161
- SELECT 1 FROM "workspaces" w
162
- WHERE w."id" = CASE WHEN f."site_id" = 'default'
163
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
164
- ELSE f."site_id" END
165
- );
166
- IF missed <> 0 THEN
167
- RAISE EXCEPTION
168
- '#6155 post-assertion: % flow_sessions row(s) had a resolvable site_id but came out of the backfill NULL',
169
- missed;
170
- END IF;
171
-
172
- -- 4. A NOTICE, never an exception: an unpinned/unseeded deployment leaving
173
- -- rows NULL is the expected, documented case (spec § 1.3).
174
- SELECT count(*) INTO null_d FROM "flow_definitions" WHERE "workspace_id" IS NULL;
175
- SELECT count(*) INTO null_s FROM "flow_sessions" WHERE "workspace_id" IS NULL;
176
- IF null_d <> 0 OR null_s <> 0 THEN
177
- RAISE NOTICE
178
- '#6155: % flow_definitions row(s) and % flow_sessions row(s) left workspace_id unset — their site_id did not resolve to a workspace this database holds',
179
- null_d, null_s;
180
- END IF;
181
- END $$;
@@ -1,172 +0,0 @@
1
- -- #6156 — S5b: a magnet is a flow row. FlowDefinition gains four
2
- -- operator-facing columns — kind, title, landingPage, status — spec D1.
3
- --
4
- -- STRICTLY ADDITIVE. Nothing dropped, renamed or relaxed; @@unique
5
- -- ([siteId, slug]), the `flow_`+UUIDv7 id default, and `listing_id` are all
6
- -- untouched. The visitor-facing `config` column is byte-identical across
7
- -- this migration (FR4, asserted below) — these four columns are how the
8
- -- operator MANAGES a magnet, never what a visitor's session SEES
9
- -- (AD-3, spec D1/§1.2).
10
- --
11
- -- `status` is backfilled 'live' (every row that exists today is already
12
- -- serving — D1's availability half) and carries NO column default (D1's
13
- -- publication-safety half, SEC-08): a DEFAULT 'live' would make
14
- -- PUBLICATION the thing that happens when a future writer says nothing,
15
- -- which is the exact asymmetry the brief's "a magnet created from the
16
- -- Admin opens as a draft" commitment forbids. So `status` is added
17
- -- nullable, backfilled, then SET NOT NULL with no default — every future
18
- -- writer must name a status or fail to compile.
19
- --
20
- -- `title` is NOT NULL with no column default, by the same three-step, for
21
- -- the positive reason: a nameless magnet must never enter the table
22
- -- silently. The slug backfills every existing row's title — an honest
23
- -- placeholder the operator renames.
24
- --
25
- -- `kind` KEEPS its column default ('contact') — unlike `status`, a
26
- -- mis-classified magnet is a mislabel in a list, not a publication or
27
- -- security control (D1). The backfill below reclassifies the rows D3's
28
- -- structural rule actually matches; every other row stays the default.
29
- --
30
- -- `landing_page` is nullable with NO backfill (D1): a magnet has THREE
31
- -- public addresses (the indexed landing route, the walk route one segment
32
- -- deeper, and the results route), so seeding a derived guess would freeze
33
- -- a route as if it were an operator decision. NULL means "no
34
- -- operator-declared landing page; the canonical route serves it" — true of
35
- -- every existing row.
36
- --
37
- -- `flow_definitions_status_domain` (D2) is a closed two-value CHECK,
38
- -- deliberately narrower than the precedent this namespace otherwise follows
39
- -- (WaitlistExperiment.status, FlowCheckout.status, ProjectListing.status —
40
- -- all plain String with no CHECK): `status` is what makes D10's
41
- -- deny-list gate in `resolveSession` SAFE rather than merely convenient —
42
- -- an unstorable unknown value means "not 'draft'" and "'live'" are the same
43
- -- set at the database level. No CHECK on `kind`: its vocabulary is six
44
- -- values and growing (Epic #6025), so a CHECK there would make every new
45
- -- kind a migration that RELAXES a constraint.
46
- --
47
- -- Statement order (D9) is fixed and load-bearing, not incidental: the
48
- -- `ADD COLUMN` runs FIRST, acquiring ACCESS EXCLUSIVE on `flow_definitions`
49
- -- before the census is taken, so no concurrent committed write (a listing
50
- -- approval via `upsertFlowDefinition`, a waitlist-register materialize) can
51
- -- land between the census and the assertions and falsely trip them. The
52
- -- whole sequence blocks every read AND write to `flow_definitions` for its
53
- -- duration — acceptable only because the table is tiny today (§1.4's
54
- -- census: four seeded presets plus two generated families); a later unit
55
- -- that grows this table materially owes a re-read of this decision rather
56
- -- than an inherited assumption.
57
- --
58
- -- Not re-runnable by `IF NOT EXISTS` guards (unlike #6155's
59
- -- 20260919120000_flow_workspace_key, whose NFR3 needed it for a reason
60
- -- specific to that migration): `prisma migrate`'s per-migration transaction
61
- -- + the `_prisma_migrations` ledger already make re-runnability a
62
- -- non-requirement here.
63
- --
64
- -- Rollback: ALTER TABLE "flow_definitions" DROP COLUMN "kind", DROP COLUMN
65
- -- "title", DROP COLUMN "landing_page", DROP COLUMN "status". Safe
66
- -- unconditionally — dropping "status" drops "flow_definitions_status_domain"
67
- -- with it, the four columns carry no other constraint, no index and no
68
- -- relation, and nothing outside this diff references them.
69
-
70
- -- AlterTable — all four, one statement, taking ACCESS EXCLUSIVE before
71
- -- anything else in this transaction reads the table (D9).
72
- ALTER TABLE "flow_definitions"
73
- ADD COLUMN "kind" TEXT NOT NULL DEFAULT 'contact',
74
- ADD COLUMN "title" TEXT,
75
- ADD COLUMN "landing_page" TEXT,
76
- ADD COLUMN "status" TEXT;
77
-
78
- -- The census the post-assertions compare against — taken SECOND, under the
79
- -- lock the ADD COLUMN above already holds, so it is a stable baseline for
80
- -- every assertion below (D9 / SEC-07). ON COMMIT DROP: `prisma migrate`
81
- -- runs each migration in one transaction, so this disappears with it.
82
- CREATE TEMP TABLE "_6156_flow_def_census" ON COMMIT DROP AS
83
- SELECT count(*) AS n,
84
- md5(string_agg("config"::text, '' ORDER BY "id")) AS config_hash
85
- FROM "flow_definitions";
86
-
87
- -- ── Backfill (D3 / D1) ───────────────────────────────────────────────────
88
- -- 1. `kind` — D3's structural rule, first match wins. Every row not
89
- -- matched by a branch below keeps the column default written above.
90
- UPDATE "flow_definitions"
91
- SET "kind" = CASE
92
- WHEN "listing_id" IS NOT NULL THEN 'diagnostic'
93
- WHEN "config"->>'mode' = 'recommending' THEN 'diagnostic'
94
- WHEN "config" ? 'deliverables' THEN 'special-report'
95
- ELSE 'contact'
96
- END;
97
-
98
- -- 2. `title` = the slug (an honest placeholder); `status` = 'live' (every
99
- -- row that exists today is already serving — D1's availability half).
100
- UPDATE "flow_definitions"
101
- SET "title" = "slug",
102
- "status" = 'live';
103
-
104
- -- SET NOT NULL + the status domain CHECK — AFTER the backfill, so neither
105
- -- constraint is ever evaluated against a NULL this migration itself wrote.
106
- ALTER TABLE "flow_definitions" ALTER COLUMN "title" SET NOT NULL;
107
- ALTER TABLE "flow_definitions" ALTER COLUMN "status" SET NOT NULL;
108
- ALTER TABLE "flow_definitions"
109
- ADD CONSTRAINT "flow_definitions_status_domain"
110
- CHECK ("status" IN ('draft', 'live'));
111
-
112
- -- ── POST-ASSERTIONS (FR2/FR3/FR4 — AC-1) ────────────────────────────────
113
- -- Asserted against the rows the target database actually holds, not a
114
- -- fixture's. A raised exception here rolls the whole migration back
115
- -- (`prisma migrate`'s per-migration transaction).
116
- DO $$
117
- DECLARE
118
- before_n bigint;
119
- before_hash text;
120
- after_n bigint;
121
- after_hash text;
122
- null_cols bigint;
123
- wrong_kind bigint;
124
- BEGIN
125
- SELECT n, config_hash INTO before_n, before_hash FROM "_6156_flow_def_census";
126
- SELECT count(*), md5(string_agg("config"::text, '' ORDER BY "id"))
127
- INTO after_n, after_hash
128
- FROM "flow_definitions";
129
-
130
- -- 1. Nothing lost or gained: row count unchanged across a purely
131
- -- additive-plus-backfill migration.
132
- IF before_n <> after_n THEN
133
- RAISE EXCEPTION
134
- '#6156 post-assertion: flow_definitions row count changed (% -> %)',
135
- before_n, after_n;
136
- END IF;
137
-
138
- -- 2. `config` is byte-identical (FR4) — this migration writes NO column
139
- -- other than kind/title/landing_page/status.
140
- IF before_hash IS DISTINCT FROM after_hash THEN
141
- RAISE EXCEPTION
142
- '#6156 post-assertion: flow_definitions.config changed across a migration that must not touch it';
143
- END IF;
144
-
145
- -- 3. Every pre-existing row leaves with a non-NULL status/title/kind
146
- -- (landing_page is NOT asserted — D1: it is deliberately unbackfilled).
147
- SELECT count(*) INTO null_cols FROM "flow_definitions"
148
- WHERE "status" IS NULL OR "title" IS NULL OR "kind" IS NULL;
149
- IF null_cols <> 0 THEN
150
- RAISE EXCEPTION
151
- '#6156 post-assertion: % flow_definitions row(s) came out of the backfill with a NULL status/title/kind',
152
- null_cols;
153
- END IF;
154
-
155
- -- 4. Backfill rule correctness (D3) — every listing-projected row, and
156
- -- every recommending-mode row, classified 'diagnostic'.
157
- SELECT count(*) INTO wrong_kind FROM "flow_definitions"
158
- WHERE "listing_id" IS NOT NULL AND "kind" <> 'diagnostic';
159
- IF wrong_kind <> 0 THEN
160
- RAISE EXCEPTION
161
- '#6156 post-assertion: % flow_definitions row(s) with listing_id set did not classify as diagnostic',
162
- wrong_kind;
163
- END IF;
164
-
165
- SELECT count(*) INTO wrong_kind FROM "flow_definitions"
166
- WHERE "config"->>'mode' = 'recommending' AND "kind" <> 'diagnostic';
167
- IF wrong_kind <> 0 THEN
168
- RAISE EXCEPTION
169
- '#6156 post-assertion: % flow_definitions row(s) with config.mode = recommending did not classify as diagnostic',
170
- wrong_kind;
171
- END IF;
172
- END $$;
@@ -1,41 +0,0 @@
1
- -- #6183 (S9b) — the WorkspaceMeetings model: the operator's enabled-slug
2
- -- gate for meeting types (spec docs/specs/6183-s9b-enabled-meeting-slugs-live-spec.md
3
- -- D1, D11). A fourth workspace site-config blob, structurally identical to
4
- -- its three siblings (workspace_themes / workspace_site_identities /
5
- -- workspace_seos) — same `@unique(workspace_id)` key, same Json-column
6
- -- convention, validation happens at the API layer.
7
- --
8
- -- One new table, one unique index, in the house style of
9
- -- 20260918120000_add_appointment (CreateTable + CreateIndex immediately
10
- -- followed by the ADR-0031 rule-3 id-format completion, in ONE migration
11
- -- since the table is new and needs no retrofit). No `workspace_id_format`
12
- -- CHECK, matching that same precedent — the baseline migration's 12
13
- -- relation-free scope checks are pinned to it alone
14
- -- (packages/ids/src/__tests__/migration-sync.test.ts), and no per-entity
15
- -- migration since has added one.
16
- --
17
- -- No backfill, no constraint relaxed, no existing table touched.
18
- --
19
- -- Rollback: DROP TABLE "workspace_meetings". Safe unconditionally while
20
- -- nothing but #6183's own code reads it.
21
-
22
- -- CreateTable
23
- CREATE TABLE "workspace_meetings" (
24
- "id" TEXT NOT NULL DEFAULT ('meet_'::text || (uuidv7())::text),
25
- "workspace_id" TEXT NOT NULL,
26
- "data" JSONB NOT NULL,
27
- "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
28
- "updated_at" TIMESTAMPTZ NOT NULL,
29
-
30
- CONSTRAINT "workspace_meetings_pkey" PRIMARY KEY ("id")
31
- );
32
-
33
- -- CreateIndex
34
- CREATE UNIQUE INDEX "workspace_meetings_workspace_id_key" ON "workspace_meetings"("workspace_id");
35
-
36
- -- ADR-0031 rule 3 — workspace_meetings: meet_ (registered in
37
- -- packages/ids/src/registry.ts ENTITY_PREFIX.workspaceMeetings; this
38
- -- migration's path is added to migration-sync.test.ts's PK_CHECK_MIGRATIONS).
39
- ALTER TABLE "workspace_meetings" ALTER COLUMN "id" TYPE text COLLATE "C";
40
- ALTER TABLE "workspace_meetings" ALTER COLUMN "id" SET DEFAULT ('meet_'::text || (uuidv7())::text);
41
- ALTER TABLE "workspace_meetings" ADD CONSTRAINT "workspace_meetings_id_format" CHECK ("id" ~ '^meet_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
@@ -1,185 +0,0 @@
1
- -- #6203 — ProjectListing and Lead gain a workspace isolation key.
2
- --
3
- -- The ownership brief (docs/internal/decisions/workspace-owns-data-surfaces-render.md)
4
- -- and AD-4 (docs/internal/decisions/flow-engine-admin-surface.md): a workspace
5
- -- owns data, and the Inbox's tenant-scope fence grows to cover the two
6
- -- models it reads. `siteId` stays a legacy alias for the anonymous write
7
- -- paths (unchanged); `workspaceId` is the isolation key every Admin read
8
- -- will go through (apps/api/src/lib/tenant-db.ts's new
9
- -- `projectListings`/`leads` accessors). Mirrors
10
- -- database/migrations/20260919120000_flow_workspace_key step for step
11
- -- (#6155's precedent for FlowDefinition/FlowSession).
12
- --
13
- -- NULLABLE, no default (spec D2) — a row whose `siteId` matches no workspace
14
- -- is left UNSET rather than guessed, and stays invisible to every accessor
15
- -- rather than visible to the wrong tenant (the same "fails empty, never
16
- -- wide" posture apps/api/src/services/leads-inbox.ts already takes).
17
- --
18
- -- RE-RUNNABLE (NFR3): `ADD COLUMN IF NOT EXISTS` / `CREATE INDEX IF NOT
19
- -- EXISTS` cover the column and index. Postgres has no
20
- -- `ADD CONSTRAINT IF NOT EXISTS`, so each format CHECK is wrapped in its own
21
- -- existence-guarded `DO $$` block, exactly as the #6155 precedent does.
22
- --
23
- -- Rollback: DROP INDEX "project_listings_workspace_id_idx",
24
- -- "leads_workspace_id_idx"; ALTER TABLE "project_listings" DROP
25
- -- CONSTRAINT "project_listings_workspace_id_format", DROP COLUMN
26
- -- "workspace_id"; ALTER TABLE "leads" DROP CONSTRAINT
27
- -- "leads_workspace_id_format", DROP COLUMN "workspace_id". Safe
28
- -- unconditionally at this pin — nothing reads the column until #6204+.
29
-
30
- -- The census the post-assertions compare against. ON COMMIT DROP: `prisma
31
- -- migrate` runs each migration in one transaction, so this disappears with it.
32
- CREATE TEMP TABLE "_6203_inbox_census" ON COMMIT DROP AS
33
- SELECT (SELECT count(*) FROM "project_listings") AS p,
34
- (SELECT count(*) FROM "leads") AS l;
35
-
36
- -- AlterTable
37
- ALTER TABLE "project_listings" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
38
- ALTER TABLE "leads" ADD COLUMN IF NOT EXISTS "workspace_id" TEXT;
39
-
40
- -- Format CHECKs — NULL-permitting, in the exact shape of the CHECKs
41
- -- 20260713192429_prefixed_uuid_entity_ids/migration.sql already applies to
42
- -- every other `workspace_id` column. Existence-guarded because Postgres has
43
- -- no `ADD CONSTRAINT IF NOT EXISTS`. This is what makes the Lead.siteId
44
- -- provenance gap (spec §1.3, §9 SEC-1) non-exploitable as an id-SHAPE
45
- -- attack: a site_id that is not a well-formed wksp_<uuidv7> cannot land in
46
- -- this column at all.
47
- DO $$ BEGIN
48
- IF NOT EXISTS (
49
- SELECT 1 FROM pg_constraint WHERE conname = 'project_listings_workspace_id_format'
50
- ) THEN
51
- ALTER TABLE "project_listings" ADD CONSTRAINT "project_listings_workspace_id_format"
52
- CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
53
- END IF;
54
- END $$;
55
-
56
- DO $$ BEGIN
57
- IF NOT EXISTS (
58
- SELECT 1 FROM pg_constraint WHERE conname = 'leads_workspace_id_format'
59
- ) THEN
60
- ALTER TABLE "leads" ADD CONSTRAINT "leads_workspace_id_format"
61
- CHECK ("workspace_id" IS NULL OR "workspace_id" ~ '^wksp_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
62
- END IF;
63
- END $$;
64
-
65
- -- CreateIndex
66
- CREATE INDEX IF NOT EXISTS "project_listings_workspace_id_idx" ON "project_listings"("workspace_id");
67
- CREATE INDEX IF NOT EXISTS "leads_workspace_id_idx" ON "leads"("workspace_id");
68
-
69
- -- ── Backfill (spec D2, §5.4) ────────────────────────────────────────────────
70
- -- Both spellings of the alias, one statement per table. The literal alias
71
- -- "default" resolves to the seeded sentinel workspace
72
- -- (database/tenancy.ts DEFAULT_WORKSPACE_ID); every other alias resolves
73
- -- only by exact workspaces.id equality. An inner join to "workspaces" means a
74
- -- row whose alias resolves to nothing is simply not updated and stays NULL
75
- -- — the correct, stated behavior (D2), not an error.
76
- --
77
- -- The sentinel id is a literal because SQL cannot import
78
- -- database/tenancy.ts. It is ADR 0031's reserved-range sentinel and changes
79
- -- only with that ADR — no test pins it against SQL (#6155's own migration
80
- -- carries the identical correction: the sibling tenancy.test.ts compares
81
- -- only the imported constant, not this SQL literal).
82
- UPDATE "project_listings" p
83
- SET "workspace_id" = w."id"
84
- FROM "workspaces" w
85
- WHERE p."workspace_id" IS NULL
86
- AND w."id" = CASE WHEN p."site_id" = 'default'
87
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
88
- ELSE p."site_id" END;
89
-
90
- UPDATE "leads" l
91
- SET "workspace_id" = w."id"
92
- FROM "workspaces" w
93
- WHERE l."workspace_id" IS NULL
94
- AND w."id" = CASE WHEN l."site_id" = 'default'
95
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
96
- ELSE l."site_id" END;
97
-
98
- -- ── POST-ASSERTIONS ─────────────────────────────────────────────────────────
99
- -- AC-1's own enforcement: nothing lost, nothing guessed, nothing missed that
100
- -- was resolvable. The fourth check is a NOTICE, not an exception — an
101
- -- unpinned/unseeded deployment leaving rows NULL is the expected, documented
102
- -- case (spec §1.9, NFR4), never an error this migration should refuse to
103
- -- run over.
104
- DO $$
105
- DECLARE
106
- before_p bigint;
107
- before_l bigint;
108
- after_p bigint;
109
- after_l bigint;
110
- guessed bigint;
111
- missed bigint;
112
- null_p bigint;
113
- null_l bigint;
114
- BEGIN
115
- SELECT p, l INTO before_p, before_l FROM "_6203_inbox_census";
116
- SELECT count(*) INTO after_p FROM "project_listings";
117
- SELECT count(*) INTO after_l FROM "leads";
118
-
119
- -- 1. Nothing lost: row counts unchanged on both tables.
120
- IF before_p <> after_p OR before_l <> after_l THEN
121
- RAISE EXCEPTION
122
- '#6203 post-assertion: row count changed across a purely additive migration (project_listings % -> %, leads % -> %)',
123
- before_p, after_p, before_l, after_l;
124
- END IF;
125
-
126
- -- 2. Nothing guessed: no row's workspace_id was set to a value absent
127
- -- from "workspaces" (the inner-join backfill above cannot produce this
128
- -- on its own, but a re-run against a database missing the sentinel row
129
- -- must never leave a dangling reference).
130
- SELECT count(*) INTO guessed FROM "project_listings" p
131
- WHERE p."workspace_id" IS NOT NULL
132
- AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = p."workspace_id");
133
- IF guessed <> 0 THEN
134
- RAISE EXCEPTION
135
- '#6203 post-assertion: % project_listings row(s) carry a workspace_id absent from workspaces',
136
- guessed;
137
- END IF;
138
- SELECT count(*) INTO guessed FROM "leads" l
139
- WHERE l."workspace_id" IS NOT NULL
140
- AND NOT EXISTS (SELECT 1 FROM "workspaces" w WHERE w."id" = l."workspace_id");
141
- IF guessed <> 0 THEN
142
- RAISE EXCEPTION
143
- '#6203 post-assertion: % leads row(s) carry a workspace_id absent from workspaces',
144
- guessed;
145
- END IF;
146
-
147
- -- 3. Nothing missed: no row whose site_id names a workspace that actually
148
- -- exists came out of the backfill with workspace_id still NULL.
149
- SELECT count(*) INTO missed FROM "project_listings" p
150
- WHERE p."workspace_id" IS NULL
151
- AND EXISTS (
152
- SELECT 1 FROM "workspaces" w
153
- WHERE w."id" = CASE WHEN p."site_id" = 'default'
154
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
155
- ELSE p."site_id" END
156
- );
157
- IF missed <> 0 THEN
158
- RAISE EXCEPTION
159
- '#6203 post-assertion: % project_listings row(s) had a resolvable site_id but came out of the backfill NULL',
160
- missed;
161
- END IF;
162
- SELECT count(*) INTO missed FROM "leads" l
163
- WHERE l."workspace_id" IS NULL
164
- AND EXISTS (
165
- SELECT 1 FROM "workspaces" w
166
- WHERE w."id" = CASE WHEN l."site_id" = 'default'
167
- THEN 'wksp_00000000-0000-7000-8000-000000000001'
168
- ELSE l."site_id" END
169
- );
170
- IF missed <> 0 THEN
171
- RAISE EXCEPTION
172
- '#6203 post-assertion: % leads row(s) had a resolvable site_id but came out of the backfill NULL',
173
- missed;
174
- END IF;
175
-
176
- -- 4. A NOTICE, never an exception: an unpinned/unseeded deployment leaving
177
- -- rows NULL is the expected, documented case (spec §1.9).
178
- SELECT count(*) INTO null_p FROM "project_listings" WHERE "workspace_id" IS NULL;
179
- SELECT count(*) INTO null_l FROM "leads" WHERE "workspace_id" IS NULL;
180
- IF null_p <> 0 OR null_l <> 0 THEN
181
- RAISE NOTICE
182
- '#6203: % project_listings row(s) and % leads row(s) left workspace_id unset — their site_id did not resolve to a workspace this database holds',
183
- null_p, null_l;
184
- END IF;
185
- END $$;
@@ -1,70 +0,0 @@
1
- -- #6204 — Lead gains a nullable triage stamp.
2
- --
3
- -- Operator decision Q1(a) (#6026, 2026-09-18): a message is a lead that needs
4
- -- an answer — same capture path, same routing, same tenancy, different
5
- -- follow-up. One nullable column beats a second entity, and
6
- -- docs/internal/decisions/flow-engine-admin-surface.md AD-4 stays true: the
7
- -- Inbox is a READ PROJECTION over tables that already hold decidable state,
8
- -- never a new decision table.
9
- --
10
- -- PURELY ADDITIVE (spec NFR1): two ADD COLUMNs, one CREATE INDEX. No UPDATE, no
11
- -- backfill, no DEFAULT, no NOT NULL — every existing row keeps NULL, which is
12
- -- exactly "unhandled" (spec D5, AC-1). There is no data transformation here, so
13
- -- this migration carries none of the census/post-assertion apparatus
14
- -- 20260919140000_inbox_workspace_key needed to prove ITS backfill correct.
15
- --
16
- -- RE-RUNNABLE (spec NFR2): IF NOT EXISTS on every statement, the house style of
17
- -- 20260919140000_inbox_workspace_key/migration.sql.
18
- --
19
- -- ORDER-SAFE (spec NFR2, security finding S-9): the index references
20
- -- "workspace_id", which 20260919140000_inbox_workspace_key adds — a migration
21
- -- this one sorts strictly after (20260920 > 20260919), so in the normal case
22
- -- the guard below is a formality. It stays a guard rather than an assumption:
23
- -- `IF NOT EXISTS` guards the INDEX's own name, not the column it references,
24
- -- so an unguarded CREATE INDEX would abort inside prisma migrate's
25
- -- per-migration transaction and wedge the ledger for the whole deployment if
26
- -- that migration were ever skipped, renamed, or rolled back on a given
27
- -- database. The two ADD COLUMNs above it are unconditional because they
28
- -- reference nothing another migration owns.
29
- --
30
- -- CREATE INDEX is NOT CONCURRENT (security finding S-11), and cannot be:
31
- -- CONCURRENTLY may not run inside prisma migrate's per-migration transaction.
32
- -- It therefore takes a lock on "leads" for its duration, which blocks the three
33
- -- anonymous capture routes (a write failure there surfaces as 503
34
- -- persist_failed, routes/leads/index.ts), and every subsequent insert
35
- -- pays a fourth index write. Accepted: "leads" carries four indexes already
36
- -- (siteId, createdAt, email, workspaceId) and is small at this pin. A
37
- -- deployment with a large leads table should create this index out-of-band
38
- -- with CONCURRENTLY first — the IF NOT EXISTS guard then makes this
39
- -- statement a no-op.
40
- --
41
- -- Rollback: DROP INDEX "leads_workspace_id_handled_at_idx";
42
- -- ALTER TABLE "leads" DROP COLUMN "handled_at", DROP COLUMN "handled_by".
43
- -- Safe ONLY at this pin, and the qualifier is load-bearing: once the Needs-you
44
- -- section ships, dropping these columns destroys operator triage state. After
45
- -- that point the rollback is a forward migration, not a DROP.
46
-
47
- -- AlterTable
48
- ALTER TABLE "leads" ADD COLUMN IF NOT EXISTS "handled_at" TIMESTAMP(3);
49
- -- The actor (spec D4, security finding S-7) — the sibling of
50
- -- project_listings."decided_by". Plain TEXT, no FK:
51
- -- ADR 0009 D2 forbids a relation crossing from inbox into auth.
52
- ALTER TABLE "leads" ADD COLUMN IF NOT EXISTS "handled_by" TEXT;
53
-
54
- -- CreateIndex — the read is
55
- -- workspace_id = $1 AND handled_at IS NULL AND source = ANY($2)
56
- -- ordered by created_at desc. A two-column (workspace_id, handled_at) index is
57
- -- the one that serves it: it narrows to the tenant and then to the unhandled
58
- -- partition, which is the small side and the side that shrinks as an operator
59
- -- works the queue. `source` is deliberately NOT a third column (spec D10).
60
- DO $$ BEGIN
61
- IF EXISTS (
62
- SELECT 1 FROM information_schema.columns
63
- WHERE table_name = 'leads' AND column_name = 'workspace_id'
64
- ) THEN
65
- CREATE INDEX IF NOT EXISTS "leads_workspace_id_handled_at_idx"
66
- ON "leads"("workspace_id", "handled_at");
67
- ELSE
68
- RAISE NOTICE '#6204: leads.workspace_id absent — index skipped. 20260919140000_inbox_workspace_key has not run against this database; the Needs-you read cannot be served until it does (spec § 10.1).';
69
- END IF;
70
- END $$;