create-ailk 0.7.0 → 0.8.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 (169) hide show
  1. package/README.md +44 -10
  2. package/component-catalog.json +520 -5
  3. package/component-catalog.md +9 -6
  4. package/dist/cli.js +22 -24
  5. package/dist/lib/extract-module-bundle.js +5 -1
  6. package/dist/lib/module-license-gate.d.ts +79 -0
  7. package/dist/lib/module-license-gate.js +160 -12
  8. package/dist/lib/module-tier.d.ts +45 -16
  9. package/dist/lib/module-tier.js +76 -0
  10. package/dist/lib/pro-callout.js +10 -2
  11. package/dist/parse-args.d.ts +78 -10
  12. package/dist/parse-args.js +204 -14
  13. package/dist/programmatic.d.ts +39 -9
  14. package/dist/programmatic.js +163 -25
  15. package/dist/site-roster.js +11 -6
  16. package/dist/starter-grid.d.ts +216 -0
  17. package/dist/starter-grid.js +449 -0
  18. package/dist/surfaces.d.ts +4 -4
  19. package/dist/surfaces.js +102 -43
  20. package/dist/template-manifest.d.ts +36 -17
  21. package/dist/template-manifest.js +121 -367
  22. package/dist/template-names.d.ts +14 -12
  23. package/dist/template-names.js +13 -17
  24. package/package.json +3 -3
  25. package/site-templates/_base/content/en/BookCall/book-call.mdx +0 -1
  26. package/site-templates/_base/content/en/Feature/feature-1.mdx +69 -0
  27. package/site-templates/_base/content/en/Feature/feature-2.mdx +69 -0
  28. package/site-templates/_base/content/en/Feature/feature-3.mdx +69 -0
  29. package/site-templates/_base/content/en/FeaturesList/features.mdx +91 -0
  30. package/site-templates/_base/content/en/GlossaryList/glossary.mdx +0 -1
  31. package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +0 -1
  32. package/site-templates/_base/content/en/PortfolioItem/sample-four.mdx +105 -0
  33. package/site-templates/_base/content/en/PortfolioItem/sample-one.mdx +34 -0
  34. package/site-templates/_base/content/en/PortfolioItem/sample-three.mdx +94 -0
  35. package/site-templates/_base/content/en/PortfolioItem/sample-two.mdx +45 -0
  36. package/site-templates/_base/content/en/PortfolioList/portfolio.mdx +169 -0
  37. package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +15 -0
  38. package/site-templates/_base/content/en/UseCase/persona-1.mdx +69 -0
  39. package/site-templates/_base/content/en/UseCase/persona-2.mdx +69 -0
  40. package/site-templates/_base/content/en/UseCase/persona-3.mdx +69 -0
  41. package/site-templates/designs/standard/DESIGN.md +5 -0
  42. package/site-templates/marketing-site/DESIGN.md +5 -0
  43. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +89 -25
  44. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +35 -14
  45. package/site-templates/marketing-site-free/DESIGN.md +5 -0
  46. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +95 -29
  47. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +74 -44
  48. package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +59 -0
  49. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +199 -0
  50. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +17 -29
  51. package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +0 -1
  52. package/site-templates/minimalism-site/content/en/PurchasePolicyPage/purchase-policy.mdx +0 -1
  53. package/site-templates/saas-product/DESIGN.md +5 -0
  54. package/site-templates/saas-product/TEMPLATE-MANIFEST.md +9 -1
  55. package/site-templates/{simple-service → shapes/local-business-service}/TEMPLATE-MANIFEST.json +96 -76
  56. package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +302 -0
  57. package/site-templates/shapes/local-business-service/ailk-template.json +10 -0
  58. package/site-templates/{simple-service → shapes/local-business-service}/content/en/AboutPage/about.mdx +4 -4
  59. package/site-templates/shapes/local-business-service/content/en/HomePage/home.mdx +70 -0
  60. package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
  61. package/site-templates/shapes/local-business-service/content/en/PortfolioList/portfolio.mdx +171 -0
  62. package/site-templates/{simple-service → shapes/local-business-service}/content/en/PurchasePolicyPage/purchase-policy.mdx +7 -8
  63. package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +39 -0
  64. package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +39 -0
  65. package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +39 -0
  66. package/site-templates/shapes/local-business-service/content/en/ServicesList/services.mdx +61 -0
  67. package/site-templates/shapes/local-business-service/provenance.json +4 -0
  68. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +362 -0
  69. package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +300 -0
  70. package/site-templates/shapes/organization-service/ailk-template.json +10 -0
  71. package/site-templates/shapes/organization-service/content/en/AboutPage/about.mdx +38 -0
  72. package/site-templates/shapes/organization-service/content/en/HomePage/home.mdx +85 -0
  73. package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
  74. package/site-templates/shapes/organization-service/content/en/PortfolioList/portfolio.mdx +171 -0
  75. package/site-templates/shapes/organization-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
  76. package/site-templates/shapes/organization-service/content/en/Service/first-engagement.mdx +38 -0
  77. package/site-templates/shapes/organization-service/content/en/Service/second-engagement.mdx +38 -0
  78. package/site-templates/shapes/organization-service/content/en/Service/third-engagement.mdx +38 -0
  79. package/site-templates/shapes/organization-service/content/en/ServicesList/services.mdx +72 -0
  80. package/site-templates/shapes/organization-service/provenance.json +4 -0
  81. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +369 -0
  82. package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +299 -0
  83. package/site-templates/shapes/person-service/ailk-template.json +10 -0
  84. package/site-templates/shapes/person-service/content/_site.mdx +85 -0
  85. package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +39 -0
  86. package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +97 -0
  87. package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
  88. package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +169 -0
  89. package/site-templates/shapes/person-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
  90. package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +38 -0
  91. package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +38 -0
  92. package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +38 -0
  93. package/site-templates/{simple-service → shapes/person-service}/content/en/ServicesList/services.mdx +16 -16
  94. package/site-templates/shapes/person-service/provenance.json +4 -0
  95. package/templates/.claude/skills/README.md +3 -2
  96. package/templates/.claude/skills/add-page/references/page-types.md +4 -0
  97. package/templates/.claude/skills/setup-database/SKILL.md +156 -0
  98. package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +154 -0
  99. package/templates/.claude/skills/setup-deploy/SKILL.md +64 -32
  100. package/templates/.claude/skills/setup-deploy/references/env-vars-template.md +19 -28
  101. package/templates/.claude/skills/setup-deploy/references/runbook-template.md +58 -102
  102. package/templates/DESIGN.md +5 -0
  103. package/templates/README.md +46 -38
  104. package/templates/apps/api/CLAUDE.md +3 -2
  105. package/templates/apps/api/src/config/__tests__/index.test.ts +115 -14
  106. package/templates/apps/api/src/config/index.ts +35 -16
  107. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +30 -0
  108. package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +96 -0
  109. package/templates/apps/api/src/lib/lead-capture-store.ts +12 -0
  110. package/templates/apps/api/src/lib/lead-delivery.ts +52 -0
  111. package/templates/apps/api/src/openapi/spec.ts +63 -12
  112. package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +88 -0
  113. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +100 -0
  114. package/templates/apps/api/src/routes/leads/index.ts +46 -1
  115. package/templates/apps/api/src/server.ts +7 -0
  116. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +194 -0
  117. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +124 -0
  118. package/templates/apps/api/src/services/lead-routing.ts +140 -30
  119. package/templates/apps/api/src/services/leads-inbox.ts +48 -11
  120. package/templates/apps/mcp/__tests__/parity.test.ts +5 -4
  121. package/templates/apps/mcp/src/tools/create_project.ts +21 -6
  122. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +14 -6
  123. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +10 -9
  124. package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-text.test.ts +32 -0
  125. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +129 -0
  126. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +227 -84
  127. package/templates/apps/web/app/[locale]/contact/contact-form-text.ts +50 -0
  128. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +73 -20
  129. package/templates/apps/web/app/[locale]/contact/page.tsx +105 -15
  130. package/templates/apps/web/app/[locale]/contact/site-contact-email.ts +54 -0
  131. package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +64 -0
  132. package/templates/apps/web/app/[locale]/features/page.tsx +52 -0
  133. package/templates/apps/web/app/[locale]/layout.tsx +37 -5
  134. package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +75 -0
  135. package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +163 -0
  136. package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +66 -0
  137. package/templates/apps/web/app/[locale]/newsletter/page.tsx +106 -14
  138. package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +59 -0
  139. package/templates/apps/web/app/[locale]/portfolio/page.tsx +49 -0
  140. package/templates/apps/web/app/[locale]/use-cases/[slug]/page.tsx +18 -3
  141. package/templates/apps/web/lib/nav-config.ts +48 -36
  142. package/templates/apps/web/lib/site-identity.ts +14 -0
  143. package/templates/apps/web/messages/en.json +28 -2
  144. package/templates/content/_site.mdx +4 -2
  145. package/templates/content/en/BlogList/blog.mdx +37 -16
  146. package/templates/content/en/ContactPage/contact.mdx +14 -44
  147. package/templates/content/en/HomePage/home.mdx +8 -16
  148. package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +0 -1
  149. package/templates/content/en/UseCasesList/use-cases.mdx +6 -6
  150. package/templates/database/CHANGELOG.md +7 -0
  151. package/templates/database/package.json +1 -1
  152. package/versions.json +33 -33
  153. package/site-templates/_base/content/en/UseCase/first-use-case.mdx +0 -23
  154. package/site-templates/_base/content/en/UseCase/second-use-case.mdx +0 -23
  155. package/site-templates/_base/content/en/UseCase/third-use-case.mdx +0 -23
  156. package/site-templates/simple-service/DESIGN.md +0 -162
  157. package/site-templates/simple-service/TEMPLATE-MANIFEST.md +0 -333
  158. package/site-templates/simple-service/ailk-template.json +0 -11
  159. package/site-templates/simple-service/content/en/BookCall/book-call.mdx +0 -53
  160. package/site-templates/simple-service/content/en/ContactPage/contact.mdx +0 -32
  161. package/site-templates/simple-service/content/en/FAQPage/faq.mdx +0 -56
  162. package/site-templates/simple-service/content/en/HomePage/home.mdx +0 -85
  163. package/site-templates/simple-service/content/en/Service/landscape-design.mdx +0 -40
  164. package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +0 -40
  165. package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +0 -40
  166. package/site-templates/simple-service/provenance.json +0 -4
  167. package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +0 -211
  168. package/templates/apps/web/app/[locale]/contact/__tests__/ContactForm.test.tsx +0 -96
  169. package/templates/content/en/SoftwareProduct/product.mdx +0 -175
