create-ailk 0.2.0 → 0.4.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 (292) hide show
  1. package/component-catalog.md +50 -53
  2. package/dist/cli.js +19 -9
  3. package/dist/lib/apply-module-patches.d.ts +47 -0
  4. package/dist/lib/apply-module-patches.js +370 -0
  5. package/dist/lib/extract-module-bundle.d.ts +51 -3
  6. package/dist/lib/extract-module-bundle.js +327 -28
  7. package/dist/lib/fetch-module.d.ts +3 -38
  8. package/dist/lib/fetch-module.js +26 -8
  9. package/dist/lib/fetch-template.d.ts +20 -0
  10. package/dist/lib/fetch-template.js +129 -0
  11. package/dist/lib/module-license-gate.d.ts +10 -0
  12. package/dist/lib/module-license-gate.js +13 -4
  13. package/dist/lib/paid-paths.d.ts +16 -0
  14. package/dist/lib/paid-paths.js +90 -0
  15. package/dist/module-architecture.d.ts +197 -23
  16. package/dist/module-architecture.js +820 -25
  17. package/dist/parse-args.d.ts +9 -2
  18. package/dist/parse-args.js +46 -1
  19. package/dist/programmatic.d.ts +28 -2
  20. package/dist/programmatic.js +115 -8
  21. package/dist/surfaces.d.ts +12 -36
  22. package/dist/surfaces.js +77 -5
  23. package/dist/sync-routes.js +31 -1
  24. package/dist/template-manifest.d.ts +92 -0
  25. package/dist/template-manifest.js +168 -0
  26. package/dist/template-names.d.ts +41 -0
  27. package/dist/template-names.js +44 -0
  28. package/package.json +3 -2
  29. package/site-templates/marketing-site-free/ailk-template.json +8 -0
  30. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +103 -0
  31. package/site-templates/marketing-site-free/content/en/PrivacyPage/privacy.mdx +79 -0
  32. package/site-templates/marketing-site-free/content/en/TermsPage/terms.mdx +80 -0
  33. package/site-templates/marketing-site-free/provenance.json +4 -0
  34. package/templates/.claude/agents/web.md +2 -3
  35. package/templates/.claude/rules/architecture.md +5 -5
  36. package/templates/.claude/skills/README.md +2 -1
  37. package/templates/.env.example +21 -9
  38. package/templates/CONVENTIONS.md +6 -7
  39. package/templates/apps/api/.env.example +9 -0
  40. package/templates/apps/api/CLAUDE.md +33 -5
  41. package/templates/apps/api/package.json +0 -1
  42. package/templates/apps/api/src/__tests__/cors.test.ts +195 -0
  43. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +60 -2
  44. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +159 -0
  45. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +166 -0
  46. package/templates/apps/api/src/config/__tests__/modules.test.ts +76 -0
  47. package/templates/apps/api/src/config/index.ts +19 -0
  48. package/templates/apps/api/src/config/modules.ts +12 -14
  49. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  50. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +89 -0
  51. package/templates/apps/api/src/lib/slice-load.ts +90 -0
  52. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +52 -32
  53. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +182 -0
  54. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +293 -0
  55. package/templates/apps/api/src/openapi/index.ts +9 -3
  56. package/templates/apps/api/src/openapi/spec.ts +505 -65
  57. package/templates/apps/api/src/openapi/surface-drift.ts +195 -0
  58. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +14 -0
  59. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +14 -0
  60. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +14 -0
  61. package/templates/apps/api/src/routes/content/index.ts +38 -11
  62. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +17 -0
  63. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +17 -0
  64. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +796 -0
  65. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +10 -0
  66. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +38 -0
  67. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +814 -0
  68. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +10 -0
  69. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +24 -0
  70. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +17 -0
  71. package/templates/apps/api/src/routes/project-listings/index.ts +37 -10
  72. package/templates/apps/api/src/routes/project-listings/me.ts +10 -0
  73. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +5 -0
  74. package/templates/apps/api/src/routes/project-listings/public.ts +5 -0
  75. package/templates/apps/api/src/routes/project-listings/respond.ts +11 -0
  76. package/templates/apps/api/src/routes/project-listings/resume-token.ts +5 -0
  77. package/templates/apps/api/src/routes/project-listings/start.ts +30 -0
  78. package/templates/apps/api/src/routes/project-listings/submit.ts +5 -0
  79. package/templates/apps/api/src/routes/project-listings/verify-email.ts +150 -0
  80. package/templates/apps/api/src/server.ts +504 -137
  81. package/templates/apps/api/src/services/deliverable-fulfillment.ts +10 -0
  82. package/templates/apps/api/src/services/listing-config.ts +58 -0
  83. package/templates/apps/api/src/services/project-listing-resume-email.ts +326 -0
  84. package/templates/apps/api/src/services/project-listings.ts +229 -4
  85. package/templates/apps/api/src/vercel-handler.ts +60 -26
  86. package/templates/apps/mcp/.env.example +8 -0
  87. package/templates/apps/mcp/CLAUDE.md +2 -2
  88. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +21 -0
  89. package/templates/apps/mcp/__tests__/config/modules.test.ts +78 -0
  90. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +1 -1
  91. package/templates/apps/mcp/src/config/modules.ts +14 -13
  92. package/templates/apps/mcp/src/server.ts +8 -4
  93. package/templates/apps/mcp/src/tools/index.ts +7 -22
  94. package/templates/apps/web/.env.example +15 -0
  95. package/templates/apps/web/app/[locale]/(authed)/{waitlist/__tests__ → __tests__}/gate.test.tsx +4 -4
  96. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +2 -1
  97. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +2 -1
  98. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +2 -1
  99. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +1 -0
  100. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +1 -0
  101. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +2 -1
  102. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +1 -0
  103. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +1 -0
  104. package/templates/apps/web/app/[locale]/pricing/page.tsx +40 -0
  105. package/templates/apps/web/app/[locale]/privacy/page.tsx +40 -0
  106. package/templates/apps/web/app/[locale]/terms/page.tsx +40 -0
  107. package/templates/apps/web/app/llms.txt/route.ts +12 -9
  108. package/templates/apps/web/jest.config.cjs +19 -13
  109. package/templates/apps/web/lib/__tests__/server-api.test.ts +61 -0
  110. package/templates/apps/web/lib/server-api.ts +28 -12
  111. package/templates/apps/web/messages/en.json +58 -0
  112. package/templates/apps/web/next.config.mjs +1 -3
  113. package/templates/apps/web/package.json +0 -6
  114. package/templates/apps/web/public/android-chrome-192x192.png +0 -0
  115. package/templates/apps/web/public/android-chrome-512x512.png +0 -0
  116. package/templates/apps/web/public/apple-touch-icon.png +0 -0
  117. package/templates/apps/web/public/favicon-16x16.png +0 -0
  118. package/templates/apps/web/public/favicon-32x32.png +0 -0
  119. package/templates/apps/web/public/favicon.ico +0 -0
  120. package/templates/apps/web/public/favicon.svg +6 -3
  121. package/templates/apps/web/public/lockup-horizontal.svg +4 -0
  122. package/templates/apps/web/public/logomark.svg +4 -0
  123. package/templates/apps/web/public/site.webmanifest +2 -2
  124. package/templates/apps/web/public/wordmark.svg +4 -0
  125. package/templates/database/CHANGELOG.md +47 -0
  126. package/templates/database/inbox/schema.prisma +67 -0
  127. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +123 -0
  128. package/templates/database/package.json +1 -1
  129. package/templates/package.json +1 -1
  130. package/templates/.claude/skills/scaffold-commerce/SKILL.md +0 -807
  131. package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +0 -71
  132. package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +0 -83
  133. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +0 -370
  134. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +0 -376
  135. package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +0 -72
  136. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +0 -471
  137. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +0 -365
  138. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +0 -60
  139. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +0 -446
  140. package/templates/apps/api/src/__tests__/server.test.ts +0 -253
  141. package/templates/apps/api/src/bin/deliverable-resend.ts +0 -144
  142. package/templates/apps/api/src/bin/deliverable-upload.ts +0 -114
  143. package/templates/apps/api/src/bin/followup-sweep.ts +0 -44
  144. package/templates/apps/api/src/bin/listing-csv-sweep.ts +0 -46
  145. package/templates/apps/api/src/bin/seed-presets.ts +0 -58
  146. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +0 -104
  147. package/templates/apps/api/src/bin/session-retention-sweep.ts +0 -46
  148. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -74
  149. package/templates/apps/api/src/lib/stripe.ts +0 -52
  150. package/templates/apps/api/src/lib/tenant-db.ts +0 -225
  151. package/templates/apps/api/src/lib/ws-token.ts +0 -91
  152. package/templates/apps/api/src/middleware/tenant.ts +0 -99
  153. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +0 -450
  154. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +0 -262
  155. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +0 -436
  156. package/templates/apps/api/src/routes/billing/index.ts +0 -36
  157. package/templates/apps/api/src/routes/billing/portal.ts +0 -182
  158. package/templates/apps/api/src/routes/billing/read.ts +0 -75
  159. package/templates/apps/api/src/routes/billing/usage.ts +0 -153
  160. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -687
  161. package/templates/apps/api/src/routes/checkout/index.ts +0 -13
  162. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -197
  163. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +0 -393
  164. package/templates/apps/api/src/routes/deliverables/index.ts +0 -200
  165. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +0 -443
  166. package/templates/apps/api/src/routes/flow-checkouts/index.ts +0 -82
  167. package/templates/apps/api/src/routes/flows/README.md +0 -147
  168. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +0 -752
  169. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +0 -671
  170. package/templates/apps/api/src/routes/flows/index.ts +0 -202
  171. package/templates/apps/api/src/routes/flows/recommender.ts +0 -443
  172. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +0 -815
  173. package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +0 -221
  174. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +0 -566
  175. package/templates/apps/api/src/routes/project-listings/get.ts +0 -70
  176. package/templates/apps/api/src/routes/project-listings/list.ts +0 -47
  177. package/templates/apps/api/src/routes/project-listings/patch.ts +0 -136
  178. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +0 -490
  179. package/templates/apps/api/src/routes/schedule/index.ts +0 -249
  180. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +0 -385
  181. package/templates/apps/api/src/routes/slack/actions.ts +0 -177
  182. package/templates/apps/api/src/routes/slack/index.ts +0 -38
  183. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +0 -577
  184. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +0 -78
  185. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +0 -41
  186. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +0 -64
  187. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +0 -42
  188. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +0 -84
  189. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +0 -119
  190. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +0 -238
  191. package/templates/apps/api/src/routes/waitlist-signups/index.ts +0 -101
  192. package/templates/apps/api/src/routes/webhooks/README.md +0 -80
  193. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -315
  194. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -247
  195. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -270
  196. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -198
  197. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -633
  198. package/templates/apps/api/src/routes/webhooks/index.ts +0 -41
  199. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -481
  200. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +0 -356
  201. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +0 -671
  202. package/templates/apps/api/src/routes/workspaces/config.ts +0 -125
  203. package/templates/apps/api/src/routes/workspaces/index.ts +0 -194
  204. package/templates/apps/api/src/routes/workspaces/sessions.ts +0 -218
  205. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +0 -425
  206. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +0 -2840
  207. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -393
  208. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +0 -560
  209. package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +0 -684
  210. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +0 -406
  211. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +0 -290
  212. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +0 -736
  213. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +0 -475
  214. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +0 -340
  215. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +0 -983
  216. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +0 -71
  217. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +0 -379
  218. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +0 -768
  219. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +0 -1314
  220. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +0 -341
  221. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +0 -567
  222. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +0 -474
  223. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +0 -354
  224. package/templates/apps/api/src/services/flow-aggregate.ts +0 -232
  225. package/templates/apps/api/src/services/flow-checkouts.ts +0 -123
  226. package/templates/apps/api/src/services/flow-engine.ts +0 -1296
  227. package/templates/apps/api/src/services/lead-promotion.ts +0 -176
  228. package/templates/apps/api/src/services/listing-csv-sweep.ts +0 -455
  229. package/templates/apps/api/src/services/listing-promotion.ts +0 -342
  230. package/templates/apps/api/src/services/playbook-compile.ts +0 -398
  231. package/templates/apps/api/src/services/playbook-render.ts +0 -263
  232. package/templates/apps/api/src/services/project-listing-decision.ts +0 -510
  233. package/templates/apps/api/src/services/project-listing-flow.ts +0 -426
  234. package/templates/apps/api/src/services/project-listing-issue.ts +0 -251
  235. package/templates/apps/api/src/services/recommender-capture.ts +0 -835
  236. package/templates/apps/api/src/services/scheduling/cal-provider.ts +0 -392
  237. package/templates/apps/api/src/services/scheduling/index.ts +0 -63
  238. package/templates/apps/api/src/services/scheduling/types.ts +0 -88
  239. package/templates/apps/api/src/services/tenant-context.ts +0 -451
  240. package/templates/apps/api/src/services/usage-metering.ts +0 -699
  241. package/templates/apps/api/src/services/waitlist-dashboard.ts +0 -947
  242. package/templates/apps/api/src/services/waitlist-experiments.ts +0 -213
  243. package/templates/apps/api/src/services/waitlist-followup.ts +0 -486
  244. package/templates/apps/api/src/services/waitlist-scoring.ts +0 -166
  245. package/templates/apps/api/src/services/waitlist-signups.ts +0 -165
  246. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +0 -242
  247. package/templates/apps/mcp/src/tools/create_booking.ts +0 -58
  248. package/templates/apps/mcp/src/tools/get_event_meta.ts +0 -55
  249. package/templates/apps/mcp/src/tools/get_flow.ts +0 -61
  250. package/templates/apps/mcp/src/tools/list_availability.ts +0 -67
  251. package/templates/apps/mcp/src/tools/submit_flow_step.ts +0 -83
  252. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +0 -309
  253. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +0 -149
  254. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +0 -273
  255. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +0 -171
  256. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +0 -179
  257. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +0 -116
  258. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +0 -66
  259. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +0 -61
  260. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +0 -114
  261. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +0 -139
  262. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +0 -85
  263. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +0 -67
  264. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +0 -179
  265. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +0 -178
  266. package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +0 -160
  267. package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +0 -25
  268. package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +0 -117
  269. package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +0 -58
  270. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +0 -119
  271. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +0 -287
  272. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +0 -254
  273. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +0 -532
  274. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +0 -121
  275. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +0 -457
  276. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +0 -396
  277. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +0 -114
  278. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +0 -110
  279. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +0 -96
  280. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +0 -117
  281. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +0 -96
  282. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +0 -92
  283. package/templates/apps/web/app/[locale]/schedule/page.tsx +0 -82
  284. package/templates/apps/web/content/en/blog/README.txt +0 -25
  285. package/templates/apps/web/content/en/docs/index.mdx +0 -28
  286. package/templates/apps/web/lib/__mocks__/source-server.js +0 -31
  287. package/templates/apps/web/lib/blog-list-data.ts +0 -95
  288. package/templates/apps/web/lib/blog-post-data.ts +0 -79
  289. package/templates/apps/web/lib/source.ts +0 -34
  290. package/templates/apps/web/public/logomark-dark.svg +0 -4
  291. package/templates/apps/web/public/logomark-light.svg +0 -4
  292. package/templates/apps/web/source.config.ts +0 -78
