create-ailk 0.3.0 → 0.5.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 (155) hide show
  1. package/component-catalog.json +11939 -0
  2. package/component-catalog.md +176 -64
  3. package/dist/cli.js +48 -10
  4. package/dist/derive-architecture.js +5 -2
  5. package/dist/lib/extract-module-bundle.d.ts +88 -1
  6. package/dist/lib/extract-module-bundle.js +375 -80
  7. package/dist/lib/fetch-guard.d.ts +28 -0
  8. package/dist/lib/fetch-guard.js +101 -0
  9. package/dist/lib/fetch-module.js +4 -0
  10. package/dist/lib/fetch-template.d.ts +20 -0
  11. package/dist/lib/fetch-template.js +133 -0
  12. package/dist/lib/module-license-gate.d.ts +8 -0
  13. package/dist/lib/module-license-gate.js +12 -4
  14. package/dist/module-architecture.d.ts +116 -7
  15. package/dist/module-architecture.js +465 -94
  16. package/dist/parse-args.d.ts +8 -1
  17. package/dist/parse-args.js +49 -2
  18. package/dist/programmatic.d.ts +36 -1
  19. package/dist/programmatic.js +216 -5
  20. package/dist/site-chrome.d.ts +80 -0
  21. package/dist/site-chrome.js +135 -0
  22. package/dist/surfaces.d.ts +21 -6
  23. package/dist/surfaces.js +140 -23
  24. package/dist/template-manifest.d.ts +144 -0
  25. package/dist/template-manifest.js +337 -0
  26. package/dist/template-names.d.ts +41 -0
  27. package/dist/template-names.js +45 -0
  28. package/package.json +10 -2
  29. package/site-templates/_base/ailk-template.json +6 -0
  30. package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
  31. package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +79 -0
  32. package/site-templates/_base/content/en/TermsPage/terms.mdx +80 -0
  33. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +331 -0
  34. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +393 -0
  35. package/site-templates/marketing-site/ailk-template.json +11 -0
  36. package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
  37. package/site-templates/marketing-site/provenance.json +4 -0
  38. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
  39. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +285 -0
  40. package/site-templates/marketing-site-free/ailk-template.json +9 -0
  41. package/site-templates/marketing-site-free/provenance.json +4 -0
  42. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +271 -0
  43. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +324 -0
  44. package/site-templates/minimalism-site/ailk-template.json +21 -0
  45. package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
  46. package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
  47. package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
  48. package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
  49. package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
  50. package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
  51. package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
  52. package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
  53. package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
  54. package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
  55. package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
  56. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
  57. package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
  58. package/site-templates/minimalism-site/content/en/HomePage/home.mdx +214 -0
  59. package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
  60. package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
  61. package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
  62. package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
  63. package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
  64. package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
  65. package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
  66. package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
  67. package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
  68. package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
  69. package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
  70. package/site-templates/minimalism-site/provenance.json +4 -0
  71. package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
  72. package/site-templates/saas-product/ailk-template.json +16 -0
  73. package/site-templates/saas-product/provenance.json +10 -0
  74. package/templates/.claude/skills/README.md +16 -25
  75. package/templates/.env.example +32 -7
  76. package/templates/.github/workflows/ci.yml +48 -0
  77. package/templates/README.md +3 -1
  78. package/templates/apps/api/CLAUDE.md +4 -1
  79. package/templates/apps/api/eslint.config.mjs +69 -0
  80. package/templates/apps/api/package.json +6 -2
  81. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +6 -2
  82. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
  83. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +173 -0
  84. package/templates/apps/api/src/config/modules.ts +1 -1
  85. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  86. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +339 -0
  87. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +108 -0
  88. package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
  89. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
  90. package/templates/apps/api/src/routes/leads/list.ts +104 -0
  91. package/templates/apps/api/src/server.ts +122 -3
  92. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
  93. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
  94. package/templates/apps/api/src/services/default-tenant.ts +261 -0
  95. package/templates/apps/api/src/services/lead-promotion.ts +161 -0
  96. package/templates/apps/api/src/services/leads-inbox.ts +263 -0
  97. package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
  98. package/templates/apps/mcp/CLAUDE.md +1 -1
  99. package/templates/apps/mcp/__tests__/parity.test.ts +2 -2
  100. package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
  101. package/templates/apps/mcp/eslint.config.mjs +69 -0
  102. package/templates/apps/mcp/package.json +6 -2
  103. package/templates/apps/mcp/src/server.ts +2 -2
  104. package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
  105. package/templates/apps/mcp/src/tools/index.ts +2 -2
  106. package/templates/apps/web/.env.example +3 -3
  107. package/templates/apps/web/CLAUDE.md +2 -0
  108. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +4 -6
  109. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +1 -1
  110. package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
  111. package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
  112. package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
  113. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
  114. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
  115. package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
  116. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
  117. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
  118. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
  119. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
  120. package/templates/apps/web/app/[locale]/layout.tsx +214 -17
  121. package/templates/apps/web/app/[locale]/pricing/page.tsx +40 -0
  122. package/templates/apps/web/app/[locale]/privacy/page.tsx +40 -0
  123. package/templates/apps/web/app/[locale]/terms/page.tsx +40 -0
  124. package/templates/apps/web/components/google-analytics.tsx +18 -1
  125. package/templates/apps/web/eslint.config.mjs +69 -0
  126. package/templates/apps/web/jest.config.cjs +12 -15
  127. package/templates/apps/web/lib/__tests__/server-api.test.ts +61 -0
  128. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +32 -5
  129. package/templates/apps/web/lib/nav-config.ts +27 -7
  130. package/templates/apps/web/lib/server-api.ts +29 -13
  131. package/templates/apps/web/lib/site-chrome.ts +29 -0
  132. package/templates/apps/web/lib/telemetry-scrub.ts +46 -0
  133. package/templates/apps/web/messages/en.json +86 -1
  134. package/templates/apps/web/next.config.mjs +20 -0
  135. package/templates/apps/web/package.json +6 -2
  136. package/templates/apps/web/sentry.client.config.ts +131 -0
  137. package/templates/apps/web/sentry.edge.config.ts +72 -0
  138. package/templates/apps/web/sentry.server.config.ts +72 -0
  139. package/templates/apps/web/src/lib/routes.ts +13 -18
  140. package/templates/brand_assets/README.md +24 -0
  141. package/templates/content/en/AboutPage/about.mdx +160 -15
  142. package/templates/content/en/ContactPage/contact.mdx +90 -13
  143. package/templates/content/en/FAQPage/faq.mdx +62 -22
  144. package/templates/content/en/HomePage/home.mdx +244 -20
  145. package/templates/content/en/LandingPage/blog.mdx +83 -0
  146. package/templates/content/en/LandingPage/features.mdx +103 -0
  147. package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
  148. package/templates/database/CHANGELOG.md +21 -0
  149. package/templates/database/__tests__/seed.test.ts +1 -1
  150. package/templates/database/eslint.config.mjs +69 -0
  151. package/templates/database/package.json +7 -3
  152. package/templates/database/seed.ts +1 -1
  153. package/templates/package.json +1 -14
  154. package/versions.json +52 -0
  155. /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
