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
@@ -252,6 +252,437 @@ Print:
252
252
  - Pointer at `scaffold-page` (#362) for wiring pricing UI.
253
253
  - Reminder: sandbox-only at v1. Live keys are hook-blocked globally.
254
254
 
255
+ ### Step 10 — Surface-selection branch (`feature:commerce` only, FEAT-023 U1)
256
+
257
+ This step is **additive** — it runs after Step 9's closing summary and
258
+ never changes anything above it. A site without the `commerce`
259
+ entitlement gets exactly today's flow (Steps 1–9) and stops there.
260
+
261
+ **10a — Fail-closed entitlement check.** Determine whether this site is
262
+ entitled to `feature:commerce` (the license-manifest feature added in
263
+ FEAT-023 U1 — `commerce` is included in the `pro` / `pro_plus` /
264
+ `enterprise` tiers, absent from `free`):
265
+
266
+ - If the consumer's project has a resolvable license — a
267
+ `WT_LICENSE_KEY` (or equivalent) env var resolving to a
268
+ `@working-theory/license` `License` — call
269
+ `hasFeature("commerce", license)` (`@working-theory/license-client`,
270
+ static-mode: sync, no network). `true` → entitled; proceed to 10b.
271
+ - **Any other case is NOT entitled — fail closed:** no license
272
+ configured, the license lacks `commerce`, or `hasFeature` throws.
273
+ Do nothing further. The consumer already has the full link path +
274
+ product-creation flow from Steps 1–9; there is no additional
275
+ action or message needed.
276
+
277
+ Do not attempt to determine entitlement by asking the consumer their
278
+ plan conversationally — the check is against the resolved license, not
279
+ a verbal claim.
280
+
281
+ **10b — Offer the surface-selection branch.** For an entitled site,
282
+ ask:
283
+
284
+ > "Want to scaffold a payment surface for these products? Pick one (or
285
+ > 'skip' for now):
286
+ >
287
+ > - **Checkout** — a hosted or embedded checkout flow (one-time or
288
+ > subscription).
289
+ > - **Pay-by-link** — a shareable payment link, no page to build.
290
+ > - **Invoice** — send + track invoices with a status badge.
291
+ > - **Subscription** — recurring billing with a status badge.
292
+ > - **Portal** — a self-serve billing-management page for customers."
293
+
294
+ **10c — Re-run the live-key check.** Before dispatching to any surface
295
+ scaffolder, re-run Step 1's live-key check (env scan + paste
296
+ detection) — belt-and-suspenders, since surface scaffolding may run in
297
+ a session well after the original product-creation pass.
298
+
299
+ **10d — Instantiate the shared config.** Copy
300
+ `templates/stripe-config.template.ts` into the consumer's `apps/api`
301
+ (or the surface's own route directory) as the shared
302
+ `loadStripeSurfaceConfig()` the surface's route handler calls at
303
+ request time. This carries the test-mode-only guard
304
+ (`assertSandboxKey`, `@working-theory/stripe-client`) into the new
305
+ surface entry point — the same belt-and-suspenders guard
306
+ `getStripeClient()` already applies, so a live key is refused here too,
307
+ not only at the shared Stripe client singleton.
308
+
309
+ **10e — Dispatch to the matching surface scaffolder.** **Checkout
310
+ (one-time)** (10e-i, FEAT-023 U2), **Pay by link** (10e-ii, FEAT-023 U3),
311
+ **Invoice** (10g, FEAT-023 U4), **Subscription** (10h, FEAT-023 U5), and
312
+ **Portal** (10i, FEAT-023 U6) are all available inline. If a future choice's
313
+ matching skill is not installed, say so plainly:
314
+
315
+ > "Payment-surface scaffolding for `<choice>` isn't available in this
316
+ > AILK version yet — the foundation (entitlement gate + shared
317
+ > config + UI primitives) is in place; the surface itself ships in a
318
+ > later update."
319
+
320
+ Do not attempt to hand-build the surface yourself when its scaffolder
321
+ is missing — that would bypass the recomposition method (§8: capability
322
+ checklist → `packages/ui` primitives, never a verbatim UI lift) every
323
+ FEAT-023 UI surface must follow.
324
+
325
+ **10e-i — Checkout (one-time), FEAT-023 U2.** When the consumer picks
326
+ **Checkout** and wants a one-time payment (not a subscription — v1 only
327
+ scaffolds one-time; acknowledge and stop if the consumer wants
328
+ recurring billing, that's a future `mode: "subscription"` branch):
329
+
330
+ 1. **Pick the product(s).** Offer the `lookup_key`s already created this
331
+ session (Step 3/5) as the checkout's product choices. If none were
332
+ created yet, run Steps 2–5 first (or ask for at least one product).
333
+ 2. **Instantiate the route.** Copy
334
+ `templates/checkout-route.template.ts` next to the already-instantiated
335
+ `stripe-config.ts` (Step 10d) in the consumer's `apps/api/src/routes/checkout/`
336
+ (checkout creation is server-side only — `apps/web` may not import
337
+ `@working-theory/stripe-client`, architecture.yaml). Register its exported
338
+ `oneTimeCheckoutHandler` on a `POST` route, e.g.
339
+ `POST /api/checkout/one-time`, SCOPED BEHIND the shared per-IP rate-limit
340
+ middleware (`apps/api/src/middleware/rate-limit.ts`, already present in
341
+ every consumer's `apps/api` — the same one `server.ts` wires around the
342
+ anonymous "leads" routes). This is a public, unauthenticated, single-purpose
343
+ session-creation endpoint by design (see the template's own header comment)
344
+ — the rate limit is its abuse guard against unbounded session-creation /
345
+ card-testing attempts; do not register it unscoped. Ask the consumer to
346
+ confirm the route path if their `apps/api` layout differs from the default.
347
+
348
+ **Two edits are MANDATORY as you instantiate it. The route is fail-closed
349
+ until both are done — every request 404s or 500s. This is deliberate: a
350
+ checkout route that is wrong in either respect is exploitable by an
351
+ anonymous internet caller.**
352
+
353
+ **(a) Pin the purchasable-product allowlist.** Replace the placeholder in
354
+ the route's `ALLOWED_LOOKUP_KEYS` with EXACTLY the `lookup_key`s the
355
+ consumer selected at step 1 — nothing else:
356
+
357
+ ```ts
358
+ const ALLOWED_LOOKUP_KEYS: ReadonlySet<string> = new Set<string>([
359
+ "pizza_onetime", // ← the step-1 selection, verbatim
360
+ "combo_onetime",
361
+ ]);
362
+ ```
363
+
364
+ Why this is not optional: without it, the route resolves ANY
365
+ caller-supplied `lookupKey` against the merchant's ENTIRE Stripe account.
366
+ A merchant with a public `pizza_onetime` ($15) and a private
367
+ `enterprise_annual_90pct_off` in the same account is one guessed lookup
368
+ key away from selling the discounted price to an anonymous `curl`; any
369
+ `$0` comp/test price becomes a free-fulfillment endpoint. Pin the
370
+ products this surface sells — never the account. Do not offer to
371
+ "allow all keys", and do not read the allowlist from an env var or the
372
+ request: it is a source-pinned constant.
373
+
374
+ **(b) Set `FRONTEND_URL`.** The route locks every `successUrl` /
375
+ `cancelUrl` to the site's own origin, read from `FRONTEND_URL` (the same
376
+ env var the consumer's `apps/api` already uses for CORS —
377
+ `apps/api/src/config/index.ts`). Confirm it is set for each environment
378
+ (dev / preview / prod). If it is unset the route returns
379
+ `500 config-error` and refuses every checkout — fail-closed by design.
380
+ Without the origin lock, an anonymous caller can mint a REAL
381
+ `checkout.stripe.com` session for the real merchant whose success page is
382
+ an attacker's — the victim pays on Stripe's real domain and lands on the
383
+ attacker's "receipt".
384
+
385
+ 3. **Compose the checkout page.** In the consumer's `apps/web`, create a
386
+ page that renders `CheckoutForm` (`@working-theory/ui`) with the
387
+ session's products as `CheckoutProduct[]` (`lookupKey` + `name` + price).
388
+ Wire `onCheckout` to POST to the Step-2 route with `{ lookupKey,
389
+ successUrl, cancelUrl }` and, on a `201` response, set
390
+ `window.location.href` to the returned `url`. Pass `testMode` (always
391
+ `true` at v1 — sandbox-only). Ask which page path to use (e.g.
392
+ `app/checkout/page.tsx`), mirroring the link path's "ask which"
393
+ convention. Send the redirects as RELATIVE PATHS (`/checkout/success`,
394
+ `/checkout/cancel`): the route composes them onto the site's own origin
395
+ server-side. A cross-origin absolute URL is refused with
396
+ `400 invalid-redirect-url`.
397
+ 4. **Compose the success/cancel pages.** Create two pages (e.g.
398
+ `app/checkout/success/page.tsx`, `app/checkout/cancel/page.tsx`) at the
399
+ `successUrl`/`cancelUrl` the checkout page passed, each rendering
400
+ `PaymentResult` (`@working-theory/ui`) with `outcome="success"` /
401
+ `outcome="cancel"` respectively and the consumer's own copy.
402
+
403
+ **Tell the consumer, in these words: the success page is a DISPLAY-ONLY
404
+ terminal state; it is NEVER proof of payment.** Anyone can navigate
405
+ straight to `/checkout/success` and be shown "Payment successful". So
406
+ never hang fulfillment, an entitlement grant, a license issue, or a file
407
+ download off that page. Real fulfillment must be driven by a verified
408
+ Stripe webhook (signature-checked) or a server-side
409
+ `stripe.checkout.sessions.retrieve(id)` whose `payment_status === "paid"`
410
+ — neither of which this v1 surface scaffolds. If the consumer asks for
411
+ fulfillment, that is the next unit of work, not a tweak to this page.
412
+
413
+ 5. **Surface the session id.** After the flow is wired, remind the
414
+ consumer that each created Checkout Session's id is logged (truncated,
415
+ no PII) by the route for verification in
416
+ <https://dashboard.stripe.com/test/payments>.
417
+ 6. **Closing note.** Confirm the surface is reachable only because
418
+ `feature:commerce` resolved entitled (10a) — a non-entitled site never
419
+ sees this branch. Note that this entitlement check is SCAFFOLD-TIME only:
420
+ the emitted route reads no license at runtime, so a lapsed
421
+ `feature:commerce` does not switch the endpoint off.
422
+
423
+ Remind the consumer this is test-mode only. Be precise about what the
424
+ live-key guard actually covers: `assertSandboxKey` (10d's
425
+ `loadStripeSurfaceConfig`, and the shared Stripe client singleton) is an
426
+ ALLOW-LIST as of AILK#3734 — it accepts only `sk_test_`, `pk_test_` and
427
+ `rk_test_`, and refuses everything else. A Stripe **restricted** live key
428
+ (`rk_live_…`) — the key type Stripe steers server-side integrations toward
429
+ — IS refused, along with any live prefix Stripe introduces later, because
430
+ it is absent from the allow-list rather than named on a ban-list.
431
+ So: "only recognized test-mode keys are accepted", not "these particular
432
+ live prefixes are blocked". Tell the consumer to use a test-mode key and to
433
+ keep live keys out of the environment entirely anyway — a working guard is
434
+ not a reason to point a live key at a non-production deployment.
435
+
436
+ Finally, confirm all three abuse guards are actually in place: the
437
+ rate-limit middleware is wired (step 2 — an unauthenticated checkout route
438
+ without it is a card-testing vector), `ALLOWED_LOOKUP_KEYS` is pinned to
439
+ the real selection (step 2a), and `FRONTEND_URL` is set for every
440
+ environment (step 2b).
441
+
442
+ **10e-ii — Pay-by-link (payment-link management), FEAT-023 U3.** When
443
+ the consumer picks **Pay by link**, instantiate
444
+ `templates/payment-link-route.template.ts` next to the already-instantiated
445
+ `stripe-config.ts` (Step 10d) in the consumer's
446
+ `apps/api/src/routes/payment-links/` — server-side only, same reason as
447
+ checkout (`apps/web` may not import `@working-theory/stripe-client`,
448
+ architecture.yaml).
449
+
450
+ This is a **MANAGEMENT** surface (list / create / archive payment objects),
451
+ NOT a storefront "buy" action — so its auth posture is the OPPOSITE of the
452
+ checkout route's. Register its three exported handlers INSIDE the
453
+ authenticated scope, so `authPreHandlerPlugin` decorates `request.userId`
454
+ (and applies its same-origin CSRF check) before any handler runs:
455
+
456
+ ```ts
457
+ await fastify.register(async (authed) => {
458
+ authed.register(authPreHandlerPlugin, { frontendOrigin: config.frontendUrl });
459
+ authed.get("/api/payment-links", listPaymentLinksHandler);
460
+ authed.post("/api/payment-links", createPaymentLinkHandler);
461
+ authed.patch("/api/payment-links", updatePaymentLinkHandler);
462
+ });
463
+ ```
464
+
465
+ The registration is **load-bearing** — do not copy the checkout route's
466
+ public, rate-limited registration here:
467
+
468
+ 1. **Authentication + authorization.** Each handler calls `requireSiteAdmin()`
469
+ FIRST — before the entitlement check and before any Stripe call. It
470
+ re-reads `User.role` from the database and admits only `admin` / `owner`.
471
+ Authentication alone is not the bar: on a site with open signup, "any
472
+ logged-in user" is "the whole internet with an extra step".
473
+ 2. **Entitlement is NOT authorization.** The `feature:commerce` check answers
474
+ "does this DEPLOYMENT have commerce?", never "may this CALLER manage
475
+ payment links?" — it reads the site's env license and returns the identical
476
+ answer to an anonymous caller. It is retained, layered UNDER the identity
477
+ gate, never as a substitute for it. (Shipping this surface with ONLY the
478
+ entitlement gate was AILK#3744: a P0 that let an anonymous caller list every
479
+ payment link, archive them all — total denial of revenue — and mint new ones
480
+ against the merchant's Stripe account.)
481
+ 3. **CSRF** is inherited from the auth scope's same-origin `Origin` check
482
+ (`apps/api/src/middleware/auth.ts`), which exists ONLY inside that scope.
483
+ 4. **Fail-closed by construction.** Mis-register the routes outside the auth
484
+ scope and `request.userId` is `undefined`, so every handler answers 401 — a
485
+ broken feature, never an open door.
486
+ 5. **Tenancy.** Deliberately NOT `tenantScope`. This surface manages the SITE
487
+ OWNER's single Stripe catalog (`packages/stripe-client` is single-tenant; a
488
+ Stripe Payment Link carries no org binding). Cross-account access is
489
+ structurally impossible — Stripe object ids are account-scoped, so a foreign
490
+ id returns `resource_missing`, which the route maps to the same 404 as an
491
+ unknown id (no enumeration oracle). The multi-tenant module's org-scoped
492
+ billing is a SEPARATE surface (`apps/api/src/routes/billing/`), which DOES
493
+ derive its org from `request.tenant.organizationId`.
494
+
495
+ Test-mode-only caveats are identical to checkout's (step 10e-i) — state the
496
+ `assertSandboxKey` allow-list (AILK#3734): only recognized test-mode prefixes
497
+ are accepted, so every live key is refused by construction.
498
+
499
+ **10f — Shared UI primitives.** Every surface scaffolder composes from
500
+ `@working-theory/ui`'s `MoneyAmount` (primitive), `PaymentStatusBadge`
501
+ (block), and `StripeActionButton` (block) — built once in FEAT-023 U1
502
+ so no surface hand-rolls its own amount formatting, status chip, or
503
+ checkout/portal button.
504
+
505
+ **10g — Invoice surface, shipped (FEAT-023 U4).** When the consumer picks
506
+ "Invoice" at 10b, this surface IS available (unlike the still-unshipped
507
+ choices in 10e). Invoice management is an ADMIN action, never a customer-
508
+ facing or anonymous one (a security review of the sibling U3 pay-by-link
509
+ template found the entitlement check alone is NOT authentication — see
510
+ `docs/research/commerce-template-audit/invoices.md` finding 7):
511
+
512
+ 1. Instantiate `templates/invoice-route.template.ts` into the consumer's
513
+ `apps/api` — mirrors 10d's `stripe-config.template.ts` instantiation. It
514
+ exports a Fastify plugin (`invoiceRoutes`: `GET` list/retrieve, `POST`
515
+ create-draft, `POST .../send` finalize+send) built on
516
+ `@working-theory/stripe-client`'s `createInvoice` / `sendInvoice` /
517
+ `listInvoices` / `retrieveInvoice`.
518
+ 2. **Register it inside the TENANT-GUARDED scope `apps/api`'s
519
+ `billingRoutes` use** — the inner `tenantScope` in `server.ts` that
520
+ registers `tenantPreHandlerPlugin` (itself nested inside `authScope`) —
521
+ e.g. `await tenantScope.register(invoiceRoutes);`. **NOT** the outer
522
+ `authScope`. This is REQUIRED, not optional, and it is a SECURITY
523
+ boundary, not a style preference:
524
+ - `authPreHandlerPlugin` (inherited from the enclosing `authScope`) gives
525
+ every handler an authenticated caller plus the cookie path's same-origin
526
+ CSRF check. The template then requires `User.role` in {`admin`,`owner`},
527
+ so anonymous and plain-authenticated callers are refused.
528
+ - `tenantPreHandlerPlugin` (default-deny) populates `request.tenant`, so
529
+ the org is **server-derived** and never client-supplied — the same
530
+ property `routes/billing/read.ts` relies on. Registering in `authScope`
531
+ instead leaves `request.tenant` undefined, and the template then fails
532
+ **closed** (403) rather than serving an unscoped admin surface.
533
+ Tell the consumer explicitly that this route is admin-only, and that a
534
+ Bearer-token caller is denied (a bearer token carries no workspace
535
+ binding) — this is a cookie-session admin-UI surface.
536
+ 3. Point the consumer at `@working-theory/ui`'s `InvoiceList` (block),
537
+ `InvoiceDetail` (panel), and `InvoiceForm` (block) to compose the
538
+ admin-facing management page — `InvoiceForm` posts directly to the
539
+ instantiated route; `InvoiceList`/`InvoiceDetail` render already-fetched
540
+ invoice records (reusing `PaymentStatusBadge` for status and
541
+ `MoneyAmount` for amounts, per 10f).
542
+ 4. Re-run 10c's live-key check before instantiating (already covered by
543
+ this step's ordering, restated for clarity). The guard has no remaining
544
+ gap: AILK#3734 converted `assertSandboxKey` to an allow-list, so a Stripe
545
+ RESTRICTED key is refused like every other non-test-mode key. Finding 10
546
+ in the audit log records the historical deny-list gap and is marked
547
+ resolved there.
548
+ 5. **Multi-tenant module — the surface REFUSES, it does not warn.** v1 has
549
+ ONE deployment-wide `STRIPE_SECRET_KEY` and no per-organization Stripe
550
+ customer namespace, so an invoice carries no org attribution and a
551
+ caller-supplied `invoiceId`/`customerId` cannot be honestly checked
552
+ against the caller's org (findings 7 + 9 in the audit log). The template's
553
+ `requireSingleTenantScope` guard therefore admits **only the deployment's
554
+ own operator org** — the fixed single-tenant sentinel `DEFAULT_ORG_ID`,
555
+ which an organization created through the multi-tenant module never
556
+ carries — and **hard-refuses every other org's admin with a 403
557
+ `tenancy_unsupported`**, before any Stripe call. That is what stops one
558
+ tenant's admin reading another tenant's customer PII (names, emails,
559
+ amounts, line items) out of the shared Stripe account. Tell the consumer
560
+ plainly: **invoice management is an OPERATOR surface at v1, not a
561
+ per-tenant self-service one.** The operator can still see every invoice in
562
+ the deployment-wide Stripe account — they hold the `STRIPE_SECRET_KEY` and
563
+ can read the same data from the Stripe dashboard, so this is not an
564
+ escalation — but per-tenant invoicing needs a per-org Stripe Connect /
565
+ customer-namespace model (tracked follow-up).
566
+
567
+ **`User.role` is DEPLOYMENT-OPERATOR-ONLY, and must not be widened to reach
568
+ this surface.** The `requireSiteAdmin` gate keys on `User.role` — a
569
+ per-deployment String (`"user" | "admin" | "owner"`), DISTINCT from the
570
+ org-membership `Role` enum the multi-tenant module uses. Do not grant
571
+ `User.role = "admin"` to an organization's admins as a way of giving them
572
+ invoice access: that is a site-wide operator grant, not an org-scoped one,
573
+ and until per-org Stripe namespacing exists it would turn the tenancy
574
+ refusal above into a cross-tenant PII leak (names, emails, amounts, line
575
+ items). The correct answer to "our org admins need invoices" at v1 is that
576
+ the surface does not support it yet — not a role change.
577
+
578
+ **10h — Subscription surface, shipped (FEAT-023 U5).** When the consumer picks
579
+ "Subscription" at 10b, this surface IS available. It splits into TWO handlers
580
+ with DELIBERATELY OPPOSITE auth postures — the same split checkout (public)
581
+ vs invoice (admin) already established:
582
+
583
+ 1. Instantiate `templates/subscription-route.template.ts` into the consumer's
584
+ `apps/api` — mirrors 10d's `stripe-config.template.ts` instantiation. It
585
+ exports `subscribeHandler` (a standalone Fastify handler) and
586
+ `subscriptionRoutes` (a Fastify plugin), built on
587
+ `@working-theory/stripe-client`'s `createCheckoutSession` (`mode:
588
+ "subscription"`, pinned) and `retrieveSubscriptionStatus`.
589
+ 2. **Register `subscribeHandler` PUBLICLY, rate-limited — mirrors 10e-i's
590
+ checkout registration exactly.** It is a storefront "subscribe" action
591
+ (the recurring-price analog of the one-time checkout route): pin
592
+ `ALLOWED_LOOKUP_KEYS` to the RECURRING lookup_key(s) the consumer selected
593
+ (step 1's selection, verbatim — never "allow all"), confirm `FRONTEND_URL`
594
+ is set (the same origin-lock 10e-i requires), and scope it behind the
595
+ shared per-IP rate-limit middleware. Both edits are MANDATORY — the route
596
+ is fail-closed until they're done.
597
+ 3. **Register `subscriptionRoutes` inside the SAME tenant-guarded scope
598
+ 10g's `invoiceRoutes` uses** — e.g. `await tenantScope.register(subscriptionRoutes);`.
599
+ NOT the outer `authScope`, and NOT the same public scope as
600
+ `subscribeHandler`. This is a READ of a specific subscription's status —
601
+ a MANAGEMENT-shaped action, so it inherits 10g's admin + tenancy guards
602
+ verbatim (`requireSiteAdmin` + `requireSingleTenantScope`, the fixed
603
+ single-tenant sentinel `DEFAULT_ORG_ID`). See the template's own header
604
+ comment for the full registration snippet.
605
+ 4. Point the consumer at `@working-theory/ui`'s `PricingTable` (section) and
606
+ `SubscriptionStatus` (block) to compose the subscribe + status pages —
607
+ `PricingTable`'s `onSubscribe` POSTs to step 2's route + redirects to the
608
+ returned Stripe Checkout URL (mirrors `CheckoutForm`'s `onCheckout`
609
+ shape); `SubscriptionStatus` renders an already-fetched status (step 3's
610
+ route) via `PaymentStatusBadge` (reused from U1) plus a portal "Manage"
611
+ action pointing at the U6 billing-portal surface — this surface never
612
+ builds self-service plan-change/cancel itself.
613
+ 5. Re-run 10c's live-key check before instantiating. State the CURRENT
614
+ behaviour to the consumer: `assertSandboxKey` is an ALLOW-LIST as of
615
+ AILK#3734 — only `sk_test_`, `pk_test_` and `rk_test_` are accepted, so
616
+ every live key including a restricted one is refused. No surface header
617
+ carries the pre-#3734 deny-list caveat any more (AILK#3765 retired the
618
+ last of them); if you find one, it is stale — fix it rather than repeat it.
619
+ 6. **Multi-tenant module — same refusal as invoices, same reason.** v1 has
620
+ ONE deployment-wide `STRIPE_SECRET_KEY` and no per-organization Stripe
621
+ customer namespace, so a subscription carries no org attribution.
622
+ `subscriptionRoutes`'s `requireSingleTenantScope` guard therefore admits
623
+ ONLY the deployment's own operator org and hard-refuses every other org's
624
+ admin with a 403 `tenancy_unsupported`, before any Stripe call. Tell the
625
+ consumer plainly: subscription-status reading is an OPERATOR surface at
626
+ v1, not a per-tenant self-service one — the same disclosed limitation as
627
+ 10g's invoice surface (see
628
+ `docs/research/commerce-template-audit/subscriptions.md`).
629
+ 7. **No subscription webhook is scaffolded at v1** — the status read is
630
+ retrieve-on-view by the subscription's own Stripe id (mirrors 10g's
631
+ `?invoiceId=` shape with `?subscriptionId=`), not webhook-driven. If a
632
+ future need requires webhook-driven status (e.g. proactive
633
+ past-due notices), that is a new unit of work, not a tweak to this
634
+ surface.
635
+
636
+ **10i — Portal surface, shipped (FEAT-023 U6).** When the consumer picks
637
+ "Portal" at 10b, this surface IS available. It is the smallest FEAT-023
638
+ surface (spec §1) — AILK owns only the entry, Stripe hosts the portal
639
+ itself:
640
+
641
+ 1. Instantiate `templates/portal-route.template.ts` into the consumer's
642
+ `apps/api` — mirrors 10d's `stripe-config.template.ts` instantiation. It
643
+ exports `portalRoutes` (a Fastify plugin: a single `POST
644
+ /v1/commerce/portal-session`), built on
645
+ `@working-theory/stripe-client`'s `createBillingPortalSession`.
646
+ 2. **Register `portalRoutes` inside the SAME tenant-guarded scope 10g's
647
+ `invoiceRoutes` uses** — e.g. `await tenantScope.register(portalRoutes);`.
648
+ NOT the outer `authScope`. Starting a billing-portal session hands the
649
+ caller a live link into a customer's full billing surface, so — like
650
+ 10h's `subscriptionRoutes` status read — this is a MANAGEMENT-shaped
651
+ action, never public/anonymous: it inherits 10g's admin + tenancy guards
652
+ verbatim (`requireSiteAdmin` + `requireSingleTenantScope`, the fixed
653
+ single-tenant sentinel `DEFAULT_ORG_ID`). See the template's own header
654
+ comment for the full registration snippet.
655
+ 3. Point the consumer at `@working-theory/ui`'s `PortalEntry` (block) to
656
+ compose a standalone billing-management entry point, AND wire it as the
657
+ target of 10h's `SubscriptionStatus` `manageAction` (the U5↔U6 seam,
658
+ spec § design decision) — both `PortalEntry`'s `onManage` and
659
+ `SubscriptionStatus`'s `manageAction.onManage` POST to step 1's route
660
+ with `{ customerId }` and, on a `200` response, set
661
+ `window.location.href` to the returned `url`. Pass `testMode` (always
662
+ `true` at v1 — sandbox-only).
663
+ 4. Re-run 10c's live-key check before instantiating. Note the guard's
664
+ current state (AILK#3734, now an allow-list) — restate the CURRENT state
665
+ to the consumer, same as 10h.
666
+ 5. **Multi-tenant module — same refusal as invoices/subscriptions, same
667
+ reason.** v1 has ONE deployment-wide `STRIPE_SECRET_KEY` and no
668
+ per-organization Stripe customer namespace, so a `customerId` carries no
669
+ org attribution. `portalRoutes`'s `requireSingleTenantScope` guard
670
+ therefore admits ONLY the deployment's own operator org and hard-refuses
671
+ every other org's admin with a 403 `tenancy_unsupported`, before any
672
+ Stripe call. Tell the consumer plainly: billing-portal sessions are an
673
+ OPERATOR surface at v1, not a per-tenant self-service one — the same
674
+ disclosed limitation as 10g/10h (see
675
+ `docs/research/commerce-template-audit/portal.md`).
676
+ 6. **The return URL is fixed, server-derived, never caller-supplied** — a
677
+ single `PORTAL_RETURN_PATH` composed from `FRONTEND_URL` (the template's
678
+ own `SCAFFOLD-TIME EDIT` if the consumer's account/billing page lives
679
+ elsewhere), mirroring `apps/api/src/routes/billing/portal.ts`'s existing
680
+ convention. There is no caller-chosen destination to validate, unlike
681
+ checkout's `successUrl`/`cancelUrl`.
682
+ 7. **No portal-session webhook is scaffolded at v1** — mint-on-demand by
683
+ the caller-supplied `customerId`, not webhook-driven, mirroring 10h's
684
+ escalation-boundary posture.
685
+
255
686
  ## Output style
256
687
 
257
688
  - **Two questions, two paths.** Don't lead with Stripe assumptions.
@@ -279,6 +710,15 @@ Print:
279
710
  recreates.
280
711
  - Auto-invoke other skills. Always points at the next skill by name;
281
712
  consumer drives.
713
+ - Guess entitlement conversationally. The `feature:commerce`
714
+ surface-selection branch (Step 10) checks the resolved license via
715
+ `hasFeature("commerce", license)` — never a verbal "I'm on Pro"
716
+ claim. No resolvable license → not entitled (fail-closed).
717
+ - Build a payment surface itself for a choice OTHER than checkout
718
+ (U3–U6). This skill's Step 10 checks entitlement, offers the choice,
719
+ and dispatches — the surface scaffolders own the actual page/route.
720
+ Checkout (one-time) is the one exception: Step 10e-i wires it inline
721
+ (FEAT-023 U2).
282
722
 
283
723
  ## References
284
724
 
@@ -291,6 +731,31 @@ Print:
291
731
  - `templates/marketplace-cta-route.tsx.tmpl` — emitted route source
292
732
  the link path writes. Includes the inline `<MarketplaceCTA>`
293
733
  component definition (not a separate package).
734
+ - `templates/stripe-config.template.ts` — shared Stripe surface config
735
+ (env contract + `assertSandboxKey` test-mode guard) every U2–U6
736
+ surface scaffolder instantiates (Step 10d, FEAT-023 U1).
737
+ - `templates/checkout-route.template.ts` — the one-time checkout POST
738
+ route (`mode: "payment"`, `stripe-client/checkout.ts`), instantiated
739
+ by Step 10e-i (FEAT-023 U2).
740
+ - `templates/payment-link-route.template.ts` — the payment-link
741
+ MANAGEMENT route (list / create / archive), instantiated by Step
742
+ 10e-ii (FEAT-023 U3). Authenticated + site-admin only, registered
743
+ inside the auth scope — see 10e-ii; its posture is the opposite of
744
+ the public checkout route's.
745
+ - `templates/invoice-route.template.ts` — the Invoice surface's admin-only
746
+ Fastify plugin (create/send/list/retrieve), instantiated at Step 10g
747
+ (FEAT-023 U4, shipped). Registration inside `apps/api`'s tenant-guarded
748
+ scope is REQUIRED — see the template's own header comment.
749
+ - `templates/subscription-route.template.ts` — the Subscription surface's
750
+ `subscribeHandler` (public, rate-limited) + `subscriptionRoutes` (admin +
751
+ tenant-guarded status read), instantiated at Step 10h (FEAT-023 U5,
752
+ shipped). The two handlers have OPPOSITE auth postures — see the
753
+ template's own header comment for both registration points.
754
+ - `templates/portal-route.template.ts` — the Portal surface's admin +
755
+ tenant-guarded `portalRoutes` (mints a billing-portal session for a
756
+ caller-supplied `customerId`), instantiated at Step 10i (FEAT-023 U6,
757
+ shipped). Registration inside `apps/api`'s tenant-guarded scope is
758
+ REQUIRED — see the template's own header comment.
294
759
 
295
760
  ## Coordination
296
761
 
@@ -307,4 +772,36 @@ Print:
307
772
  - **`@working-theory/stripe-client`** — wrapper for Stripe SDK. The skill does
308
773
  not import this directly (the skill is a prompt/template, not
309
774
  runtime code). The consumer's `apps/api/routes/checkout/sessions.ts`
310
- uses it.
775
+ (generic primitive) and the Step-10e-i-instantiated
776
+ `checkout-route.template.ts` (one-time surface) use it.
777
+ `assertSandboxKey` is reused by `stripe-config.template.ts`.
778
+ - **`@working-theory/license-client`** — `hasFeature("commerce", license)`
779
+ is the entitlement read Step 10a consults (static-mode, sync, no
780
+ network). The skill does not import this directly either — it
781
+ describes the check a consumer's own license-aware tooling performs.
782
+ - **FEAT-023 U2–U6 (checkout · pay-by-link · invoice · subscription ·
783
+ portal)** — the five payment surfaces Step 10e dispatches to. Checkout
784
+ (one-time) shipped in U2 (10e-i); pay-by-link shipped in U3 (10e-ii);
785
+ invoice shipped in U4 (10g); subscription shipped in U5 (10h); portal
786
+ shipped in U6 (10i).
787
+ - **FEAT-023 U4 (invoice)** — shipped (Step 10g). `@working-theory/stripe-client`'s
788
+ `invoice.ts` (create/send/list/retrieve) + `@working-theory/ui`'s
789
+ `InvoiceList`/`InvoiceDetail`/`InvoiceForm` compose the surface; the
790
+ route template lives at `templates/invoice-route.template.ts`.
791
+ - **FEAT-023 U5 (subscription)** — shipped (Step 10h).
792
+ `@working-theory/stripe-client`'s `checkout.ts` (`createCheckoutSession`
793
+ `mode: "subscription"` + `retrieveSubscriptionStatus`) +
794
+ `@working-theory/ui`'s `PricingTable`/`SubscriptionStatus` compose the
795
+ surface; the route template lives at
796
+ `templates/subscription-route.template.ts`.
797
+ - **FEAT-023 U6 (portal)** — shipped (Step 10i). `@working-theory/stripe-client`'s
798
+ `billing-portal.ts` (`createBillingPortalSession`) + `@working-theory/ui`'s
799
+ `PortalEntry` (reuses U1's `StripeActionButton`) compose the surface — the
800
+ target of U5's `SubscriptionStatus` "Manage" action; the route template
801
+ lives at `templates/portal-route.template.ts`.
802
+ - **`@working-theory/ui`'s `MoneyAmount` / `PaymentStatusBadge` /
803
+ `StripeActionButton`** — the shared presentational components every
804
+ U2–U6 surface composes (Step 10f, FEAT-023 U1).
805
+ - **`@working-theory/ui`'s `CheckoutForm` / `PaymentResult`** — the
806
+ one-time checkout surface's own capability-bearing blocks (FEAT-023
807
+ U2), composed at Step 10e-i steps 3–4.