@@ -3,7 +3,6 @@ import { join } from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import bundleAnalyzer from "@next/bundle-analyzer";
5
5
  import { withSentryConfig } from "@sentry/nextjs";
6
- import { createMDX } from "fumadocs-mdx/next";
7
6
  import createNextIntlPlugin from "next-intl/plugin";
8
7
 
9
8
  // @next/bundle-analyzer — enabled when ANALYZE=true (set by ci-bundle.yml).
@@ -110,7 +109,6 @@ const nextConfig = {
110
109
  "@working-theory/analytics",
111
110
  "@working-theory/api-client",
112
111
  "@working-theory/content-adapters",
113
- "@working-theory/fumadocs-schema",
114
112
  "@working-theory/jsonld",
115
113
  "@working-theory/metadata",
116
114
  "@working-theory/page-renderer",
@@ -168,6 +166,6 @@ const sentryBuildOptions = {
168
166
  };
169
167
 
170
168
  export default withSentryConfig(
171
- withBundleAnalyzer(withNextIntl(createMDX()(nextConfig))),
169
+ withBundleAnalyzer(withNextIntl(nextConfig)),
172
170
  sentryBuildOptions,
173
171
  );
@@ -6,14 +6,11 @@
6
6
  "license": "Apache-2.0",
7
7
  "scripts": {
8
8
  "dev": "next dev",
9
- "prebuild": "fumadocs-mdx",
10
9
  "build": "next build --webpack",
11
10
  "postbuild": "tsx scripts/assert-content-bundled.ts",
12
11
  "start": "next start",
13
- "pretypecheck": "fumadocs-mdx",
14
12
  "type-check": "tsc --noEmit",
15
13
  "lint": "next lint",
16
- "pretest": "fumadocs-mdx",
17
14
  "test": "jest",
18
15
  "test:watch": "jest --watch"
19
16
  },
@@ -24,7 +21,6 @@
24
21
  "@working-theory/api-client": "workspace:*",
25
22
  "@working-theory/content-adapters": "workspace:*",
26
23
  "@working-theory/database": "workspace:*",
27
- "@working-theory/fumadocs-schema": "workspace:*",
28
24
  "@working-theory/jsonld": "workspace:*",
29
25
  "@working-theory/metadata": "workspace:*",
30
26
  "@working-theory/page-renderer": "workspace:*",
@@ -38,8 +34,6 @@
38
34
  "@working-theory/validation": "workspace:*",
39
35
  "@working-theory/web-i18n": "workspace:*",
40
36
  "better-auth": "1.6.22",
41
- "fumadocs-core": "^16.10.4",
42
- "fumadocs-mdx": "^15.0.12",
43
37
  "lucide-react": "^0.577.0",
44
38
  "next": "^16.3.3",
45
39
  "next-intl": "^3.26.3",
@@ -1,4 +1,7 @@
1
- <svg width="1080" height="1080" viewBox="0 0 1080 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <rect width="1080" height="1080" rx="190" fill="#F0B100"/>
3
- <path d="M127.624 704.255C127.624 704.255 303.232 444.079 606.336 428.823C606.336 428.823 827.464 405.543 824.36 645.295C824.36 645.295 824.344 832.999 659.872 916.079C507.8 992.887 380.56 892.023 383.664 757.023C386.552 631.327 467.136 458.695 824.392 199.167L792.552 155.463L952.384 137.359L893.416 288.399L861.864 246.503C861.864 246.503 529.792 463.231 475.216 676.335C475.216 676.335 432.544 848.583 558.992 852.527C689.056 856.591 794.88 637.543 696.856 535.383C613.632 448.639 369.888 498.079 163.504 733.167L127.616 704.263L127.624 704.255Z" fill="white"/>
1
+ <svg width="1520" height="1520" viewBox="0 0 1520 1520" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="AI Launch Kit loop logomark">
2
+ <title>AI Launch Kit — favicon (the loop logomark on the #0656E9 tile)</title>
3
+ <rect x="0" y="0" width="1520" height="1520" rx="267" fill="#0656E9"/>
4
+ <svg x="258.4" y="270.1" width="1003.2" height="979.9" viewBox="127.6 137.2 824.8 805.6" fill="#FFFFFF">
5
+ <path d="M127.624 704.255C127.624 704.255 303.232 444.079 606.336 428.823C606.336 428.823 827.464 405.543 824.36 645.295C824.36 645.295 824.344 832.999 659.872 916.079C507.8 992.887 380.56 892.023 383.664 757.023C386.552 631.327 467.136 458.695 824.392 199.167L792.552 155.463L952.384 137.359L893.416 288.399L861.864 246.503C861.864 246.503 529.792 463.231 475.216 676.335C475.216 676.335 432.544 848.583 558.992 852.527C689.056 856.591 794.88 637.543 696.856 535.383C613.632 448.639 369.888 498.079 163.504 733.167L127.616 704.263L127.624 704.255Z" fill="#FFFFFF"/>
6
+ </svg>
4
7
  </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-196.50 -128.00 1248.00 143.50" width="1248" height="144">
2
+ <!-- AI Launch Kit — horizontal lockup from the portfolio logo builder (mark: loop; wordmark: custom lettering). -->
3
+ <g transform="scale(100)" fill="#2F2E2F"><g transform="translate(-1.9050 -1.2206) scale(0.0016343825665859565) translate(-127.0 -137.0)"><path d="M127.624 704.255C127.624 704.255 303.232 444.079 606.336 428.823C606.336 428.823 827.464 405.543 824.36 645.295C824.36 645.295 824.344 832.999 659.872 916.079C507.8 992.887 380.56 892.023 383.664 757.023C386.552 631.327 467.136 458.695 824.392 199.167L792.552 155.463L952.384 137.359L893.416 288.399L861.864 246.503C861.864 246.503 529.792 463.231 475.216 676.335C475.216 676.335 432.544 848.583 558.992 852.527C689.056 856.591 794.88 637.543 696.856 535.383C613.632 448.639 369.888 498.079 163.504 733.167L127.616 704.263L127.624 704.255Z" fill="#2F2E2F"/></g><g><g transform="translate(0.0000 0) scale(1.12)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -4.7020750267431675 -1.4599045865638782)"><path d="M 2017.5 402.472656 L 2069.75 282.953125 L 2117.78125 403.007812 Z M 2058.75 183.148438 L 1880.675781 584.136719 L 1942.238281 584.136719 L 2008.234375 422.691406 L 2125.605469 423.199219 L 2190.234375 584.136719 L 2271.277344 584.136719 L 2099.449219 183.226562 Z M 2058.75 183.148438 "/></g></g><g transform="translate(1.2171 0) scale(1.12)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -7.51705209867612 -1.835802888450144)"><path d="M1740.19 194.304L1719.36 191.411V202.211V419.908H1760.75V202.211V191.411L1740.19 194.304Z"/></g></g><g transform="translate(2.0699 0) scale(1.12)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -5.830939525076691 -1.4599045865638782)"><path d="M 2402.425781 183.285156 C 2396.121094 184.144531 2381.960938 186.203125 2370.179688 188.808594 L 2369.75 188.917969 L 2369.320312 188.871094 C 2368.386719 188.792969 2347.890625 187.152344 2332.242188 182.320312 L 2332.367188 584.121094 L 2560.800781 582.847656 L 2560.800781 553.59375 L 2403.714844 559.222656 Z M 2402.425781 183.285156 "/></g></g><g transform="translate(2.7492 0) scale(1)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -4.7020750267431675 -1.4599045865638782)"><path d="M 2017.5 402.472656 L 2069.75 282.953125 L 2117.78125 403.007812 Z M 2058.75 183.148438 L 1880.675781 584.136719 L 1942.238281 584.136719 L 2008.234375 422.691406 L 2125.605469 423.199219 L 2190.234375 584.136719 L 2271.277344 584.136719 L 2099.449219 183.226562 Z M 2058.75 183.148438 "/></g></g><g transform="translate(3.8551 0) scale(1)"><g transform="matrix(0.008439053643785967 0 0 0.008439053643785967 -4.32856421446149 -1.661401981511421)"><path d="M588.43 79.8509L582.5 78.3809H582.4V152.431C582.4 170.651 578.49 184.301 560.57 185.691C535.83 187.611 534.33 168.701 534.33 152.431V78.3809H512.93V154.271C512.93 180.851 526.97 196.901 553.55 196.901C580.13 196.901 594.34 180.851 594.34 154.271V78.3809H594.29L588.43 79.8509Z"/></g></g><g transform="translate(4.7219 0) scale(1)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -8.030530574367127 -1.835802888450144)"><path d="M2004.42 193.996L1983.67 191.122V201.903V362.052H1981.67L1873.96 191.064H1836.85V419.909H1878.31V249.847H1880.2L1988.03 419.909H2024.9V201.903V191.122L2004.42 193.996Z"/></g></g><g transform="translate(5.7438 0) scale(1)"><g transform="matrix(0.008439053643785967 0 0 0.008439053643785967 -6.325890417670046 -1.661401981511421)"><path d="M836.41 164.341C835.23 168.751 829.47 176.661 826.1 179.451C821.03 183.641 815.16 185.671 808.28 185.381C799.06 185.021 790.21 178.941 786.62 175.401C774.31 163.261 763.42 126.371 785.6 101.151C790.38 95.7213 799.06 90.4313 808.28 90.0713C815.16 89.7813 821.04 91.8113 826.1 96.0013C829.47 98.8013 835.23 106.701 836.41 111.111L850.58 110.991L856.37 95.5913C854.69 93.3213 842.2 80.1513 813.29 78.3613H811.99C806.12 78.3613 800.73 78.8913 795.85 79.7713C774.34 83.6413 757.22 100.051 752.05 121.281C750.58 127.301 749.55 133.771 749.57 140.021C749.57 141.261 749.62 142.501 749.71 143.731C751.72 171.321 773.66 193.211 801.13 196.451C802.11 196.571 803.52 196.671 805.04 196.751C814.56 197.281 824.14 196.051 833.09 192.741C847.38 187.471 854.38 179.421 855.59 177.781L850.57 164.441L836.4 164.321L836.41 164.341Z"/></g></g><g transform="translate(6.8247 0) scale(1)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -7.397806839178519 -1.4599045865638782)"><path d="M 3259.496094 184.34375 L 3259.21875 356.253906 L 3141.863281 363.183594 L 3028.496094 355.238281 L 3028.773438 185.34375 L 2994.425781 190.695312 L 2959.082031 185.328125 L 2959.082031 584.074219 L 2991.449219 583.722656 L 3028.789062 584.074219 L 3029.003906 381.808594 L 3142.355469 380.472656 L 3258.726562 381.808594 L 3259.496094 583.109375 L 3293.84375 582.753906 L 3329.21875 583.109375 L 3329.21875 184.359375 L 3294.855469 189.714844 Z M 3259.496094 184.34375 "/></g></g><g transform="translate(8.3998 0) scale(1.12)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -6.531551848288504 -1.835802888450144)"><path d="M1582.59 274.514L1651.05 192.742H1636.78L1536.82 312.159V192.761L1515.7 195.828L1493.99 192.742V419.909H1536.82V329.69L1553.7 309.247L1614.7 419.909H1664.03L1582.59 274.514Z"/></g></g><g transform="translate(9.3625 0) scale(1)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -7.51705209867612 -1.835802888450144)"><path d="M1740.19 194.304L1719.36 191.411V202.211V419.908H1760.75V202.211V191.411L1740.19 194.304Z"/></g></g><g transform="translate(9.6336 0) scale(1)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -6.402239906111586 -1.4599045865638782)"><path d="M 2691.488281 583.628906 L 2758.25 583.628906 L 2758.25 204.410156 L 2888.722656 218.15625 L 2888.832031 183.714844 L 2560.910156 183.714844 L 2560.800781 217.679688 L 2691.488281 204.425781 Z M 2691.488281 583.628906 "/></g></g></g></g>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
2
+ <!-- loop logomark in the portfolio square frame (fills 90% of the side) -->
3
+ <g transform="translate(5.000 6.090) scale(0.108959) translate(-127.0 -137.0)" fill="#2F2E2F"><path d="M127.624 704.255C127.624 704.255 303.232 444.079 606.336 428.823C606.336 428.823 827.464 405.543 824.36 645.295C824.36 645.295 824.344 832.999 659.872 916.079C507.8 992.887 380.56 892.023 383.664 757.023C386.552 631.327 467.136 458.695 824.392 199.167L792.552 155.463L952.384 137.359L893.416 288.399L861.864 246.503C861.864 246.503 529.792 463.231 475.216 676.335C475.216 676.335 432.544 848.583 558.992 852.527C689.056 856.591 794.88 637.543 696.856 535.383C613.632 448.639 369.888 498.079 163.504 733.167L127.616 704.263L127.624 704.255Z" fill="#2F2E2F"/></g>
4
+ </svg>
@@ -13,7 +13,7 @@
13
13
  "type": "image/png"
14
14
  }
15
15
  ],
