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
@@ -21,6 +21,9 @@
21
21
  * `architecture.yaml` template files — a fixed template IS the drift this
22
22
  * removes.
23
23
  */
24
+ import { readFileSync, readdirSync } from "node:fs";
25
+ import { dirname, join, sep } from "node:path";
26
+ import { PAID_MODULES } from "./lib/module-tier.js";
24
27
  import { RECOGNIZED_MODULES } from "./parse-args.js";
25
28
  /**
26
29
  * The standalone-site "no upstream-monorepo import" forbidden pattern, assembled
@@ -65,6 +68,19 @@ const PUBLIC_SCHEDULING_VAR_PATTERN = [
65
68
  "CAL",
66
69
  "_|CALCOM|CALENDAR|SCHEDUL|BOOKING)",
67
70
  ].join("");
71
+ /**
72
+ * The commerce module's one credential-boundary pattern: a live-mode Stripe
73
+ * key shape (`sk_`/`pk_`/`rk_` + the live-mode infix), assembled from
74
+ * fragments for the same reason as CAL_KEY_PATTERN above — a literal
75
+ * live-key-shaped token in this file's source would itself trip the repo's
76
+ * own live-key hard block (`rules/environment-boundaries.md`). `apps/api` is
77
+ * excluded because `STRIPE_SECRET_KEY` legitimately lives there, gated by
78
+ * `assertSandboxKey`'s sandbox-only allow-list at request time and by
79
+ * `assertCommerceSandboxKey` at boot (server.ts) — this pattern catches a
80
+ * live-shaped key leaking anywhere ELSE (a fixture, a client component, a
81
+ * `NEXT_PUBLIC_` var).
82
+ */
83
+ const STRIPE_LIVE_KEY_PATTERN = ["(sk|pk|rk)_li", "ve_[A-Za-z0-9]"].join("");
68
84
  /**
69
85
  * The module-independent base: `apps/web` (the Next.js presentation shell every
70
86
  * AILK site has) plus the standalone forbidden-pattern floor. `may_import_from`
@@ -193,8 +209,8 @@ export const MODULE_SLICES = {
193
209
  },
194
210
  },
195
211
  },
196
- // ── docs — mountable presentation surface (fumadocs) ─────────────────────
197
- docs: {
212
+ // ── docs-blog — Pro module: both fumadocs surfaces (D-46) ────────────────
213
+ "docs-blog": {
198
214
  webImports: ["packages/fumadocs-schema"],
199
215
  packages: {
200
216
  "fumadocs-schema": {
@@ -202,27 +218,179 @@ export const MODULE_SLICES = {
202
218
  may_import_from: ["packages/schema", "packages/validation"],
203
219
  },
204
220
  },
205
- // ownedFiles (S1, #5088). packages/fumadocs-schema/** is deliberately NOT
206
- // listed: it is a JSON-LD bridge over the oss `packages/schema`, consumed
207
- // by the Free `/blog` route, and was tiered `oss` in architecture.yaml
208
- // (Sam, 2026-09-10, REL-015 #5182 Q3). The docs module owns its ROUTES,
209
- // not that helper — the strip keeps the package in the public tree.
210
- // (`webImports` above still names it, so a docs scaffold still installs it.)
221
+ // ownedFiles (S1, #5088; widened by #5365). The standing reason to list
222
+ // only the `/docs` route was that `/blog` was a FREE route, which made
223
+ // `lib/source.ts` and `source.config.ts` genuinely mixed files a strip
224
+ // tool could not touch without breaking every OSS build. D-46 removed
225
+ // that premise: docs and blog are ONE Pro module and Free has no blog, so
226
+ // both files are wholly module-owned and move whole (#5365 D3). Nothing
227
+ // in a Free `apps/web` resolves a Fumadocs collection afterwards, which is
228
+ // also why `apps/web/content/<locale>/{blog,docs}` no longer has to exist
229
+ // in every scaffold — see `programmatic.ts`'s note and #5365 D3.
230
+ //
231
+ // `packages/fumadocs-schema/**` is still deliberately NOT listed. It is a
232
+ // permission, not a physical slice: `webImports` above names it so a
233
+ // scaffold that installs this module declares the import, while the
234
+ // package itself stays in the tree for this repo's own marketing site. Its
235
+ // tier, licence and npm access flip together under #5363 (D-43) — after
236
+ // this extraction, because architecture.yaml's recorded reason for the
237
+ // current `oss` tier is the Free `/blog` route this card just retired.
238
+ //
239
+ // SOURCE files only (the rule above): the three `content/en/**` MDX files
240
+ // and every test/story companion carry the `oss: false` marker but are not
241
+ // listed here.
242
+ ownedFiles: [
243
+ // The two route surfaces the module mounts.
244
+ "apps/web/app/[locale]/blog/[[...slug]]/page.tsx",
245
+ "apps/web/app/[locale]/docs/[[...slug]]/page.tsx",
246
+ // The Fumadocs source pair — both whole, both loaders (D3).
247
+ "apps/web/source.config.ts",
248
+ "apps/web/lib/source.ts",
249
+ // The route adapters and the jest stub for the codegen output.
250
+ "apps/web/lib/blog-list-data.ts",
251
+ "apps/web/lib/blog-post-data.ts",
252
+ "apps/web/lib/__mocks__/source-server.js",
253
+ // The module's own /llms.txt variant; the bundle installs it onto
254
+ // app/llms.txt/route.ts (#5365 D9, install-path mapping owned by #5373).
255
+ "apps/web/lib/slices/docs-blog/llms-route.ts",
256
+ // The two shells and the six components, each off its public barrel and
257
+ // reachable only from the slice barrel that leaves with them (D-45, D5).
258
+ "packages/templates/src/shells/BlogShell.tsx",
259
+ "packages/templates/src/shells/DocsShell.tsx",
260
+ "packages/templates/src/shells/slices/docs-blog.ts",
261
+ "packages/ui/src/blocks/DocsActionBar.tsx",
262
+ "packages/ui/src/blocks/DocsArticleHeader.tsx",
263
+ "packages/ui/src/blocks/DocsFeedback.tsx",
264
+ "packages/ui/src/blocks/DocsSearch.tsx",
265
+ "packages/ui/src/blocks/DocsTocButton.tsx",
266
+ "packages/ui/src/chrome/DocsSidebar.tsx",
267
+ "packages/ui/src/chrome/slices/docs-blog.ts",
268
+ ],
269
+ // pageContributions (spec 5478; parent § D6 shape 1) — the two route
270
+ // surfaces this module mounts, each with the seed MDX it renders. The
271
+ // three MDX files are exactly the three on the tree and cannot come from
272
+ // the `templates/` snapshot: `apps/web/content` is in build-templates.mjs's
273
+ // EXCLUDE_TEMPLATE_DIRS and `template-overlay/` carries no blog or docs
274
+ // stub. They ride this module's own bundle, consistent with the route
275
+ // files above — STAGING them into it is #5481's change to
276
+ // build-module-bundles.mjs, since `collectModuleFiles` unions `ownedFiles`
277
+ // with `installMap` keys and nothing else. This unit only DECLARES them.
278
+ pageContributions: [
279
+ {
280
+ route: "/blog",
281
+ content: [
282
+ "apps/web/content/en/blog/index.mdx",
283
+ "apps/web/content/en/blog/launching-ailk.mdx",
284
+ ],
285
+ },
286
+ {
287
+ route: "/docs",
288
+ content: ["apps/web/content/en/docs/getting-started.mdx"],
289
+ },
290
+ ],
291
+ // patches (spec 5418 § 3 D1, Group G) — every Group G row that edits a
292
+ // file the buyer's scaffold already has. Values transcribed verbatim
293
+ // from docs/specs/5365-docs-blog-module-spec.md § 4 Group G via
294
+ // docs/specs/5418-module-bundle-patch-set-spec.md § 3 D1's table; the
295
+ // fumadocs-core/fumadocs-mdx version strings are read from this repo's
296
+ // own apps/web/package.json at HEAD.
211
297
  //
212
- // apps/web's own `/docs` catch-all route IS module-owned and IS listed
213
- // (verified against the filesystem — it exists today, contrary to this
214
- // slice's header comment suggesting docs may have "zero apps/web files
215
- // today"). Its two closest neighbors are deliberately NOT listed, because
216
- // both are genuinely mixed with the base `/blog` route rather than
217
- // docs-exclusive (checked by reading each file in full):
218
- // - apps/web/lib/source.ts — exports BOTH `blogSource` and
219
- // `docsSource` from one file; `/blog` (base) needs the former.
220
- // - apps/web/source.config.ts — defines BOTH the `blog` and `docs`
221
- // Fumadocs collections in one `defineDocs` pair.
222
- // A strip tool cannot delete either without breaking `/blog`, which ships
223
- // in every OSS build regardless of the docs module. apps/web/content/en/docs/
224
- // (the MDX content itself, not source) is also excluded — content, not code.
225
- ownedFiles: ["apps/web/app/[locale]/docs/[[...slug]]/page.tsx"],
298
+ // Group G's tsconfig.json row (D1 row 8 — restoring the
299
+ // `../../packages/fumadocs-schema` project reference) is deliberately
300
+ // NOT included here. Re-anchoring D1 against a real keyless scaffold
301
+ // (5418 build, Deliverable 0) found `apps/web/tsconfig.json` carries no
302
+ // `references` key at all in ANY scaffold, regardless of `--modules`
303
+ // selection: `build-templates.mjs`'s `stripDanglingProjectReferences()`
304
+ // runs once over the fixed templates/ snapshot, which never contains a
305
+ // `packages/` directory, so every one of the monorepo's 15 references
306
+ // (including `fumadocs-schema`, position 6) resolves to a nonexistent
307
+ // path and the whole property is dropped. `@working-theory/fumadocs-schema`
308
+ // is delivered to a scaffold as an npm/bundle dependency under
309
+ // `node_modules/`, never as a `packages/fumadocs-schema` source sibling
310
+ // (see this slice's own `ownedFiles` comment above), so restoring the
311
+ // reference would immediately be dangling again in every scaffold — the
312
+ // exact condition `stripDanglingProjectReferences()` exists to prevent,
313
+ // which breaks `next build`'s TypeScript pass (#4090 item 2). This is
314
+ // the same reasoning the commerce row (5418 D1 row 9) already applies to
315
+ // its own tsconfig.json omission; applied here for consistency.
316
+ //
317
+ // Group G's fourth jest.config.cjs item (the `import.meta` transform
318
+ // entry) needs no patch either — the scaffold already ships it
319
+ // untouched (5418 D1 row 7b) — so `add-transform` has no concrete use
320
+ // here.
321
+ patches: [
322
+ {
323
+ target: "apps/web/package.json",
324
+ kind: "json",
325
+ ops: [
326
+ {
327
+ op: "merge-keys",
328
+ path: ["dependencies"],
329
+ entries: {
330
+ "fumadocs-core": "^16.10.4",
331
+ "fumadocs-mdx": "^15.0.12",
332
+ "@working-theory/fumadocs-schema": "workspace:*",
333
+ },
334
+ },
335
+ {
336
+ op: "merge-keys",
337
+ path: ["scripts"],
338
+ entries: {
339
+ prebuild: "fumadocs-mdx",
340
+ pretypecheck: "fumadocs-mdx",
341
+ pretest: "fumadocs-mdx",
342
+ },
343
+ },
344
+ ],
345
+ },
346
+ {
347
+ target: "apps/web/next.config.mjs",
348
+ kind: "next-config",
349
+ ops: [
350
+ {
351
+ op: "add-transpile-package",
352
+ package: "@working-theory/fumadocs-schema",
353
+ },
354
+ {
355
+ op: "wrap-export-with",
356
+ importLine: 'import { createMDX } from "fumadocs-mdx/next";\n',
357
+ find: "withNextIntl(nextConfig)",
358
+ replaceWith: "withNextIntl(createMDX()(nextConfig))",
359
+ },
360
+ ],
361
+ },
362
+ {
363
+ target: "apps/web/jest.config.cjs",
364
+ kind: "jest-config",
365
+ ops: [
366
+ {
367
+ op: "add-transform-ignore-pattern",
368
+ find: '"/node_modules/(?!(\\\\.pnpm/)?@working-theory(?:\\\\+[^/]*?@?[^/]*?|/[^/]+)(/|$))"',
369
+ replaceWith: '"/node_modules/(?!(\\\\.pnpm/)?(fumadocs-[^/]*?@?[^/]*?|@working-theory(?:\\\\+[^/]*?@?[^/]*?|/[^/]+))(/|$))"',
370
+ },
371
+ {
372
+ op: "add-module-name-mapper",
373
+ pattern: "^@/\\\\.source/server$",
374
+ value: "<rootDir>/lib/__mocks__/source-server.js",
375
+ },
376
+ {
377
+ op: "add-module-name-mapper",
378
+ pattern: "^@/\\\\.source$",
379
+ value: "<rootDir>/.source/index.ts",
380
+ },
381
+ {
382
+ op: "add-module-name-mapper",
383
+ pattern: "^@working-theory/fumadocs-schema$",
384
+ value: "<rootDir>/node_modules/@working-theory/fumadocs-schema/dist/index.js",
385
+ },
386
+ ],
387
+ },
388
+ ],
389
+ // postInstall shrinks to what patches cannot express (D5): the patch
390
+ // step edits package.json but never runs a package manager on the
391
+ // buyer's behalf, so a fresh pnpm-lock.yaml resolution is still the
392
+ // buyer's own next step.
393
+ postInstall: "run pnpm install",
226
394
  },
227
395
  // ── multi-tenant — full-stack module (data + api + presentation) ─────────
228
396
  //
@@ -241,6 +409,12 @@ export const MODULE_SLICES = {
241
409
  "packages/theme",
242
410
  "packages/ui",
243
411
  "packages/validation",
412
+ // The `workspace` module's shared settings components (#5371 req. 6)
413
+ // — the scaffolded-site twin of the root architecture.yaml grant on
414
+ // `apps_paid.workspace`. Pro Plus inherits Pro's module set, so a
415
+ // saas-product scaffold carries both modules and this app composes
416
+ // the identical panels apps/web does, with no forked copy.
417
+ "packages/workspace-ui",
244
418
  ],
245
419
  may_not_import_from: [
246
420
  "apps/api",
@@ -252,7 +426,7 @@ export const MODULE_SLICES = {
252
426
  },
253
427
  api: {
254
428
  path: "apps/api",
255
- description: "Public HTTP API — routes + services (Fastify). Owns webhooks, checkout, email, server-side analytics.",
429
+ description: "Public HTTP API — routes + services (Fastify). Owns webhooks, email, server-side analytics — checkout moved to the commerce module (D-47).",
256
430
  may_import_from: [
257
431
  "database",
258
432
  "packages/analytics",
@@ -317,7 +491,7 @@ export const MODULE_SLICES = {
317
491
  // Object.assign merge in deriveArchitecture is order-independent (a
318
492
  // multi-tenant-only selection stays self-consistent).
319
493
  templates: {
320
- description: "Shell/Chrome composition — WorkspaceShell + ArtifactEditorShell (tier-5 shells)",
494
+ description: "Shell/Chrome composition — AppShell (tier-5 shell); WorkspaceShell + ArtifactEditorShell ship inside this module itself, not the public package (D-45)",
321
495
  may_import_from: ["packages/theme", "packages/ui", "packages/validation"],
322
496
  },
323
497
  theme: {
@@ -435,6 +609,24 @@ export const MODULE_SLICES = {
435
609
  // not a documented module boundary — flagged for reviewer judgment
436
610
  // rather than asserted here.
437
611
  //
612
+ // apps/web/app/[locale]/(authed)/waitlist/** (#5367 FR4, D8): the operator
613
+ // waitlist-experiments dashboard UI. Reads GET /v1/waitlist-experiments,
614
+ // whose API half is already listed above. No prior module claimed
615
+ // apps/web at all for multi-tenant — #5182 §8 flagged this gap and left
616
+ // it for this card. Presentation follows its API (D-48: "Operator review
617
+ // dashboards stay in Pro Plus").
618
+ //
619
+ // ESCALATION (§5.3, posted to Epic #5378): the spec's §4.3 file list names
620
+ // 12 files including format.ts and components/ForbiddenState.tsx as
621
+ // multi-tenant-owned. Both are imported by the FREE
622
+ // apps/web/app/[locale]/(authed)/account/page.tsx (lines 55-56) — D-49
623
+ // keeps that page in Free. Listing them here would remove a Free
624
+ // consumer's dependency from the starter (§5.3's stop condition). Treated
625
+ // the same as this slice's own project-listings/index.ts + respond.ts
626
+ // precedent immediately above: a file shared between the free and paid
627
+ // surfaces stays OUT of ownedFiles. Only the other 10 of the 12 are
628
+ // listed below.
629
+ //
438
630
  // database/auth/ and database/content/ — investigated, NOT listed.
439
631
  // Both namespaces DO have their own schema.prisma file (a real
440
632
  // filesystem split via Prisma's multi-file schema feature), but each
@@ -472,9 +664,103 @@ export const MODULE_SLICES = {
472
664
  "apps/api/src/services/waitlist-dashboard.ts",
473
665
  "apps/api/src/lib/tenant-db.ts",
474
666
  "apps/api/src/lib/ws-token.ts",
667
+ "apps/web/app/[locale]/(authed)/waitlist/page.tsx",
668
+ "apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx",
669
+ "apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx",
670
+ "apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx",
671
+ "apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx",
672
+ "apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx",
673
+ "apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx",
674
+ "apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx",
675
+ "apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx",
676
+ "apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx",
475
677
  "apps-paid/workspace/**",
476
678
  ],
477
679
  },
680
+ // ── workspace — Pro module: the single-workspace settings shell (D-27/D-49)
681
+ //
682
+ // `/workspace` in apps/web's `(authed)` segment — General (rename), Site
683
+ // (identity / SEO / theme, over the already-shipped apps/api config routes)
684
+ // and Team (a read-only roster). The three panels live in the shared
685
+ // `packages/workspace-ui` package so the multi-tenant workspace app (the
686
+ // Pro Plus app) composes the identical components rather than a fork.
687
+ //
688
+ // No `apps` / `namespaces` / `forbiddenPatterns` block: this module
689
+ // contributes to already-declared apps only (apps/web is BASE; apps/api is
690
+ // declared by multi-tenant / scheduling / commerce and is snapshotted whole).
691
+ workspace: {
692
+ // A PERMISSION, not a physical slice (#5371 D1a) — the identical shape
693
+ // docs-blog uses for `packages/fumadocs-schema`. `packages/` is never in
694
+ // SNAPSHOT_DIRS, so the package reaches a buyer as an npm/bundle
695
+ // dependency under `node_modules/`, never as a `packages/workspace-ui`
696
+ // source sibling; the `patches` entry below is what puts it in the
697
+ // buyer's apps/web/package.json.
698
+ webImports: ["packages/workspace-ui"],
699
+ packages: {
700
+ "workspace-ui": {
701
+ description: "Shared workspace-settings components — the settings shell plus the General / Site / Team panels, presentational only (data + save callbacks as props)",
702
+ // packages/templates is required because WorkspaceSettingsShell
703
+ // composes AppShell from @working-theory/templates/shells — the same
704
+ // composition apps/web's own account page uses.
705
+ may_import_from: [
706
+ "packages/templates",
707
+ "packages/theme",
708
+ "packages/ui",
709
+ "packages/validation",
710
+ ],
711
+ },
712
+ },
713
+ // SOURCE files only, and only the ones this module physically owns: the
714
+ // three apps/web route files and the two apps/api route files.
715
+ //
716
+ // `packages/workspace-ui/**` is deliberately NOT listed (D1a). It is a
717
+ // whole package that is paid end to end, whose strip disposition is
718
+ // carried by architecture.yaml's `commercial_tier_moves_to` field (its
719
+ // future paid-tree home) rather than by a per-file inventory —
720
+ // exactly the disposition `packages/fumadocs-schema` carries for
721
+ // docs-blog. `multi-tenant`'s whole-app glob (above) works because
722
+ // SNAPSHOT_DIRS physically copies apps wholesale; a package is never in a
723
+ // snapshot root, so an ownedFiles enumeration would add nothing.
724
+ //
725
+ // `packages/api-client/src/workspace.ts` is NOT listed either (D1b):
726
+ // api-client is a base package every scaffold receives regardless of
727
+ // `--modules`, and the field doc above names it by name as a shared
728
+ // package deliberately left out even when genuinely single-purpose. The
729
+ // wrapper is harmless on a scaffold without this module — its calls 404,
730
+ // the same non-event any client wrapper produces against an unregistered
731
+ // route.
732
+ //
733
+ // Test companions (`__tests__/`) are never listed — the SOURCE-files-only
734
+ // rule above.
735
+ ownedFiles: [
736
+ "apps/web/app/[locale]/(authed)/workspace/page.tsx",
737
+ "apps/web/app/[locale]/(authed)/workspace/site/page.tsx",
738
+ "apps/web/app/[locale]/(authed)/workspace/team/page.tsx",
739
+ "apps/api/src/routes/workspaces/general.ts",
740
+ "apps/api/src/routes/workspaces/members.ts",
741
+ ],
742
+ // patches — the dependency add that delivers the shared package to the
743
+ // buyer's apps/web, mirroring docs-blog's own fumadocs-schema merge-keys
744
+ // op. No next.config.mjs `add-transpile-package` op: the package ships
745
+ // compiled ESM in dist/ the same way every other @working-theory/*
746
+ // dependency apps/web already consumes does, and docs-blog's transpile op
747
+ // exists for the fumadocs SDK boundary, not for the dependency add itself.
748
+ patches: [
749
+ {
750
+ target: "apps/web/package.json",
751
+ kind: "json",
752
+ ops: [
753
+ {
754
+ op: "merge-keys",
755
+ path: ["dependencies"],
756
+ entries: {
757
+ "@working-theory/workspace-ui": "workspace:*",
758
+ },
759
+ },
760
+ ],
761
+ },
762
+ ],
763
+ },
478
764
  // ── scheduling — full-stack module (provider + routes + booker pages) ────
479
765
  //
480
766
  // The #4416 scheduling seam, made installable. Three tiers into the
@@ -511,7 +797,7 @@ export const MODULE_SLICES = {
511
797
  apps: {
512
798
  api: {
513
799
  path: "apps/api",
514
- description: "Public HTTP API — routes + services (Fastify). Owns webhooks, checkout, email, server-side analytics.",
800
+ description: "Public HTTP API — routes + services (Fastify). Owns webhooks, email, server-side analytics — checkout moved to the commerce module (D-47).",
515
801
  may_import_from: [
516
802
  "database",
517
803
  "packages/analytics",
@@ -642,6 +928,377 @@ export const MODULE_SLICES = {
642
928
  "apps/web/app/[locale]/schedule/page.tsx",
643
929
  "apps/web/app/[locale]/schedule/confirmed/page.tsx",
644
930
  ],
931
+ // pageContributions (spec 5478; parent § D6 shape 1) — the booker and its
932
+ // confirmation page, whose route files `ownedFiles` above already carries.
933
+ // Neither row names seed content: both render from the /v1/schedule/*
934
+ // routes, not from an MDX file.
935
+ pageContributions: [
936
+ { route: "/schedule" },
937
+ { route: "/schedule/confirmed" },
938
+ ],
939
+ },
940
+ // ── signal-collectors — flows, deliverables, project listings, waitlist
941
+ // signups (D-48, #5366) ───────────────────────────────────────────────
942
+ //
943
+ // ownedFiles verified against the current filesystem, server.ts's
944
+ // module-conditional registration (isModuleEnabled('signal-collectors')),
945
+ // and a repo-wide grep of every importer of each candidate file (an
946
+ // independent re-grep, not carried over from the spec's own table).
947
+ //
948
+ // apps/api/src/routes/project-listings/index.ts is MIXED (D4): it holds
949
+ // this module's five anonymous/founder-read re-exports (lines 1-55) AND
950
+ // multi-tenant's TENANT_HANDLERS table + projectListingTenantRoutes
951
+ // (lines 57-139) in one file. NOT listed here — never deleted/edited on
952
+ // its own, exactly like the scheduling/multi-tenant precedent for a mixed
953
+ // barrel above.
954
+ //
955
+ // apps/api/src/routes/project-listings/respond.ts (`sendValidated`) IS
956
+ // listed, even though it is ALSO imported directly by the tenant routes
957
+ // get.ts/list.ts/patch.ts/patch-copy.ts (multi-tenant, Pro Plus territory
958
+ // — grep-verified, four direct importers outside this module). Same D6
959
+ // reasoning as the services below: every non-territory consumer is a
960
+ // strictly higher (Pro Plus) level, never Free, so this module owns the
961
+ // file and #5367 records the prerequisite.
962
+ //
963
+ // Ten services below are reached from Pro Plus's project-listings tenant
964
+ // routes (get.ts/list.ts/patch.ts/patch-copy.ts) or commerce's recommender,
965
+ // directly or transitively through project-listing-decision.ts /
966
+ // project-listing-flow.ts — grep-verified, not carried over from the
967
+ // spec's table (which independently reaches the same eight; this pass adds
968
+ // waitlist-scoring.ts and playbook-compile.ts, both of which the finalize
969
+ // scorecard already flagged as under-described). D6's rule: this module
970
+ // owns the shared file because every OTHER consumer is a superset level
971
+ // (OFFERINGS.md §4a Table 1 — Pro Plus contains Pro; the commerce add-on is
972
+ // never sold at Free); #5364/#5367 each record this module as a
973
+ // prerequisite of the routes that reach it rather than re-homing the file.
974
+ // - flow-engine.ts — pro-plus, via project-listing-flow.ts:49 ←
975
+ // project-listing-decision.ts:81 ← patch.ts:38
976
+ // - waitlist-scoring.ts — pro-plus, transitively (sole importer is
977
+ // flow-engine.ts:56, which is itself shared)
978
+ // - playbook-compile.ts — pro-plus, transitively (imported by
979
+ // waitlist-scoring.ts:33 and, in-module, by
980
+ // playbook-render.ts:62 and
981
+ // routes/deliverables/index.ts:44)
982
+ // - project-listings.ts, listing-config.ts, project-listing-decision.ts,
983
+ // project-listing-flow.ts, project-listing-issue.ts,
984
+ // waitlist-experiments.ts — pro-plus, direct or transitive via the
985
+ // decision/flow chain above
986
+ // - deliverable-fulfillment.ts — commerce, via recommender-capture.ts:70.
987
+ // NOT ADDED to ownedFiles below, unlike the rest of this list — spec
988
+ // §5.4's escalation boundary fires for this ONE file: D6's premise is
989
+ // "every OTHER consumer is a superset level, never Free," but
990
+ // recommender-capture.ts (and recommender.ts above it, and server.ts
991
+ // above THAT via a plain static import) is UNCONDITIONAL today — #5364
992
+ // has not landed to gate it behind isModuleEnabled("commerce"). Adding
993
+ // this file to ownedFiles made `paidModuleTemplateExclusions()`'s
994
+ // import-graph closure exclude recommender.ts, then server.ts itself,
995
+ // from every keyless scaffold (empirically found, #5366 resume pass) —
996
+ // a base-breaking regression a template snapshot must never produce.
997
+ // Revert this line once #5364 lands and gates the recommender routes;
998
+ // until then the file ships in every scaffold regardless of module
999
+ // selection, exactly as it did before this card (a narrower, temporary
1000
+ // miss than the one D6 accepts on purpose for the other nine).
1001
+ //
1002
+ // apps/api/src/services/listing-promotion.ts has ZERO production
1003
+ // importers anywhere in the repo (only its own test) — confirmed dead code
1004
+ // (spec §7 Q5). Listed anyway: it is listing code by subject, D-48 places
1005
+ // the instrument here, and a reachability grep cannot place an unreached
1006
+ // file — this is a recorded pre-existing defect, not fixed by this card.
1007
+ //
1008
+ // apps/api/src/bin/deliverable-upload.ts imports only `prisma` (no moved
1009
+ // service) but is a deliverables CLI a Free starter should not ship —
1010
+ // listed by subject, not by the import-criterion the other six bins meet.
1011
+ //
1012
+ // apps/web: FlowStepperClient.tsx, ResultsClient.tsx and
1013
+ // ListingApplicationClient.tsx are this module's OWN files (below), so
1014
+ // their packages/ui imports (FlowStepper, FlowSidePanel,
1015
+ // ListingApplicationFlow) are in-module edges, not Free ones, despite
1016
+ // living under apps/web rather than apps-paid.
1017
+ //
1018
+ // packages/ui: eleven components (D8) plus the NEW slice registration
1019
+ // entry point (D8a, mirroring sections/slices/scheduling.ts). Four of the
1020
+ // eleven (ProjectListingShelves, ListingPrivacyNotice, ProjectListingCard,
1021
+ // ResultsReveal) had no consumer anywhere IN THIS REPO at D8 authoring
1022
+ // time — grep-confirmed, a module-boundary fact only in-repo. Three of the
1023
+ // four (ProjectListingShelves, ProjectListingCard, ListingPrivacyNotice —
1024
+ // plus the ListingWindowBand type ProjectListingCard exports) are, in
1025
+ // fact, consumed CROSS-ORG: the samhenry site (salessmyth org) imports all
1026
+ // four from the signal-collectors slice's re-export entry point
1027
+ // (packages/ui/src/sections/slices/signal-collectors.ts, rules/cross-org-
1028
+ // sharing.md), which #5488 additionally re-exports them from so that
1029
+ // consumer can reach them by subpath. "No consumer anywhere in the repo"
1030
+ // was therefore true in-repo and false across the org boundary — not a
1031
+ // reachability fact a repo-scoped grep can see. ResultsReveal remains the
1032
+ // one component of the four with no known consumer anywhere (in-repo or
1033
+ // out) and is NOT re-exported by #5488 — it is not this card's claim to
1034
+ // make it non-orphaned. D8's placement rule (by subject — project-listing
1035
+ // / results-reveal UI — rather than by reachability, since reachability
1036
+ // cannot place an unreached file either) is unchanged and still governs
1037
+ // where all eleven live.
1038
+ "signal-collectors": {
1039
+ ownedFiles: [
1040
+ "apps/api/src/routes/flows/index.ts",
1041
+ // README.md documents the flows rate-limit rationale (#4730) — not a
1042
+ // test/generated file, and its absence is exactly what left a stray
1043
+ // apps/api/src/routes/flows/ directory in a keyless templates/ snapshot
1044
+ // (empirically found, #5366) since the import-graph closure below only
1045
+ // scans source extensions, never markdown.
1046
+ "apps/api/src/routes/flows/README.md",
1047
+ "apps/api/src/routes/deliverables/index.ts",
1048
+ "apps/api/src/routes/slack/index.ts",
1049
+ "apps/api/src/routes/slack/actions.ts",
1050
+ "apps/api/src/routes/waitlist-signups/index.ts",
1051
+ // apps/api/src/routes/project-listings/{start,patch-draft,submit,public,
1052
+ // me,respond,resume-token}.ts and services/{project-listings,
1053
+ // listing-config}.ts deliberately NOT listed — see the comment above
1054
+ // this array (D-48/#5366 escalation: the mixed-barrel `index.ts` in
1055
+ // that directory is reached from server.ts by a plain, literal
1056
+ // `import()` for multi-tenant's tenant routes, and statically imports
1057
+ // all nine of these files itself, so `paidModuleTemplateExclusions()`'s
1058
+ // import-graph closure excluded `index.ts`, then `server.ts` itself,
1059
+ // from every keyless scaffold (empirically found, #5366 resume pass,
1060
+ // same failure mode as the deliverable-fulfillment.ts case below but a
1061
+ // permanent structural one rather than a temporal gap pending another
1062
+ // PR). This is also the ORIGINAL multi-tenant/#5088 card's own
1063
+ // conclusion about these same nine files (lines 556-571 above) —
1064
+ // D-48 moves their tier ownership to signal-collectors but not their
1065
+ // must-ship-unconditionally constraint. Registration of every route
1066
+ // that reaches them stays gated by `isModuleEnabled("signal-collectors")`
1067
+ // in server.ts; only the source files themselves ship regardless.
1068
+ "apps/api/src/services/flow-engine.ts",
1069
+ "apps/api/src/services/flow-aggregate.ts",
1070
+ "apps/api/src/services/waitlist-signups.ts",
1071
+ "apps/api/src/services/waitlist-scoring.ts",
1072
+ "apps/api/src/services/playbook-compile.ts",
1073
+ "apps/api/src/services/playbook-render.ts",
1074
+ "apps/api/src/services/listing-csv-sweep.ts",
1075
+ "apps/api/src/services/waitlist-followup.ts",
1076
+ "apps/api/src/services/listing-promotion.ts",
1077
+ "apps/api/src/services/project-listing-decision.ts",
1078
+ "apps/api/src/services/project-listing-flow.ts",
1079
+ "apps/api/src/services/project-listing-issue.ts",
1080
+ "apps/api/src/services/waitlist-experiments.ts",
1081
+ // deliverable-fulfillment.ts deliberately NOT listed — see the comment
1082
+ // above this array (spec §5.4 escalation boundary, temporary pending #5364).
1083
+ "apps/api/src/bin/deliverable-resend.ts",
1084
+ "apps/api/src/bin/deliverable-upload.ts",
1085
+ "apps/api/src/bin/session-retention-sweep.ts",
1086
+ "apps/api/src/bin/seed-presets.ts",
1087
+ "apps/api/src/bin/listing-csv-sweep.ts",
1088
+ "apps/api/src/bin/followup-sweep.ts",
1089
+ "apps/api/src/bin/seed-waitlist-experiments.ts",
1090
+ "apps/mcp/src/tools/get_flow.ts",
1091
+ "apps/mcp/src/tools/submit_flow_step.ts",
1092
+ // apps/web/app/[locale]/flows/** deliberately NOT listed here (unlike
1093
+ // the docs-blog precedent's own `blog`/`docs` page.tsx entries above),
1094
+ // even though every file under it is signal-collectors territory and
1095
+ // still carries its own `oss: false` marker for the OSS-strip. Every
1096
+ // one of these files is ALREADY excluded from a keyless scaffold by the
1097
+ // pre-existing, independent `MODULE_SURFACES["signal-collectors"] =
1098
+ // ["flows"]` → `excludedRoutePaths()` mechanism (#4154), which removes
1099
+ // the WHOLE `apps/web/app/[locale]/flows` directory at copy time
1100
+ // whenever the "flows" surface isn't selected — and `resolveSurfaces`
1101
+ // refuses `--surfaces flows` without the module (#5365 FR5/D12), so
1102
+ // there is no way to reach "flows carried, signal-collectors absent".
1103
+ // Adding them to `ownedFiles` ALSO excludes them from `templates/`
1104
+ // itself (build-templates.mjs), which broke `sync-routes`'s pre-#5366
1105
+ // upgrade-path fixture (`flows/[slug]/results/[payload]`, #4891/#4986)
1106
+ // and — more importantly — would silently strip a REAL consumer's
1107
+ // `sync-routes` tool of the ability to ever restore these files after
1108
+ // scaffolding, since that tool diffs against `templates/` alone and has
1109
+ // no module/licence concept of its own (empirically found, #5366 resume
1110
+ // pass). `projects/apply/ListingApplicationClient.tsx` below is
1111
+ // different in kind: it sits under the UNRELATED "projects" content
1112
+ // surface's URL segment, which `excludedRoutePaths` does not gate on
1113
+ // signal-collectors at all, so it genuinely needs `ownedFiles` to be
1114
+ // excluded from a keyless scaffold — see `sync-routes.ts`'s
1115
+ // `NESTED_FOREIGN_MODULE_ENTRIES` for the companion fix that keeps its
1116
+ // presence from falsely reading as "projects carried".
1117
+ "apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx",
1118
+ "packages/ui/src/sections/ListingApplicationFlow.tsx",
1119
+ "packages/ui/src/sections/ProjectListingShelves.tsx",
1120
+ "packages/ui/src/sections/FlowStepper.tsx",
1121
+ "packages/ui/src/blocks/FlowSidePanel.tsx",
1122
+ "packages/ui/src/blocks/ListingFullPreview.tsx",
1123
+ "packages/ui/src/blocks/ListingQuestionReview.tsx",
1124
+ "packages/ui/src/blocks/ListingTilePreview.tsx",
1125
+ "packages/ui/src/blocks/ListingPrivacyNotice.tsx",
1126
+ "packages/ui/src/blocks/ProjectListingCard.tsx",
1127
+ "packages/ui/src/blocks/ResponseChart.tsx",
1128
+ "packages/ui/src/blocks/ResultsReveal.tsx",
1129
+ "packages/ui/src/sections/slices/signal-collectors.ts",
1130
+ ],
1131
+ // pageContributions (spec 5478; parent § D6 shape 2) — the three flow
1132
+ // routes, named exactly as `surfaces.ts`'s ROUTE_SINCE keys name them.
1133
+ // ROWS AND SEED ONLY: unlike docs-blog and scheduling above, this module's
1134
+ // route code is deliberately NOT in `ownedFiles` (see the comment inside
1135
+ // that array). The first two rows' code reaches a scaffold from the
1136
+ // `templates/` snapshot, retained because the `flows` surface is enabled;
1137
+ // the results row's code is `commerce`'s (`commerce.ownedFiles` carries
1138
+ // `results/[payload]/page.tsx` + its two client files below, and
1139
+ // `paidModuleTemplateExclusions` strips every paid module's owned files
1140
+ // from the snapshot unconditionally), so that one row rides a DIFFERENT
1141
+ // module's bundle — recorded in spec 5478 § 8 item 1, whose delivery
1142
+ // consequence is #5481's and #5464's. Declaring the rows here changes none
1143
+ // of it: a contribution is data, and which delivery shape carries the code
1144
+ // is a property of the module, not of the contribution.
1145
+ pageContributions: [
1146
+ { route: "/flows/[slug]" },
1147
+ { route: "/flows/[slug]/start" },
1148
+ { route: "/flows/[slug]/results/[payload]" },
1149
+ ],
1150
+ },
1151
+ // ── commerce — per-site paid add-on (D-47, amending D-26) ─────────────────
1152
+ //
1153
+ // Checkout, the Stripe webhook receiver, flow checkouts and the recommender
1154
+ // (D-47's disposition sentence, verbatim). The base API keeps only
1155
+ // `isModuleEnabled("commerce")`; every registration loads through
1156
+ // `SLICE_PLUGINS` (server.ts). `apps.api.may_import_from` names
1157
+ // `packages/stripe-client` — the module's own package (D5) — mirroring the
1158
+ // `multi-tenant`/`scheduling` slices' identical entry, which already
1159
+ // document "checkout moved to the commerce module (D-47)" for the same
1160
+ // reason those two slices state theirs: `deriveArchitecture` merges `api`
1161
+ // blocks with `Object.assign`, so every slice declaring `api` must agree.
1162
+ //
1163
+ // Q1 (spec §7): a `multi-tenant` deployment using the Stripe billing portal
1164
+ // or org-scoped webhook routing (`routes/billing/portal.ts`,
1165
+ // `routes/webhooks/stripe.ts`'s org-billing branch) also depends on
1166
+ // `lib/stripe.ts` and the webhook receiver this module owns — declared
1167
+ // here, not engineered around.
1168
+ commerce: {
1169
+ apps: {
1170
+ api: {
1171
+ path: "apps/api",
1172
+ description: "Public HTTP API — routes + services (Fastify). Owns checkout, the Stripe webhook receiver, flow checkouts and the recommender (D-47) — the Stripe setup and stripe-client's sole apps/api reach.",
1173
+ may_import_from: [
1174
+ "database",
1175
+ "packages/analytics",
1176
+ "packages/email",
1177
+ "packages/license",
1178
+ "packages/schema",
1179
+ "packages/site-config",
1180
+ "packages/stripe-client",
1181
+ "packages/validation",
1182
+ ],
1183
+ may_not_import_from: [
1184
+ "apps/mcp",
1185
+ "apps/web",
1186
+ "packages/theme",
1187
+ "packages/ui",
1188
+ ],
1189
+ },
1190
+ },
1191
+ packages: {
1192
+ // Duplicated VERBATIM from the multi-tenant slice's own "stripe-client"
1193
+ // entry (§7 Q1 — a multi-tenant deployment using the Stripe billing
1194
+ // portal or org-billing webhook routing also installs commerce, so
1195
+ // both slices can be selected together) — the same order-independence
1196
+ // reason the multi-tenant/scheduling slices state for `validation`
1197
+ // below: `deriveArchitecture` merges `packages` blocks with
1198
+ // `Object.assign`, so two slices declaring this key differently would
1199
+ // make the derivation depend on module order.
1200
+ "stripe-client": {
1201
+ description: "Stripe SDK wrapper + webhook signature verification",
1202
+ may_import_from: ["packages/validation"],
1203
+ },
1204
+ // Duplicated VERBATIM from the marketing slice for the same
1205
+ // order-independence reason the multi-tenant/scheduling slices state.
1206
+ validation: {
1207
+ description: "Zod schemas + shared types",
1208
+ may_import_from: [],
1209
+ },
1210
+ },
1211
+ // webImports omitted (§4.4): no commerce component reaches apps/web
1212
+ // through a package apps/web does not already import — packages/ui is
1213
+ // base, and architecture.yaml already forbids apps/web -> stripe-client.
1214
+ forbiddenPatterns: [
1215
+ {
1216
+ pattern: STRIPE_LIVE_KEY_PATTERN,
1217
+ paths_excluded: ["apps/api/**"],
1218
+ reason: "The commerce credential belongs to apps/api, behind assertSandboxKey's sandbox-only allow-list. A live-mode-shaped key anywhere else is a leak, not a config choice",
1219
+ },
1220
+ ],
1221
+ // ownedFiles (S1, #5088). SOURCE files only (#5182 D6) — no __tests__/,
1222
+ // no .stories.tsx, no .text.ts... except .text.ts siblings ARE source
1223
+ // (they ship as runtime copy modules, not test fixtures), so the ten
1224
+ // annotated below are listed. 42 in all: 9 apps/api + 26 packages/ui
1225
+ // (15 components + 10 .text.ts siblings + this slice's own
1226
+ // sections/slices/commerce.ts) + 7 apps/web.
1227
+ ownedFiles: [
1228
+ "apps/api/src/lib/stripe.ts",
1229
+ "apps/api/src/routes/checkout/index.ts",
1230
+ "apps/api/src/routes/checkout/sessions.ts",
1231
+ "apps/api/src/routes/webhooks/index.ts",
1232
+ "apps/api/src/routes/webhooks/stripe.ts",
1233
+ // README.md documents the Stripe webhook receiver — not a test/
1234
+ // generated file, and its absence is exactly what left a stray
1235
+ // apps/api/src/routes/webhooks/ directory in a keyless templates/
1236
+ // snapshot (empirically found, #5364 build pass): the import-graph
1237
+ // closure only scans source extensions, never markdown, so once
1238
+ // index.ts and stripe.ts are excluded this is the one file left
1239
+ // behind. Same defect class #5366 found and fixed the same way for
1240
+ // routes/flows/README.md — see that entry above.
1241
+ "apps/api/src/routes/webhooks/README.md",
1242
+ "apps/api/src/routes/flow-checkouts/index.ts",
1243
+ "apps/api/src/routes/flows/recommender.ts",
1244
+ "apps/api/src/services/recommender-capture.ts",
1245
+ "apps/api/src/services/flow-checkouts.ts",
1246
+ "packages/ui/src/blocks/CheckoutForm.tsx",
1247
+ "packages/ui/src/blocks/CheckoutForm.text.ts",
1248
+ "packages/ui/src/blocks/CheckoutPriceCard.tsx",
1249
+ "packages/ui/src/blocks/PaymentLinkCard.tsx",
1250
+ "packages/ui/src/blocks/PaymentLinkCard.text.ts",
1251
+ "packages/ui/src/blocks/PaymentLinkForm.tsx",
1252
+ "packages/ui/src/blocks/PaymentLinkForm.text.ts",
1253
+ "packages/ui/src/blocks/PaymentLinkList.tsx",
1254
+ "packages/ui/src/blocks/InvoiceForm.tsx",
1255
+ "packages/ui/src/blocks/InvoiceForm.text.ts",
1256
+ "packages/ui/src/blocks/InvoiceList.tsx",
1257
+ "packages/ui/src/blocks/InvoiceList.text.ts",
1258
+ "packages/ui/src/blocks/SubscriptionStatus.tsx",
1259
+ "packages/ui/src/blocks/SubscriptionStatus.text.ts",
1260
+ "packages/ui/src/blocks/StripeActionButton.tsx",
1261
+ "packages/ui/src/blocks/PaymentStatusBadge.tsx",
1262
+ "packages/ui/src/blocks/PaymentStatusBadge.text.ts",
1263
+ "packages/ui/src/blocks/PaymentResult.tsx",
1264
+ "packages/ui/src/blocks/PaymentResult.text.ts",
1265
+ "packages/ui/src/blocks/PortalEntry.tsx",
1266
+ "packages/ui/src/panels/InvoiceDetail.tsx",
1267
+ "packages/ui/src/panels/InvoiceDetail.text.ts",
1268
+ "packages/ui/src/sections/PaymentReceipt.tsx",
1269
+ "packages/ui/src/sections/PricingTable.tsx",
1270
+ "packages/ui/src/sections/PricingTable.text.ts",
1271
+ "packages/ui/src/sections/slices/commerce.ts",
1272
+ "apps/web/app/[locale]/flows/[slug]/checkout.actions.ts",
1273
+ "apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx",
1274
+ "apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx",
1275
+ "apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx",
1276
+ "apps/web/app/[locale]/dev/purchase/page.tsx",
1277
+ "apps/web/app/[locale]/dev/purchase/error.tsx",
1278
+ "apps/web/app/[locale]/dev/purchase/purchase.actions.ts",
1279
+ ],
1280
+ // The scaffold-commerce authoring skill (D-47, #5449): moved by `git mv`
1281
+ // out of the Free starter's `templates/.claude/skills/` (never stripped —
1282
+ // it sits outside build-templates.mjs's SNAPSHOT_DIRS/overlay, so
1283
+ // ownedFiles/paidModuleTemplateExclusions cannot reach it) to
1284
+ // `module-templates/commerce/.claude/skills/scaffold-commerce/`, and
1285
+ // delivered into a keyed scaffold by `installMap` instead. Ten literal
1286
+ // entries, not one directory entry: `extractModuleBundle`'s
1287
+ // `validateInstallMap` requires every source to be a FILE the bundle
1288
+ // produced (`extract-module-bundle.ts`'s `!sourceStat.isFile()` check) —
1289
+ // a directory source is rejected outright.
1290
+ installMap: {
1291
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/SKILL.md": ".claude/skills/scaffold-commerce/SKILL.md",
1292
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/references/lookup-keys-template.md": ".claude/skills/scaffold-commerce/references/lookup-keys-template.md",
1293
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md": ".claude/skills/scaffold-commerce/references/marketplace-brand-pages.md",
1294
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts": ".claude/skills/scaffold-commerce/templates/checkout-route.template.ts",
1295
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts": ".claude/skills/scaffold-commerce/templates/invoice-route.template.ts",
1296
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl": ".claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl",
1297
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts": ".claude/skills/scaffold-commerce/templates/payment-link-route.template.ts",
1298
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/portal-route.template.ts": ".claude/skills/scaffold-commerce/templates/portal-route.template.ts",
1299
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts": ".claude/skills/scaffold-commerce/templates/stripe-config.template.ts",
1300
+ "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts": ".claude/skills/scaffold-commerce/templates/subscription-route.template.ts",
1301
+ },
645
1302
  },
646
1303
  };
647
1304
  /**
@@ -661,3 +1318,141 @@ export function normalizeModules(modules) {
661
1318
  }
662
1319
  return RECOGNIZED_MODULES.filter((m) => requested.has(m));
663
1320
  }
1321
+ /**
1322
+ * Template-relative paths that must never be written into `templates/` for a
1323
+ * keyless starter: every PAID module's `ownedFiles` entry that resolves under
1324
+ * a `build-templates` snapshot root, plus every file under those roots that
1325
+ * imports one (transitively), which would otherwise ship dangling.
1326
+ *
1327
+ * This is the SINGLE starter-removal input for all four R1 extractions
1328
+ * (#5364 commerce, #5365 docs + blog, #5366 signal-collectors, #5367 Pro Plus
1329
+ * + scheduling). A module joins by gaining `ownedFiles` entries — never by
1330
+ * adding a path to a list here.
1331
+ *
1332
+ * The input is `ownedFiles`, NOT the `oss: false` marker set. The two answer
1333
+ * different questions: the strip's authority is their union (see the
1334
+ * `ownedFiles` doc above), while the STARTER's authority is `ownedFiles`
1335
+ * alone. The marker set includes the content-write trio, which D-44 keeps in
1336
+ * public code licence-checked at runtime — deriving the starter exclusion from
1337
+ * markers would remove a surface D-44 says must ship.
1338
+ *
1339
+ * Entries outside the snapshot roots — every `packages/**` path, and the paid
1340
+ * app roots — are not snapshotted at all and are filtered here rather than
1341
+ * special-cased at the call site. Today every glob entry is also out-of-root,
1342
+ * so globs never reach the literal-path matcher below.
1343
+ *
1344
+ * @param snapshotRoots repo-relative roots `build-templates.mjs` snapshots.
1345
+ * @param repoRoot absolute path to the monorepo root, for the import scan.
1346
+ * Passed in rather than derived from `import.meta.url`, which ts-jest cannot
1347
+ * load — the same reason `sync-routes.ts` documents for staying pure.
1348
+ */
1349
+ export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
1350
+ const roots = new Set(snapshotRoots);
1351
+ const excluded = new Set();
1352
+ for (const moduleName of PAID_MODULES) {
1353
+ for (const owned of MODULE_SLICES[moduleName]?.ownedFiles ?? []) {
1354
+ const rel = owned.split(sep).join("/");
1355
+ if (!roots.has(rel.split("/")[0] ?? ""))
1356
+ continue;
1357
+ if (rel.includes("*"))
1358
+ continue;
1359
+ excluded.add(rel);
1360
+ }
1361
+ }
1362
+ // Companion closure by import graph, to a fixed point — never a filename
1363
+ // stem rule (#5182 D7), which misses eleven files and over-marks three.
1364
+ const candidates = listFilesUnder(repoRoot, [...roots]);
1365
+ for (;;) {
1366
+ let grew = false;
1367
+ for (const file of candidates) {
1368
+ if (excluded.has(file))
1369
+ continue;
1370
+ const imports = relativeImportsOf(join(repoRoot, file));
1371
+ for (const spec of imports) {
1372
+ const target = resolveRelativeImport(file, spec, candidates);
1373
+ if (target !== undefined && excluded.has(target)) {
1374
+ excluded.add(file);
1375
+ grew = true;
1376
+ break;
1377
+ }
1378
+ }
1379
+ }
1380
+ if (!grew)
1381
+ break;
1382
+ }
1383
+ return [...excluded].sort();
1384
+ }
1385
+ const SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
1386
+ /** Every source file under the given repo-relative roots, as "/"-joined paths. */
1387
+ function listFilesUnder(repoRoot, roots) {
1388
+ const out = new Set();
1389
+ const skip = new Set(["node_modules", ".next", "dist", ".turbo", ".source"]);
1390
+ const stack = roots.map((r) => r);
1391
+ while (stack.length > 0) {
1392
+ const rel = stack.pop();
1393
+ const abs = join(repoRoot, rel);
1394
+ let entries;
1395
+ try {
1396
+ entries = readdirSync(abs, { withFileTypes: true });
1397
+ }
1398
+ catch {
1399
+ continue;
1400
+ }
1401
+ for (const entry of entries) {
1402
+ if (skip.has(entry.name))
1403
+ continue;
1404
+ const childRel = `${rel}/${entry.name}`;
1405
+ if (entry.isDirectory())
1406
+ stack.push(childRel);
1407
+ else if (SOURCE_EXTS.some((e) => entry.name.endsWith(e)))
1408
+ out.add(childRel);
1409
+ }
1410
+ }
1411
+ return out;
1412
+ }
1413
+ /** The relative import specifiers a source file names (static imports + re-exports). */
1414
+ function relativeImportsOf(absPath) {
1415
+ let src;
1416
+ try {
1417
+ src = readFileSync(absPath, "utf8");
1418
+ }
1419
+ catch {
1420
+ return [];
1421
+ }
1422
+ const out = [];
1423
+ for (const m of src.matchAll(/(?:^|\n)\s*(?:import|export)[\s\S]{0,400}?from\s*["'](\.[^"']*)["']/g))
1424
+ out.push(m[1]);
1425
+ for (const m of src.matchAll(/\bimport\s*\(\s*["'](\.[^"']*)["']\s*\)/g))
1426
+ out.push(m[1]);
1427
+ return out;
1428
+ }
1429
+ /**
1430
+ * Resolve a relative specifier against the candidate set. TypeScript ESM writes
1431
+ * `./x.js` for `./x.ts`, so the `.js` → `.ts`/`.tsx` rewrite is the common case.
1432
+ */
1433
+ function resolveRelativeImport(fromFile, spec, candidates) {
1434
+ const base = posixJoin(dirname(fromFile.split("/").join(sep)), spec);
1435
+ const stem = base.replace(/\.(js|jsx|mjs|cjs)$/, "");
1436
+ for (const cand of [
1437
+ base,
1438
+ ...SOURCE_EXTS.map((e) => stem + e),
1439
+ ...SOURCE_EXTS.map((e) => `${stem}/index${e}`),
1440
+ ])
1441
+ if (candidates.has(cand))
1442
+ return cand;
1443
+ return undefined;
1444
+ }
1445
+ /** Join and normalize a "/"-separated path, resolving `.` and `..` segments. */
1446
+ function posixJoin(dir, spec) {
1447
+ const parts = `${dir.split(sep).join("/")}/${spec}`.split("/");
1448
+ const out = [];
1449
+ for (const part of parts) {
1450
+ if (part === "" || part === ".")
1451
+ continue;
1452
+ if (part === "..")
1453
+ out.pop();
1454
+ else
1455
+ out.push(part);
1456
+ }
1457
+ return out.join("/");
1458
+ }