package/dist/surfaces.js CHANGED
@@ -81,13 +81,6 @@ export const CORE_ROUTE_DIRS = [
81
81
  // scaffold carrying it and no content is byte-for-byte a scaffold with one
82
82
  // more 404, nothing else.
83
83
  `${ROUTE_ROOT}/[slug]`,
84
- // #6658 D3 — the product's own detail page (`/software/product`, a
85
- // SoftwareProduct → SoftwareApplication), which replaced the /features
86
- // stand-in. Core for the same reason [slug] is: its content
87
- // (`content/en/SoftwareProduct/product.mdx`) is in the always-copied overlay
88
- // and the generated nav always links it, so every scaffold — template or
89
- // not — must carry the route.
90
- `${ROUTE_ROOT}/software`,
91
84
  // #6084 D12 (the operator's shape-2 disposition) — the checkout return
92
85
  // pair. The launchers pointing at them (`apps/mcp/src/tools/purchase.ts`,
93
86
  // `apps/web/.../dev/purchase/purchase.actions.ts`) are ALREADY
@@ -180,6 +173,13 @@ export const GATEABLE_SURFACES = [
180
173
  routes: [`${ROUTE_ROOT}/events`],
181
174
  content: ["content/<locale>/EventsList", "content/<locale>/Event"],
182
175
  },
176
+ {
177
+ // #6721 — the product's feature pages: a FeaturesList hub over one
178
+ // Feature page per feature. The Free roster enables it by its rows.
179
+ id: "features",
180
+ routes: [`${ROUTE_ROOT}/features`],
181
+ content: ["content/<locale>/FeaturesList", "content/<locale>/Feature"],
182
+ },
183
183
  {
184
184
  // API-backed (the flow engine); no MDX of its own.
185
185
  id: "flows",
@@ -240,6 +240,11 @@ export const GATEABLE_SURFACES = [
240
240
  routes: [`${ROUTE_ROOT}/partners`],
241
241
  content: ["content/<locale>/PartnersList", "content/<locale>/Partner"],
242
242
  },
243
+ {
244
+ id: "portfolio",
245
+ routes: [`${ROUTE_ROOT}/portfolio`],
246
+ content: ["content/<locale>/PortfolioList", "content/<locale>/PortfolioItem"],
247
+ },
243
248
  {
244
249
  // Module-LESS by construction (#5479 D16): no MODULE_SURFACES value names
245
250
  // it, so a site template may enable it by roster alone. Same for
@@ -276,6 +281,13 @@ export const GATEABLE_SURFACES = [
276
281
  routes: [`${ROUTE_ROOT}/services`],
277
282
  content: ["content/<locale>/ServicesList", "content/<locale>/Service"],
278
283
  },
284
+ {
285
+ // #6721 — the product's own page (#6658 D3), a gateable surface again now
286
+ // that the Free roster links /features instead of /software/product.
287
+ id: "software",
288
+ routes: [`${ROUTE_ROOT}/software`],
289
+ content: ["content/<locale>/SoftwareProduct"],
290
+ },
279
291
  {
280
292
  id: "tech-articles",
281
293
  routes: [`${ROUTE_ROOT}/tech-articles`],
@@ -319,7 +331,10 @@ export const SURFACE_ALIASES = {
319
331
  * so a site scaffolded with `--surfaces software` keeps working: its route
320
332
  * is copied regardless.
321
333
  */
322
- export const CORE_PROMOTED_SURFACES = new Set(["software"]);
334
+ // #6721 — `software` is a gateable surface again (the Free roster now carries
335
+ // /features + /features/[slug] instead); the set is kept, empty, so the
336
+ // accept-and-drop read sites stay in place for the next promotion.
337
+ export const CORE_PROMOTED_SURFACES = new Set([]);
323
338
  /** Canonical id for a `--surfaces` value, resolving a retired alias. */
324
339
  function canonicalizeSurface(s) {
325
340
  return SURFACE_ALIASES[s] ?? s;
@@ -509,6 +524,9 @@ export const ROUTE_SINCE = {
509
524
  downloads: "0.0.0",
510
525
  events: "0.0.0",
511
526
  "events/[slug]": "0.0.0",
527
+ // #6721 — the feature pages, a new route pair at today's line.
528
+ features: "0.27.0",
529
+ "features/[slug]": "0.27.0",
512
530
  faq: "0.0.0",
513
531
  "flows/[slug]": "0.0.0",
514
532
  "flows/[slug]/results/[payload]": "0.14.0",
@@ -531,6 +549,9 @@ export const ROUTE_SINCE = {
531
549
  // #5479 — three genuinely new route directories, so they attach at today's
532
550
  // fixed-group version rather than the "predates the manifest" 0.0.0 above,
533
551
  // the same rule #5048 ("projects/apply") and #5014 ("(authed)") applied.
552
+ // #6687 — the before/after portfolio, a new route pair at today's line.
553
+ portfolio: "0.27.0",
554
+ "portfolio/[slug]": "0.27.0",
534
555
  pricing: "0.22.0",
535
556
  privacy: "0.22.0",
536
557
  products: "0.0.0",
@@ -750,10 +771,10 @@ export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
750
771
  *
751
772
  * The base items are the Free marketing-site chrome (#5469 D5, the approved
752
773
  * chrome diff recorded on that card, re-pointed by #6658 D8):
753
- * a Product flyout (Use cases · Features · FAQ), then Use cases, Blog, Pricing
754
- * and About. Features links the product's own page (`/software/product`), a
755
- * core route like `/faq` and `/about`; `/blog` is always linked (the teaser or
756
- * the docs-blog module serves it). Use cases and Pricing are gateable
774
+ * a Product flyout in two columns (Features · Use Cases, #6721), then Blog,
775
+ * Pricing and About. The feature and use-case links ride the `features` and
776
+ * `use-cases` surfaces; `/blog` is always linked (the teaser or the docs-blog
777
+ * module serves it). Pricing is gateable
757
778
  * surfaces: each item is emitted only when its surface is enabled (a template
758
779
  * roster enables it, #5469 D10), so the nav still never links a page the
759
780
  * scaffold does not carry.
@@ -781,49 +802,77 @@ export function renderNavConfig(enabled) {
781
802
  s.id !== "blog" &&
782
803
  s.id !== "use-cases");
783
804
  const useCases = on.has("use-cases");
805
+ const features = on.has("features");
806
+ // #6721 (operator ruling 2026-09-25): the Product flyout is two columns —
807
+ // Features and Use Cases — each a heading over three label-only links. The
808
+ // `groups` render the desktop panel; `features` carries the same links for
809
+ // the mobile disclosure and the active-path match. "Use cases" left the top
810
+ // nav (it lives in the flyout and its own footer column).
811
+ const featureLinks = features
812
+ ? [
813
+ ' { labelKey: "feature1", path: "/features/feature-1" },',
814
+ ' { labelKey: "feature2", path: "/features/feature-2" },',
815
+ ' { labelKey: "feature3", path: "/features/feature-3" },',
816
+ ]
817
+ : [];
818
+ const useCaseLinks = useCases
819
+ ? [
820
+ ' { labelKey: "useCase1", path: "/use-cases/persona-1" },',
821
+ ' { labelKey: "useCase2", path: "/use-cases/persona-2" },',
822
+ ' { labelKey: "useCase3", path: "/use-cases/persona-3" },',
823
+ ]
824
+ : [];
825
+ const indent = (line) => ` ${line}`;
826
+ const group = (headingKey, links) => links.length === 0
827
+ ? []
828
+ : [
829
+ " {",
830
+ ` headingKey: "${headingKey}",`,
831
+ " items: [",
832
+ ...links.map(indent),
833
+ " ],",
834
+ " },",
835
+ ];
784
836
  const entries = [
785
837
  " {",
786
838
  ' labelKey: "product",',
787
839
  " flyout: {",
788
- // SOFT_LOCK_OK: #6508 — width "panel" (D4/D5): a fixed 640px, two-column
789
- // grid, replacing the "list"/"trigger" one-column menu. `presentation` is
790
- // omitted — it defaults to "grid" (D5). The `docs` action is dropped (it
791
- // duplicated the first feature's /how-it-works destination, § 1.4).
840
+ // SOFT_LOCK_OK: #6508 — width "panel" (D4/D5): a fixed 640px panel.
792
841
  ' width: "panel",',
793
842
  " features: [",
794
- ...(useCases
795
- ? [' { labelKey: "useCases", descKey: "useCasesDesc", icon: Layers, path: "/use-cases" },']
796
- : []),
797
- ' { labelKey: "features", descKey: "featuresDesc", icon: Sparkles, path: "/software/product" },',
798
- ' { labelKey: "faq", descKey: "faqDesc", icon: HelpCircle, path: "/faq" },',
843
+ ...featureLinks,
844
+ ...useCaseLinks,
845
+ " ],",
846
+ " groups: [",
847
+ ...group("flyoutFeaturesHeading", featureLinks),
848
+ ...group("flyoutUseCasesHeading", useCaseLinks),
799
849
  " ],",
800
850
  " actions: [],",
801
851
  " },",
802
852
  " },",
803
- // Operator ruling 2026-09-23: Product · Use cases · Blog · Pricing · About.
853
+ // Operator ruling 2026-09-25: Product · Blog · Pricing · About.
804
854
  // `/blog` is always linked — the `blog` surface serves the real route, and
805
855
  // with it off the `[slug]` route serves the overlay's module page.
806
- ...(useCases ? [' { labelKey: "useCases", path: "/use-cases" },'] : []),
807
856
  ' { labelKey: "blog", path: "/blog" },',
808
857
  ...(on.has("pricing") ? [' { labelKey: "pricing", path: "/pricing" },'] : []),
809
858
  ' { labelKey: "about", path: "/about" },',
810
859
  ...linked.map((s) => ` { labelKey: "${s.navLabelKey}", path: "/${s.id}" },`),
811
860
  ];
812
- // The footer's column groups (operator ruling 2026-09-23, replacing #6468's
813
- // Product/Company pair): Site pages, Features, Resources. Pricing rides the
814
- // same `pricing` gate as the header item; `/blog` is always linked (above).
815
- // The product links point at the product's own page, `/software/product`
816
- // (#6658 D3): `software` is core, so they stay unconditional. Book a call,
817
- // Glossary and Newsletter (#6658 D8) each ride their own surface's gate.
861
+ // The footer's column groups (operator rulings 2026-09-23 + 2026-09-25):
862
+ // Site pages, Features, Use Cases, Resources. Pricing rides the same
863
+ // `pricing` gate as the header item; `/blog` is always linked (above). The
864
+ // Features column links the three feature pages and the /features hub; the
865
+ // Use Cases column the three use cases and the /use-cases hub — each rides
866
+ // its surface's gate. Book a call, Glossary and Newsletter (#6658 D8) each
867
+ // ride their own surface's gate.
818
868
  const footerEntries = [
819
869
  " {",
820
870
  ' headingKey: "footerSitePagesHeading",',
821
871
  " items: [",
822
- ' { labelKey: "features", path: "/software/product" },',
872
+ ...(features ? [' { labelKey: "features", path: "/features" },'] : []),
823
873
  ...(on.has("pricing")
824
874
  ? [' { labelKey: "pricing", path: "/pricing" },']
825
875
  : []),
826
- ...(useCases ? [' { labelKey: "useCases", path: "/use-cases" },'] : []),
827
876
  ' { labelKey: "about", path: "/about" },',
828
877
  ' { labelKey: "contact", path: "/contact" },',
829
878
  ...(on.has("book-a-call")
@@ -831,17 +880,28 @@ export function renderNavConfig(enabled) {
831
880
  : []),
832
881
  " ],",
833
882
  " },",
834
- " {",
835
- ' headingKey: "footerFeaturesHeading",',
836
- " items: [",
837
- ' { labelKey: "feature1", path: "/software/product" },',
838
- ' { labelKey: "feature2", path: "/software/product" },',
839
- ' { labelKey: "feature3", path: "/software/product" },',
840
- ' { labelKey: "feature4", path: "/software/product" },',
841
- ' { labelKey: "feature5", path: "/software/product" },',
842
- ' { labelKey: "allFeatures", path: "/software/product" },',
843
- " ],",
844
- " },",
883
+ ...(features
884
+ ? [
885
+ " {",
886
+ ' headingKey: "footerFeaturesHeading",',
887
+ " items: [",
888
+ ...featureLinks.map((l) => l.slice(2)),
889
+ ' { labelKey: "allFeatures", path: "/features" },',
890
+ " ],",
891
+ " },",
892
+ ]
893
+ : []),
894
+ ...(useCases
895
+ ? [
896
+ " {",
897
+ ' headingKey: "footerUseCasesHeading",',
898
+ " items: [",
899
+ ...useCaseLinks.map((l) => l.slice(2)),
900
+ ' { labelKey: "allUseCases", path: "/use-cases" },',
901
+ " ],",
902
+ " },",
903
+ ]
904
+ : []),
845
905
  " {",
846
906
  ' headingKey: "footerResourcesHeading",',
847
907
  " items: [",
@@ -878,7 +938,6 @@ export function renderNavConfig(enabled) {
878
938
  // \`apps/web/messages/<locale>.json\`. Paths are locale-agnostic — Nav prefixes
879
939
  // them with the active locale at render.
880
940
  ${navItemImport}
881
- import { HelpCircle, Layers, Sparkles } from "lucide-react";
882
941
 
883
942
  export const navItems: NavItem[] = [
884
943
  ${entries.join("\n")}
@@ -18,6 +18,7 @@ import type { CategorySlug } from "@working-theory/schema/categories";
18
18
  import { type PageRosterRow } from "./module-architecture.js";
19
19
  import type { ModuleName } from "./parse-args.js";
20
20
  import type { TemplateLayout } from "./site-chrome.js";
21
+ import { type BuildableShape, type TemplateAlias } from "./starter-grid.js";
21
22
  /**
22
23
  * The closed **shape** enum (starter-template contract § The three axes,
23
24
  * operator-disposed 2026-09-24) — `<primary node>-<offering node>` in
@@ -155,21 +156,10 @@ export declare class TemplateError extends Error {
155
156
  constructor(message: string);
156
157
  }
157
158
  /**
158
- * The three settled templates and their fixed `requires` lists — settled by
159
- * Sam, 2026-09-14 (parent § D12). The plain name is the Pro one: it is the
160
- * flagship and the demo.
161
- *
162
- * `workspace` appears in none of them. It is a Pro module, but an authed app
163
- * surface contributing no PUBLIC page to a marketing site, and `requires`
164
- * names what the roster uses (parent § D15). A Pro key unlocks it regardless
165
- * of any template. `ModuleName` could express it either way — `workspace` is
166
- * one of the seven `RECOGNIZED_MODULES` names; it is simply not one any
167
- * template's fixed `requires` list names.
168
- *
169
- * `commerce` is likewise in none of them: it is a per-site add-on available at
170
- * any level, so a template for it would contradict the offering model and
171
- * multiply the set combinatorially (parent § D13). It still contributes rows
172
- * to whichever template takes it, through `mergeRoster` below.
159
+ * The declared templates, each composed once at load from its `ALIASES` row
160
+ * (#6753) — no manifest here is hand-typed, so a template's shape, design,
161
+ * modules and roster have one authority: `starter-grid.ts`. Declaration order
162
+ * is `ALIASES`'s, which is `RECOGNIZED_TEMPLATES`'s.
173
163
  *
174
164
  * Frozen (#5507): this registry is also the issuer's authorization allowlist
175
165
  * (spec 5480 § C7) — a mutable global there could be monkey-patched at
@@ -177,8 +167,37 @@ export declare class TemplateError extends Error {
177
167
  * fixture rather than mutating this one.
178
168
  */
179
169
  export declare const TEMPLATE_MANIFESTS: Readonly<Record<string, TemplateManifest>>;
180
- /** Look up a declared template. @throws TemplateError on an undeclared name. */
181
- export declare function resolveTemplate(name: string): TemplateManifest;
170
+ /**
171
+ * The composed FREE manifest per shape with no `--template` alias of its own
172
+ * (#6757 D5) — the coordinates a keyless `--shape S` builds. `Object.freeze`d,
173
+ * composed once at load by the same module-private `composeManifest` over
174
+ * `SHAPE_STARTER_ALIASES`, each manifest's `name` the shape. Data holding
175
+ * composed manifests, the status D5 gives `TEMPLATE_MANIFESTS`;
176
+ * `resolveTemplate` stays the only exported function that yields a
177
+ * template's roster, and its behavior is unchanged — it does NOT resolve a
178
+ * shape name (NFR-3, the issuer allowlist stays `TEMPLATE_MANIFESTS`-only).
179
+ */
180
+ export declare const SHAPE_STARTERS: Readonly<Partial<Record<BuildableShape, TemplateManifest>>>;
181
+ /**
182
+ * Where a starter's files live under `site-templates/` (#6757 D5) — the one
183
+ * place that says so. A `SHAPE_STARTERS` key (a bare-shape starter with no
184
+ * `--template` alias) lives at `shapes/<name>`, out of the template showcase
185
+ * (§ 1.6); every other name (a declared `--template` alias) is unchanged.
186
+ */
187
+ export declare function templateTreeDir(name: string): string;
188
+ /**
189
+ * Look up a declared template — the one exported function that yields a
190
+ * template's roster (#6753 AC-1); `TEMPLATE_MANIFESTS`, the frozen record it
191
+ * reads, holds only manifests composed from `ALIASES`.
192
+ *
193
+ * With no `aliases`, returns the frozen `TEMPLATE_MANIFESTS` entry itself.
194
+ * With an injected alias table — the `resolveTemplateLineage` precedent
195
+ * (#5507) — composes `aliases[name]` against the shipped shape rosters, so a
196
+ * test exercises the composer's refusals without touching the shipped table.
197
+ *
198
+ * @throws TemplateError on an undeclared name, or a malformed injected alias.
199
+ */
200
+ export declare function resolveTemplate(name: string, aliases?: Readonly<Record<string, TemplateAlias>>): TemplateManifest;
182
201
  /**
183
202
  * Resolve a template's `extends` chain — `[name, …ancestors, "_base"]` — in
184
203
  * the client registry, before any fetch (#5618 D2). `_base` is the only