16
- "theme_color": "#F0B100",
17
- "background_color": "#ffffff",
16
+ "theme_color": "#0656E9",
17
+ "background_color": "#0656E9",
18
18
  "display": "standalone"
19
19
  }
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-6.00 -118.50 1057.50 124.50" width="1058" height="125">
2
+ <!-- AI Launch Kit — wordmark from the portfolio logo builder (custom lettering). -->
3
+ <g transform="scale(100)" fill="#2F2E2F"><g><g transform="translate(0.0000 0) scale(1.12)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -4.7020750267431675 -1.4599045865638782)"><path d="M 2017.5 402.472656 L 2069.75 282.953125 L 2117.78125 403.007812 Z M 2058.75 183.148438 L 1880.675781 584.136719 L 1942.238281 584.136719 L 2008.234375 422.691406 L 2125.605469 423.199219 L 2190.234375 584.136719 L 2271.277344 584.136719 L 2099.449219 183.226562 Z M 2058.75 183.148438 "/></g></g><g transform="translate(1.2171 0) scale(1.12)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -7.51705209867612 -1.835802888450144)"><path d="M1740.19 194.304L1719.36 191.411V202.211V419.908H1760.75V202.211V191.411L1740.19 194.304Z"/></g></g><g transform="translate(2.0699 0) scale(1.12)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -5.830939525076691 -1.4599045865638782)"><path d="M 2402.425781 183.285156 C 2396.121094 184.144531 2381.960938 186.203125 2370.179688 188.808594 L 2369.75 188.917969 L 2369.320312 188.871094 C 2368.386719 188.792969 2347.890625 187.152344 2332.242188 182.320312 L 2332.367188 584.121094 L 2560.800781 582.847656 L 2560.800781 553.59375 L 2403.714844 559.222656 Z M 2402.425781 183.285156 "/></g></g><g transform="translate(2.7492 0) scale(1)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -4.7020750267431675 -1.4599045865638782)"><path d="M 2017.5 402.472656 L 2069.75 282.953125 L 2117.78125 403.007812 Z M 2058.75 183.148438 L 1880.675781 584.136719 L 1942.238281 584.136719 L 2008.234375 422.691406 L 2125.605469 423.199219 L 2190.234375 584.136719 L 2271.277344 584.136719 L 2099.449219 183.226562 Z M 2058.75 183.148438 "/></g></g><g transform="translate(3.8551 0) scale(1)"><g transform="matrix(0.008439053643785967 0 0 0.008439053643785967 -4.32856421446149 -1.661401981511421)"><path d="M588.43 79.8509L582.5 78.3809H582.4V152.431C582.4 170.651 578.49 184.301 560.57 185.691C535.83 187.611 534.33 168.701 534.33 152.431V78.3809H512.93V154.271C512.93 180.851 526.97 196.901 553.55 196.901C580.13 196.901 594.34 180.851 594.34 154.271V78.3809H594.29L588.43 79.8509Z"/></g></g><g transform="translate(4.7219 0) scale(1)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -8.030530574367127 -1.835802888450144)"><path d="M2004.42 193.996L1983.67 191.122V201.903V362.052H1981.67L1873.96 191.064H1836.85V419.909H1878.31V249.847H1880.2L1988.03 419.909H2024.9V201.903V191.122L2004.42 193.996Z"/></g></g><g transform="translate(5.7438 0) scale(1)"><g transform="matrix(0.008439053643785967 0 0 0.008439053643785967 -6.325890417670046 -1.661401981511421)"><path d="M836.41 164.341C835.23 168.751 829.47 176.661 826.1 179.451C821.03 183.641 815.16 185.671 808.28 185.381C799.06 185.021 790.21 178.941 786.62 175.401C774.31 163.261 763.42 126.371 785.6 101.151C790.38 95.7213 799.06 90.4313 808.28 90.0713C815.16 89.7813 821.04 91.8113 826.1 96.0013C829.47 98.8013 835.23 106.701 836.41 111.111L850.58 110.991L856.37 95.5913C854.69 93.3213 842.2 80.1513 813.29 78.3613H811.99C806.12 78.3613 800.73 78.8913 795.85 79.7713C774.34 83.6413 757.22 100.051 752.05 121.281C750.58 127.301 749.55 133.771 749.57 140.021C749.57 141.261 749.62 142.501 749.71 143.731C751.72 171.321 773.66 193.211 801.13 196.451C802.11 196.571 803.52 196.671 805.04 196.751C814.56 197.281 824.14 196.051 833.09 192.741C847.38 187.471 854.38 179.421 855.59 177.781L850.57 164.441L836.4 164.321L836.41 164.341Z"/></g></g><g transform="translate(6.8247 0) scale(1)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -7.397806839178519 -1.4599045865638782)"><path d="M 3259.496094 184.34375 L 3259.21875 356.253906 L 3141.863281 363.183594 L 3028.496094 355.238281 L 3028.773438 185.34375 L 2994.425781 190.695312 L 2959.082031 185.328125 L 2959.082031 584.074219 L 2991.449219 583.722656 L 3028.789062 584.074219 L 3029.003906 381.808594 L 3142.355469 380.472656 L 3258.726562 381.808594 L 3259.496094 583.109375 L 3293.84375 582.753906 L 3329.21875 583.109375 L 3329.21875 184.359375 L 3294.855469 189.714844 Z M 3259.496094 184.34375 "/></g></g><g transform="translate(8.3998 0) scale(1.12)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -6.531551848288504 -1.835802888450144)"><path d="M1582.59 274.514L1651.05 192.742H1636.78L1536.82 312.159V192.761L1515.7 195.828L1493.99 192.742V419.909H1536.82V329.69L1553.7 309.247L1614.7 419.909H1664.03L1582.59 274.514Z"/></g></g><g transform="translate(9.3625 0) scale(1)"><g transform="matrix(0.00437188995905497 0 0 0.00437188995905497 -7.51705209867612 -1.835802888450144)"><path d="M1740.19 194.304L1719.36 191.411V202.211V419.908H1760.75V202.211V191.411L1740.19 194.304Z"/></g></g><g transform="translate(9.6336 0) scale(1)"><g transform="matrix(0.002500025961207091 0 0 0.002500025961207091 -6.402239906111586 -1.4599045865638782)"><path d="M 2691.488281 583.628906 L 2758.25 583.628906 L 2758.25 204.410156 L 2888.722656 218.15625 L 2888.832031 183.714844 L 2560.910156 183.714844 L 2560.800781 217.679688 L 2691.488281 204.425781 Z M 2691.488281 583.628906 "/></g></g></g></g>
4
+ </svg>
@@ -1,5 +1,52 @@
1
1
  # @working-theory/database
