create-ailk 0.4.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 (146) hide show
  1. package/component-catalog.json +11939 -0
  2. package/component-catalog.md +176 -64
  3. package/dist/cli.js +29 -1
  4. package/dist/derive-architecture.js +5 -2
  5. package/dist/lib/extract-module-bundle.d.ts +50 -2
  6. package/dist/lib/extract-module-bundle.js +223 -15
  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.js +4 -0
  11. package/dist/module-architecture.d.ts +59 -8
  12. package/dist/module-architecture.js +334 -115
  13. package/dist/parse-args.js +6 -2
  14. package/dist/programmatic.d.ts +10 -0
  15. package/dist/programmatic.js +134 -19
  16. package/dist/site-chrome.d.ts +80 -0
  17. package/dist/site-chrome.js +135 -0
  18. package/dist/surfaces.d.ts +21 -6
  19. package/dist/surfaces.js +107 -32
  20. package/dist/template-manifest.d.ts +55 -3
  21. package/dist/template-manifest.js +181 -12
  22. package/dist/template-names.d.ts +1 -1
  23. package/dist/template-names.js +1 -0
  24. package/package.json +9 -2
  25. package/site-templates/_base/ailk-template.json +6 -0
  26. package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
  27. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +331 -0
  28. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +393 -0
  29. package/site-templates/marketing-site/ailk-template.json +11 -0
  30. package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
  31. package/site-templates/marketing-site/provenance.json +4 -0
  32. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
  33. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +285 -0
  34. package/site-templates/marketing-site-free/ailk-template.json +2 -1
  35. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +271 -0
  36. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +324 -0
  37. package/site-templates/minimalism-site/ailk-template.json +21 -0
  38. package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
  39. package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
  40. package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
  41. package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
  42. package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
  43. package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
  44. package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
  45. package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
  46. package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
  47. package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
  48. package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
  49. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
  50. package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
  51. package/site-templates/minimalism-site/content/en/HomePage/home.mdx +214 -0
  52. package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
  53. package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
  54. package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
  55. package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
  56. package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
  57. package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
  58. package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
  59. package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
  60. package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
  61. package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
  62. package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
  63. package/site-templates/minimalism-site/provenance.json +4 -0
  64. package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
  65. package/site-templates/saas-product/ailk-template.json +16 -0
  66. package/site-templates/saas-product/provenance.json +10 -0
  67. package/templates/.claude/skills/README.md +16 -25
  68. package/templates/.env.example +32 -7
  69. package/templates/.github/workflows/ci.yml +48 -0
  70. package/templates/README.md +3 -1
  71. package/templates/apps/api/CLAUDE.md +4 -1
  72. package/templates/apps/api/eslint.config.mjs +69 -0
  73. package/templates/apps/api/package.json +6 -2
  74. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +6 -2
  75. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
  76. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +8 -1
  77. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  78. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +339 -0
  79. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +108 -0
  80. package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
  81. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
  82. package/templates/apps/api/src/routes/leads/list.ts +104 -0
  83. package/templates/apps/api/src/server.ts +82 -6
  84. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
  85. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
  86. package/templates/apps/api/src/services/default-tenant.ts +261 -0
  87. package/templates/apps/api/src/services/lead-promotion.ts +161 -0
  88. package/templates/apps/api/src/services/leads-inbox.ts +263 -0
  89. package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
  90. package/templates/apps/mcp/CLAUDE.md +1 -1
  91. package/templates/apps/mcp/__tests__/parity.test.ts +2 -2
  92. package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
  93. package/templates/apps/mcp/eslint.config.mjs +69 -0
  94. package/templates/apps/mcp/package.json +6 -2
  95. package/templates/apps/mcp/src/server.ts +2 -2
  96. package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
  97. package/templates/apps/mcp/src/tools/index.ts +2 -2
  98. package/templates/apps/web/.env.example +3 -3
  99. package/templates/apps/web/CLAUDE.md +2 -0
  100. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +4 -6
  101. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +1 -1
  102. package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
  103. package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
  104. package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
  105. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
  106. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
  107. package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
  108. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
  109. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
  110. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
  111. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
  112. package/templates/apps/web/app/[locale]/layout.tsx +214 -17
  113. package/templates/apps/web/components/google-analytics.tsx +18 -1
  114. package/templates/apps/web/eslint.config.mjs +69 -0
  115. package/templates/apps/web/jest.config.cjs +7 -15
  116. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +32 -5
  117. package/templates/apps/web/lib/nav-config.ts +27 -7
  118. package/templates/apps/web/lib/server-api.ts +1 -1
  119. package/templates/apps/web/lib/site-chrome.ts +29 -0
  120. package/templates/apps/web/lib/telemetry-scrub.ts +46 -0
  121. package/templates/apps/web/messages/en.json +29 -2
  122. package/templates/apps/web/next.config.mjs +20 -0
  123. package/templates/apps/web/package.json +6 -2
  124. package/templates/apps/web/sentry.client.config.ts +131 -0
  125. package/templates/apps/web/sentry.edge.config.ts +72 -0
  126. package/templates/apps/web/sentry.server.config.ts +72 -0
  127. package/templates/apps/web/src/lib/routes.ts +13 -18
  128. package/templates/brand_assets/README.md +24 -0
  129. package/templates/content/en/AboutPage/about.mdx +160 -15
  130. package/templates/content/en/ContactPage/contact.mdx +90 -13
  131. package/templates/content/en/FAQPage/faq.mdx +62 -22
  132. package/templates/content/en/HomePage/home.mdx +244 -20
  133. package/templates/content/en/LandingPage/blog.mdx +83 -0
  134. package/templates/content/en/LandingPage/features.mdx +103 -0
  135. package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
  136. package/templates/database/CHANGELOG.md +7 -0
  137. package/templates/database/__tests__/seed.test.ts +1 -1
  138. package/templates/database/eslint.config.mjs +69 -0
  139. package/templates/database/package.json +7 -3
  140. package/templates/database/seed.ts +1 -1
  141. package/templates/package.json +1 -14
  142. package/versions.json +52 -0
  143. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
  144. /package/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
  145. /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
  146. /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
