create-ailk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (620) hide show
  1. package/README.md +124 -3
  2. package/component-catalog.md +456 -0
  3. package/dist/cli.js +92 -16
  4. package/dist/component-catalog.d.ts +11 -0
  5. package/dist/component-catalog.js +39 -0
  6. package/dist/copy.d.ts +7 -0
  7. package/dist/copy.js +24 -0
  8. package/dist/derive-architecture.d.ts +13 -0
  9. package/dist/derive-architecture.js +238 -0
  10. package/dist/index.d.ts +7 -2
  11. package/dist/index.js +19 -2
  12. package/dist/lib/extract-module-bundle.d.ts +16 -0
  13. package/dist/lib/extract-module-bundle.js +184 -0
  14. package/dist/lib/fetch-module.d.ts +55 -0
  15. package/dist/lib/fetch-module.js +125 -0
  16. package/dist/lib/module-license-gate.d.ts +48 -0
  17. package/dist/lib/module-license-gate.js +67 -0
  18. package/dist/lib/module-tier.d.ts +35 -0
  19. package/dist/lib/module-tier.js +37 -0
  20. package/dist/module-architecture.d.ts +175 -0
  21. package/dist/module-architecture.js +663 -0
  22. package/dist/parse-args.d.ts +47 -1
  23. package/dist/parse-args.js +143 -2
  24. package/dist/programmatic.d.ts +160 -3
  25. package/dist/programmatic.js +196 -7
  26. package/dist/surfaces.d.ts +211 -0
  27. package/dist/surfaces.js +532 -0
  28. package/dist/sync-routes.d.ts +92 -0
  29. package/dist/sync-routes.js +350 -0
  30. package/package.json +12 -5
  31. package/templates/.claude/rules/architecture.md +6 -2
  32. package/templates/.claude/skills/README.md +9 -9
  33. package/templates/.claude/skills/add-schema/SKILL.md +14 -7
  34. package/templates/.claude/skills/add-schema/references/schema-types.md +109 -6
  35. package/templates/.claude/skills/provision-config/SKILL.md +1 -1
  36. package/templates/.claude/skills/scaffold-commerce/SKILL.md +498 -1
  37. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +370 -0
  38. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +376 -0
  39. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +471 -0
  40. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +365 -0
  41. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +60 -0
  42. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +446 -0
  43. package/templates/.claude/skills/suggest-site-pages/references/categories.md +4 -4
  44. package/templates/.env.example +126 -2
  45. package/templates/CLAUDE.md +11 -5
  46. package/templates/README.md +1 -1
  47. package/templates/apps/api/.env.example +38 -0
  48. package/templates/apps/api/CLAUDE.md +78 -1
  49. package/templates/apps/api/LICENSE +201 -0
  50. package/templates/apps/api/api/index.ts +6 -141
  51. package/templates/apps/api/jest.config.cjs +16 -0
  52. package/templates/apps/api/package.json +22 -6
  53. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +141 -0
  54. package/templates/apps/api/src/__tests__/server.test.ts +46 -0
  55. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +211 -0
  56. package/templates/apps/api/src/bin/abandon-sweep.ts +42 -0
  57. package/templates/apps/api/src/bin/deliverable-resend.ts +144 -0
  58. package/templates/apps/api/src/bin/deliverable-upload.ts +114 -0
  59. package/templates/apps/api/src/bin/followup-sweep.ts +44 -0
  60. package/templates/apps/api/src/bin/listing-csv-sweep.ts +46 -0
  61. package/templates/apps/api/src/bin/seed-presets.ts +58 -0
  62. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +104 -0
  63. package/templates/apps/api/src/bin/session-retention-sweep.ts +46 -0
  64. package/templates/apps/api/src/cli.ts +5 -11
  65. package/templates/apps/api/src/config/index.ts +35 -0
  66. package/templates/apps/api/src/config/modules.ts +43 -0
  67. package/templates/apps/api/src/lib/__mocks__/prisma-client.js +11 -0
  68. package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -0
  69. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +2 -2
  70. package/templates/apps/api/src/lib/site.ts +16 -0
  71. package/templates/apps/api/src/lib/stripe.ts +22 -0
  72. package/templates/apps/api/src/lib/tenant-db.ts +225 -0
  73. package/templates/apps/api/src/lib/ws-token.ts +91 -0
  74. package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +7 -1
  75. package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +7 -1
  76. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +91 -4
  77. package/templates/apps/api/src/middleware/auth.ts +43 -8
  78. package/templates/apps/api/src/middleware/rate-limit.ts +78 -3
  79. package/templates/apps/api/src/middleware/tenant.ts +99 -0
  80. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +92 -0
  81. package/templates/apps/api/src/openapi/spec.ts +312 -3
  82. package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +175 -0
  83. package/templates/apps/api/src/routes/aeo/index.ts +107 -0
  84. package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +5 -2
  85. package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +22 -3
  86. package/templates/apps/api/src/routes/auth/tokens.ts +13 -1
  87. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +450 -0
  88. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +262 -0
  89. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +436 -0
  90. package/templates/apps/api/src/routes/billing/index.ts +36 -0
  91. package/templates/apps/api/src/routes/billing/portal.ts +182 -0
  92. package/templates/apps/api/src/routes/billing/read.ts +75 -0
  93. package/templates/apps/api/src/routes/billing/usage.ts +153 -0
  94. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +99 -0
  95. package/templates/apps/api/src/routes/checkout/sessions.ts +20 -8
  96. package/templates/apps/api/src/routes/content/create.ts +1 -0
  97. package/templates/apps/api/src/routes/content/delete.ts +1 -0
  98. package/templates/apps/api/src/routes/content/index.ts +42 -6
  99. package/templates/apps/api/src/routes/content/update.ts +1 -0
  100. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +393 -0
  101. package/templates/apps/api/src/routes/deliverables/index.ts +200 -0
  102. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +443 -0
  103. package/templates/apps/api/src/routes/flow-checkouts/index.ts +82 -0
  104. package/templates/apps/api/src/routes/flows/README.md +147 -0
  105. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +752 -0
  106. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +671 -0
  107. package/templates/apps/api/src/routes/flows/index.ts +202 -0
  108. package/templates/apps/api/src/routes/flows/recommender.ts +443 -0
  109. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +438 -31
  110. package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +10 -10
  111. package/templates/apps/api/src/routes/leads/index.ts +43 -9
  112. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +558 -0
  113. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +815 -0
  114. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +869 -0
  115. package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +221 -0
  116. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +468 -0
  117. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +424 -0
  118. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +707 -0
  119. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +375 -0
  120. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +467 -0
  121. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +566 -0
  122. package/templates/apps/api/src/routes/project-listings/get.ts +70 -0
  123. package/templates/apps/api/src/routes/project-listings/index.ts +120 -0
  124. package/templates/apps/api/src/routes/project-listings/list.ts +47 -0
  125. package/templates/apps/api/src/routes/project-listings/me.ts +76 -0
  126. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +222 -0
  127. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +97 -0
  128. package/templates/apps/api/src/routes/project-listings/patch.ts +136 -0
  129. package/templates/apps/api/src/routes/project-listings/public.ts +52 -0
  130. package/templates/apps/api/src/routes/project-listings/respond.ts +37 -0
  131. package/templates/apps/api/src/routes/project-listings/resume-token.ts +22 -0
  132. package/templates/apps/api/src/routes/project-listings/start.ts +106 -0
  133. package/templates/apps/api/src/routes/project-listings/submit.ts +122 -0
  134. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +490 -0
  135. package/templates/apps/api/src/routes/schedule/index.ts +249 -0
  136. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +385 -0
  137. package/templates/apps/api/src/routes/slack/actions.ts +177 -0
  138. package/templates/apps/api/src/routes/slack/index.ts +38 -0
  139. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +16 -6
  140. package/templates/apps/api/src/routes/test/auth/session.ts +8 -6
  141. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +577 -0
  142. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +78 -0
  143. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +41 -0
  144. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +64 -0
  145. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +42 -0
  146. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +84 -0
  147. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +119 -0
  148. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +238 -0
  149. package/templates/apps/api/src/routes/waitlist-signups/index.ts +101 -0
  150. package/templates/apps/api/src/routes/webhooks/README.md +35 -12
  151. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +315 -0
  152. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +270 -0
  153. package/templates/apps/api/src/routes/webhooks/stripe.ts +248 -20
  154. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +356 -0
  155. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +671 -0
  156. package/templates/apps/api/src/routes/workspaces/config.ts +125 -0
  157. package/templates/apps/api/src/routes/workspaces/index.ts +194 -0
  158. package/templates/apps/api/src/routes/workspaces/sessions.ts +218 -0
  159. package/templates/apps/api/src/server.ts +307 -7
  160. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +489 -0
  161. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +475 -0
  162. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +132 -0
  163. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
  164. package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
  165. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +523 -0
  166. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +359 -0
  167. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +142 -0
  168. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +425 -0
  169. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +2840 -0
  170. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +393 -0
  171. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +8 -8
  172. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +560 -0
  173. package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +684 -0
  174. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +406 -0
  175. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +290 -0
  176. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +736 -0
  177. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +475 -0
  178. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +340 -0
  179. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +983 -0
  180. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +278 -0
  181. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +71 -0
  182. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +379 -0
  183. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +941 -0
  184. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +588 -0
  185. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +768 -0
  186. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +1314 -0
  187. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +341 -0
  188. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +567 -0
  189. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +474 -0
  190. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +354 -0
  191. package/templates/apps/api/src/services/abandon-email.ts +307 -0
  192. package/templates/apps/api/src/services/aeo-score.ts +288 -0
  193. package/templates/apps/api/src/services/answer-distributions.ts +138 -0
  194. package/templates/apps/api/src/services/consent.ts +236 -0
  195. package/templates/apps/api/src/services/deliverable-fulfillment.ts +523 -0
  196. package/templates/apps/api/src/services/entitlements.ts +102 -0
  197. package/templates/apps/api/src/services/flow-aggregate.ts +232 -0
  198. package/templates/apps/api/src/services/flow-checkouts.ts +123 -0
  199. package/templates/apps/api/src/services/flow-engine.ts +1296 -0
  200. package/templates/apps/api/src/services/lead-promotion.ts +176 -0
  201. package/templates/apps/api/src/services/lead-routing.ts +3 -3
  202. package/templates/apps/api/src/services/listing-config.ts +102 -0
  203. package/templates/apps/api/src/services/listing-csv-sweep.ts +455 -0
  204. package/templates/apps/api/src/services/listing-promotion.ts +342 -0
  205. package/templates/apps/api/src/services/playbook-compile.ts +398 -0
  206. package/templates/apps/api/src/services/playbook-render.ts +263 -0
  207. package/templates/apps/api/src/services/project-listing-decision.ts +510 -0
  208. package/templates/apps/api/src/services/project-listing-flow.ts +426 -0
  209. package/templates/apps/api/src/services/project-listing-issue.ts +251 -0
  210. package/templates/apps/api/src/services/project-listings.ts +1472 -0
  211. package/templates/apps/api/src/services/recommender-capture.ts +835 -0
  212. package/templates/apps/api/src/services/scheduling/cal-provider.ts +392 -0
  213. package/templates/apps/api/src/services/scheduling/index.ts +63 -0
  214. package/templates/apps/api/src/services/scheduling/types.ts +88 -0
  215. package/templates/apps/api/src/services/slack-notify.ts +240 -0
  216. package/templates/apps/api/src/services/tenant-context.ts +451 -0
  217. package/templates/apps/api/src/services/usage-aggregation.ts +516 -0
  218. package/templates/apps/api/src/services/usage-credits.ts +340 -0
  219. package/templates/apps/api/src/services/usage-metering.ts +699 -0
  220. package/templates/apps/api/src/services/waitlist-dashboard.ts +947 -0
  221. package/templates/apps/api/src/services/waitlist-experiments.ts +213 -0
  222. package/templates/apps/api/src/services/waitlist-followup.ts +486 -0
  223. package/templates/apps/api/src/services/waitlist-scoring.ts +166 -0
  224. package/templates/apps/api/src/services/waitlist-signups.ts +165 -0
  225. package/templates/apps/api/src/vercel-handler.ts +165 -0
  226. package/templates/apps/api/tsconfig.json +0 -12
  227. package/templates/apps/mcp/CLAUDE.md +19 -9
  228. package/templates/apps/mcp/LICENSE +201 -0
  229. package/templates/apps/mcp/__tests__/create_page.test.ts +1 -0
  230. package/templates/apps/mcp/__tests__/delete_page.test.ts +1 -0
  231. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +105 -0
  232. package/templates/apps/mcp/__tests__/parity.test.ts +70 -6
  233. package/templates/apps/mcp/__tests__/request_callback.test.ts +4 -4
  234. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +242 -0
  235. package/templates/apps/mcp/__tests__/submit_lead.test.ts +4 -4
  236. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +4 -4
  237. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +47 -0
  238. package/templates/apps/mcp/__tests__/update_page.test.ts +1 -0
  239. package/templates/apps/mcp/package.json +6 -4
  240. package/templates/apps/mcp/src/config/modules.ts +37 -0
  241. package/templates/apps/mcp/src/server.ts +109 -18
  242. package/templates/apps/mcp/src/tools/capture_lead.ts +1 -1
  243. package/templates/apps/mcp/src/tools/create_booking.ts +58 -0
  244. package/templates/apps/mcp/src/tools/create_page.ts +1 -0
  245. package/templates/apps/mcp/src/tools/delete_page.ts +1 -0
  246. package/templates/apps/mcp/src/tools/get_event_meta.ts +55 -0
  247. package/templates/apps/mcp/src/tools/get_flow.ts +61 -0
  248. package/templates/apps/mcp/src/tools/index.ts +25 -0
  249. package/templates/apps/mcp/src/tools/list_availability.ts +67 -0
  250. package/templates/apps/mcp/src/tools/purchase.ts +3 -0
  251. package/templates/apps/mcp/src/tools/submit_flow_step.ts +83 -0
  252. package/templates/apps/mcp/src/tools/update_page.ts +1 -0
  253. package/templates/apps/mcp/tsconfig.json +1 -9
  254. package/templates/apps/web/.env.example +44 -0
  255. package/templates/apps/web/CLAUDE.md +28 -0
  256. package/templates/apps/web/LICENSE +201 -0
  257. package/templates/apps/web/__tests__/__mocks__/code-highlight.jest-stub.ts +24 -0
  258. package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +33 -0
  259. package/templates/apps/web/__tests__/__mocks__/next-intl.ts +18 -0
  260. package/templates/apps/web/__tests__/__mocks__/server-only.ts +13 -0
  261. package/templates/apps/web/__tests__/__mocks__/style-mock.js +4 -0
  262. package/templates/apps/web/__tests__/__mocks__/vercel-analytics-server.ts +9 -0
  263. package/templates/apps/web/__tests__/jest-support/import-meta-transformer.cjs +90 -0
  264. package/templates/apps/web/__tests__/setup.ts +22 -32
  265. package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +6 -3
  266. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +8 -2
  267. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +260 -0
  268. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +263 -0
  269. package/templates/apps/web/app/[locale]/(authed)/account/sign-out-button.tsx +39 -0
  270. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +54 -0
  271. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +96 -0
  272. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +309 -0
  273. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +149 -0
  274. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +273 -0
  275. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/gate.test.tsx +172 -0
  276. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +171 -0
  277. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +179 -0
  278. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +116 -0
  279. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ForbiddenState.tsx +24 -0
  280. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +66 -0
  281. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +61 -0
  282. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +114 -0
  283. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +139 -0
  284. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +85 -0
  285. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +67 -0
  286. package/templates/apps/web/app/[locale]/(authed)/waitlist/format.ts +111 -0
  287. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +179 -0
  288. package/templates/apps/web/app/[locale]/about/page.tsx +10 -1
  289. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +6 -1
  290. package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +9 -0
  291. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
  292. package/templates/apps/web/app/[locale]/careers/page.tsx +9 -1
  293. package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +9 -0
  294. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
  295. package/templates/apps/web/app/[locale]/case-studies/page.tsx +13 -1
  296. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +11 -1
  297. package/templates/apps/web/app/[locale]/clients/page.tsx +9 -1
  298. package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +21 -1
  299. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +188 -0
  300. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +242 -0
  301. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +29 -0
  302. package/templates/apps/web/app/[locale]/contact/__tests__/intake-analytics.test.ts +51 -0
  303. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +39 -15
  304. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +21 -3
  305. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +49 -0
  306. package/templates/apps/web/app/[locale]/contact/intake-script.ts +66 -0
  307. package/templates/apps/web/app/[locale]/contact/page.tsx +14 -4
  308. package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +13 -1
  309. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
  310. package/templates/apps/web/app/[locale]/courses/page.tsx +9 -1
  311. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +1 -0
  312. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +11 -1
  313. package/templates/apps/web/app/[locale]/downloads/page.tsx +13 -1
  314. package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +9 -0
  315. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +12 -2
  316. package/templates/apps/web/app/[locale]/events/page.tsx +9 -1
  317. package/templates/apps/web/app/[locale]/faq/page.tsx +10 -1
  318. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +265 -0
  319. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +445 -0
  320. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +341 -0
  321. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +287 -0
  322. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +302 -0
  323. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +128 -0
  324. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +254 -0
  325. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +144 -0
  326. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +108 -0
  327. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +532 -0
  328. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +121 -0
  329. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +457 -0
  330. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +396 -0
  331. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +114 -0
  332. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +52 -0
  333. package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +13 -1
  334. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  335. package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +9 -0
  336. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
  337. package/templates/apps/web/app/[locale]/integrations/page.tsx +13 -1
  338. package/templates/apps/web/app/[locale]/launch-service/page.tsx +14 -1
  339. package/templates/apps/web/app/[locale]/layout.tsx +127 -16
  340. package/templates/apps/web/app/[locale]/page.tsx +10 -1
  341. package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +9 -0
  342. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
  343. package/templates/apps/web/app/[locale]/partners/page.tsx +9 -1
  344. package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +9 -0
  345. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
  346. package/templates/apps/web/app/[locale]/products/page.tsx +9 -1
  347. package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +13 -1
  348. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +10 -0
  349. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +110 -0
  350. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +96 -0
  351. package/templates/apps/web/app/[locale]/projects/page.tsx +13 -1
  352. package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +9 -0
  353. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +12 -2
  354. package/templates/apps/web/app/[locale]/resources/page.tsx +13 -1
  355. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +117 -0
  356. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +96 -0
  357. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +92 -0
  358. package/templates/apps/web/app/[locale]/schedule/page.tsx +82 -0
  359. package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +9 -0
  360. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
  361. package/templates/apps/web/app/[locale]/services/page.tsx +9 -1
  362. package/templates/apps/web/app/[locale]/sign-in/__tests__/next-path.test.ts +31 -0
  363. package/templates/apps/web/app/[locale]/sign-in/__tests__/page.test.tsx +152 -0
  364. package/templates/apps/web/app/[locale]/sign-in/next-path.ts +0 -0
  365. package/templates/apps/web/app/[locale]/sign-in/page.tsx +83 -0
  366. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +123 -0
  367. package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +13 -1
  368. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  369. package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +13 -1
  370. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  371. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +11 -1
  372. package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
  373. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +93 -0
  374. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +48 -0
  375. package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +11 -1
  376. package/templates/apps/web/app/[locale]/use-cases/page.tsx +13 -1
  377. package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +13 -1
  378. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  379. package/templates/apps/web/app/globals.css +35 -0
  380. package/templates/apps/web/app/llms.txt/route.ts +2 -1
  381. package/templates/apps/web/app/og/route.tsx +10 -2
  382. package/templates/apps/web/auth.ts +8 -0
  383. package/templates/apps/web/components/sign-out-trigger.tsx +42 -0
  384. package/templates/apps/web/content/en/blog/README.txt +25 -0
  385. package/templates/apps/web/content/en/docs/index.mdx +28 -0
  386. package/templates/apps/web/jest.config.cjs +90 -27
  387. package/templates/apps/web/lib/__tests__/site-brand.test.ts +119 -0
  388. package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
  389. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +499 -0
  390. package/templates/apps/web/lib/account-config.ts +13 -0
  391. package/templates/apps/web/lib/server-api.ts +85 -0
  392. package/templates/apps/web/lib/site-brand.tsx +121 -0
  393. package/templates/apps/web/lib/site-theme.ts +74 -0
  394. package/templates/apps/web/messages/en.json +275 -7
  395. package/templates/apps/web/middleware.ts +11 -3
  396. package/templates/apps/web/next.config.mjs +8 -2
  397. package/templates/apps/web/package.json +17 -7
  398. package/templates/apps/web/postcss.config.mjs +23 -0
  399. package/templates/apps/web/public/.well-known/security.txt +3 -3
  400. package/templates/apps/web/src/lib/routes.ts +15 -2
  401. package/templates/apps/web/tsconfig.json +1 -16
  402. package/templates/content/_site.mdx +36 -25
  403. package/templates/content/en/AboutPage/about.mdx +24 -14
  404. package/templates/content/en/ContactPage/contact.mdx +23 -14
  405. package/templates/content/en/FAQPage/faq.mdx +10 -392
  406. package/templates/content/en/HomePage/home.mdx +14 -285
  407. package/templates/database/CHANGELOG.md +443 -0
  408. package/templates/database/LICENSE +201 -0
  409. package/templates/database/README.md +3 -1
  410. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +392 -0
  411. package/templates/database/__tests__/flows-presets.test.ts +173 -0
  412. package/templates/database/__tests__/prefixed-ids.db.test.ts +146 -0
  413. package/templates/database/__tests__/seed.test.ts +88 -0
  414. package/templates/database/__tests__/user-cascade.test.ts +19 -4
  415. package/templates/database/auth/schema.prisma +521 -0
  416. package/templates/database/config/schema.prisma +234 -0
  417. package/templates/database/content/schema.prisma +263 -0
  418. package/templates/database/flows/listing-diagnostic.ts +182 -0
  419. package/templates/database/flows/presets.ts +315 -0
  420. package/templates/database/inbox/__tests__/schema.test.ts +4 -0
  421. package/templates/database/inbox/schema.prisma +574 -3
  422. package/templates/database/jest.config.cjs +8 -0
  423. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +201 -0
  424. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +67 -0
  425. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +46 -0
  426. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +25 -0
  427. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +49 -0
  428. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +197 -0
  429. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +32 -0
  430. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +22 -0
  431. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +151 -0
  432. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +223 -0
  433. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +22 -0
  434. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +32 -0
  435. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +40 -0
  436. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +4 -0
  437. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +2 -0
  438. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +2 -0
  439. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +29 -0
  440. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +6 -0
  441. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +39 -0
  442. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +24 -0
  443. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +3 -0
  444. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +33 -0
  445. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +30 -0
  446. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +47 -0
  447. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +16 -0
  448. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +47 -0
  449. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +10 -0
  450. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +26 -0
  451. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +90 -0
  452. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +174 -0
  453. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +14 -0
  454. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +25 -0
  455. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
  456. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
  457. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
  458. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
  459. package/templates/database/ops/schema.prisma +27 -0
  460. package/templates/database/package.json +42 -4
  461. package/templates/database/prisma.config.ts +1 -1
  462. package/templates/database/schema.prisma +19 -435
  463. package/templates/database/seed-test.ts +14 -4
  464. package/templates/database/seed.ts +148 -0
  465. package/templates/database/tenancy.ts +45 -0
  466. package/templates/database/tsconfig.flows.json +15 -0
  467. package/templates/database/tsconfig.json +7 -1
  468. package/templates/database/tsconfig.tenancy.json +14 -0
  469. package/templates/package.json +30 -55
  470. package/templates/pnpm-workspace.yaml +0 -3
  471. package/templates/project.yaml +1 -1
  472. package/templates/tsconfig.json +1 -37
  473. package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +0 -94
  474. package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +0 -75
  475. package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +0 -82
  476. package/templates/apps/web/__tests__/auth/env-helpers.test.ts +0 -168
  477. package/templates/apps/web/__tests__/authority-list-routes.test.tsx +0 -208
  478. package/templates/apps/web/__tests__/better-stack.test.ts +0 -66
  479. package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +0 -87
  480. package/templates/apps/web/__tests__/blog-source-populated.test.ts +0 -130
  481. package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +0 -60
  482. package/templates/apps/web/__tests__/google-analytics.test.tsx +0 -104
  483. package/templates/apps/web/__tests__/i18n/middleware.test.ts +0 -119
  484. package/templates/apps/web/__tests__/i18n/routing.test.ts +0 -114
  485. package/templates/apps/web/__tests__/i18n/translations.test.ts +0 -191
  486. package/templates/apps/web/__tests__/json-ld.test.ts +0 -98
  487. package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +0 -580
  488. package/templates/apps/web/__tests__/layout-stealth.test.ts +0 -39
  489. package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +0 -103
  490. package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +0 -114
  491. package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +0 -94
  492. package/templates/apps/web/__tests__/page.test.tsx +0 -230
  493. package/templates/apps/web/__tests__/products-services-phase1.test.tsx +0 -321
  494. package/templates/apps/web/__tests__/projects-routes.test.tsx +0 -392
  495. package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +0 -475
  496. package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +0 -53
  497. package/templates/apps/web/__tests__/routes/blog-route.test.tsx +0 -191
  498. package/templates/apps/web/__tests__/routes/llms-txt.test.ts +0 -123
  499. package/templates/apps/web/__tests__/routes/robots-txt.test.ts +0 -52
  500. package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +0 -78
  501. package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +0 -203
  502. package/templates/apps/web/__tests__/section-routes.test.tsx +0 -693
  503. package/templates/apps/web/__tests__/security-headers.test.ts +0 -201
  504. package/templates/apps/web/__tests__/sentry-config.test.ts +0 -116
  505. package/templates/apps/web/__tests__/sitemap-locales.test.ts +0 -415
  506. package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +0 -844
  507. package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +0 -120
  508. package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +0 -202
  509. package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +0 -152
  510. package/templates/apps/web/content/en/blog/index.mdx +0 -15
  511. package/templates/apps/web/content/en/blog/launching-ailk.mdx +0 -20
  512. package/templates/apps/web/content/en/docs/getting-started.mdx +0 -30
  513. package/templates/content/.gitkeep +0 -0
  514. package/templates/content/CLAUDE.md +0 -85
  515. package/templates/content/ar/HomePage/home.mdx +0 -123
  516. package/templates/content/ar/_site.mdx +0 -35
  517. package/templates/content/de/_site.mdx +0 -29
  518. package/templates/content/en/.gitkeep +0 -0
  519. package/templates/content/en/AboutPage/about.schema.json +0 -82
  520. package/templates/content/en/BookCall/book-call.mdx +0 -19
  521. package/templates/content/en/BookCall/book-call.schema.json +0 -121
  522. package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +0 -20
  523. package/templates/content/en/CareersList/careers.mdx +0 -24
  524. package/templates/content/en/CaseStudiesList/case-studies.mdx +0 -22
  525. package/templates/content/en/CaseStudiesList/showcase.mdx +0 -8
  526. package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +0 -32
  527. package/templates/content/en/CaseStudy/schema-builder-migration.mdx +0 -41
  528. package/templates/content/en/ClientsList/clients.mdx +0 -38
  529. package/templates/content/en/ContactPage/contact.schema.json +0 -100
  530. package/templates/content/en/ContactPoint/primary.mdx +0 -14
  531. package/templates/content/en/Course/aeo-fundamentals.mdx +0 -37
  532. package/templates/content/en/Course/schema-builder-workshop.mdx +0 -62
  533. package/templates/content/en/CoursesList/courses.mdx +0 -22
  534. package/templates/content/en/DownloadsList/downloads.mdx +0 -22
  535. package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +0 -39
  536. package/templates/content/en/Event/ailk-launch-day-2026.mdx +0 -35
  537. package/templates/content/en/EventsList/events.mdx +0 -26
  538. package/templates/content/en/FAQPage/faq.schema.json +0 -231
  539. package/templates/content/en/GlossaryList/glossary.mdx +0 -19
  540. package/templates/content/en/GlossaryList/glossary.schema.json +0 -300
  541. package/templates/content/en/HomePage/home.schema.json +0 -88
  542. package/templates/content/en/HomePage/launch-service.mdx +0 -475
  543. package/templates/content/en/HomePage/launch-service.schema.json +0 -138
  544. package/templates/content/en/HowTo/add-mdx-content.mdx +0 -38
  545. package/templates/content/en/HowTo/configure-aeo-scoring.mdx +0 -35
  546. package/templates/content/en/HowTo/set-up-json-ld.mdx +0 -36
  547. package/templates/content/en/Integration/github-actions.mdx +0 -31
  548. package/templates/content/en/Integration/openai-api.mdx +0 -26
  549. package/templates/content/en/Integration/vercel.mdx +0 -32
  550. package/templates/content/en/IntegrationsList/integrations.mdx +0 -22
  551. package/templates/content/en/JobPosting/content-strategist.mdx +0 -46
  552. package/templates/content/en/JobPosting/developer-advocate.mdx +0 -47
  553. package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +0 -57
  554. package/templates/content/en/LandingPage/aeo-audit.mdx +0 -19
  555. package/templates/content/en/LandingPage/aeo-audit.schema.json +0 -92
  556. package/templates/content/en/LandingPage/agency-waitlist.mdx +0 -340
  557. package/templates/content/en/LandingPage/agency-waitlist.schema.json +0 -152
  558. package/templates/content/en/LandingPage/developers.mdx +0 -19
  559. package/templates/content/en/LandingPage/developers.schema.json +0 -109
  560. package/templates/content/en/LandingPage/for-agencies.mdx +0 -390
  561. package/templates/content/en/LandingPage/for-agencies.schema.json +0 -107
  562. package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +0 -417
  563. package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +0 -105
  564. package/templates/content/en/LandingPage/for-businesses.mdx +0 -19
  565. package/templates/content/en/LandingPage/for-businesses.schema.json +0 -96
  566. package/templates/content/en/LandingPage/platform.mdx +0 -369
  567. package/templates/content/en/LandingPage/platform.schema.json +0 -166
  568. package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +0 -19
  569. package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +0 -102
  570. package/templates/content/en/LandingPage/vs-wordpress.mdx +0 -19
  571. package/templates/content/en/LandingPage/vs-wordpress.schema.json +0 -53
  572. package/templates/content/en/NewsletterSignup/newsletter.mdx +0 -236
  573. package/templates/content/en/NewsletterSignup/newsletter.schema.json +0 -91
  574. package/templates/content/en/Organization/site-owner.mdx +0 -16
  575. package/templates/content/en/Partner/prisma.mdx +0 -31
  576. package/templates/content/en/Partner/supabase.mdx +0 -30
  577. package/templates/content/en/Partner/vercel.mdx +0 -28
  578. package/templates/content/en/PartnersList/partners.mdx +0 -22
  579. package/templates/content/en/Person/founder.mdx +0 -18
  580. package/templates/content/en/Pricing/pricing.mdx +0 -538
  581. package/templates/content/en/Pricing/pricing.schema.json +0 -209
  582. package/templates/content/en/PrivacyPage/privacy.mdx +0 -165
  583. package/templates/content/en/PrivacyPage/privacy.schema.json +0 -64
  584. package/templates/content/en/Product/ailk.mdx +0 -19
  585. package/templates/content/en/Product/ailk.schema.json +0 -94
  586. package/templates/content/en/Product/api-launch-kit.mdx +0 -26
  587. package/templates/content/en/Product/launch-kit.mdx +0 -28
  588. package/templates/content/en/ProductsList/products.mdx +0 -26
  589. package/templates/content/en/Project/ailk-phase-2.mdx +0 -54
  590. package/templates/content/en/ProjectsList/projects.mdx +0 -25
  591. package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +0 -27
  592. package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +0 -28
  593. package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +0 -39
  594. package/templates/content/en/ResourcesHub/resources.mdx +0 -22
  595. package/templates/content/en/Service/consulting.mdx +0 -22
  596. package/templates/content/en/Service/implementation.mdx +0 -30
  597. package/templates/content/en/Service/launch-service.mdx +0 -232
  598. package/templates/content/en/Service/launch.mdx +0 -228
  599. package/templates/content/en/ServicesList/services.mdx +0 -84
  600. package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +0 -44
  601. package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +0 -47
  602. package/templates/content/en/SoftwareProduct/ailk-schema.mdx +0 -49
  603. package/templates/content/en/TeamList/team.mdx +0 -63
  604. package/templates/content/en/TeamMember/founder.mdx +0 -72
  605. package/templates/content/en/TermsPage/terms.mdx +0 -19
  606. package/templates/content/en/TermsPage/terms.schema.json +0 -99
  607. package/templates/content/en/TestimonialsList/testimonials.mdx +0 -44
  608. package/templates/content/en/UseCasesList/use-cases.mdx +0 -30
  609. package/templates/content/en/Whitepaper/ax-first-product-development.mdx +0 -27
  610. package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +0 -27
  611. package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +0 -31
  612. package/templates/content/en-XA/AboutPage/about.mdx +0 -8
  613. package/templates/content/en-XA/FAQPage/faq.mdx +0 -8
  614. package/templates/content/en-XA/HomePage/home.mdx +0 -8
  615. package/templates/content/es/_site.mdx +0 -30
  616. package/templates/content/fr/_site.mdx +0 -30
  617. package/templates/content/ja/_site.mdx +0 -30
  618. package/templates/content/pt-BR/AboutPage/about.mdx +0 -8
  619. package/templates/content/pt-BR/FAQPage/faq.mdx +0 -8
  620. package/templates/content/pt-BR/HomePage/home.mdx +0 -23