2
2
 
3
+ ## 0.23.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`10a9fc9`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/10a9fc97455ecadb760c06d295965035b45aa605)]:
8
+ - @working-theory/validation@0.23.0
9
+
10
+ ## 0.22.1
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies []:
15
+ - @working-theory/validation@0.22.1
16
+
17
+ ## 0.22.0
18
+
19
+ ### Minor Changes
20
+
21
+ - [#5412](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/pull/5412) [`e94f08c`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/e94f08cccfe6cb9d096b6746a092c1b6921677cf) Thanks [@claudio-planck](https://github.com/claudio-planck)! - The founder application collects first name, last name and phone on step one, and records email verification when the emailed resume link is opened ([#5407](https://github.com/working-theory-labs/ai-launch-kit-internal/issues/5407)).
22
+
23
+ **Migration: `20260914120000_listing_founder_identity`.** Five nullable columns on `project_listings` — `founder_first_name`, `founder_last_name`, `founder_phone`, `email_verify_nonce_hash` and `email_verified_at` — with no backfill and no change to any existing column. `founder_name` stays NOT NULL and stays the single-line DISPLAY name every existing reader reads; the parts sit beside it, exactly as [#5279](https://github.com/working-theory-labs/ai-launch-kit-internal/issues/5279) put the six address parts beside `address`. Composition runs one way only, so no existing row's `founder_name` is split by guessing.
24
+
25
+ The platform's default contact screen now asks first name, last name, email, phone and consent. Phone is required by default and a site relaxes it with `questions: [{ id: "founder_phone", required: false }]`. A site that still asks for one "Your name" line keeps parsing untouched: the required `founderName` contract row is satisfied either by a question writing it or by the two parts that compose it.
26
+
27
+ `POST /v1/project-listings` accepts `founderFirstName` / `founderLastName` / `founderPhone` and composes `founder_name` server-side; a body carrying only `founderName`, as every caller before this release sends, is accepted unchanged.
28
+
29
+ New anonymous, credential-gated route `POST /v1/project-listings/drafts/:id/verify-email`, reached from `projectListings.verifyEmail(id, resumeToken, verifyNonce)`. It takes two credentials, and only the second carries the proof: the resume token is in `start`'s own 201 body, so it says only that you opened the draft, while the verification NONCE is minted at `start`, stored as a digest, and handed to nothing but the resume-email builder — it reaches the outside world only as the `?verify=` parameter of the emailed link. The route stamps `email_verified_at` write-once by compare-and-swap, so repeat openings answer the first stamp; a wrong token, a wrong nonce and an unknown draft all return the same `404 not_found` the other token-gated routes return.
30
+
31
+ `ListingApplicationFlow` calls it once when it mounts with a `resume.verifyNonce`, and a failure never blocks the founder. A resume link with no nonce still resumes the draft and records nothing — which is also what a site that has not switched the resume email on gets, permanently, since no nonce is ever delivered there.
32
+
33
+ `start`'s request and response shapes are unchanged: the nonce is never in a response body and never in a log line.
34
+
35
+ `founder_phone` and `email_verified_at` are omitted from the public listing read. The operator sees both on the tenant dashboard row and in the decision/review payload, and the weekly CSV sweep's outcome log now says whether the address it mailed had been proved.
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies [[`941db5d`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/941db5d317b7332c3c5bb4134d0cc18f80678781), [`cbe99c2`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/cbe99c228200b4720b2f36af50c822a4bcca2275), [`e94f08c`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/e94f08cccfe6cb9d096b6746a092c1b6921677cf), [`f42b96b`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/f42b96b7489b5dae503b6c32e2045a14f382c583), [`4d28c33`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/4d28c33978dde5cd33540813ec6317436a42a877)]:
40
+ - @working-theory/ids@0.7.0
41
+ - @working-theory/validation@0.22.0
42
+
43
+ ## 0.21.0
44
+
45
+ ### Patch Changes
46
+
47
+ - Updated dependencies []:
48
+ - @working-theory/validation@0.21.0
49
+
3
50
  ## 0.20.0
4
51
 
5
52
  ### Minor Changes
@@ -501,8 +501,75 @@ model ProjectListing {
501
501
  // `operator-only` answer reaches the dashboard and the review payload and
502
502
  // stops there.
503
503
  siteAnswers Json? @map("site_answers")
504
+ // #5407 — still the single-line DISPLAY name, and still NOT NULL: `start`
505
+ // has always required one and every reader below expects one. Where the two
506
+ // part columns beside it are written, this is COMPOSED from them
507
+ // server-side (`startProjectListingDraft`); where they are not, it is the
508
+ // line the founder typed, verbatim.
509
+ //
510
+ // Read by: the submit-time slug fallback, the approve/reject email's
511
+ // greeting, the Slack review card's Founder row, the build issue, and the
512
+ // `routeLead` identity. None of them changes.
504
513
  founderName String @map("founder_name")
514
+ // #5407 — the structured parts of the display name above, beside it rather
515
+ // than instead of it, exactly as #5279 put the six address parts beside
516
+ // `address`. Nullable for the #5036 D1 reason and one more: a row written
517
+ // before #5407 has no parts, and none may ever be GUESSED from its
518
+ // `founder_name`. "First Last" composes one way only; a name does not
519
+ // decompose (a two-word split mangles "van der Berg", "Maria del Carmen"
520
+ // and every mononym), so the migration backfills nothing and a null part
521
+ // stays null for the life of the row.
522
+ founderFirstName String? @map("founder_first_name")
523
+ founderLastName String? @map("founder_last_name")
505
524
  founderEmail String @map("founder_email")
525
+ // #5407 — the founder's phone, collected on the default contact screen
526
+ // alongside the email (samhenry spec 316 D1). Nullable at rest for the
527
+ // #5036 D1 reason AND permanently: a row written before #5407 has none, and
528
+ // a site may declare the question optional through its own config.
529
+ //
530
+ // NEVER on the public read: `projectListingPublicSchema` omits it beside
531
+ // `founderEmail`. The address on this row is a legal control a visitor is
532
+ // owed; a phone number is a contact detail nobody outside the operator is.
533
+ founderPhone String? @map("founder_phone")
534
+ // #5407 — sha-256 of the EMAIL VERIFICATION NONCE, never the nonce itself.
535
+ //
536
+ // A second credential beside `resumeTokenHash`, and the reason
537
+ // `emailVerifiedAt` below means what its name says. The resume token is
538
+ // returned to WHOEVER POSTED `start`'s FORM, so presenting it proves only
539
+ // that you opened the draft. This nonce is minted at `start`, digested
540
+ // here, and handed to nothing except the resume-email builder
541
+ // (`services/project-listing-resume-email.ts`): it is never in a response
542
+ // body, never in a log, and never in the browser that filled the form. The
543
+ // only way to hold it is to have read mail sent to `founder_email`.
544
+ //
545
+ // Null on every row written before #5407, and on every row whose site has
546
+ // not switched the resume email on — there the nonce exists and is simply
547
+ // never delivered, so nothing can verify, which is the correct outcome.
548
+ // Cleared at `submit` beside `resumeTokenHash`, for the same reason: a
549
+ // credential that leaks after the draft stops being a draft is inert twice
550
+ // over.
551
+ emailVerifyNonceHash String? @unique @map("email_verify_nonce_hash")
552
+ // #5407 — when the founder proved control of `founder_email`, by opening
553
+ // the link mailed to it.
554
+ //
555
+ // WRITE-ONCE. `verifyProjectListingDraftEmail` sets it only where it is
556
+ // still null, so a founder who opens the link five times is stamped with
557
+ // the FIRST opening rather than the latest.
558
+ //
559
+ // WHAT IT PROVES, and its exact limit. The stamp requires the nonce above,
560
+ // which left this server only inside the email. So it says the holder can
561
+ // read mail sent to `founder_email` — which is the whole of what "email
562
+ // verified" claims. It says nothing about WHO they are: `owner_user_id`
563
+ // (#5295) is the column that carries an identity claim, and it still needs
564
+ // a signed-in user whose own address the auth system verified. A row can
565
+ // carry either, both, or neither.
566
+ //
567
+ // Permanently nullable, and NOT a backfill target. Every row written before
568
+ // #5407 predates the stamp; asserting verification on one would assert
569
+ // exactly the thing nobody proved. Null means "not verified", never
570
+ // "verification failed", and it is also what a site running no resume email
571
+ // reads forever.
572
+ emailVerifiedAt DateTime? @map("email_verified_at")
506
573
  founderCompany String? @map("founder_company")
507
574
  status String
508
575
  // #5036 D1/D6 — when the founder ticked step one's consent box. Never null:
@@ -0,0 +1,123 @@
1
+ -- The founder's structured identity and PROVEN email address on
2
+ -- `project_listings` (#5407).
3
+ --
4
+ -- Step one of the application collected ONE `founder_name` line and an email.
5
+ -- samhenry spec 316 D1 asks for first name, last name, email and phone, with
6
+ -- the two name parts stored separately, and 316 D6b asks that an application
7
+ -- whose emailed resume link was opened be marked verified so the operator can
8
+ -- filter on it at review.
9
+ --
10
+ -- FIVE NULLABLE COLUMNS, NO BACKFILL, NO CHANGE TO ANY EXISTING ONE.
11
+ --
12
+ -- `founder_first_name` / `founder_last_name` sit BESIDE `founder_name`, not
13
+ -- instead of it — the #5279 precedent that put six address parts beside the
14
+ -- single-line `address`. `founder_name` stays NOT NULL and stays the DISPLAY
15
+ -- string every existing reader already reads (the submit-time slug fallback,
16
+ -- the approve/reject email's greeting, the Slack review card's Founder row,
17
+ -- the build issue, the `routeLead` identity); where the parts are written the
18
+ -- server COMPOSES it from them, and where they are not it is the line the
19
+ -- founder typed, verbatim.
20
+ --
21
+ -- Composition runs ONE WAY ONLY, and that is why nothing here backfills.
22
+ -- "First" + "Last" composes into a display line deterministically; a display
23
+ -- line does not decompose into parts. Splitting on the last space mangles
24
+ -- "van der Berg", "Maria del Carmen" and every mononym, and a wrong split is
25
+ -- silent — it would put an invented surname on the row the approve email
26
+ -- greets and the operator reviews, with nothing to mark it as invented. A
27
+ -- null part is honest; a guessed one is not. Every existing row therefore
28
+ -- keeps `founder_name` byte-for-byte and keeps both parts NULL for the life
29
+ -- of the row.
30
+ --
31
+ -- `founder_phone` is the contact detail spec 316 D1 adds. Nullable at rest
32
+ -- for the #5036 D1 reason — every field the submit payload requires must be
33
+ -- storable half-filled — and nullable permanently besides: a row written
34
+ -- before #5407 has none, and a site may declare the question optional through
35
+ -- its own listing config. It NEVER rides the public read; the public row
36
+ -- shape omits it beside `founder_email`.
37
+ --
38
+ -- `email_verify_nonce_hash` and `email_verified_at` are the verification pair,
39
+ -- and the first is what makes the second mean what it says.
40
+ --
41
+ -- The obvious design is to stamp verification when the emailed resume link is
42
+ -- opened, keyed on the resume token that link carries. That design does not
43
+ -- work, and the reason is worth stating here because the column name does not
44
+ -- carry it: `start` returns the resume token to WHOEVER POSTED THE FORM, in
45
+ -- its own 201 body. The emailed link is a second copy of a credential the
46
+ -- applicant already holds — so two HTTP calls (open a draft under a
47
+ -- stranger's address, take the token out of the response, present it back)
48
+ -- would forge the stamp with no mailbox involved. Caught by this PR's
49
+ -- security review before it shipped.
50
+ --
51
+ -- So verification requires a SECOND credential the applicant never receives
52
+ -- any other way. `email_verify_nonce_hash` holds the sha-256 digest of a
53
+ -- 256-bit nonce minted at `start`, handed to nothing except the resume-email
54
+ -- builder (apps/api/src/services/project-listing-resume-email.ts), and
55
+ -- present in no response body and no log line. It reaches exactly one place:
56
+ -- the `?verify=` parameter of the link inside the email. Holding it means
57
+ -- having read mail sent to `founder_email`, which is what the stamp claims.
58
+ --
59
+ -- `email_verified_at` is therefore genuine evidence of address control, and
60
+ -- is still WRITE-ONCE: the verify call sets it only where it is still NULL,
61
+ -- by compare-and-swap, so a founder who opens the link five times is stamped
62
+ -- with the first opening and two concurrent openings produce one stamp
63
+ -- between them.
64
+ --
65
+ -- WHAT IT STILL DOES NOT SAY is who the founder is. `owner_user_id`
66
+ -- (20260912120000_listing_owner_link) is the identity claim and needs a
67
+ -- signed-in user whose own address the auth system verified. A row can carry
68
+ -- either, both, or neither.
69
+ --
70
+ -- Neither is a backfill target. Stamping an existing row would assert exactly
71
+ -- the event nobody recorded. NULL means "not verified", never "verification
72
+ -- failed" — and it is also what a site that has not switched the resume email
73
+ -- on will read forever, because the nonce is minted there and simply never
74
+ -- delivered. That is the correct outcome, not a gap.
75
+ --
76
+ -- `email_verify_nonce_hash` is UNIQUE, mirroring `resume_token_hash`: two
77
+ -- rows can never share a nonce, and the digest lookup is indexed. It is
78
+ -- cleared at `submit` beside the resume-token digest, so a credential that
79
+ -- leaks after the draft stops being a draft is inert twice over. It has NO
80
+ -- EXPIRY otherwise, by design and for the reason the service records: the
81
+ -- same message carries the resume token, so a TTL on the nonce alone would
82
+ -- bound nothing.
83
+ --
84
+ -- OPERATIONAL NOTE on the index. `CREATE UNIQUE INDEX` below is not
85
+ -- `CONCURRENTLY`, so it takes a SHARE lock and blocks writes to
86
+ -- `project_listings` while it builds. Every existing row is NULL there and
87
+ -- Postgres treats NULLs as distinct in a unique index (no NULLS NOT
88
+ -- DISTINCT), so it cannot fail on existing data and builds fast — and the
89
+ -- same property is why `submit` setting many rows back to NULL never
90
+ -- collides. Stated so whoever runs this against a large table knows it is
91
+ -- write-blocking DDL; it matches the `resume_token_hash` precedent.
92
+ --
93
+ -- Distinct from `owner_user_id` (20260912120000_listing_owner_link), which
94
+ -- needs a SIGNED-IN user whose own address the auth system verified. This
95
+ -- column needs no account at all: it records that a capability sent to
96
+ -- `founder_email` came back. A row can carry either, both, or neither.
97
+ --
98
+ -- NO INDEX on the other four. The verify write is keyed by the row's primary
99
+ -- key plus two digests, both already UNIQUE, and the operator filters
100
+ -- verified applications inside a site's already-indexed page of listings
101
+ -- (`project_listings_site_id_status_idx`) rather than by scanning on
102
+ -- `email_verified_at`.
103
+ --
104
+ -- Forward-additive only: all five columns are nullable with no default, so
105
+ -- every existing read and write is unaffected.
106
+ --
107
+ -- Rollback:
108
+ -- DROP INDEX "project_listings_email_verify_nonce_hash_key";
109
+ -- ALTER TABLE "project_listings" DROP COLUMN "email_verified_at";
110
+ -- ALTER TABLE "project_listings" DROP COLUMN "email_verify_nonce_hash";
111
+ -- ALTER TABLE "project_listings" DROP COLUMN "founder_phone";
112
+ -- ALTER TABLE "project_listings" DROP COLUMN "founder_last_name";
113
+ -- ALTER TABLE "project_listings" DROP COLUMN "founder_first_name";
114
+
115
+ -- AlterTable
116
+ ALTER TABLE "project_listings" ADD COLUMN "founder_first_name" TEXT,
117
+ ADD COLUMN "founder_last_name" TEXT,
118
+ ADD COLUMN "founder_phone" TEXT,
119
+ ADD COLUMN "email_verify_nonce_hash" TEXT,
120
+ ADD COLUMN "email_verified_at" TIMESTAMP(3);
121
+
122
+ -- CreateIndex
123
+ CREATE UNIQUE INDEX "project_listings_email_verify_nonce_hash_key" ON "project_listings"("email_verify_nonce_hash");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@working-theory/database",
3
- "version": "0.20.0",
3
+ "version": "0.23.0",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -60,7 +60,7 @@
60
60
  "yaml": "^2.9.0",
61
61
  "zod": "^4.2.1",
62
62
  "zod-to-ts": "^2.1.0",
63
- "@working-theory/cli": "^0.20.0"
63
+ "@working-theory/cli": "^0.23.0"
64
64
  },
65
65
  "lint-staged": {
66
66
  "*.{ts,tsx}": [