@@ -253,18 +253,47 @@ export const MODULE_SLICES = {
253
253
  // The module's own /llms.txt variant; the bundle installs it onto
254
254
  // app/llms.txt/route.ts (#5365 D9, install-path mapping owned by #5373).
255
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",
256
+ // The two shells and the six components moved to packages-paid/ui-docs-blog
257
+ // (#5581) — that package is a physical part of the tree, not a listed
258
+ // ownedFiles entry, and is installed via this slice's `patches` block below.
259
+ ],
260
+ // pageContributions (spec 5478; parent § D6 shape 1) — the two route
261
+ // surfaces this module mounts, each with the seed MDX it renders. The
262
+ // nine MDX files are exactly the nine on the tree and cannot come from
263
+ // the `templates/` snapshot: `apps/web/content` is in build-templates.mjs's
264
+ // EXCLUDE_TEMPLATE_DIRS and `template-overlay/` carries no blog or docs
265
+ // stub. They ride this module's own bundle, consistent with the route
266
+ // files above: `collectModuleFiles` in build-module-bundles.mjs stages
267
+ // every `pageContributions[].content` entry alongside `ownedFiles` and
268
+ // the `installMap` keys, and `copyTree` lands each at this same path.
269
+ //
270
+ // #5618 D6.2 — the module OWNS its sample content (Rule B): the blog
271
+ // listing, four sample posts and the three docs pages moved here out of
272
+ // `site-templates/marketing-site/`, so any template requiring docs-blog
273
+ // gets the samples and a keyed `--modules docs-blog` scaffold with no
274
+ // template lands them too. `launching-ailk.mdx` stays the module's fifth
275
+ // seed post; the Pro template maps its own post 05 ONTO that path as a
276
+ // copy override (D6.2a — spec § 7 finding 9 carries the seed's fate).
277
+ pageContributions: [
278
+ {
279
+ route: "/blog",
280
+ content: [
281
+ "apps/web/content/en/blog/index.mdx",
282
+ "apps/web/content/en/blog/01-8-things-that-prove-coding-skills-are-directly-linked-to-successfully-assembling-ikea-furniture.mdx",
283
+ "apps/web/content/en/blog/02-5-ways-to-guarantee-your-startup-goes-nowhere-fast.mdx",
284
+ "apps/web/content/en/blog/03-10-ideas-that-will-guarantee-1000x-growth.mdx",
285
+ "apps/web/content/en/blog/04-7-keys-to-saas-success-the-elite-doesnt-want-you-to-know.mdx",
286
+ "apps/web/content/en/blog/launching-ailk.mdx",
287
+ ],
288
+ },
289
+ {
290
+ route: "/docs",
291
+ content: [
292
+ "apps/web/content/en/docs/getting-started.mdx",
293
+ "apps/web/content/en/docs/configure.mdx",
294
+ "apps/web/content/en/docs/deploy.mdx",
295
+ ],
296
+ },
268
297
  ],
269
298
  // patches (spec 5418 § 3 D1, Group G) — every Group G row that edits a
270
299
  // file the buyer's scaffold already has. Values transcribed verbatim
@@ -308,6 +337,8 @@ export const MODULE_SLICES = {
308
337
  "fumadocs-core": "^16.10.4",
309
338
  "fumadocs-mdx": "^15.0.12",
310
339
  "@working-theory/fumadocs-schema": "workspace:*",
340
+ // #5581 — the docs-blog module's paid component package (D5).
341
+ "@working-theory/ui-docs-blog": "workspace:*",
311
342
  },
312
343
  },