@@ -253,39 +253,46 @@ 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.
268
259
  ],
269
260
  // pageContributions (spec 5478; parent § D6 shape 1) — the two route
270
261
  // 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
262
+ // nine MDX files are exactly the nine on the tree and cannot come from
272
263
  // the `templates/` snapshot: `apps/web/content` is in build-templates.mjs's
273
264
  // EXCLUDE_TEMPLATE_DIRS and `template-overlay/` carries no blog or docs
274
265
  // 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.
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).
278
277
  pageContributions: [
279
278
  {
280
279
  route: "/blog",
281
280
  content: [
282
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",
283
286
  "apps/web/content/en/blog/launching-ailk.mdx",
284
287
  ],
285
288
  },
286
289
  {
287
290
  route: "/docs",
288
- content: ["apps/web/content/en/docs/getting-started.mdx"],
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
+ ],
289
296
  },
290
297
  ],
291
298
  // patches (spec 5418 § 3 D1, Group G) — every Group G row that edits a
@@ -330,6 +337,8 @@ export const MODULE_SLICES = {
330
337
  "fumadocs-core": "^16.10.4",
331
338
  "fumadocs-mdx": "^15.0.12",
332
339
  "@working-theory/fumadocs-schema": "workspace:*",
340
+ // #5581 — the docs-blog module's paid component package (D5).
341
+ "@working-theory/ui-docs-blog": "workspace:*",
333
342
  },
334
343
  },