@@ -0,0 +1,234 @@
1
+ // config namespace — site-wide settings and customer configuration (ADR-0009).
2
+ //
3
+ // SiteSetting is the site-wide key-value config store. Generator + datasource
4
+ // live in database/schema.prisma (the folder's single config file).
5
+ //
6
+ // The three site-config models (WorkspaceTheme / WorkspaceSiteIdentity /
7
+ // WorkspaceSEO) are workspace-scoped: keyed on a plain `workspaceId String` with
8
+ // NO cross-namespace @relation to auth/Workspace (a stable-ID per ADR-0009
9
+ // invariant #1). Cross-namespace integrity + cascade are the service layer's
10
+ // (ADR-0009 D3), delivered by the Phase 4 workspace-scoped write surface. Every
11
+ // model here is fenced by the architecture.yaml tenant-scope forbidden-pattern.
12
+
13
+ /// SiteSetting - Key-value settings for the customer site
14
+ /// Use for: feature flags, custom config, cached Platform data
15
+ model SiteSetting {
16
+ id String @id @default(dbgenerated("('siteset_'::text || (uuidv7())::text)")) @db.Text
17
+ key String @unique // "feature_flags", "cached_config", "custom_theme"
18
+ value Json
19
+
20
+ createdAt DateTime @default(now()) @map("created_at")
21
+ updatedAt DateTime @updatedAt @map("updated_at")
22
+
23
+ @@map("site_settings")
24
+ }
25
+
26
+ // ============================================================================
27
+ // Workspace site config — workspace-scoped design-system persistence (#3641).
28
+ //
29
+ // Three tables, one per @working-theory/site-config schema. Each row stores the
30
+ // Zod-validated JSON blob for a single workspace. Validation is enforced at the
31
+ // API layer; the DB stores the already-validated blob in a Json column.
32
+ //
33
+ // Design decisions (site-config-reanchor-spec §3):
34
+ // - workspaceId is a plain String stable-ID — NO @relation to auth/Workspace
35
+ // (ADR-0009 invariant #1: no cross-namespace @relation).
36
+ // - One table per schema (not a polymorphic config table) — different update
37
+ // cadences + consumer access patterns.
38
+ // - Json column for the blob — schema evolves without a DB migration on change.
39
+ // - @unique(workspaceId) — one active record per workspace per config type.
40
+ // ============================================================================
41
+
42
+ /// WorkspaceTheme — per-workspace ThemeSchema blob (#3641)
43
+ model WorkspaceTheme {
44
+ id String @id @default(dbgenerated("('theme_'::text || (uuidv7())::text)")) @db.Text
45
+ workspaceId String @unique @map("workspace_id")
46
+ data Json
47
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
48
+ updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
49
+
50
+ @@map("workspace_themes")
51
+ }
52
+
53
+ /// WorkspaceSiteIdentity — per-workspace SiteIdentitySchema blob (#3641)
54
+ model WorkspaceSiteIdentity {
55
+ id String @id @default(dbgenerated("('siteid_'::text || (uuidv7())::text)")) @db.Text
56
+ workspaceId String @unique @map("workspace_id")
57
+ data Json
58
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
59
+ updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
60
+
61
+ @@map("workspace_site_identities")
62
+ }
63
+
64
+ /// WorkspaceSEO — per-workspace SEOSchema blob (#3641)
65
+ model WorkspaceSEO {
66
+ id String @id @default(dbgenerated("('seo_'::text || (uuidv7())::text)")) @db.Text
67
+ workspaceId String @unique @map("workspace_id")
68
+ data Json
69
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
70
+ updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
71
+
72
+ @@map("workspace_seos")
73
+ }
74
+
75
+ /// SiteListingConfig — per-SITE project-listings config blob (#5033).
76
+ ///
77
+ /// The fourth site-config model, and the one keyed on `siteId` rather than
78
+ /// `workspaceId`. `ProjectListing` is scoped by `siteId`, and its create path
79
+ /// is anonymous (a public application form, no session) — at the moment a
80
+ /// listing is created there is no workspace to resolve, so a tenant-guarded
81
+ /// workspace config would be unreadable from the one place these values are
82
+ /// needed. Otherwise identical to its three siblings above: `@unique` key,
83
+ /// one Zod-validated JSON blob (`siteListingConfigSchema` in
84
+ /// @working-theory/validation), validation at the API layer.
85
+ ///
86
+ /// Carries the listing-window bounds and the featured switch — a site's own
87
+ /// commercial terms, which is why they are here and not a constant in the
88
+ /// published package. Never written from a request body; read server-side by
89
+ /// the anonymous create handler.
90
+ model SiteListingConfig {
91
+ id String @id @default(dbgenerated("('sltcfg_'::text || (uuidv7())::text)")) @db.Text
92
+ siteId String @unique @map("site_id")
93
+
94
+ /// The site's PUBLIC site key (#5048 D2) — the addressing token an anonymous
95
+ /// application carries in place of a siteId, so the server resolves the site
96
+ /// rather than trusting a request body. Public by design: it ships in browser
97
+ /// JavaScript and in an agent's request, and grants nothing on its own.
98
+ ///
99
+ /// Nullable and @unique: a site that has not minted one is unreachable by key
100
+ /// and falls back to the single-tenant env path (AILK_LISTING_PUBLIC_KEY, see
101
+ /// apps/api/src/services/listing-config.ts), which is how the default
102
+ /// create-ailk scaffold works with no config row at all.
103
+ publicKey String? @unique @map("public_key")
104
+
105
+ data Json
106
+ createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
107
+ updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
108
+
109
+ @@map("site_listing_configs")
110
+ }
111
+
112
+ // ============================================================================
113
+ // Usage-billing pricing/markup/model config (FEAT-047 P1.S2, #3710 — Epic
114
+ // #3704). Per ADR 0009 D2 (roadmap §4), pricing/markup/model config lives in
115
+ // `config`; balances/subscription/plan live in `auth` (see
116
+ // database/auth/schema.prisma). organizationId is a plain String stable-id —
117
+ // no @relation (ADR 0009 invariant 1). LlmModel.tier references ModelTier,
118
+ // defined in database/content/schema.prisma (FEAT-047 P1.S1, #3709, spec
119
+ // OQ-1) — an enum reference through the merged prismaSchemaFolder needs no
120
+ // @relation, so this is not a cross-namespace FK; ModelTier is NOT
121
+ // redefined here. Field roster ported from
122
+ // docs/research/wt-billing-capture/schema.md.
123
+ // ============================================================================
124
+
125
+ /// BillingMode — org- vs individual-billed. Ported from the capture (schema.md
126
+ /// § balance counters) for forward compatibility; no P1.S2 field consumes it
127
+ /// yet (Organization's billing-mode projection is out of this spec's field
128
+ /// roster — spec §2.1 item 1). Captured now so the enum name is stable when a
129
+ /// later phase wires it.
130
+ enum BillingMode {
131
+ INDIVIDUAL
132
+ ORGANIZATION
133
+ }
134
+
135
+ /// SubscriptionPaymentMode — how a Subscription is paid. Self-serve
136
+ /// subscriptions default to CARD; Subscription.paymentMode is nullable
137
+ /// (enterprise-only field, spec §5.3 — no business default invented).
138
+ enum SubscriptionPaymentMode {
139
+ CARD
140
+ INVOICE
141
+ }
142
+
143
+ /// BillingCycle — MONTHLY vs YEARLY. Ported from the capture for forward
144
+ /// compatibility; no P1.S2 field consumes it yet (Plan's Stripe price id is
145
+ /// singular in this spec's field roster — a monthly/yearly price-id split is
146
+ /// a future additive column, not invented here).
147
+ enum BillingCycle {
148
+ MONTHLY
149
+ YEARLY
150
+ }
151
+
152
+ /// ContractType — how a Subscription's deal was made. Subscription.contractType
153
+ /// is nullable (enterprise-only field — self-serve subscriptions leave it
154
+ /// unset).
155
+ enum ContractType {
156
+ SELF_SERVE
157
+ SALES_ASSISTED
158
+ ENTERPRISE
159
+ }
160
+
161
+ /// AiMarkupConfig — hierarchical per-org markup config: null provider+model =
162
+ /// org default; null model = provider-wide; both set = model-specific
163
+ /// (spec §2.1 item 5).
164
+ model AiMarkupConfig {
165
+ id String @id @default(dbgenerated("('markup_'::text || (uuidv7())::text)")) @db.Text
166
+ organizationId String @map("organization_id")
167
+ provider String?
168
+ model String?
169
+
170
+ markupPercent Decimal @map("markup_percent") @db.Decimal(5, 2)
171
+
172
+ createdAt DateTime @default(now()) @map("created_at")
173
+ updatedAt DateTime @updatedAt @map("updated_at")
174
+
175
+ @@unique([organizationId, provider, model])
176
+ @@map("ai_markup_configs")
177
+ }
178
+
179
+ /// LlmModelPricing — per-token provider rates, time-versioned by
180
+ /// effectiveFrom/effectiveTo (spec §2.1 item 6).
181
+ model LlmModelPricing {
182
+ id String @id @default(dbgenerated("('llmprc_'::text || (uuidv7())::text)")) @db.Text
183
+ provider String
184
+ model String
185
+
186
+ inputPricePerMillion Decimal @map("input_price_per_million") @db.Decimal(10, 4)
187
+ outputPricePerMillion Decimal @map("output_price_per_million") @db.Decimal(10, 4)
188
+ cachedInputPricePerMillion Decimal? @map("cached_input_price_per_million") @db.Decimal(10, 4)
189
+
190
+ effectiveFrom DateTime @map("effective_from")
191
+ effectiveTo DateTime? @map("effective_to")
192
+
193
+ createdAt DateTime @default(now()) @map("created_at")
194
+ updatedAt DateTime @updatedAt @map("updated_at")
195
+
196
+ @@unique([provider, model, effectiveFrom])
197
+ @@map("llm_model_pricing")
198
+ }
199
+
200
+ /// LlmModel — the active model catalog: tier, consumption weighting, context
201
+ /// window (spec §2.1 item 7). provider+model is the natural key (not
202
+ /// time-versioned, unlike LlmModelPricing — this is the current-state
203
+ /// catalog row per model).
204
+ model LlmModel {
205
+ id String @id @default(dbgenerated("('llmmdl_'::text || (uuidv7())::text)")) @db.Text
206
+ provider String
207
+ model String
208
+
209
+ tier ModelTier
210
+ consumptionMultiplier Decimal @default(1.0) @map("consumption_multiplier") @db.Decimal(5, 2)
211
+ chargeExtendedContextAt2x Boolean @map("charge_extended_context_at_2x")
212
+ isAutoModel Boolean @map("is_auto_model")
213
+ defaultContext Int @map("default_context")
214
+ maxContext Int @map("max_context")
215
+
216
+ createdAt DateTime @default(now()) @map("created_at")
217
+ updatedAt DateTime @updatedAt @map("updated_at")
218
+
219
+ @@unique([provider, model])
220
+ @@map("llm_models")
221
+ }
222
+
223
+ /// HobbyResetTracker — drives the 90-day Hobby-tier reset (P4, spec §2.1
224
+ /// item 8).
225
+ model HobbyResetTracker {
226
+ id String @id @default(dbgenerated("('hbyrst_'::text || (uuidv7())::text)")) @db.Text
227
+ organizationId String @unique @map("organization_id")
228
+ lastResetAt DateTime @map("last_reset_at")
229
+ nextResetAt DateTime @map("next_reset_at")
230
+
231
+ createdAt DateTime @default(now()) @map("created_at")
232
+
233
+ @@map("hobby_reset_trackers")
234
+ }
@@ -0,0 +1,263 @@
1
+ // content namespace — chat + artifact substrate (AgentSession, ConversationEvent,
2
+ // SessionReadReceipt, Artifact, ArtifactVersion)
3
+ //
4
+ // Backs the event-sourced conversation transport + artifact persistence in
5
+ // docs/research/ploy-capture/BACKEND-REQUIREMENTS.md (§1 transport R-T1..R-T7,
6
+ // §2 event envelope, §3 endpoints). Workspaces own all domain content, so every
7
+ // table here carries a non-null `workspaceId` tenant scope.
8
+ //
9
+ // Per ADR 0009 invariant 1: workspaceId / userId are plain String stable IDs —
10
+ // NO @relation into the auth namespace (Workspace/User live in database/auth/).
11
+ // Relations WITHIN this namespace (event → session, version → artifact) are
12
+ // real FKs. Cross-namespace assembly (e.g. tenant guard resolving a session's
13
+ // workspace) routes through apps/api/src/services/ (invariant 4).
14
+ //
15
+ // Generator + datasource blocks live in database/schema.prisma (the root schema
16
+ // file). This file holds only model definitions for the content namespace.
17
+
18
+ /// AgentSession — one agent conversation ("session"/captured "ploy") within a
19
+ /// workspace. lastSeq is the append watermark for the event log (R-T1).
20
+ model AgentSession {
21
+ id String @id @default(dbgenerated("('agsess_'::text || (uuidv7())::text)")) @db.Text
22
+ workspaceId String @map("workspace_id")
23
+ title String?
24
+ status String @default("idle")
25
+ lastSeq Int @default(0) @map("last_seq")
26
+ lastMessageAt DateTime? @map("last_message_at")
27
+ draftMessage String? @map("draft_message")
28
+ createdAt DateTime @default(now()) @map("created_at")
29
+ updatedAt DateTime @updatedAt @map("updated_at")
30
+
31
+ events ConversationEvent[]
32
+ artifacts Artifact[]
33
+ readReceipts SessionReadReceipt[]
34
+
35
+ @@index([workspaceId, lastMessageAt(sort: Desc)])
36
+ @@map("agent_sessions")
37
+ }
38
+
39
+ /// ConversationEvent — append-only, seq-ordered event log entry (R-T1/R-T4).
40
+ /// Envelope: { agentId, seq, type, turnId, payload, createdAt } (§2).
41
+ /// workspaceId is denormalized so the scope layer filters events without a
42
+ /// join through agent_sessions.
43
+ model ConversationEvent {
44
+ id String @id @default(dbgenerated("('convev_'::text || (uuidv7())::text)")) @db.Text
45
+ sessionId String @map("session_id")
46
+ workspaceId String @map("workspace_id")
47
+ agentId String @map("agent_id")
48
+ seq Int
49
+ type String
50
+ turnId String? @map("turn_id")
51
+ payload Json
52
+ createdAt DateTime @default(now()) @map("created_at")
53
+
54
+ session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
55
+
56
+ @@unique([sessionId, seq])
57
+ @@index([workspaceId])
58
+ @@map("conversation_events")
59
+ }
60
+
61
+ /// SessionReadReceipt — per-user read position for a session (R-T7 mark-read;
62
+ /// drives unread badges). userId is a stable auth-namespace ID — no FK.
63
+ model SessionReadReceipt {
64
+ id String @id @default(dbgenerated("('receipt_'::text || (uuidv7())::text)")) @db.Text
65
+ sessionId String @map("session_id")
66
+ workspaceId String @map("workspace_id")
67
+ userId String @map("user_id")
68
+ lastReadSeq Int @default(0) @map("last_read_seq")
69
+ updatedAt DateTime @updatedAt @map("updated_at")
70
+
71
+ session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
72
+
73
+ @@unique([sessionId, userId])
74
+ @@index([workspaceId])
75
+ @@map("session_read_receipts")
76
+ }
77
+
78
+ /// Artifact — a session's produced output (first type: text/document, #3846).
79
+ model Artifact {
80
+ id String @id @default(dbgenerated("('artif_'::text || (uuidv7())::text)")) @db.Text
81
+ sessionId String @map("session_id")
82
+ workspaceId String @map("workspace_id")
83
+ type String @default("text/document")
84
+ title String
85
+ createdAt DateTime @default(now()) @map("created_at")
86
+ updatedAt DateTime @updatedAt @map("updated_at")
87
+
88
+ session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
89
+ versions ArtifactVersion[]
90
+
91
+ @@index([sessionId])
92
+ @@index([workspaceId])
93
+ @@map("artifacts")
94
+ }
95
+
96
+ /// ArtifactVersion — auto-saved version history; exactly one isCurrent per
97
+ /// artifact (enforced at the service layer on save/restore).
98
+ model ArtifactVersion {
99
+ id String @id @default(dbgenerated("('artiv_'::text || (uuidv7())::text)")) @db.Text
100
+ artifactId String @map("artifact_id")
101
+ workspaceId String @map("workspace_id")
102
+ content Json
103
+ isCurrent Boolean @default(true) @map("is_current")
104
+ savedAt DateTime @default(now()) @map("saved_at")
105
+
106
+ artifact Artifact @relation(fields: [artifactId], references: [id], onDelete: Cascade)
107
+
108
+ @@index([artifactId, savedAt(sort: Desc)])
109
+ @@index([workspaceId])
110
+ @@map("artifact_versions")
111
+ }
112
+
113
+ // ============================================================================
114
+ // Usage-billing metering (FEAT-047 P1.S1, #3709 — Epic #3704)
115
+ //
116
+ // The metering event + its org-level aggregates. Per ADR 0009 D2 (roadmap §4),
117
+ // the metering event + UsagePeriod + UsageBillingReport live in `content`;
118
+ // balances/plans (S2) live in auth/config. organizationId/workspaceId/userId
119
+ // are plain String stable-ids — no @relation into auth (ADR 0009 invariant 1).
120
+ // Field roster ported from docs/research/wt-billing-capture/schema.md, modeled
121
+ // on WT's TraceLog billing columns (spec D1), with workspaceId/userId added to
122
+ // the event grain (spec D2). Cross-namespace assembly (e.g. resolving an
123
+ // event's owning Workspace) routes through apps/api/src/services/.
124
+ // ============================================================================
125
+
126
+ /// ModelTier — the pricing tier at event time. BASE is unlimited/free-ish;
127
+ /// PREMIUM/FRONTIER consume credits. Defined here (S1, the dependency root)
128
+ /// per spec OQ-1 — S2's LlmModel references the same enum through the merged
129
+ /// prismaSchemaFolder (one logical schema; an enum reference needs no
130
+ /// @relation, so this is not a cross-namespace FK).
131
+ enum ModelTier {
132
+ BASE
133
+ PREMIUM
134
+ FRONTIER
135
+ }
136
+
137
+ /// UsageBillingStatus — UsageBillingReport lifecycle: PENDING → REPORTED →
138
+ /// INVOICED → PAID, or FAILED.
139
+ enum UsageBillingStatus {
140
+ PENDING
141
+ REPORTED
142
+ INVOICED
143
+ PAID
144
+ FAILED
145
+ }
146
+
147
+ /// UsageEvent — one row per billable AI call (the metering event; modeled on
148
+ /// WT's TraceLog billing columns, spec D1). org+workspace+user grain (spec
149
+ /// D2); all three scope ids are plain String stable-ids — no cross-namespace
150
+ /// @relation (ADR 0009 invariant 1, spec D3). The three attribution portion
151
+ /// columns are the load-bearing billing split; only onDemandPortionCents is
152
+ /// ever reported to Stripe (P4, spec D4). completionStatus 'success' rows
153
+ /// drive billing — aggregation (P2) filters on it.
154
+ model UsageEvent {
155
+ id String @id @default(dbgenerated("('usagev_'::text || (uuidv7())::text)")) @db.Text
156
+ organizationId String @map("organization_id")
157
+ workspaceId String @map("workspace_id")
158
+ userId String @map("user_id")
159
+
160
+ provider String
161
+ model String
162
+
163
+ // Cache-aware token breakdown.
164
+ inputTokensCacheWrite Int @map("input_tokens_cache_write")
165
+ inputTokensNoCacheWrite Int @map("input_tokens_no_cache_write")
166
+ cacheReadTokens Int @map("cache_read_tokens")
167
+ outputTokens Int @map("output_tokens")
168
+ thinkingTokens Int @map("thinking_tokens")
169
+ totalTokens Int @map("total_tokens")
170
+
171
+ providerCostUsd Decimal @map("provider_cost_usd") @db.Decimal(10, 6)
172
+ billedCostUsd Decimal @map("billed_cost_usd") @db.Decimal(10, 6)
173
+ markupPercent Decimal @map("markup_percent") @db.Decimal(5, 2)
174
+
175
+ modelTier ModelTier @default(BASE) @map("model_tier")
176
+ wasAutoModel Boolean @map("was_auto_model")
177
+
178
+ // Attribution split — billedCostUsd apportioned across the three buckets;
179
+ // only onDemandPortionCents is ever Stripe-metered (P4, spec D4).
180
+ includedPortionCents Int @default(0) @map("included_portion_cents")
181
+ creditBalancePortionCents Int @default(0) @map("credit_balance_portion_cents")
182
+ onDemandPortionCents Int @default(0) @map("on_demand_portion_cents")
183
+
184
+ completionStatus String @map("completion_status")
185
+ createdAt DateTime @default(now()) @map("created_at")
186
+
187
+ @@index([organizationId, createdAt])
188
+ @@index([workspaceId, createdAt])
189
+ @@index([userId, createdAt])
190
+ @@index([provider, model])
191
+ @@index([modelTier, wasAutoModel])
192
+ @@map("usage_events")
193
+ }
194
+
195
+ /// UsagePeriod — one row per (organizationId, periodStart); the org-scoped
196
+ /// spend/tier rollup the P2 write path upserts as UsageEvent rows land.
197
+ /// Carries the event-grain roll-up support for the Org → Workspace → User
198
+ /// aggregation (spec D2) — whether this model itself gains per-workspace
199
+ /// sub-rows is a P2 decision, not a P1 schema requirement (spec OQ-2).
200
+ model UsagePeriod {
201
+ id String @id @default(dbgenerated("('usagper_'::text || (uuidv7())::text)")) @db.Text
202
+ organizationId String @map("organization_id")
203
+
204
+ periodStart DateTime @map("period_start")
205
+ periodEnd DateTime @map("period_end")
206
+
207
+ tokensConsumed BigInt @default(0) @map("tokens_consumed")
208
+ baseTokensConsumed BigInt @default(0) @map("base_tokens_consumed")
209
+ premiumTokensConsumed BigInt @default(0) @map("premium_tokens_consumed")
210
+ frontierTokensConsumed BigInt @default(0) @map("frontier_tokens_consumed")
211
+
212
+ usageSpendCents Int @default(0) @map("usage_spend_cents")
213
+
214
+ @@unique([organizationId, periodStart])
215
+ @@index([baseTokensConsumed, premiumTokensConsumed, frontierTokensConsumed], map: "usage_periods_tier_tracking_idx")
216
+ @@map("usage_periods")
217
+ }
218
+
219
+ /// UsageBillingReport — one per (organizationId, periodStart, periodEnd); the
220
+ /// Stripe-reporting unit rolled up from UsageEvent by P2 aggregation. Only
221
+ /// onDemandCents is ever reported to Stripe (P4, spec D4) — includedCents/
222
+ /// creditBalanceCents are informational attribution, never metered.
223
+ model UsageBillingReport {
224
+ id String @id @default(dbgenerated("('usagrept_'::text || (uuidv7())::text)")) @db.Text
225
+ organizationId String @map("organization_id")
226
+
227
+ periodStart DateTime @map("period_start")
228
+ periodEnd DateTime @map("period_end")
229
+
230
+ // AGGREGATE precision (#3738 H2) — these hold a MONTH of events, not one
231
+ // event, so they must NOT reuse UsageEvent's per-event widths. At the
232
+ // original Decimal(10,6)/Int, an org whose month crossed $9,999.999999 (or
233
+ // 2.15B tokens) overflowed the column, the upsert threw, and the report was
234
+ // never written on ANY subsequent run — silently stalling revenue for the
235
+ // largest customers, worsening monotonically. Decimal(20,6) ≈ $1e14 and int8
236
+ // give headroom far above the service's own MAX_TOTAL_USD / token caps, so
237
+ // the service's loud `aggregate_overflow` assertion is always the gate and a
238
+ // silent DB overflow is unreachable. See usage-aggregation.ts § Bounds.
239
+ totalProviderCost Decimal @map("total_provider_cost") @db.Decimal(20, 6)
240
+ totalMarkupAmount Decimal @map("total_markup_amount") @db.Decimal(20, 6)
241
+ totalBilledAmount Decimal @map("total_billed_amount") @db.Decimal(20, 6)
242
+
243
+ totalTokens BigInt @map("total_tokens")
244
+ eventCount Int @map("event_count")
245
+
246
+ // Attribution split — only onDemandCents is ever Stripe-metered (P4, spec D4).
247
+ includedCents Int @default(0) @map("included_cents")
248
+ creditBalanceCents Int @default(0) @map("credit_balance_cents")
249
+ onDemandCents Int @default(0) @map("on_demand_cents")
250
+
251
+ stripeUsageRecordId String? @map("stripe_usage_record_id")
252
+ reportedToStripeAt DateTime? @map("reported_to_stripe_at")
253
+ stripeInvoiceId String? @map("stripe_invoice_id")
254
+
255
+ status UsageBillingStatus @default(PENDING)
256
+ errorMessage String? @map("error_message")
257
+
258
+ @@unique([organizationId, periodStart, periodEnd], map: "usage_billing_reports_period_key")
259
+ @@index([organizationId, status])
260
+ @@index([status])
261
+ @@index([periodStart])
262
+ @@map("usage_billing_reports")
263
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * @file listing-diagnostic.ts
3
+ * @description The ONE scoring RULE every project-listing diagnostic shares
4
+ * (#5001, reworked by #5036 D4) — the template's Q3–Q6 resolved by option
5
+ * VALUE, never by option position.
6
+ *
7
+ * The mechanism (#5036 D4, "scoring by value, not position"): the generated
8
+ * flow emits each scored option under the FOUNDER'S OWN `option.value`, and
9
+ * the register's `optionWeights` map is keyed on those same values. Q3/Q4 are
10
+ * founder-authored, so their weights map is built PER LISTING from that
11
+ * listing's own options — the WEIGHT still comes from the founder's declared
12
+ * order via `listingOptionPoints(index, arity)`, but it is stored against the
13
+ * value rather than an index. Q5/Q6 carry fixed values
14
+ * (`projectListingQuestionsSchema`, #4998), so their weights are declared once
15
+ * here, shared by every listing.
16
+ *
17
+ * What that buys, and why the old `q{n}:{i}of{k}` encoding had to go: meaning
18
+ * now travels with the value. A register may reorder or insert options and an
19
+ * already-stored answer keeps scoring what it always meant, so the subset a
20
+ * surface renders no longer has to be a prefix of the register's list — which
21
+ * is what unblocks the shared team-composition value set (#5036 D3). It also
22
+ * dissolves the "shared register needs a shared value universe" problem
23
+ * (docs/lessons/2026-09-07-…): the generated experiment's `questionSet` now
24
+ * declares the listing's OWN options, so
25
+ * `waitlistExperimentConfigSchema`'s "every weights key must be a declared
26
+ * option" cross-validation is satisfied by construction rather than by
27
+ * declaring a finite universe of positional tokens.
28
+ *
29
+ * What is shared is therefore the RULE and the shape — same point names, same
30
+ * `total` sum, same tier floors, same 0–12 range — not a single frozen
31
+ * constant. Two listings' registers differ only in the Q3/Q4 KEYS, which are
32
+ * the founders' own option values.
33
+ *
34
+ * Q5 runs STRONGEST-FIRST, deliberately. `projectListingQuestionsSchema` pins
35
+ * Q5's values as `urgent | planning | exploring`, which is urgency descending,
36
+ * while the positional rule scores index 0 lowest — so the old positional
37
+ * encoding scored `urgent` 0 and `exploring` 3, backwards, a standing defect
38
+ * recorded in docs/lessons/2026-09-07-q5-fixed-order-is-strongest-first.md.
39
+ * Scoring by value is exactly the mechanism that resolves it: the weights
40
+ * below name `urgent` 3 and `exploring` 0 explicitly, with no reordering of
41
+ * the founder's rendered options and no per-question special case hidden
42
+ * inside a positional formula. That lesson's open question is closed here.
43
+ * Q6 (`low → critical`) is genuinely weakest-first and keeps 0 → 3.
44
+ *
45
+ * Q1 (contact), Q2 (segment), Q7 (free text) and the extras carry no points —
46
+ * they are absent from `points` entirely, so they contribute 0 by
47
+ * construction rather than by a zero weight that could drift.
48
+ *
49
+ * This module is data + the pure functions that generate it — it imports only
50
+ * TYPES from `@working-theory/validation` and introduces no dependency from
51
+ * `database/` toward `apps/*` (architecture.yaml's import boundary), exactly
52
+ * like `presets.ts` beside it. The rule lives here and only here; the
53
+ * projection (`apps/api/src/services/project-listing-flow.ts`) calls
54
+ * `buildListingDiagnosticScoring` rather than re-deriving weights, so the
55
+ * register and the flow can never disagree about what an answer is worth.
56
+ */
57
+
58
+ import type {
59
+ ProjectListingQuestions,
60
+ WaitlistScoring,
61
+ } from "@working-theory/validation";
62
+
63
+ /** The four scored template questions, in template order. */
64
+ export const LISTING_DIAGNOSTIC_SCORED_QUESTIONS = ["q3", "q4", "q5", "q6"] as const;
65
+ export type ListingDiagnosticScoredQuestion =
66
+ (typeof LISTING_DIAGNOSTIC_SCORED_QUESTIONS)[number];
67
+
68
+ /** The highest score any one question can contribute. */
69
+ export const LISTING_DIAGNOSTIC_MAX_QUESTION_POINTS = 3;
70
+
71
+ /**
72
+ * The weight of option `index` (zero-based) of `arity` options —
73
+ * `round(3·i/(k−1))`, so the founder's first option always scores 0 and their
74
+ * last always scores 3, whatever the arity. This is still how a
75
+ * founder-authored question's weights are DERIVED (the founder orders their
76
+ * options weakest→strongest); what changed in #5036 D4 is where the result is
77
+ * STORED — against the option's value, not its index.
78
+ *
79
+ * A single-option question (`arity <= 1`) has no spread to score and yields 0;
80
+ * the template never produces one, and this guard exists so the expression can
81
+ * never divide by zero.
82
+ */
83
+ export function listingOptionPoints(index: number, arity: number): number {
84
+ if (arity <= 1) return 0;
85
+ return Math.round((LISTING_DIAGNOSTIC_MAX_QUESTION_POINTS * index) / (arity - 1));
86
+ }
87
+
88
+ /**
89
+ * `optionWeights` for one founder-authored question (Q3/Q4), keyed by the
90
+ * founder's own option values and weighted by their declared order.
91
+ */
92
+ export function listingDiagnosticOptionWeights(
93
+ options: readonly { value: string }[],
94
+ ): Record<string, number> {
95
+ const weights: Record<string, number> = {};
96
+ options.forEach((option, index) => {
97
+ weights[option.value] = listingOptionPoints(index, options.length);
98
+ });
99
+ return weights;
100
+ }
101
+
102
+ /**
103
+ * Q5's fixed urgency weights — STRONGEST FIRST, against the positional rule
104
+ * and deliberately so (see the file header). `urgent` is the strongest buying
105
+ * signal the question can carry and scores 3; `exploring` is the weakest and
106
+ * scores 0. The three weights are the same multiset a 3-option positional
107
+ * question produces (`{0, 2, 3}`), so the 0–12 total range is unchanged.
108
+ */
109
+ export const LISTING_DIAGNOSTIC_Q5_WEIGHTS: Readonly<Record<string, number>> = {
110
+ urgent: 3,
111
+ planning: 2,
112
+ exploring: 0,
113
+ };
114
+
115
+ /**
116
+ * Q6's fixed band weights — weakest first, matching the schema's own
117
+ * `low → critical` low→high ordering, so these are exactly what the positional
118
+ * rule produced for a 4-option question.
119
+ */
120
+ export const LISTING_DIAGNOSTIC_Q6_WEIGHTS: Readonly<Record<string, number>> = {
121
+ low: 0,
122
+ medium: 1,
123
+ high: 2,
124
+ critical: 3,
125
+ };
126
+
127
+ /** The four tier keys, strongest first — the order `tiers` is evaluated in. */
128
+ export const LISTING_DIAGNOSTIC_TIERS = ["now", "soon", "later", "curious"] as const;
129
+
130
+ /**
131
+ * The inclusive lower bound of each tier over the 0–12 total (4 questions ×
132
+ * 3 points): `now` 10–12, `soon` 7–9, `later` 4–6, `curious` 0–3. Ordered
133
+ * strongest-first because `evaluateWaitlistScoring` takes the FIRST matching
134
+ * tier, so a `gte` ladder read top-down is total and unambiguous.
135
+ */
136
+ const TIER_FLOORS: Readonly<Record<(typeof LISTING_DIAGNOSTIC_TIERS)[number], number>> = {
137
+ now: 10,
138
+ soon: 7,
139
+ later: 4,
140
+ curious: 0,
141
+ };
142
+
143
+ /**
144
+ * Build one listing's register from its own `questions` block. Embedded
145
+ * verbatim into that listing's generated `WaitlistExperiment.config` —
146
+ * `waitlistExperimentConfigSchema` carries the scoring block by value and
147
+ * offers no by-reference slot.
148
+ *
149
+ * Every listing gets the same point names, the same `total` sum, the same
150
+ * tier ladder and the same 0–12 range; only the Q3/Q4 weight KEYS differ,
151
+ * because those keys are the founder's own option values (#5036 D4).
152
+ */
153
+ export function buildListingDiagnosticScoring(
154
+ questions: ProjectListingQuestions,
155
+ ): WaitlistScoring {
156
+ const weights: Record<ListingDiagnosticScoredQuestion, Record<string, number>> = {
157
+ q3: listingDiagnosticOptionWeights(questions.q3.options),
158
+ q4: listingDiagnosticOptionWeights(questions.q4.options),
159
+ q5: { ...LISTING_DIAGNOSTIC_Q5_WEIGHTS },
160
+ q6: { ...LISTING_DIAGNOSTIC_Q6_WEIGHTS },
161
+ };
162
+
163
+ return {
164
+ version: 1,
165
+ points: LISTING_DIAGNOSTIC_SCORED_QUESTIONS.map((question) => ({
166
+ name: `${question}_points`,
167
+ question,
168
+ kind: "optionWeights" as const,
169
+ weights: weights[question],
170
+ })),
171
+ scores: {
172
+ total: {
173
+ sum: LISTING_DIAGNOSTIC_SCORED_QUESTIONS.map((q) => `${q}_points`),
174
+ },
175
+ },
176
+ tiers: LISTING_DIAGNOSTIC_TIERS.map((key) => ({
177
+ key,
178
+ when: { score: "total", gte: TIER_FLOORS[key] },
179
+ })),
180
+ default: "curious",
181
+ };
182
+ }