313
344
  {
@@ -374,19 +405,25 @@ export const MODULE_SLICES = {
374
405
  //
375
406
  // The FEAT-042 Organization → Workspace → Membership tenancy layer:
376
407
  // schema (auth/content namespaces) + workspace-scoped API (apps/api) +
377
- // the authed workspace app surface (apps/workspace). Single-tenant sites
408
+ // the authed Admin app surface (apps/admin). Single-tenant sites
378
409
  // (module absent) ship none of this — same base apps/web, fixed scope.
379
410
  "multi-tenant": {
380
411
  apps: {
381
- workspace: {
382
- path: "apps/workspace",
383
- description: "Next.js — authed workspace app (Organization → Workspace → Membership). Hosts the organization-plugin Better Auth handlers; imports the database only via lib/prisma-for-auth.ts (auth-adapter exception); all domain data via apps/api's workspace-scoped routes over HTTP.",
412
+ admin: {
413
+ path: "apps/admin",
414
+ description: "Next.js — authed Admin app (Organization → Workspace → Membership). Hosts the organization-plugin Better Auth handlers; imports the database only via lib/prisma-for-auth.ts (auth-adapter exception); all domain data via apps/api's workspace-scoped routes over HTTP.",
384
415
  may_import_from: [
385
416
  "database",
386
417
  "packages/templates",
387
418
  "packages/theme",
388
419
  "packages/ui",
389
420
  "packages/validation",
421
+ // The `workspace` module's shared settings components (#5371 req. 6)
422
+ // — the scaffolded-site twin of the root architecture.yaml grant on
423
+ // `apps_paid.admin`. Pro Plus inherits Pro's module set, so a
424
+ // saas-product scaffold carries both modules and this app composes
425
+ // the identical panels apps/web does, with no forked copy.
426
+ "packages/admin-ui",
390
427
  ],
391
428
  may_not_import_from: [
392
429
  "apps/api",
@@ -458,12 +495,12 @@ export const MODULE_SLICES = {
458
495
  description: "Stripe SDK wrapper + webhook signature verification",
459
496
  may_import_from: ["packages/validation"],
460
497
  },
461
- // apps/workspace presentation deps. templates is net-new here; theme/ui/
498
+ // apps/admin presentation deps. templates is net-new here; theme/ui/
462
499
  // validation duplicate the marketing slice's definitions VERBATIM so the
463
500
  // Object.assign merge in deriveArchitecture is order-independent (a
464
501
  // multi-tenant-only selection stays self-consistent).
465
502
  templates: {
466
- description: "Shell/Chrome composition — AppShell (tier-5 shell); WorkspaceShell + ArtifactEditorShell ship inside this module itself, not the public package (D-45)",
503
+ description: "Shell/Chrome composition — AppShell (tier-5 shell); AdminShell + ArtifactEditorShell ship inside this module itself, not the public package (D-45)",
467
504
  may_import_from: ["packages/theme", "packages/ui", "packages/validation"],
468
505
  },
469
506
  theme: {
@@ -525,11 +562,12 @@ export const MODULE_SLICES = {
525
562
  // ownedFiles (S1, #5088). Verified against the current filesystem and
526
563
  // server.ts's module-conditional registration (isModuleEnabled('multi-tenant')).
527
564
  //
528
- // apps-paid/workspace/** is a single glob — the whole app (43 files as of
565
+ // apps-paid/admin/** is a single glob — the whole app (43 files as of
529
566
  // this pass, excluding __tests__/node_modules), just moved here from
530
- // apps/workspace by a parallel workstream on this same branch. Unlike
531
- // every other entry below, this one is intentionally a glob, not an
532
- // enumeration.
567
+ // apps/workspace by a parallel workstream on this same branch. Later
568
+ // renamed in place from its former Workspace-prefixed directory name by
569
+ // #5637, the Admin-vocabulary rename. Unlike every other entry below,
570
+ // this one is intentionally a glob, not an enumeration.
533
571
  //
534
572
  // apps/api/src/routes/project-listings/ is MIXED (free anonymous surface
535
573
  // + tenant dashboard sharing one directory) — only the tenant-guarded
@@ -646,7 +684,112 @@ export const MODULE_SLICES = {
646
684
  "apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx",
647
685
  "apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx",
648
686
  "apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx",
649
- "apps-paid/workspace/**",
687
+ "apps-paid/admin/**",
688
+ ],
689
+ // The whole-app glob above lands its files at their REPO-relative path
690
+ // unless something re-roots them (`installBundleTree`'s `copyTree`
691
+ // excludes only `installMap` KEYS from its verbatim copy), so without a
692
+ // rename rule a keyed install left a stray source-tree-relative
693
+ // directory in the scaffold instead of the working `apps/admin` app
694
+ // this slice's own `apps.admin.path` (and `paid-paths.ts`'s
695
+ // `introducedAppPaths()`) already declares (#5626 D3/D4). The fix lives
696
+ // here, on the module, so `--modules multi-tenant` alone is fixed too —
697
+ // never as a per-template workaround.
698
+ //
699
+ // `installRoot`, not a literal `installMap` (#5644 D1): a committed
700
+ // 58-entry literal map — one line per staged file's proprietary source
701
+ // path — put every one of those paths into this OSS-shipped file's
702
+ // SOURCE TEXT, the exact leak class `scripts/check-oss-strip.sh` exists
703
+ // to catch. `build-module-bundles.mjs`'s `deriveGlobInstallMap` derives
704
+ // the equivalent per-file `installMap` at bundle-build time instead —
705
+ // one entry per file `expandEntry` yields for the glob entry above,
706
+ // byte-identical to the literal map it replaces — and the mock registry
707
+ // fixture calls the same derivation so it cannot drift from the
708
+ // producer's. See `installRoot`'s own doc on `ModuleSlice` above.
709
+ installRoot: "apps/admin/",
710
+ },
711
+ // ── workspace — Pro module: the single-workspace settings shell (D-27/D-49)
712
+ //
713
+ // `/admin` in apps/web's `(authed)` segment — General (rename), Site
714
+ // (identity / SEO / theme, over the already-shipped apps/api config routes)
715
+ // and Team (a read-only roster). The three panels live in the shared
716
+ // `packages/admin-ui` package so the multi-tenant Admin app (the
717
+ // Pro Plus app) composes the identical components rather than a fork.
718
+ //
719
+ // No `apps` / `namespaces` / `forbiddenPatterns` block: this module
720
+ // contributes to already-declared apps only (apps/web is BASE; apps/api is
721
+ // declared by multi-tenant / scheduling / commerce and is snapshotted whole).
722
+ workspace: {
723
+ // A PERMISSION, not a physical slice (#5371 D1a) — the identical shape
724
+ // docs-blog uses for `packages/fumadocs-schema`. `packages/` is never in
725
+ // SNAPSHOT_DIRS, so the package reaches a buyer as an npm/bundle
726
+ // dependency under `node_modules/`, never as a `packages/admin-ui`
727
+ // source sibling; the `patches` entry below is what puts it in the
728
+ // buyer's apps/web/package.json.
729
+ webImports: ["packages/admin-ui"],
730
+ packages: {
731
+ "admin-ui": {
732
+ description: "Shared Admin-settings components — the settings shell plus the General / Site / Team panels, presentational only (data + save callbacks as props)",
733
+ // packages/templates is required because AdminSettingsShell
734
+ // composes AppShell from @working-theory/templates/shells — the same
735
+ // composition apps/web's own account page uses.
736
+ may_import_from: [
737
+ "packages/templates",
738
+ "packages/theme",
739
+ "packages/ui",
740
+ "packages/validation",
741
+ ],
742
+ },
743
+ },
744
+ // SOURCE files only, and only the ones this module physically owns: the
745
+ // three apps/web route files and the two apps/api route files.
746
+ //
747
+ // `packages/admin-ui/**` is deliberately NOT listed (D1a). It is a
748
+ // whole package that is paid end to end, whose strip disposition is
749
+ // carried by architecture.yaml's `commercial_tier_moves_to` field (its
750
+ // future paid-tree home) rather than by a per-file inventory —
751
+ // exactly the disposition `packages/fumadocs-schema` carries for
752
+ // docs-blog. `multi-tenant`'s whole-app glob (above) works because
753
+ // SNAPSHOT_DIRS physically copies apps wholesale; a package is never in a
754
+ // snapshot root, so an ownedFiles enumeration would add nothing.
755
+ //
756
+ // `packages/api-client/src/workspace.ts` is NOT listed either (D1b):
757
+ // api-client is a base package every scaffold receives regardless of
758
+ // `--modules`, and the field doc above names it by name as a shared
759
+ // package deliberately left out even when genuinely single-purpose. The
760
+ // wrapper is harmless on a scaffold without this module — its calls 404,
761
+ // the same non-event any client wrapper produces against an unregistered
762
+ // route.
763
+ //
764
+ // Test companions (`__tests__/`) are never listed — the SOURCE-files-only
765
+ // rule above.
766
+ ownedFiles: [
767
+ "apps/web/app/[locale]/(authed)/admin/page.tsx",
768
+ "apps/web/app/[locale]/(authed)/admin/site/page.tsx",
769
+ "apps/web/app/[locale]/(authed)/admin/team/page.tsx",
770
+ "apps/api/src/routes/workspaces/general.ts",
771
+ "apps/api/src/routes/workspaces/members.ts",
772
+ ],
773
+ // patches — the dependency add that delivers the shared package to the
774
+ // buyer's apps/web, mirroring docs-blog's own fumadocs-schema merge-keys
775
+ // op. No next.config.mjs `add-transpile-package` op: the package ships
776
+ // compiled ESM in dist/ the same way every other @working-theory/*
777
+ // dependency apps/web already consumes does, and docs-blog's transpile op
778
+ // exists for the fumadocs SDK boundary, not for the dependency add itself.
779
+ patches: [
780
+ {
781
+ target: "apps/web/package.json",
782
+ kind: "json",
783
+ ops: [
784
+ {
785
+ op: "merge-keys",
786
+ path: ["dependencies"],
787
+ entries: {
788
+ "@working-theory/admin-ui": "workspace:*",
789
+ },
790
+ },
791
+ ],
792
+ },
650
793
  ],
651
794
  },
652
795
  // ── scheduling — full-stack module (provider + routes + booker pages) ────
@@ -658,8 +801,10 @@ export const MODULE_SLICES = {
658
801
  // the ONLY holder of the scheduling credential.
659
802
  // api apps/api's own /v1/schedule/availability + /v1/schedule/book.
660
803
  // presentation the `booker` section (packages/ui, no iframe) rendered by
661
- // the `schedule` route surface, plus the module-owned
662
- // confirmation page at /schedule/confirmed.
804
+ // the `meet` route surface, plus the per-booking-type page
805
+ // and the module-owned confirmation page at /meet/:slug and
806
+ // /meet/confirmed (renamed from the schedule/ directory by
807
+ // #5563).
663
808
  //
664
809
  // The four forbidden patterns below are the module's whole security
665
810
  // argument, restated as boundary rules rather than left in a comment: the
@@ -752,16 +897,19 @@ export const MODULE_SLICES = {
752
897
  // consumer — unlike packages/validation below, a strip tool CAN delete
753
898
  // exactly these files without touching anything another module needs.
754
899
  //
755
- // BOTH apps/web/app/[locale]/schedule pages are listed (#5182 Q9, disposed
756
- // 2026-09-11). confirmed/page.tsx says outright in its own doc comment that
757
- // "The module OWNS this page... a scaffold without the module has no such
758
- // route", and its sibling page.tsx is the SOLE production constructor of a
759
- // `booker` section: it builds the section literally in TypeScript
760
- // (page.tsx:60, `type: "booker"`) rather than reading it from content, and
761
- // its own header calls the route API-backed with "no content file that
762
- // could disagree with the calendar". The create-ailk scaffolder already
763
- // gates the whole `schedule` route surface (src/surfaces.ts), so the two
764
- // pages arrive and depart together.
900
+ // ALL THREE apps/web/app/[locale]/meet pages are listed (#5182 Q9, disposed
901
+ // 2026-09-11; directory renamed from schedule/ to meet/ by #5563).
902
+ // confirmed/page.tsx says outright in its own doc comment that "The module
903
+ // OWNS this page... a scaffold without the module has no such route", and
904
+ // its sibling page.tsx is the SOLE production constructor of a `booker`
905
+ // section: it builds the section literally in TypeScript (page.tsx:60,
906
+ // `type: "booker"`) rather than reading it from content, and its own
907
+ // header calls the route API-backed with "no content file that could
908
+ // disagree with the calendar". [slug]/page.tsx is the third — the
909
+ // per-booking-type page #5563 added, composing its calLink from the same
910
+ // configured owner rather than reading one of its own. The create-ailk
911
+ // scaffolder already gates the whole `meet` route surface (src/surfaces.ts),
912
+ // so all three pages arrive and depart together.
765
913
  //
766
914
  // packages/sections-registry/src/registry.ts (the `booker:` entry) and
767
915
  // packages/validation/src/schemas/page-sections.ts (BookerSectionDataSchema,
@@ -790,13 +938,14 @@ export const MODULE_SLICES = {
790
938
  // scheduling-specific. This is a judgment call, not covered by this
791
939
  // card's explicit checklist: flagged for reviewer confirmation.
792
940
  //
793
- // `packages/ui/src/sections/slices/scheduling.ts` (#5214) is the slice's own
794
- // registration entry point: it statically imports Booker and registers it as
795
- // the `booker` renderer, so it is the one file inside packages/ui that may
796
- // reach a scheduling component — because it strips alongside them. The three
797
- // files that path added on the OSS side — `SectionRenderer.tsx`,
798
- // `slice-renderers.ts` and `ModuleAbsent.tsx` — are deliberately NOT listed
799
- // here: they ship, and listing one would take the whole dispatch table with it.
941
+ // The Booker components and the slice's registration entry point (#5214)
942
+ // moved to packages-paid/ui-scheduling (#5581 U3); that package is a
943
+ // physical part of the tree, not a listed ownedFiles entry, and reaches a
944
+ // keyed scaffold through this slice's `patches` block below (D5). The
945
+ // three files the old in-packages/ui path added on the OSS side —
946
+ // `SectionRenderer.tsx`, `slice-renderers.ts` and `ModuleAbsent.tsx` — are
947
+ // deliberately NOT listed here: they ship, and listing one would take the
948
+ // whole dispatch table with it.
800
949
  ownedFiles: [
801
950
  "apps/api/src/routes/schedule/index.ts",
802
951
  "apps/api/src/services/scheduling/index.ts",
@@ -805,16 +954,36 @@ export const MODULE_SLICES = {
805
954
  "apps/mcp/src/tools/list_availability.ts",
806
955
  "apps/mcp/src/tools/get_event_meta.ts",
807
956
  "apps/mcp/src/tools/create_booking.ts",
808
- "packages/ui/src/sections/Booker.tsx",
809
- "packages/ui/src/sections/BookerIsland.tsx",
810
- "packages/ui/src/blocks/BookerPanel.tsx",
811
- "packages/ui/src/blocks/BookerSlotGrid.tsx",
812
- "packages/ui/src/blocks/BookerAttendeeFields.tsx",
813
- "packages/ui/src/blocks/BookerEventHeader.tsx",
814
- "packages/ui/src/blocks/BookerDateStrip.tsx",
815
- "packages/ui/src/sections/slices/scheduling.ts",
816
- "apps/web/app/[locale]/schedule/page.tsx",
817
- "apps/web/app/[locale]/schedule/confirmed/page.tsx",
957
+ "apps/web/app/[locale]/meet/page.tsx",
958
+ "apps/web/app/[locale]/meet/[slug]/page.tsx",
959
+ "apps/web/app/[locale]/meet/confirmed/page.tsx",
960
+ ],
961
+ // pageContributions (spec 5478; parent § D6 shape 1) — the booker, the
962
+ // per-booking-type page and the confirmation page, whose route files
963
+ // `ownedFiles` above already carries. No row names seed content: all
964
+ // three render from the /v1/schedule/* routes, not from an MDX file.
965
+ pageContributions: [
966
+ { route: "/meet" },
967
+ { route: "/meet/[slug]" },
968
+ { route: "/meet/confirmed" },
969
+ ],
970
+ // patches (#5581 U3, D5) — delivers the extracted component package to a
971
+ // keyed scaffold's apps/web, mirroring the signal-collectors slice's
972
+ // identical merge-keys op below.
973
+ patches: [
974
+ {
975
+ target: "apps/web/package.json",
976
+ kind: "json",
977
+ ops: [
978
+ {
979
+ op: "merge-keys",
980
+ path: ["dependencies"],
981
+ entries: {
982
+ "@working-theory/ui-scheduling": "workspace:*",
983
+ },
984
+ },
985
+ ],
986
+ },
818
987
  ],
819
988
  },
820
989
  // ── signal-collectors — flows, deliverables, project listings, waitlist
@@ -898,10 +1067,23 @@ export const MODULE_SLICES = {
898
1067
  // packages/ui: eleven components (D8) plus the NEW slice registration
899
1068
  // entry point (D8a, mirroring sections/slices/scheduling.ts). Four of the
900
1069
  // eleven (ProjectListingShelves, ListingPrivacyNotice, ProjectListingCard,
901
- // ResultsReveal) have no consumer anywhere in the repo today — grep-
902
- // confirmed orphaned exports, not a module-boundary fact; D8 places them
903
- // here by subject (project-listing / results-reveal UI) rather than by
904
- // reachability, since reachability cannot place an unreached file either.
1070
+ // ResultsReveal) had no consumer anywhere IN THIS REPO at D8 authoring
1071
+ // time — grep-confirmed, a module-boundary fact only in-repo. Three of the
1072
+ // four (ProjectListingShelves, ProjectListingCard, ListingPrivacyNotice —
1073
+ // plus the ListingWindowBand type ProjectListingCard exports) are, in
1074
+ // fact, consumed CROSS-ORG: the samhenry site (salessmyth org) imports all
1075
+ // four from the signal-collectors slice's re-export entry point
1076
+ // (packages/ui/src/sections/slices/signal-collectors.ts, rules/cross-org-
1077
+ // sharing.md), which #5488 additionally re-exports them from so that
1078
+ // consumer can reach them by subpath. "No consumer anywhere in the repo"
1079
+ // was therefore true in-repo and false across the org boundary — not a
1080
+ // reachability fact a repo-scoped grep can see. ResultsReveal remains the
1081
+ // one component of the four with no known consumer anywhere (in-repo or
1082
+ // out) and is NOT re-exported by #5488 — it is not this card's claim to
1083
+ // make it non-orphaned. D8's placement rule (by subject — project-listing
1084
+ // / results-reveal UI — rather than by reachability, since reachability
1085
+ // cannot place an unreached file either) is unchanged and still governs
1086
+ // where all eleven live.
905
1087
  "signal-collectors": {
906
1088
  ownedFiles: [
907
1089
  "apps/api/src/routes/flows/index.ts",
@@ -956,6 +1138,72 @@ export const MODULE_SLICES = {
956
1138
  "apps/api/src/bin/seed-waitlist-experiments.ts",
957
1139
  "apps/mcp/src/tools/get_flow.ts",
958
1140
  "apps/mcp/src/tools/submit_flow_step.ts",
1141
+ // ── The review surface (#5677) — CO-OWNED with multi-tenant ──────────
1142
+ //
1143
+ // Every path below is ALSO a multi-tenant `ownedFiles` entry and stays
1144
+ // one. Listing a file under two modules is additive, not a transfer:
1145
+ // `paidModuleTemplateExclusions()` unions every paid module's entries,
1146
+ // so the `templates/` snapshot is unchanged, and a multi-tenant scaffold
1147
+ // keeps receiving these from the multi-tenant bundle exactly as before.
1148
+ // What changes is that a signal-collectors bundle now carries them too.
1149
+ //
1150
+ // WHY THEY MUST BE HERE. #5677 widened `server.ts`'s review-surface
1151
+ // registration from `multi-tenant` to `multi-tenant || signal-collectors`,
1152
+ // because signal-collectors is the module that SELLS the instruments
1153
+ // these routes read back. Registration and DELIVERY are separate layers:
1154
+ // the widened gate calls `sliceRoutePlugin("waitlistExperimentRoutes")`
1155
+ // and imports `projectListingTenantRoutes`, so on a scaffold whose
1156
+ // `AILK_ENABLED_MODULES` names signal-collectors but whose tree lacks
1157
+ // these files, `loadSlice` throws ERR_MODULE_NOT_FOUND during `build()`
1158
+ // — construction, so EVERY route 500s rather than one. That is not a
1159
+ // feature gap; it is the outage shape `lib/slice-load.ts` exists to
1160
+ // diagnose.
1161
+ //
1162
+ // It is not hypothetical: `marketing-site` — the Pro flagship in
1163
+ // `template-manifest.ts` — declares
1164
+ // `requires: ["marketing", "docs-blog", "scheduling", "signal-collectors"]`,
1165
+ // i.e. signal-collectors WITHOUT multi-tenant. That is exactly the
1166
+ // configuration that would crash, which is why the gate and the delivery
1167
+ // land together rather than the delivery being deferred.
1168
+ //
1169
+ // The closure is tight, verified by grep rather than assumed: none of
1170
+ // these files imports `lib/tenant-db.ts` or `services/tenant-context.ts`
1171
+ // (both multi-tenant-only). #5677 retargeted `waitlist-dashboard.ts`'s
1172
+ // type import of `tenant-context.js` to the base `services/default-tenant.ts`,
1173
+ // which severed the one cross-slice edge that existed.
1174
+ //
1175
+ // `routes/project-listings/{start,patch-draft,submit,public,me,respond,
1176
+ // resume-token,patch-copy}.ts` are NOT listed — they are the free
1177
+ // anonymous surface and the mixed barrel, covered by the note above this
1178
+ // array, and they already ship unconditionally.
1179
+ "apps/api/src/routes/waitlist-experiments/index.ts",
1180
+ "apps/api/src/routes/waitlist-experiments/list.ts",
1181
+ "apps/api/src/routes/waitlist-experiments/comparison.ts",
1182
+ "apps/api/src/routes/waitlist-experiments/signup.ts",
1183
+ "apps/api/src/routes/waitlist-experiments/signups.ts",
1184
+ "apps/api/src/routes/waitlist-experiments/respond.ts",
1185
+ "apps/api/src/services/waitlist-dashboard.ts",
1186
+ "apps/api/src/routes/project-listings/get.ts",
1187
+ "apps/api/src/routes/project-listings/list.ts",
1188
+ "apps/api/src/routes/project-listings/patch.ts",
1189
+ // The dashboard itself. Without these the Pro scaffold would boot with
1190
+ // reachable review routes and no UI to read them through, and the two
1191
+ // files that DO already ship into every scaffold
1192
+ // (`waitlist/format.ts`, `waitlist/components/ForbiddenState.tsx` — never
1193
+ // owned by any module) would stay orphaned. They import only
1194
+ // `@working-theory/api-client`, `@working-theory/ui` and
1195
+ // `@working-theory/validation`, all base packages, so this pulls in no
1196
+ // multi-tenant dependency.
1197
+ "apps/web/app/[locale]/(authed)/waitlist/page.tsx",
1198
+ "apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx",
1199
+ "apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx",
1200
+ "apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx",
1201
+ "apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx",
1202
+ "apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx",
1203
+ "apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx",
1204
+ "apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx",
1205
+ "apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx",
1206
+ "apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx",
959
1207
  // apps/web/app/[locale]/flows/** deliberately NOT listed here (unlike
960
1208
  // the docs-blog precedent's own `blog`/`docs` page.tsx entries above),
961
1209
  // even though every file under it is signal-collectors territory and
@@ -982,18 +1230,103 @@ export const MODULE_SLICES = {
982
1230
  // `NESTED_FOREIGN_MODULE_ENTRIES` for the companion fix that keeps its
983
1231
  // presence from falsely reading as "projects carried".
984
1232
  "apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx",
985
- "packages/ui/src/sections/ListingApplicationFlow.tsx",
986
- "packages/ui/src/sections/ProjectListingShelves.tsx",
987
- "packages/ui/src/sections/FlowStepper.tsx",
988
- "packages/ui/src/blocks/FlowSidePanel.tsx",
989
- "packages/ui/src/blocks/ListingFullPreview.tsx",
990
- "packages/ui/src/blocks/ListingQuestionReview.tsx",
991
- "packages/ui/src/blocks/ListingTilePreview.tsx",
992
- "packages/ui/src/blocks/ListingPrivacyNotice.tsx",
993
- "packages/ui/src/blocks/ProjectListingCard.tsx",
994
- "packages/ui/src/blocks/ResponseChart.tsx",
995
- "packages/ui/src/blocks/ResultsReveal.tsx",
996
- "packages/ui/src/sections/slices/signal-collectors.ts",
1233
+ // #5581 U4 — the eleven signal-collectors components + the slice's own
1234
+ // re-export entry point moved to packages-paid/ui-signal-collectors;
1235
+ // the package is delivered to a keyed scaffold via `patches` below,
1236
+ // not via `ownedFiles`/`installMap` (D5).
1237
+ // #5562 — the report reader at `/reports/<slug>`. UNLIKE the flow routes
1238
+ // above, this one DOES belong in `ownedFiles`, and for the same reason
1239
+ // `projects/apply/ListingApplicationClient.tsx` does: `/reports` is a URL
1240
+ // segment `MODULE_SURFACES["signal-collectors"] = ["flows"]` does not gate,
1241
+ // so `excludedRoutePaths()` would leave it in a keyless scaffold. It has no
1242
+ // upgrade-path fixture to protect either — it is net-new here, so nothing
1243
+ // in `templates/` can regress by its absence.
1244
+ "apps/web/app/[locale]/reports/[slug]/page.tsx",
1245
+ //
1246
+ // The two sections that route composes — `report-header` and
1247
+ // `reports-list` — are deliberately NOT owned here. They are
1248
+ // `SectionRenderer` DISPATCH entries with static imports, so stripping
1249
+ // them would leave the renderer with dangling imports in a keyless
1250
+ // scaffold; the escape hatch for a slice-owned dispatch entry is the
1251
+ // `ModuleAbsent` placeholder (`booker`, `payment-receipt`), which is
1252
+ // machinery neither of these needs. `document-viewer` (#5270) — the
1253
+ // reader this route is built around, and the section with the strongest
1254
+ // claim to being deliverables-only — is likewise unowned base
1255
+ // `packages/ui`. A section is vocabulary; the ROUTE is the module.
1256
+ ],
1257
+ // pageContributions (spec 5478; parent § D6 shape 2) — the three flow
1258
+ // routes, named exactly as `surfaces.ts`'s ROUTE_SINCE keys name them.
1259
+ // ROWS AND SEED ONLY: unlike docs-blog and scheduling above, this module's
1260
+ // route code is deliberately NOT in `ownedFiles` (see the comment inside
1261
+ // that array). The first two rows' code reaches a scaffold from the
1262
+ // `templates/` snapshot, retained because the `flows` surface is enabled;
1263
+ // the results row's code is `commerce`'s (`commerce.ownedFiles` carries
1264
+ // `results/[payload]/page.tsx` + its two client files below, and
1265
+ // `paidModuleTemplateExclusions` strips every paid module's owned files
1266
+ // from the snapshot unconditionally), so that one row rides a DIFFERENT
1267
+ // module's bundle — recorded in spec 5478 § 8 item 1, whose delivery
1268
+ // consequence is #5481's and #5464's. Declaring the rows here changes none
1269
+ // of it: a contribution is data, and which delivery shape carries the code
1270
+ // is a property of the module, not of the contribution.
1271
+ pageContributions: [
1272
+ { route: "/flows/[slug]" },
1273
+ { route: "/flows/[slug]/start" },
1274
+ { route: "/flows/[slug]/results/[payload]" },
1275
+ // #5562 — the report reader. Unlike the three rows above, this row's
1276
+ // code IS this module's own `ownedFiles` (see the note beside it), so
1277
+ // the contribution and the delivery shape carrying it agree here.
1278
+ { route: "/reports/[slug]" },
1279
+ ],
1280
+ // installMap (#5618 D6.3) — the module's ten sample files, moved here out
1281
+ // of `site-templates/marketing-site/` so the module OWNS its samples
1282
+ // (Rule B) and a keyed `--modules signal-collectors` scaffold with no
1283
+ // template lands them. Their in-repo home is
1284
+ // `packages/create-ailk/module-templates/signal-collectors/**`, mirroring
1285
+ // `commerce.installMap` below — the only other slice-level map — because
1286
+ // an `installMap` KEY is a repo-relative source the producer copies
1287
+ // literally, and `content/en/ProjectsList/projects.mdx` is ALSO this
1288
+ // repo's own live marketing copy (a different file), which
1289
+ // build-templates.mjs keeps out of every scaffold: under
1290
+ // `module-templates/` the two never meet.
1291
+ //
1292
+ // Four entries are real renames (the flow and waitlist seeds the API
1293
+ // reads, and the report PDF under apps/web/public/); the six MDX entries
1294
+ // are self-addressed because no `pageContributions` row declares
1295
+ // `/projects`, `/projects/[slug]` or `/thanks/[slug]` yet (spec § 7
1296
+ // finding 7 — declaring them changes the enabled-surface set and so the
1297
+ // Pro scaffold's file set, a separate decision). Once those rows exist
1298
+ // the six move to `pageContributions[].content` with no further
1299
+ // migration. `generate-sample-report.py` keeps its root-level
1300
+ // destination (finding 1 — AC7 forbids changing the Pro file set).
1301
+ installMap: {
1302
+ "packages/create-ailk/module-templates/signal-collectors/content/en/ProjectsList/projects.mdx": "content/en/ProjectsList/projects.mdx",
1303
+ "packages/create-ailk/module-templates/signal-collectors/content/en/Project/fit-check-funnel.mdx": "content/en/Project/fit-check-funnel.mdx",
1304
+ "packages/create-ailk/module-templates/signal-collectors/content/en/Project/launch-site-in-fourteen-days.mdx": "content/en/Project/launch-site-in-fourteen-days.mdx",
1305
+ "packages/create-ailk/module-templates/signal-collectors/content/en/ThankYouPage/contact.mdx": "content/en/ThankYouPage/contact.mdx",
1306
+ "packages/create-ailk/module-templates/signal-collectors/content/en/ThankYouPage/newsletter.mdx": "content/en/ThankYouPage/newsletter.mdx",
1307
+ "packages/create-ailk/module-templates/signal-collectors/content/en/ThankYouPage/apply.mdx": "content/en/ThankYouPage/apply.mdx",
1308
+ "packages/create-ailk/module-templates/signal-collectors/flows/fit-check.flow.json": "database/flows/fit-check.flow.json",
1309
+ "packages/create-ailk/module-templates/signal-collectors/waitlist/launch-waitlist.experiment.json": "database/waitlist/launch-waitlist.experiment.json",
1310
+ "packages/create-ailk/module-templates/signal-collectors/reports/sample-report.pdf": "apps/web/public/reports/sample-report.pdf",
1311
+ "packages/create-ailk/module-templates/signal-collectors/reports/generate-sample-report.py": "reports/generate-sample-report.py",
1312
+ },
1313
+ // patches (#5581 U4, D5) — delivers the extracted component package to a
1314
+ // keyed scaffold's apps/web, mirroring the workspace slice's identical
1315
+ // merge-keys op above.
1316
+ patches: [
1317
+ {
1318
+ target: "apps/web/package.json",
1319
+ kind: "json",
1320
+ ops: [
1321
+ {
1322
+ op: "merge-keys",
1323
+ path: ["dependencies"],
1324
+ entries: {
1325
+ "@working-theory/ui-signal-collectors": "workspace:*",
1326
+ },
1327
+ },
1328
+ ],
1329
+ },
997
1330
  ],
998
1331
  },
999
1332
  // ── commerce — per-site paid add-on (D-47, amending D-26) ─────────────────
@@ -1091,32 +1424,10 @@ export const MODULE_SLICES = {
1091
1424
  "apps/api/src/routes/flows/recommender.ts",
1092
1425
  "apps/api/src/services/recommender-capture.ts",
1093
1426
  "apps/api/src/services/flow-checkouts.ts",
1094
- "packages/ui/src/blocks/CheckoutForm.tsx",
1095
- "packages/ui/src/blocks/CheckoutForm.text.ts",
1096
- "packages/ui/src/blocks/CheckoutPriceCard.tsx",
1097
- "packages/ui/src/blocks/PaymentLinkCard.tsx",
1098
- "packages/ui/src/blocks/PaymentLinkCard.text.ts",
1099
- "packages/ui/src/blocks/PaymentLinkForm.tsx",
1100
- "packages/ui/src/blocks/PaymentLinkForm.text.ts",
1101
- "packages/ui/src/blocks/PaymentLinkList.tsx",
1102
- "packages/ui/src/blocks/InvoiceForm.tsx",
1103
- "packages/ui/src/blocks/InvoiceForm.text.ts",
1104
- "packages/ui/src/blocks/InvoiceList.tsx",
1105
- "packages/ui/src/blocks/InvoiceList.text.ts",
1106
- "packages/ui/src/blocks/SubscriptionStatus.tsx",
1107
- "packages/ui/src/blocks/SubscriptionStatus.text.ts",
1108
- "packages/ui/src/blocks/StripeActionButton.tsx",
1109
- "packages/ui/src/blocks/PaymentStatusBadge.tsx",
1110
- "packages/ui/src/blocks/PaymentStatusBadge.text.ts",
1111
- "packages/ui/src/blocks/PaymentResult.tsx",
1112
- "packages/ui/src/blocks/PaymentResult.text.ts",
1113
- "packages/ui/src/blocks/PortalEntry.tsx",
1114
- "packages/ui/src/panels/InvoiceDetail.tsx",
1115
- "packages/ui/src/panels/InvoiceDetail.text.ts",
1116
- "packages/ui/src/sections/PaymentReceipt.tsx",
1117
- "packages/ui/src/sections/PricingTable.tsx",
1118
- "packages/ui/src/sections/PricingTable.text.ts",
1119
- "packages/ui/src/sections/slices/commerce.ts",
1427
+ // #5581 U5 — the fifteen commerce components (+ ten .text.ts extractors
1428
+ // + the slice's own re-export entry point) moved to
1429
+ // packages-paid/ui-commerce; the package is delivered to a keyed
1430
+ // scaffold via `patches` below, not via `ownedFiles`/`installMap` (D5).
1120
1431
  "apps/web/app/[locale]/flows/[slug]/checkout.actions.ts",
1121
1432
  "apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx",
1122
1433
  "apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx",
@@ -1147,6 +1458,24 @@ export const MODULE_SLICES = {
1147
1458
  "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts": ".claude/skills/scaffold-commerce/templates/stripe-config.template.ts",
1148
1459
  "packages/create-ailk/module-templates/commerce/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts": ".claude/skills/scaffold-commerce/templates/subscription-route.template.ts",
1149
1460
  },
1461
+ // patches (#5581 U5, D5) — delivers the extracted component package to a
1462
+ // keyed scaffold's apps/web, mirroring the workspace slice's identical
1463
+ // merge-keys op above.
1464
+ patches: [
1465
+ {
1466
+ target: "apps/web/package.json",
1467
+ kind: "json",
1468
+ ops: [
1469
+ {
1470
+ op: "merge-keys",
1471
+ path: ["dependencies"],
1472
+ entries: {
1473
+ "@working-theory/ui-commerce": "workspace:*",
1474
+ },
1475
+ },
1476
+ ],
1477
+ },
1478
+ ],
1150
1479
  },
1151
1480
  };
1152
1481
  /**
@@ -1194,6 +1523,48 @@ export function normalizeModules(modules) {
1194
1523
  * Passed in rather than derived from `import.meta.url`, which ts-jest cannot
1195
1524
  * load — the same reason `sync-routes.ts` documents for staying pure.
1196
1525
  */
1526
+ /**
1527
+ * Scaffold destinations that MORE THAN ONE module's `ownedFiles` declares —
1528
+ * the co-ownership the `#5618 D4` install-time ownership registry must treat
1529
+ * as intentional rather than as an overwrite (#5702 follow-up).
1530
+ *
1531
+ * Co-ownership is how a file reaches a scaffold under either of two modules
1532
+ * without being transferred away from the other. #5677 needed it for the
1533
+ * submissions review surface: the routes register under
1534
+ * `multi-tenant || signal-collectors`, so BOTH bundles must be able to
1535
+ * deliver them, or the module that lacks them throws
1536
+ * `ERR_MODULE_NOT_FOUND` during `build()`.
1537
+ *
1538
+ * `validateLayerOwnership` (`lib/extract-module-bundle.ts`) refuses a
1539
+ * destination an earlier layer already wrote, because a silent
1540
+ * last-writer-wins between two modules that disagree is the bug `#5618 D4`
1541
+ * exists to catch. Two modules that both DECLARE the same repo file are not
1542
+ * that case: they stage the same bytes from the same source, so whichever
1543
+ * lands second writes what is already there. Without this allowance a
1544
+ * scaffold selecting both modules — `saas-product`, whose `requires` names
1545
+ * `signal-collectors` AND `multi-tenant` — aborts mid-install, after the
1546
+ * first module's files are on disk.
1547
+ *
1548
+ * Only a DECLARED overlap is allowed. A destination two layers collide on
1549
+ * without both naming it in `ownedFiles` is still refused, which is the
1550
+ * whole invariant.
1551
+ */
1552
+ export function coOwnedModuleDestinations() {
1553
+ const seen = new Map();
1554
+ for (const slice of Object.values(MODULE_SLICES)) {
1555
+ // A module's own list is deduplicated first: repetition within one slice
1556
+ // is not co-ownership and must not license an overlap on its own.
1557
+ for (const file of new Set(slice.ownedFiles ?? [])) {
1558
+ seen.set(file, (seen.get(file) ?? 0) + 1);
1559
+ }
1560
+ }
1561
+ const coOwned = new Set();
1562
+ for (const [file, count] of seen) {
1563
+ if (count > 1)
1564
+ coOwned.add(file);
1565
+ }
1566
+ return coOwned;
1567
+ }
1197
1568
  export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
1198
1569
  const roots = new Set(snapshotRoots);
1199
1570
  const excluded = new Set();