335
344
  {
@@ -396,13 +405,13 @@ export const MODULE_SLICES = {
396
405
  //
397
406
  // The FEAT-042 Organization → Workspace → Membership tenancy layer:
398
407
  // schema (auth/content namespaces) + workspace-scoped API (apps/api) +
399
- // the authed workspace app surface (apps/workspace). Single-tenant sites
408
+ // the authed Admin app surface (apps/admin). Single-tenant sites
400
409
  // (module absent) ship none of this — same base apps/web, fixed scope.
401
410
  "multi-tenant": {
402
411
  apps: {
403
- workspace: {
404
- path: "apps/workspace",
405
- 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.",
406
415
  may_import_from: [
407
416
  "database",
408
417
  "packages/templates",
@@ -411,10 +420,10 @@ export const MODULE_SLICES = {
411
420
  "packages/validation",
412
421
  // The `workspace` module's shared settings components (#5371 req. 6)
413
422
  // — the scaffolded-site twin of the root architecture.yaml grant on
414
- // `apps_paid.workspace`. Pro Plus inherits Pro's module set, so a
423
+ // `apps_paid.admin`. Pro Plus inherits Pro's module set, so a
415
424
  // saas-product scaffold carries both modules and this app composes
416
425
  // the identical panels apps/web does, with no forked copy.
417
- "packages/workspace-ui",
426
+ "packages/admin-ui",
418
427
  ],
419
428
  may_not_import_from: [
420
429
  "apps/api",
@@ -486,12 +495,12 @@ export const MODULE_SLICES = {
486
495
  description: "Stripe SDK wrapper + webhook signature verification",
487
496
  may_import_from: ["packages/validation"],
488
497
  },
489
- // apps/workspace presentation deps. templates is net-new here; theme/ui/
498
+ // apps/admin presentation deps. templates is net-new here; theme/ui/
490
499
  // validation duplicate the marketing slice's definitions VERBATIM so the
491
500
  // Object.assign merge in deriveArchitecture is order-independent (a
492
501
  // multi-tenant-only selection stays self-consistent).
493
502
  templates: {
494
- 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)",
495
504
  may_import_from: ["packages/theme", "packages/ui", "packages/validation"],
496
505
  },
497
506
  theme: {
@@ -553,11 +562,12 @@ export const MODULE_SLICES = {
553
562
  // ownedFiles (S1, #5088). Verified against the current filesystem and
554
563
  // server.ts's module-conditional registration (isModuleEnabled('multi-tenant')).
555
564
  //
556
- // 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
557
566
  // this pass, excluding __tests__/node_modules), just moved here from
558
- // apps/workspace by a parallel workstream on this same branch. Unlike
559
- // every other entry below, this one is intentionally a glob, not an
560
- // 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.
561
571
  //
562
572
  // apps/api/src/routes/project-listings/ is MIXED (free anonymous surface
563
573
  // + tenant dashboard sharing one directory) — only the tenant-guarded
@@ -674,15 +684,36 @@ export const MODULE_SLICES = {
674
684
  "apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx",
675
685
  "apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx",
676
686
  "apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx",
677
- "apps-paid/workspace/**",
687
+ "apps-paid/admin/**",
678
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/",
679
710
  },
680
711
  // ── workspace — Pro module: the single-workspace settings shell (D-27/D-49)
681
712
  //
682
- // `/workspace` in apps/web's `(authed)` segment — General (rename), Site
713
+ // `/admin` in apps/web's `(authed)` segment — General (rename), Site
683
714
  // (identity / SEO / theme, over the already-shipped apps/api config routes)
684
715
  // 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
716
+ // `packages/admin-ui` package so the multi-tenant Admin app (the
686
717
  // Pro Plus app) composes the identical components rather than a fork.
687
718
  //
688
719
  // No `apps` / `namespaces` / `forbiddenPatterns` block: this module
@@ -692,14 +723,14 @@ export const MODULE_SLICES = {
692
723
  // A PERMISSION, not a physical slice (#5371 D1a) — the identical shape
693
724
  // docs-blog uses for `packages/fumadocs-schema`. `packages/` is never in
694
725
  // SNAPSHOT_DIRS, so the package reaches a buyer as an npm/bundle
695
- // dependency under `node_modules/`, never as a `packages/workspace-ui`
726
+ // dependency under `node_modules/`, never as a `packages/admin-ui`
696
727
  // source sibling; the `patches` entry below is what puts it in the
697
728
  // buyer's apps/web/package.json.
698
- webImports: ["packages/workspace-ui"],
729
+ webImports: ["packages/admin-ui"],
699
730
  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
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
703
734
  // composes AppShell from @working-theory/templates/shells — the same
704
735
  // composition apps/web's own account page uses.
705
736
  may_import_from: [
@@ -713,7 +744,7 @@ export const MODULE_SLICES = {
713
744
  // SOURCE files only, and only the ones this module physically owns: the
714
745
  // three apps/web route files and the two apps/api route files.
715
746
  //
716
- // `packages/workspace-ui/**` is deliberately NOT listed (D1a). It is a
747
+ // `packages/admin-ui/**` is deliberately NOT listed (D1a). It is a
717
748
  // whole package that is paid end to end, whose strip disposition is
718
749
  // carried by architecture.yaml's `commercial_tier_moves_to` field (its
719
750
  // future paid-tree home) rather than by a per-file inventory —
@@ -733,9 +764,9 @@ export const MODULE_SLICES = {
733
764
  // Test companions (`__tests__/`) are never listed — the SOURCE-files-only
734
765
  // rule above.
735
766
  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",
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",
739
770
  "apps/api/src/routes/workspaces/general.ts",
740
771
  "apps/api/src/routes/workspaces/members.ts",
741
772
  ],
@@ -754,7 +785,7 @@ export const MODULE_SLICES = {
754
785
  op: "merge-keys",
755
786
  path: ["dependencies"],
756
787
  entries: {
757
- "@working-theory/workspace-ui": "workspace:*",
788
+ "@working-theory/admin-ui": "workspace:*",
758
789
  },
759
790
  },
760
791
  ],
@@ -770,8 +801,10 @@ export const MODULE_SLICES = {
770
801
  // the ONLY holder of the scheduling credential.
771
802
  // api apps/api's own /v1/schedule/availability + /v1/schedule/book.
772
803
  // presentation the `booker` section (packages/ui, no iframe) rendered by
773
- // the `schedule` route surface, plus the module-owned
774
- // 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).
775
808
  //
776
809
  // The four forbidden patterns below are the module's whole security
777
810
  // argument, restated as boundary rules rather than left in a comment: the
@@ -864,16 +897,19 @@ export const MODULE_SLICES = {
864
897
  // consumer — unlike packages/validation below, a strip tool CAN delete
865
898
  // exactly these files without touching anything another module needs.
866
899
  //
867
- // BOTH apps/web/app/[locale]/schedule pages are listed (#5182 Q9, disposed
868
- // 2026-09-11). confirmed/page.tsx says outright in its own doc comment that
869
- // "The module OWNS this page... a scaffold without the module has no such
870
- // route", and its sibling page.tsx is the SOLE production constructor of a
871
- // `booker` section: it builds the section literally in TypeScript
872
- // (page.tsx:60, `type: "booker"`) rather than reading it from content, and
873
- // its own header calls the route API-backed with "no content file that
874
- // could disagree with the calendar". The create-ailk scaffolder already
875
- // gates the whole `schedule` route surface (src/surfaces.ts), so the two
876
- // 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.
877
913
  //
878
914
  // packages/sections-registry/src/registry.ts (the `booker:` entry) and
879
915
  // packages/validation/src/schemas/page-sections.ts (BookerSectionDataSchema,
@@ -902,13 +938,14 @@ export const MODULE_SLICES = {
902
938
  // scheduling-specific. This is a judgment call, not covered by this
903
939
  // card's explicit checklist: flagged for reviewer confirmation.
904
940
  //
905
- // `packages/ui/src/sections/slices/scheduling.ts` (#5214) is the slice's own
906
- // registration entry point: it statically imports Booker and registers it as
907
- // the `booker` renderer, so it is the one file inside packages/ui that may
908
- // reach a scheduling component — because it strips alongside them. The three
909
- // files that path added on the OSS side — `SectionRenderer.tsx`,
910
- // `slice-renderers.ts` and `ModuleAbsent.tsx` — are deliberately NOT listed
911
- // 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.
912
949
  ownedFiles: [
913
950
  "apps/api/src/routes/schedule/index.ts",
914
951
  "apps/api/src/services/scheduling/index.ts",
@@ -917,24 +954,36 @@ export const MODULE_SLICES = {
917
954
  "apps/mcp/src/tools/list_availability.ts",
918
955
  "apps/mcp/src/tools/get_event_meta.ts",
919
956
  "apps/mcp/src/tools/create_booking.ts",
920
- "packages/ui/src/sections/Booker.tsx",
921
- "packages/ui/src/sections/BookerIsland.tsx",
922
- "packages/ui/src/blocks/BookerPanel.tsx",
923
- "packages/ui/src/blocks/BookerSlotGrid.tsx",
924
- "packages/ui/src/blocks/BookerAttendeeFields.tsx",
925
- "packages/ui/src/blocks/BookerEventHeader.tsx",
926
- "packages/ui/src/blocks/BookerDateStrip.tsx",
927
- "packages/ui/src/sections/slices/scheduling.ts",
928
- "apps/web/app/[locale]/schedule/page.tsx",
929
- "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",
930
960
  ],
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.
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.
935
965
  pageContributions: [
936
- { route: "/schedule" },
937
- { route: "/schedule/confirmed" },
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
+ },
938
987
  ],
939
988
  },
940
989
  // ── signal-collectors — flows, deliverables, project listings, waitlist
@@ -1089,6 +1138,72 @@ export const MODULE_SLICES = {
1089
1138
  "apps/api/src/bin/seed-waitlist-experiments.ts",
1090
1139
  "apps/mcp/src/tools/get_flow.ts",
1091
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",
1092
1207
  // apps/web/app/[locale]/flows/** deliberately NOT listed here (unlike
1093
1208
  // the docs-blog precedent's own `blog`/`docs` page.tsx entries above),
1094
1209
  // even though every file under it is signal-collectors territory and
@@ -1115,18 +1230,29 @@ export const MODULE_SLICES = {
1115
1230
  // `NESTED_FOREIGN_MODULE_ENTRIES` for the companion fix that keeps its
1116
1231
  // presence from falsely reading as "projects carried".
1117
1232
  "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",
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.
1130
1256
  ],
1131
1257
  // pageContributions (spec 5478; parent § D6 shape 2) — the three flow
1132
1258
  // routes, named exactly as `surfaces.ts`'s ROUTE_SINCE keys name them.
@@ -1146,6 +1272,61 @@ export const MODULE_SLICES = {
1146
1272
  { route: "/flows/[slug]" },
1147
1273
  { route: "/flows/[slug]/start" },
1148
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
+ },
1149
1330
  ],
1150
1331
  },
1151
1332
  // ── commerce — per-site paid add-on (D-47, amending D-26) ─────────────────
@@ -1243,32 +1424,10 @@ export const MODULE_SLICES = {
1243
1424
  "apps/api/src/routes/flows/recommender.ts",
1244
1425
  "apps/api/src/services/recommender-capture.ts",
1245
1426
  "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",
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).
1272
1431
  "apps/web/app/[locale]/flows/[slug]/checkout.actions.ts",
1273
1432
  "apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx",
1274
1433
  "apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx",
@@ -1299,6 +1458,24 @@ export const MODULE_SLICES = {
1299
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",
1300
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",
1301
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
+ ],
1302
1479
  },
1303
1480
  };
1304
1481
  /**
@@ -1346,6 +1523,48 @@ export function normalizeModules(modules) {
1346
1523
  * Passed in rather than derived from `import.meta.url`, which ts-jest cannot
1347
1524
  * load — the same reason `sync-routes.ts` documents for staying pure.
1348
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
+ }
1349
1568
  export function paidModuleTemplateExclusions(snapshotRoots, repoRoot) {
1350
1569
  const roots = new Set(snapshotRoots);
1351
1570
  const excluded = new Set();