@thorprovider/storefront 0.3.1 → 0.4.0-mvp.2

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 (263) hide show
  1. package/README.md +321 -152
  2. package/VENDOR.md +69 -0
  3. package/assets/openspec/config.yaml +38 -0
  4. package/assets/templates/AGENTS.md +76 -0
  5. package/assets/templates/app/app/[[...slug]]/page.tsx +54 -2
  6. package/assets/templates/app/app/[[...slug]]/renderer.tsx +13 -2
  7. package/assets/templates/app/app/globals.css +5 -8
  8. package/assets/templates/app/app/layout.tsx +17 -7
  9. package/assets/templates/app/app/robots.ts +19 -0
  10. package/assets/templates/app/app/sitemap.ts +71 -0
  11. package/assets/templates/app/lib/__STOREFRONT__/actions.ts +612 -0
  12. package/assets/templates/app/lib/__STOREFRONT__/cart-state.ts +66 -0
  13. package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +50 -57
  14. package/assets/templates/app/lib/__STOREFRONT__/commerce.ts +679 -0
  15. package/assets/templates/app/lib/__STOREFRONT__/composition.ts +26 -0
  16. package/assets/templates/app/lib/__STOREFRONT__/handlers.core.ts +197 -0
  17. package/assets/templates/app/lib/__STOREFRONT__/handlers.ts +5 -36
  18. package/assets/templates/app/lib/__STOREFRONT__/icon-map.ts +27 -0
  19. package/assets/templates/app/lib/__STOREFRONT__/mappers.ts +13 -11
  20. package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +264 -51
  21. package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +6 -3
  22. package/assets/templates/app/lib/__STOREFRONT__/schema.ts +136 -0
  23. package/assets/templates/app/lib/__STOREFRONT__/section-observation.ts +182 -0
  24. package/assets/templates/app/lib/__STOREFRONT__/section-sources.ts +130 -0
  25. package/assets/templates/app/lib/__STOREFRONT__/session.core.ts +470 -0
  26. package/assets/templates/app/lib/__STOREFRONT__/session.ts +169 -0
  27. package/assets/templates/app/lib/__STOREFRONT__/shell/content.ts +120 -0
  28. package/assets/templates/app/lib/__STOREFRONT__/shell/props.ts +38 -0
  29. package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +129 -10
  30. package/assets/templates/app/lib/__STOREFRONT__/spec/page-root.ts +5 -1
  31. package/assets/templates/app/lib/__STOREFRONT__/spec/seo.ts +191 -0
  32. package/assets/templates/app/lib/__STOREFRONT__/state.core.ts +257 -0
  33. package/assets/templates/app/lib/__STOREFRONT__/state.ts +7 -31
  34. package/assets/templates/app/lib/__STOREFRONT__/store-context.ts +161 -0
  35. package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.client.tsx +25 -0
  36. package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.tsx +16 -0
  37. package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.client.tsx +24 -0
  38. package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.tsx +5 -18
  39. package/assets/templates/app/lib/__STOREFRONT__/views/sections/index.ts +18 -0
  40. package/assets/templates/app/lib/__STOREFRONT__/views/sections/logoCloud.tsx +26 -14
  41. package/assets/templates/app/lib/__STOREFRONT__/views/sections/pricing.tsx +36 -18
  42. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.client.tsx +100 -0
  43. package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.tsx +19 -0
  44. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.cta.ts +19 -0
  45. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.faq.ts +18 -0
  46. package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.products.ts +39 -0
  47. package/assets/templates/app/lib/__STOREFRONT__/views/sections/statistics.tsx +35 -14
  48. package/assets/templates/app/lib/__STOREFRONT__/views/sections/testimonials.tsx +31 -13
  49. package/assets/templates/app/lib/__STOREFRONT__/views/types.ts +5 -3
  50. package/assets/templates/features/checkout/handlers.ts +159 -0
  51. package/assets/templates/features/checkout/state.ts +61 -0
  52. package/assets/templates/features/customer-auth/handlers.ts +59 -0
  53. package/assets/templates/features/customer-auth/state.ts +45 -0
  54. package/assets/templates/features/recommendations/state.ts +11 -0
  55. package/assets/templates/views/account/content.ts +35 -0
  56. package/assets/templates/views/account/index.tsx +114 -0
  57. package/assets/templates/views/account/registry.tsx +439 -0
  58. package/assets/templates/views/account/schema.ts +53 -0
  59. package/assets/templates/views/cart/content.ts +11 -0
  60. package/assets/templates/views/cart/data.ts +23 -0
  61. package/assets/templates/views/cart/index.tsx +53 -0
  62. package/assets/templates/views/cart/props.ts +12 -0
  63. package/assets/templates/views/cart/registry.tsx +81 -0
  64. package/assets/templates/views/cart/schema.ts +52 -0
  65. package/assets/templates/views/category/assembly/assembly.json +29 -0
  66. package/assets/templates/views/category/assembly/index.skeleton.tsx +251 -0
  67. package/assets/templates/views/category/assembly/registry.skeleton.tsx +34 -0
  68. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/binding.json +5 -0
  69. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/catalog.part +4 -0
  70. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/element.part +7 -0
  71. package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/registry.part +18 -0
  72. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/catalog.part +5 -0
  73. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/element.part +34 -0
  74. package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/registry.part +71 -0
  75. package/assets/templates/views/category/content.ts +30 -0
  76. package/assets/templates/views/category/data.ts +12 -0
  77. package/assets/templates/views/category/index.tsx +299 -0
  78. package/assets/templates/views/category/props.ts +23 -0
  79. package/assets/templates/views/category/registry.tsx +122 -0
  80. package/assets/templates/views/category/schema.ts +91 -0
  81. package/assets/templates/views/checkout/content.ts +105 -0
  82. package/assets/templates/views/checkout/data.ts +23 -0
  83. package/assets/templates/views/checkout/index.tsx +111 -0
  84. package/assets/templates/views/checkout/props.ts +49 -0
  85. package/assets/templates/views/checkout/registry.tsx +217 -0
  86. package/assets/templates/views/checkout/schema.ts +229 -0
  87. package/assets/templates/views/home/assembly/assembly.json +33 -0
  88. package/assets/templates/views/home/assembly/index.skeleton.tsx +90 -0
  89. package/assets/templates/views/home/assembly/registry.skeleton.tsx +25 -0
  90. package/assets/templates/views/home/assembly/slots/benefits/Feature1/binding.json +5 -0
  91. package/assets/templates/views/home/assembly/slots/benefits/Feature1/catalog.part +4 -0
  92. package/assets/templates/views/home/assembly/slots/benefits/Feature1/element.part +4 -0
  93. package/assets/templates/views/home/assembly/slots/benefits/Feature1/registry.part +11 -0
  94. package/assets/templates/views/home/assembly/slots/benefits/Feature2/binding.json +5 -0
  95. package/assets/templates/views/home/assembly/slots/benefits/Feature2/catalog.part +4 -0
  96. package/assets/templates/views/home/assembly/slots/benefits/Feature2/element.part +4 -0
  97. package/assets/templates/views/home/assembly/slots/benefits/Feature2/registry.part +10 -0
  98. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/binding.json +5 -0
  99. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/catalog.part +4 -0
  100. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/element.part +5 -0
  101. package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/registry.part +16 -0
  102. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/binding.json +5 -0
  103. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/catalog.part +4 -0
  104. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/element.part +5 -0
  105. package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/registry.part +26 -0
  106. package/assets/templates/views/home/content.ts +70 -0
  107. package/assets/templates/views/home/data.ts +24 -0
  108. package/assets/templates/views/home/index.tsx +57 -85
  109. package/assets/templates/views/home/props.ts +22 -0
  110. package/assets/templates/views/home/registry.tsx +38 -22
  111. package/assets/templates/views/home/schema.ts +93 -0
  112. package/assets/templates/views/product/content.ts +25 -0
  113. package/assets/templates/views/product/data.ts +50 -0
  114. package/assets/templates/views/product/index.tsx +148 -0
  115. package/assets/templates/views/product/props.ts +27 -0
  116. package/assets/templates/views/product/registry.tsx +103 -0
  117. package/assets/templates/views/product/schema.ts +121 -0
  118. package/assets/templates/views/static/content.ts +99 -0
  119. package/assets/templates/views/static/index.tsx +60 -0
  120. package/assets/templates/views/static/registry.tsx +66 -0
  121. package/assets/templates/views/static/schema.ts +45 -0
  122. package/bin/storefront.js +2424 -148
  123. package/examples/merchandising/category-led.json +37 -0
  124. package/examples/merchandising/collection-led.json +37 -0
  125. package/package.json +15 -9
  126. package/recipes/archetypes.json +28 -14
  127. package/recipes/catalog.json +400 -0
  128. package/recipes/catalog.mjs +128 -0
  129. package/recipes/catalog.schema.json +64 -0
  130. package/recipes/feature.schema.json +89 -0
  131. package/recipes/features.json +162 -0
  132. package/recipes/manifest.schema.json +69 -0
  133. package/recipes/operations.schema.json +43 -0
  134. package/recipes/recipe.schema.json +11 -2
  135. package/recipes/recipes.json +76 -19
  136. package/recipes/sections.json +34 -5
  137. package/recipes/validate.mjs +838 -32
  138. package/scripts/candidate.mjs +236 -0
  139. package/scripts/composers.mjs +160 -0
  140. package/scripts/env-file.mjs +86 -0
  141. package/scripts/file-classes.mjs +121 -0
  142. package/scripts/logical-controls.mjs +61 -0
  143. package/scripts/manifest.mjs +298 -0
  144. package/scripts/migrate-english-routes.mjs +276 -0
  145. package/scripts/operation-registry.mjs +256 -0
  146. package/scripts/operations.mjs +720 -0
  147. package/scripts/ownership-conflict.mjs +356 -0
  148. package/scripts/pinning.mjs +573 -0
  149. package/scripts/plan-apply.mjs +987 -0
  150. package/scripts/preflight.mjs +51 -0
  151. package/scripts/reconcile.mjs +549 -0
  152. package/scripts/render-templates.mjs +338 -68
  153. package/scripts/schema-validate.mjs +132 -0
  154. package/scripts/section-compat.mjs +300 -0
  155. package/scripts/section-config.mjs +253 -0
  156. package/scripts/section-operations.mjs +508 -0
  157. package/scripts/starter.mjs +17 -0
  158. package/scripts/sync-assets.mjs +22 -15
  159. package/scripts/theme/color.mjs +185 -0
  160. package/scripts/theme/css-theme.mjs +233 -0
  161. package/scripts/theme/fonts.mjs +123 -0
  162. package/scripts/theme/import-export.mjs +125 -0
  163. package/scripts/theme/preset.mjs +93 -0
  164. package/scripts/theme/theme-plan.mjs +292 -0
  165. package/scripts/theme/tokens.mjs +137 -0
  166. package/scripts/view-assembly.mjs +337 -0
  167. package/scripts/view-selection.mjs +211 -0
  168. package/skills/sf-feature/SKILL.md +80 -0
  169. package/skills/sf-setup/SKILL.md +79 -0
  170. package/skills/sf-theme-gen/SKILL.md +100 -30
  171. package/skills/sf-theme-gen/references/shadcn-theming.md +87 -0
  172. package/skills/{shadcn-theming/references/templates → sf-theme-gen/references}/theme-setup.md +15 -19
  173. package/skills/{shadcn-theming → sf-theme-gen}/references/theming-guide.md +6 -11
  174. package/skills/sf-view-gen/SKILL.md +146 -93
  175. package/skills/sf-view-gen/assets/recipes/archetypes.json +28 -14
  176. package/skills/sf-view-gen/assets/recipes/catalog.json +400 -0
  177. package/skills/sf-view-gen/assets/recipes/catalog.schema.json +64 -0
  178. package/skills/sf-view-gen/assets/recipes/features.json +162 -0
  179. package/skills/sf-view-gen/assets/recipes/recipes.json +76 -19
  180. package/skills/sf-view-gen/assets/recipes/sections.json +34 -5
  181. package/skills/sf-view-gen/assets/templates/views/sections/cta.client.tsx +25 -0
  182. package/skills/sf-view-gen/assets/templates/views/sections/cta.tsx +16 -0
  183. package/skills/sf-view-gen/assets/templates/views/sections/faq.client.tsx +13 -9
  184. package/skills/sf-view-gen/assets/templates/views/sections/faq.tsx +2 -7
  185. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.client.tsx +12 -8
  186. package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.tsx +2 -7
  187. package/skills/sf-view-gen/assets/templates/views/sections/pricing.client.tsx +14 -10
  188. package/skills/sf-view-gen/assets/templates/views/sections/pricing.tsx +2 -9
  189. package/skills/sf-view-gen/assets/templates/views/sections/products.client.tsx +100 -0
  190. package/skills/sf-view-gen/assets/templates/views/sections/products.tsx +19 -0
  191. package/skills/sf-view-gen/assets/templates/views/sections/schema.cta.ts +19 -0
  192. package/skills/sf-view-gen/assets/templates/views/sections/schema.faq.ts +18 -0
  193. package/skills/sf-view-gen/assets/templates/views/sections/schema.logoCloud.ts +19 -0
  194. package/skills/sf-view-gen/assets/templates/views/sections/schema.pricing.ts +26 -0
  195. package/skills/sf-view-gen/assets/templates/views/sections/schema.products.ts +39 -0
  196. package/skills/sf-view-gen/assets/templates/views/sections/schema.statistics.ts +25 -0
  197. package/skills/sf-view-gen/assets/templates/views/sections/schema.testimonials.ts +23 -0
  198. package/skills/sf-view-gen/assets/templates/views/sections/statistics.client.tsx +16 -8
  199. package/skills/sf-view-gen/assets/templates/views/sections/statistics.tsx +2 -7
  200. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.client.tsx +13 -9
  201. package/skills/sf-view-gen/assets/templates/views/sections/testimonials.tsx +2 -5
  202. package/skills/{json-render-core/SKILL.md → sf-view-gen/references/json-render-core.md} +4 -27
  203. package/skills/{json-render-next/SKILL.md → sf-view-gen/references/json-render-next.md} +5 -8
  204. package/skills/{json-render-react/SKILL.md → sf-view-gen/references/json-render-react.md} +3 -5
  205. package/skills/{json-render-shadcn/SKILL.md → sf-view-gen/references/json-render-shadcn.md} +3 -5
  206. package/skills/sf-view-gen/references/views.md +58 -33
  207. package/skills/sf-view-gen/scripts/catalog.mjs +128 -0
  208. package/skills/sf-view-gen/scripts/schema-validate.mjs +132 -0
  209. package/skills/sf-view-gen/scripts/validate.mjs +838 -32
  210. package/skills/storefront-workflow/SKILL.md +179 -0
  211. package/skills/{storefront-best-practices/reference/design.md → storefront-workflow/references/design-consistency.md} +13 -29
  212. package/CHANGELOG.md +0 -61
  213. package/commands/sf-add-view.md +0 -26
  214. package/commands/sf-theme.md +0 -20
  215. package/commands/sf-view.md +0 -25
  216. package/skills/building-storefronts/SKILL.md +0 -178
  217. package/skills/building-storefronts/references/frontend-integration.md +0 -229
  218. package/skills/json-render-remotion/SKILL.md +0 -111
  219. package/skills/json-render-solid/SKILL.md +0 -204
  220. package/skills/nextjs-shadcn/SKILL.md +0 -367
  221. package/skills/nextjs-shadcn/references/architecture.md +0 -499
  222. package/skills/nextjs-shadcn/references/project-setup.md +0 -162
  223. package/skills/nextjs-shadcn/references/shadcn-platform.md +0 -261
  224. package/skills/nextjs-shadcn/references/sidebar.md +0 -274
  225. package/skills/nextjs-shadcn/references/styling.md +0 -555
  226. package/skills/sf-view-gen/assets/templates/views/category/index.tsx +0 -86
  227. package/skills/sf-view-gen/assets/templates/views/category/registry.tsx +0 -48
  228. package/skills/sf-view-gen/assets/templates/views/checkout/index.tsx +0 -192
  229. package/skills/sf-view-gen/assets/templates/views/checkout/registry.tsx +0 -45
  230. package/skills/sf-view-gen/assets/templates/views/product/index.tsx +0 -122
  231. package/skills/sf-view-gen/assets/templates/views/product/registry.tsx +0 -49
  232. package/skills/sf-view-gen/assets/templates/views/user-panel/index.tsx +0 -168
  233. package/skills/sf-view-gen/assets/templates/views/user-panel/registry.tsx +0 -49
  234. package/skills/shadcn-component-discovery/SKILL.md +0 -273
  235. package/skills/shadcn-component-discovery/references/registries.md +0 -226
  236. package/skills/shadcn-theming/SKILL.md +0 -104
  237. package/skills/storefront-best-practices/SKILL.md +0 -421
  238. package/skills/storefront-best-practices/reference/components/breadcrumbs.md +0 -123
  239. package/skills/storefront-best-practices/reference/components/cart-popup.md +0 -189
  240. package/skills/storefront-best-practices/reference/components/country-selector.md +0 -298
  241. package/skills/storefront-best-practices/reference/components/footer.md +0 -112
  242. package/skills/storefront-best-practices/reference/components/hero.md +0 -241
  243. package/skills/storefront-best-practices/reference/components/megamenu.md +0 -239
  244. package/skills/storefront-best-practices/reference/components/navbar.md +0 -397
  245. package/skills/storefront-best-practices/reference/components/popups.md +0 -221
  246. package/skills/storefront-best-practices/reference/components/product-card.md +0 -125
  247. package/skills/storefront-best-practices/reference/components/product-reviews.md +0 -217
  248. package/skills/storefront-best-practices/reference/components/product-slider.md +0 -174
  249. package/skills/storefront-best-practices/reference/components/search.md +0 -101
  250. package/skills/storefront-best-practices/reference/connecting-to-backend.md +0 -391
  251. package/skills/storefront-best-practices/reference/features/promotions.md +0 -307
  252. package/skills/storefront-best-practices/reference/features/wishlist.md +0 -230
  253. package/skills/storefront-best-practices/reference/layouts/account.md +0 -380
  254. package/skills/storefront-best-practices/reference/layouts/cart.md +0 -316
  255. package/skills/storefront-best-practices/reference/layouts/checkout.md +0 -486
  256. package/skills/storefront-best-practices/reference/layouts/home-page.md +0 -264
  257. package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +0 -231
  258. package/skills/storefront-best-practices/reference/layouts/product-details.md +0 -527
  259. package/skills/storefront-best-practices/reference/layouts/product-listing.md +0 -520
  260. package/skills/storefront-best-practices/reference/layouts/static-pages.md +0 -356
  261. package/skills/storefront-best-practices/reference/medusa.md +0 -307
  262. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/mobile-responsiveness.md +0 -0
  263. /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/seo.md +0 -0
package/README.md CHANGED
@@ -1,18 +1,23 @@
1
1
  ---
2
2
  title: "@thorprovider/storefront"
3
- purpose: "Deterministic storefront scaffolder and installable command suite for Thor Commerce"
4
- lastReviewed: "2026-10-01"
3
+ purpose: "Deterministic storefront scaffolder and installable skill suite for Thor Commerce"
4
+ lastReviewed: "2026-10-14"
5
5
  ---
6
6
 
7
7
  # @thorprovider/storefront
8
8
 
9
- Deterministic storefront scaffolder and installable command suite for Thor Commerce.
9
+ Deterministic storefront scaffolder and installable skill suite for Thor Commerce.
10
10
 
11
- `npx @thorprovider/storefront create <dir>` scaffolds a complete, buildable storefront
12
- and installs the `/sf-*` OpenCode command suite into `<dir>/.opencode/` in the same
13
- run. It ships static project templates, a recipe registry, and vendored skills, and
14
- installs them into any project's `.opencode/` directory. Nothing else from the Thor
15
- Commerce monorepo is required at generation time.
11
+ `npx @thorprovider/storefront create <dir>` scaffolds a complete, buildable storefront,
12
+ installs its OpenCode skill suite into `<dir>/.opencode/skills/`, and writes a managed
13
+ root `AGENTS.md` — all from static templates, a recipe registry, and vendored skills,
14
+ with nothing else from the monorepo needed at generation time.
15
+
16
+ A generated storefront ships **no `/sf-*` commands**. Guidance lives in the root
17
+ `AGENTS.md`, which states the authority rules and routes an intent (add a view, change
18
+ the theme, add a feature, connect a backend) to the skill that owns the procedure: open
19
+ the project in OpenCode and ask in natural language. The `storefront` CLI stays the
20
+ structural authority.
16
21
 
17
22
  This README is organised in two parts:
18
23
 
@@ -36,43 +41,50 @@ storefront it generates resolves its own dependencies from the registry.
36
41
  | Package manager | Yarn with the `node-modules` linker (for generated projects) |
37
42
  | OpenCode | a version that supports project skills in `.opencode/skills/` |
38
43
  | OS | macOS, Linux, Windows (via WSL) |
39
- | Network | access to the npm registry (the generated project installs its dependencies from it) |
44
+ | Network | access to the npm registry — only for the first scaffold per machine (repeat scaffolds can run offline, see [troubleshooting](#flaky-network-etimedout)) |
40
45
 
41
46
  Generated projects use Yarn with the `node-modules` linker, not Plug'n'Play (PnP).
42
47
  Yarn 4 defaults to PnP, which Next.js 16's Turbopack cannot resolve from, so the
43
48
  scaffold writes a `.yarnrc.yml` setting `nodeLinker: node-modules` before installing
44
49
  dependencies. PnP is not used. You do not need to configure this yourself.
45
50
 
51
+ The same `.yarnrc.yml` hardens installs for unreliable networks: `httpRetry: 10`
52
+ and `networkConcurrency: 8`. The first successful scaffold warms the global Yarn
53
+ cache (`~/.yarn/berry/cache`); repeat scaffolds then download no packages, and
54
+ `YARN_ENABLE_OFFLINE_MODE=1` drops even the metadata requests.
55
+
46
56
  ### Install
47
57
 
48
- Run the scaffolder directly from npm — no global install required. `npx` and
49
- `yarn dlx` are equivalent:
58
+ Run the scaffolder directly from npm — no global install required; the equivalent is
59
+ `yarn dlx @thorprovider/storefront`:
50
60
 
51
61
  ```bash
52
62
  npx @thorprovider/storefront create my-shop \
53
63
  --site-name "My Shop" \
54
- --backend-url http://localhost:9000 \
55
64
  --preset maia
56
65
  ```
57
66
 
58
- ```bash
59
- yarn dlx @thorprovider/storefront create my-shop --preset maia
60
- ```
61
-
62
67
  The first positional argument is the target directory: it is created if missing, and
63
68
  it is where `.opencode/`, the Next.js app, and `lib/<name>/` live. Run the command
64
69
  from the parent directory you want the project created in.
65
70
 
66
71
  In a terminal, `create` runs an interactive wizard for any input you omit (name,
67
- site name, backend URL, preset — Maia is preselected). Without a terminal, every
68
- required input must be passed as a flag; the command fails fast and writes nothing
69
- rather than guessing. Brand color is never asked by the wizard — apply it later with
70
- `/sf-theme`, or pass `--brand` on the command line.
72
+ site name, preset — Maia is preselected); an empty prompt accepts the suggested
73
+ default (name from the target basename, site name from its Title Case). Without a
74
+ terminal every required input must be a flag — the command fails fast and writes
75
+ nothing. Brand color is never prompted: pass `--brand`, or change it later in natural
76
+ language (the theme skill runs `storefront theme`).
77
+
78
+ `create` is structural and offline: it scaffolds a storefront that runs in **mock
79
+ mode** with no backend. Connecting a real backend is a separate, re-runnable step —
80
+ run `storefront setup` from inside the project (see [Backend
81
+ configuration](#backend-configuration)).
71
82
 
72
83
  Once the package is installed as a dependency, the shorter binary form is available:
73
84
 
74
85
  ```bash
75
- storefront create my-shop --preset maia # scaffold + install the suite
86
+ storefront create my-shop --preset maia # scaffold + install the suite (mock mode)
87
+ storefront setup --publishable-key <key> --region-id <id> # connect a real backend
76
88
  storefront update # refresh an existing install
77
89
  storefront doctor # report the recorded version and status
78
90
  ```
@@ -87,7 +99,6 @@ project root that will hold `.opencode/`:
87
99
  storefront create my-shop \
88
100
  --name my-shop \
89
101
  --site-name "My Shop" \
90
- --backend-url http://localhost:9000 \
91
102
  --preset maia \
92
103
  [--brand orange]
93
104
  ```
@@ -96,25 +107,24 @@ storefront create my-shop \
96
107
  | --- | --- | --- |
97
108
  | `<dir>` (positional) | target project directory (created if missing) | required |
98
109
  | `--name <name>` | kebab-case storefront name = `lib/<name>/` = `__STOREFRONT__` | prompted in the wizard |
99
- | `--site-name <name>` | human site name (`__SITE_NAME__`) | `<name>` |
100
- | `--backend-url <url>` | Medusa backend URL | `http://localhost:9000` |
110
+ | `--site-name <name>` | human site name (`__SITE_NAME__`) | Title Case of `<name>` (`my-shop` → `My Shop`) |
101
111
  | `--preset <preset>` | visual preset: `maia`, `nova`, `vega`, `luma`, `lyra`, `mira` | prompted in the wizard (Maia preselected) |
102
- | `--brand <color>` | named brand color overriding preset primary/nav/ring tokens (never prompted) | none |
112
+ | `--starter` | seed the full curated composition — every curated view and feature — instead of the minimal `home`+`cart` baseline | off |
113
+ | `--brand <color>` | named brand color (`orange`, `blue`, …) applied as a chromatic primary family (primary/foreground/ring/nav) over the preset tokens (never prompted) | none |
103
114
  | `--force`, `-f` | overwrite files that already exist | off |
104
115
 
105
- The pipeline runs: preconditions → Next.js base → shadcn base with the preset →
106
- `.yarnrc.yml` (`nodeLinker: node-modules`) → dependency install → template copy +
107
- placeholder substitution → default-page removal → `.env.local` + theme → suite
108
- install into `.opencode/`.
116
+ The pipeline runs: preconditions → Next.js base (files only) → `.yarnrc.yml`
117
+ (`nodeLinker: node-modules`) → shadcn base with the preset → dependency install →
118
+ template copy + placeholder substitution → mock `.env.local` + theme → suite install.
109
119
 
110
- Same inputs produce a byte-identical tree (verified by recursive `diff -r`). Running
111
- `create` twice with different inputs is the only reason the output differs.
120
+ Same inputs produce a byte-identical tree (verified by recursive `diff -r`).
112
121
 
113
- The suite install copies `commands/` and the shipped skills under `skills/` (own
114
- skills and vendored skills, flat) into `<dir>/.opencode/`. It verifies its own
115
- generated copies against their source before copying, records the installed version
116
- in `.opencode/storefront.json`, and is idempotent: use `--force` to overwrite files
117
- that already exist.
122
+ The suite install copies the shipped skills (own and vendored, flat) into
123
+ `<dir>/.opencode/skills/`, verifying generated copies against their source first. It
124
+ records the installed version in `.opencode/storefront.json` and is idempotent
125
+ (`--force` overwrites). `create` also writes the managed root `AGENTS.md` from the
126
+ derived template path, so `storefront sync` refreshes it and `storefront doctor`
127
+ reports drift.
118
128
 
119
129
  ### Run the storefront
120
130
 
@@ -125,31 +135,70 @@ cd my-shop
125
135
  yarn dev
126
136
  ```
127
137
 
128
- The storefront renders its home view — hero, benefits, and category sections — using
129
- only `@thorprovider/blocks` for UI and `@thorprovider/adapters` for data. Then grow
130
- it incrementally from OpenCode:
138
+ A fresh `create` seeds `home` + `cart` (home hero/benefits/category sections, a
139
+ navbar-owned cart sheet, a standalone `/cart` review route). Pass `--starter` to
140
+ seed the full curated composition instead — every curated view and feature, read
141
+ from the registry, held units excluded. Everything renders with only
142
+ `@thorprovider/blocks` (UI) and `@thorprovider/adapters` (data). Then grow it by asking
143
+ in natural language — the `AGENTS.md` router maps each intent to a skill that delegates
144
+ the structural write to the CLI:
131
145
 
132
146
  ```
133
- /sf-add-view product # add a view from its recipe
134
- /sf-view home add a testimonials section # modify an existing view
135
- /sf-theme # colors, typography, identity
147
+ add a product view # the view skill runs `storefront view add product`
148
+ add a testimonials section to home # the view skill composes the section, then syncs
149
+ change the theme to dark with a teal brand # the theme skill runs `storefront theme`
150
+ connect the backend to my Medusa store # the setup skill runs `storefront setup`
136
151
  ```
137
152
 
138
153
  ### Backend configuration
139
154
 
140
- Generated projects expect a Medusa backend. `create` writes a `.env.local` with the
141
- backend URL you passed and blank placeholders for the rest:
155
+ `create` writes a git-ignored `.env.local` that starts the store in **mock mode** —
156
+ no backend, a generated cookie secret, the store identity — plus a committed
157
+ `.env.example` documenting every variable:
158
+
159
+ ```bash
160
+ # .env.local (git-ignored) — written by create
161
+ THOR_DATA_SOURCE=mock
162
+ THOR_STORE_ID=my-shop
163
+ THOR_SESSION_SECRET=<generated per install>
164
+ ```
165
+
166
+ Mock mode is complete: views and the cart render from labelled demo data with no
167
+ configuration. To connect a real backend, run `storefront setup` from inside the project:
142
168
 
143
169
  ```bash
144
- MEDUSA_BACKEND_URL=http://localhost:9000
145
- MEDUSA_REGION_ID=
146
- MEDUSA_PUBLISHABLE_KEY=
170
+ storefront setup --publishable-key <key> --sales-channel-id <id> --region-id <region-id> [--backend-url <url>]
147
171
  ```
148
172
 
149
- Set `MEDUSA_REGION_ID` and `MEDUSA_PUBLISHABLE_KEY` as needed, then restart
150
- `yarn dev`. The storefront shows no catalog data until these are set (see
151
- [Backend not configured](#backend-not-configured)). During local development you can
152
- use the `mock` adapter data source instead of a live backend.
173
+ In a terminal, `setup` prompts for the backend URL, publishable key, sales channel id,
174
+ and region. It takes the sales channel id as an explicit input (Medusa admin → Settings
175
+ → Sales Channels), derives the region's currency (`GET /store/regions`), then **upserts**
176
+ only the keys it manages — `THOR_STORE_ID`, `THOR_SESSION_SECRET`, and user additions are
177
+ untouched. It is re-runnable (rotate the key, change the channel or region, re-point the
178
+ backend). Without a publishable key it stays in mock mode; without a channel id it reports the missing input and stays in mock mode.
179
+
180
+ The store context is **explicit and validated** in real mode: `MEDUSA_SALES_CHANNEL_ID`
181
+ scopes the store, `MEDUSA_REGION_ID` supplies currency, countries, and shipping, and
182
+ `MEDUSA_CURRENCY_CODE` (ISO 4217) comes from the environment and is **never hardcoded**.
183
+ `THOR_SESSION_SECRET` is the host-only cookie signing secret (≥16 chars, 32 recommended).
184
+
185
+ > **Renamed:** the currency variable was `THOR_CURRENCY_CODE` and is now
186
+ > `MEDUSA_CURRENCY_CODE`. An existing store renames it in `.env.local`, or re-runs
187
+ > `storefront setup`, which writes it.
188
+
189
+ `THOR_DATA_SOURCE` selects the mode **explicitly** and is never inferred from a failure.
190
+ `mock` renders labelled demo data and resolves a fixture store context in code (channel
191
+ `mock-sales-channel`, region `mock-region-us`, currency `USD`), so no context variables
192
+ are required. `medusa` uses the real backend: a missing provider or invalid config is a
193
+ **visible error** — never a silent fallback to demo data. Run `storefront setup`, then
194
+ restart `yarn dev`.
195
+
196
+ The guest flow is backend-authoritative end to end: the signed host cookie binds a
197
+ persistent backend cart; the PDP adds the explicitly selected variant; the cart and
198
+ checkout render the backend's totals; checkout applies the one approved manual TEST
199
+ provider and completes the cart with its native discriminator. An ambiguous completion
200
+ is never retried — the shopper reconciles the committed cart, and the active cart is
201
+ cleared only after a confirmed order. No card data is collected at launch.
153
202
 
154
203
  ### Troubleshooting / Known Issues
155
204
 
@@ -157,90 +206,201 @@ Each entry below follows the same shape: **cause** → **symptom** → **remedy*
157
206
 
158
207
  #### Ancestor `.opencode/`
159
208
 
160
- - **Cause**: the project is created under a directory tree in which an ancestor
161
- directory already contains a `.opencode/`.
162
- - **Symptom**: OpenCode resolves the wrong command set — `/sf-add-view`, `/sf-view`,
163
- and `/sf-theme` run commands from the ancestor's `.opencode/` instead of the ones
164
- this package installed, so you see stale or unrelated behaviour.
165
- - **Remedy**: scaffold in a clean root with no ancestor `.opencode/` (for example a
166
- fresh directory outside the monorepo), or remove/relocate the ancestor `.opencode/`
167
- before opening the project.
209
+ - **Cause**: an ancestor directory already contains a `.opencode/`.
210
+ - **Symptom**: OpenCode resolves the wrong skill set or an ancestor's `AGENTS.md`, so you see stale or unrelated behaviour.
211
+ - **Remedy**: scaffold in a clean root with no ancestor `.opencode/`, or remove/relocate the ancestor `.opencode/` before opening the project.
168
212
 
169
213
  #### Non-empty target directory
170
214
 
171
- - **Cause**: `create` runs against a directory whose `lib/<name>/` already exists (a
172
- previous scaffold with the same name), or whose root is already a Next.js project.
173
- - **Symptom**: the run stops at the preconditions stage with
174
- `lib/<name>/ already exists — refusing to overwrite`.
175
- - **Remedy**: choose a different `--name`, or remove the existing `lib/<name>/`
176
- directory. To extend an existing storefront instead of creating one, use
177
- `/sf-add-view`. `create-next-app` also refuses to initialize a directory that is
178
- already a Next.js project unless it can reuse it; run `create` in an empty directory.
215
+ - **Cause**: `lib/<name>/` already exists, or the root is already a Next.js project.
216
+ - **Symptom**: the run stops at the preconditions stage with `lib/<name>/ already exists — refusing to overwrite`.
217
+ - **Remedy**: choose a different `--name`, or remove the existing `lib/<name>/`. To extend an existing storefront, run the `storefront` CLI from inside it (or ask in natural language — see `AGENTS.md`); run `create` in an empty directory.
179
218
 
180
219
  #### Missing or invalid `--preset`
181
220
 
182
- - **Cause**: `--preset` was omitted (in a non-interactive run) or the value is not one
183
- of the recognized presets.
184
- - **Symptom**: the run stops at the input-validation stage with
185
- `--preset must be one of: maia, nova, vega, luma, lyra, mira`.
186
- - **Remedy**: pass `--preset` with one of `maia`, `nova`, `vega`, `luma`, `lyra`,
187
- `mira`. In a terminal the wizard preselects `maia`, so the preset is only required
188
- on the command line for non-interactive runs.
221
+ - **Cause**: `--preset` was omitted (non-interactive) or is not a recognized preset.
222
+ - **Symptom**: the run stops at input validation with `--preset must be one of: maia, nova, vega, luma, lyra, mira`.
223
+ - **Remedy**: pass one of those presets. In a terminal the wizard preselects `maia`, so the flag is only required for non-interactive runs.
189
224
 
190
225
  #### Yarn Plug'n'Play / linker
191
226
 
192
227
  - **Cause**: a generated project installed with Yarn's default PnP linker.
193
- - **Symptom**: Next.js 16's Turbopack fails to resolve the Next.js package
194
- (`Could not find the Next.js package`).
195
- - **Remedy**: this is why `create` writes `.yarnrc.yml` with
196
- `nodeLinker: node-modules` before installing. If you set up the project manually,
197
- add that file and re-run `yarn install` so a real `node_modules/` is materialized.
198
- Do not use PnP.
228
+ - **Symptom**: Next.js 16's Turbopack fails to resolve Next.js (`Could not find the Next.js package`).
229
+ - **Remedy**: `create` writes `.yarnrc.yml` with `nodeLinker: node-modules` before installing; if you set the project up manually, add that file and re-run `yarn install`. Do not use PnP.
230
+
231
+ #### Flaky network (ETIMEDOUT)
232
+
233
+ - **Cause**: an unstable connection stalls registry requests mid-transfer (`RequestError … AggregateError [ETIMEDOUT]`).
234
+ - **Remedy**: the scaffolded `.yarnrc.yml` sets `httpRetry: 10` and `networkConcurrency: 8`; that run warms the global cache (`~/.yarn/berry/cache`), which later scaffolds reuse. For offline re-scaffolding run `YARN_ENABLE_OFFLINE_MODE=1 storefront create <dir> …`; the first scaffold per machine still needs one successful online run.
199
235
 
200
236
  #### Backend not configured
201
237
 
202
- - **Cause**: `MEDUSA_REGION_ID` / `MEDUSA_PUBLISHABLE_KEY` (or a reachable backend) are
203
- not set in `.env.local`.
204
- - **Symptom**: the storefront renders its layout but shows no catalog data.
205
- - **Remedy**: fill in the variables in [Backend configuration](#backend-configuration)
206
- and restart `yarn dev`, or use the `mock` adapter data source during local
207
- development.
238
+ - **Cause**: `THOR_DATA_SOURCE=medusa` but `MEDUSA_BASE_URL`/`MEDUSA_PUBLISHABLE_KEY` are missing, empty, or unreachable (for example, a store still in mock mode).
239
+ - **Symptom**: the storefront renders a visible error notice (`role="alert"`) instead of backend data; real-mode failures never degrade to a fake catalog.
240
+ - **Remedy**: run `storefront setup` from inside the project to write the real-mode variables (see [Backend configuration](#backend-configuration)), then restart `yarn dev`. To stay offline, leave `THOR_DATA_SOURCE=mock`.
208
241
 
209
242
  ---
210
243
 
211
244
  ## What gets installed
212
245
 
213
246
  ```
214
- <project>/.opencode/
215
- storefront.json # { version, installedAt } — what generation is installed
216
- commands/
217
- sf-add-view.md
218
- sf-view.md
219
- sf-theme.md
220
- skills/
221
- sf-view-gen/ # assets/templates/views/, assets/recipes/, scripts/validate.mjs
222
- sf-theme-gen/ # assets/
223
- nextjs-shadcn/
224
- shadcn-theming/
225
- shadcn-component-discovery/
226
- storefront-best-practices/
227
- building-storefronts/
247
+ <project>/
248
+ AGENTS.md # managed (derived): RULE 0 rules + intent router
249
+ .opencode/
250
+ storefront.json # install record: version, pinning, owned asset BOM
251
+ skills/
252
+ storefront-workflow/ # four phases, CLI/manifest authority; references/ = design/mobile/SEO
253
+ sf-view-gen/ # view generation; references/ = json-render + views.md
254
+ sf-feature/ # feature bundles (feature-owned views + mounts)
255
+ sf-theme-gen/ # theme; references/ = theming guidance
256
+ sf-setup/ # connect/re-point a Medusa backend via `storefront setup`
228
257
  ```
229
258
 
230
- Each skill carries the templates, recipes, and scripts it uses inside its own
231
- directory. Skills address them by relative path, so nothing needs the package to be
232
- located on disk at generation time.
259
+ The suite ships exactly these five operative skills plus their scoped references;
260
+ the retained generic knowledge is loaded explicitly by the owning skill and is not
261
+ separately installable. The workflow model is
262
+ [`skills/storefront-workflow/SKILL.md`](./skills/storefront-workflow/SKILL.md).
263
+ `create`/`update` also provision the pinned `@fission-ai/openspec` suite as a co-tenant (`openspec init --tools opencode` writes `openspec/`, the `openspec-*` skills, and the `opsx-*` commands, then the CLI seeds a storefront-aware `openspec/config.yaml`); the suite never installs over or removes those co-tenanted files. A non-trivial customization runs through the spec-driven loop surfaced as explore → plan → build → verify → inform, while a deterministic CLI change recorded in `storefront.config.json` needs no change and completion waits on `storefront sync` + `storefront doctor`.
233
264
 
234
- ## Commands
265
+ ## Workflow: bootstrap → compose → customize → verify
235
266
 
236
- | Command | Purpose |
237
- | --- | --- |
238
- | `/sf-add-view <view>` | Add one view incrementally (`product`, `category`, `checkout`, `user-panel`) |
239
- | `/sf-view <view> <change>` | Modify an existing view's spec, preserving structure |
240
- | `/sf-theme` | Customize OKLCH color tokens (light/dark), typography, and identity metadata |
267
+ The project manifest (`storefront.config.json`) is the structural source of truth;
268
+ the `storefront` CLI is the only writer of derived files; the developer/agent edits
269
+ only user-owned files and the bounded external surface. Read the installed
270
+ `storefront-workflow` skill for the full model. Every command runs from inside the
271
+ project (no directory argument).
272
+
273
+ | Phase | Authority | Command |
274
+ | --- | --- | --- |
275
+ | **Bootstrap** | CLI only | `npx @thorprovider/storefront create <dir> --name <name> --preset <preset>` |
276
+ | **Compose** | CLI only (agents choose, CLI writes) | `view add\|remove`, `feature add\|remove`, `section add\|remove\|move`, `theme` |
277
+ | **Customize** | developer/agent | edit user-owned `content.ts`/`props.ts`/`data.ts`/`schema.ts`/`mappers.ts` |
278
+ | **Verify** | CLI + build | `npx @thorprovider/storefront sync`, `doctor`, `yarn build` |
279
+
280
+ **Ownership classes.** *Derived* files (`views/index.ts`, `views/registry.ts`,
281
+ `index.tsx`, `registry.tsx`, `catalog.ts`, `runtime.ts`, `spec/index.ts`,
282
+ `state.ts`, `handlers.ts`, `features/*/adapter.ts`) are CLI-regenerated and never
283
+ hand-edited. *User-owned* files (content, props, data, local schema, mappers)
284
+ survive `sync`. *External* files (`app/layout.tsx`, `app/globals.css`) expose a
285
+ **bounded** surface — theme tokens/foreground pairs, fonts, global identity — that
286
+ must preserve the CLI-managed wiring (stylesheet import, provider/mode markers,
287
+ block token contract); the CLI appends it idempotently.
288
+
289
+ **Curated composition.** The CLI registry (`storefront views --json`,
290
+ `features --json`, `recipes --json`) is the authority for what is installable.
291
+ Curated MVP views: `home`, `product`, `cart`, `category`, `checkout`, `account`,
292
+ `static`. Curated MVP features: `checkout` (owns the `checkout` view —
293
+ `view add checkout` is rejected, use `feature add checkout`), `customer-auth`
294
+ (owns `account`), `static-pages` (owns `static`), and the mount-only `benefits`,
295
+ `catalog-search`, `category-filters`, and `navigability`. `recommendations` is
296
+ held post-MVP and rejected before any write. `create` seeds `home` and `cart`;
297
+ `--starter` seeds every curated view and feature. Home editorial (`faq`, `cta`) and
298
+ data-bound `products` sections: see [Product sections](docs/product-sections.md).
299
+
300
+ **Cart lifecycle and checkout independence.** The cart review is a normal view,
301
+ not a shell element: `view add cart` installs it, `view remove cart` drops it
302
+ (which also hides the lateral's "View cart" action and leaves `/cart` unrouted;
303
+ the navbar icon still opens the compact lateral). `sync` never silently adds it
304
+ to an existing store — upgrading is an explicit `view add cart`. Cart and
305
+ checkout are independent: review works without checkout (its action is hidden),
306
+ and adding `checkout` never adds the cart view. The CLI derives
307
+ `cartViewAvailable`/`checkoutAvailable` from the installed views/features into
308
+ `composition.ts`, which the generated wiring reads.
309
+
310
+ **Prerequisites and pinning.** Node `>=20.9.0`, `node-modules` linker, and the
311
+ canonical Yarn pinned in the generated `package.json` as
312
+ `"packageManager": "yarn@4.5.1"` (Corepack resolves it; the scaffold runs every
313
+ install under that version). The generated project pins an exact compatible
314
+ candidate toolset and installs the `@thorprovider/*` packages from the npm
315
+ registry by default. `--packs <dir>` is an optional path for local candidates
316
+ (published or unpublished): it maps each dependency to a packed tarball via Yarn `resolutions`
317
+ (no workspace import), and the CLI verifies every tarball before writing
318
+ anything — the manifest `name`/`version` must match the expected toolset, and the
319
+ packed `@thorprovider/runtime` must export every value the generated host
320
+ `commerce.ts` imports; a stale or mismatched pack is refused with a named code
321
+ before any file is written. No package-manager safety policy is suppressed: the generated
322
+ `.yarnrc.yml` sets no `npmPreapprovedPackages`, and the pinned versions are
323
+ intended to be published past Yarn's age gate. `doctor` reports the observed linker/Node/Yarn
324
+ against the declared ranges and exits non-zero on a mismatch instead of a false
325
+ clean stamp.
326
+
327
+ ### Configurable composition (E1)
328
+
329
+ The composition engine is a truthful semantic catalog plus a structured
330
+ `inspect → plan → apply` boundary. Run these inside the project:
331
+
332
+ ```bash
333
+ storefront inspect --json
334
+ storefront plan --operations operations.json [--out plan.json] [--json]
335
+ storefront apply --plan plan.json [--json]
336
+ ```
337
+
338
+ - **`inspect --json`** is read-only and deterministic: `formatVersion: 1` /
339
+ `catalogVersion: 1`, every slot/variant with `executable` or `held` + a reason, and a
340
+ capability catalog with `verified`/`unverified` state. A held or unverified unit is
341
+ never reported as available.
342
+ - **`plan`** validates the whole intended state and emits before/after fingerprints and
343
+ the exact derived-file diff. Without `--out` it writes nothing and prints the plan to
344
+ stdout; with `--out` it writes only a safe plan artifact. `--out` is refused
345
+ (`E_OUT_PATH_REFUSED`) for owned, derived, secret-bearing, or installation paths and
346
+ for the operation input.
347
+ - **`apply`** is the only composition mutation. It applies from the recorded
348
+ before-state, reports a **verified no-op** at the recorded after-state, and rejects
349
+ anything else as stale (`E_STALE_PLAN`); concurrent runs are serialized by a local
350
+ lock. A handled write failure rolls back every touched file. An abrupt process kill is
351
+ **not** crash-atomic — the manifest is written last and `sync` detects and repairs the
352
+ divergence.
353
+
354
+ The operations envelope is `{ "formatVersion": 1, "operations": [ ... ] }` (missing or
355
+ different `formatVersion` → `E_INVALID_ENVELOPE`). Operation names are
356
+ `view.add|remove|configure` and `feature.add|remove|configure`; `view add|remove` and
357
+ `feature add|remove` remain thin aliases over the same engine, and so do
358
+ `section add|remove|move`/`sections [<view>]` over `section.add|remove|reorder` (see [Product sections](docs/product-sections.md)).
359
+
360
+ **What E1 proves.** The only executable non-default swap is `home.benefits`
361
+ `Feature1 → Feature2` (identical `BenefitsItem[]` data shape, no bridge); every bridged
362
+ variant is `held`. A feature may declare a `config` and `mounts`; the bounded `benefits`
363
+ feature mounts at `home.benefits` with `{ "presentation": "compact" | "detailed" }`,
364
+ claims the slot exactly once, and preserves user-owned content on removal. Checkout
365
+ keeps its existing feature-owned route only — unproven checkout variants or route
366
+ options are rejected (`E_UNPROVEN_CHECKOUT_OPTION`). The category filter's action is
367
+ **held** (`unverified`, owner E2) and its control is omitted rather than exposed as a
368
+ no-op; catalog search/filters (E2) and composable sections (E3) are **not** implemented
369
+ here.
370
+
371
+ ## Agent guidance and skills
372
+
373
+ A generated storefront ships a managed root `AGENTS.md` and no `/sf-*` commands.
374
+ `AGENTS.md` states the non-negotiable authority rules (RULE 0) and routes each intent
375
+ to the skill that owns the procedure; the skill then delegates the structural write to
376
+ the `storefront` CLI.
377
+
378
+ | Intent | Skill | CLI it drives |
379
+ | --- | --- | --- |
380
+ | Add or remove a view; compose a view's sections; edit user-owned content/props/data | `sf-view-gen` | `storefront view add\|remove`, `section …`, `views --json` |
381
+ | Change colors, theme mode, typography, radius, presets, or identity/SEO metadata | `sf-theme-gen` | `storefront theme` (`--brand`, `--primary`, `--accent`, `--radius`, `--font-sans`, `--set`, `--preset`, `--import`, `--export`, `--mode`) |
382
+ | Add or remove a feature bundle (e.g. `checkout`) | `sf-feature` | `storefront feature add\|remove` |
383
+ | Connect or re-point a Medusa backend | `sf-setup` | `storefront setup` |
384
+ | Edit a view's content/props/data, map props or data, reshape composition, or any change whose acceptance is not a CLI assertion (non-trivial) | `storefront-workflow` | the spec-driven loop, surfaced as **explore → plan → build → verify → inform** |
385
+
386
+ Scaffolding is `npx @thorprovider/storefront create <dir>`; connecting a real backend is
387
+ `storefront setup` from inside the project (see [Backend configuration](#backend-configuration)).
241
388
 
242
- Scaffolding itself is `npx @thorprovider/storefront create <dir>` — there is no
243
- `/sf-init` command; the wizard lives in the CLI and is deterministic.
389
+ ## Philosophy: determinism first; the LLM only where there is judgment
390
+
391
+ A storefront task is one of three kinds, and the tool follows the kind:
392
+
393
+ - **Deterministic** (output is a pure function of input) → a CLI subcommand with flags
394
+ (e.g. `storefront theme --brand orange --radius 0.75rem`). Repeatable, reviewable, no model in the loop.
395
+ - **Mixed** → split it: the script applies the deterministic part, the LLM decides the
396
+ judgment part (the `sf-theme-gen` skill chooses the brand color; `storefront theme --brand` applies it).
397
+ - **Pure judgment** → an `sf-*` skill (e.g. `sf-view-gen`): variant selection, copy, and
398
+ layout choices live here.
399
+
400
+ **Test criterion**: if you can assert the result without a model ("after this, the file
401
+ contains X"), it must be a script; if you cannot, it is skill work. LLM edits are
402
+ non-deterministic and expensive to review, so keep the judgment-heavy work where a
403
+ model adds value and make the rest a reliable script.
244
404
 
245
405
  ## Design guarantees
246
406
 
@@ -252,40 +412,60 @@ Scaffolding itself is `npx @thorprovider/storefront create <dir>` — there is n
252
412
  installer copies anything.
253
413
  - **Stack-constrained**: UI comes only from `@thorprovider/blocks`; data only from
254
414
  `@thorprovider/adapters` and `@thorprovider/runtime/commerce`.
255
- - **Varied output**: a recipe registry lets the skills pick block variants and optional
256
- sections (testimonials, FAQ, statistics, logo cloud, pricing) from your description,
257
- so sites are not identical.
415
+ - **Truthful output**: a versioned recipe/catalog exposes every slot and variant with an
416
+ `executable` or `held` status and a reason. Only executable variants render; a held
417
+ variant is rejected before any write, so a site is never presented with a choice that
418
+ silently does nothing. E1 ships a small proven set (`home.benefits` `Feature1`↔
419
+ `Feature2`); bridged variants and optional sections stay held.
258
420
 
259
421
  ## Generated project structure
260
422
 
261
- Each view is a self-contained module. Shared files compose whatever the view barrel
262
- exports and never name a view, so adding one is a copy plus a single registration.
423
+ Each view is a self-contained module. The CLI generates the barrels and wiring
424
+ from the manifest; shared files compose whatever the barrel exports and never name
425
+ a view. Only user-owned files (marked `*`) are hand-edited.
263
426
 
264
427
  ```
265
428
  lib/<name>/
266
- catalog.ts, registry.tsx, runtime.ts, handlers.ts, state.ts # shell; compose the barrel
267
- mappers.ts # CardProduct -> FeaturedProduct / CategoryCard
268
- spec/index.ts, spec/page-root.ts, spec/types.ts # routes built from the barrel
429
+ catalog.ts, registry.tsx, runtime.ts, handlers.ts, state.ts # derived shell; compose the barrel
430
+ mappers.ts * # user-owned: CardProduct -> block props
431
+ shell/
432
+ content.ts * # user-owned: wordmark accents + navbar note + footer newsletter
433
+ props.ts * # user-owned: shell element props
434
+ spec/index.ts, spec/page-root.ts, spec/types.ts # derived routes built from the barrel
269
435
  views/
270
- index.ts # barrel: export const views: ViewModule[] = [home, ...]
271
- types.ts # ViewModule contract
272
- sections/*.tsx # optional-section fragments (testimonials, faq, ...)
273
- home/index.tsx # one folder per view
274
- product/index.tsx
275
- category/index.tsx
276
- checkout/index.tsx
277
- user-panel/index.tsx
436
+ index.ts # derived server barrel: export const views: ViewModule[] = [home, ...]
437
+ registry.ts # derived client barrel: viewRegistries (no server loaders)
438
+ types.ts # ViewModule / ViewRegistry contract
439
+ home/
440
+ index.tsx # derived thin wrapper (imports ./content, ./props, ./data)
441
+ registry.tsx # derived 'use client' component mappings
442
+ content.ts * # user-owned copy/data
443
+ props.ts * # user-owned element props
444
+ data.ts * # user-owned loader shaping
445
+ schema.ts * # user-owned local Zod mirror
446
+ product/ category/ checkout/ # same shape
278
447
  ```
279
448
 
280
- A view folder ships the package's default block variants; `/sf-add-view` picks the
281
- intent/variant from your description (validated by the skill's `scripts/validate.mjs`)
282
- and substitutes only the imported block identifier. The scaffold templates live at
283
- the package root, and the view templates travel inside the skills that use them:
449
+ The shell's derived `spec/index.ts` imports the user-owned `shell/props.ts`
450
+ (`...navbarShellProps`, `...footerShellProps`) and composes it **last**, so the brand
451
+ surface and footer copy are configured without touching derived files. Configure
452
+ `shell/content.ts` (`logoAccent`/`brandAccent`, `navbarClassName`/`footerClassName`,
453
+ `brandDescription`, `linkColumns`); defaults are neutral and link only to served routes
454
+ (`/`, `/categories`, `/checkout`), never dead `#` links. `newsletter` defaults to `false`
455
+ and the scaffold ships no submit callback, so a content-only newsletter renders no form
456
+ — never a fake form. `shippingNote` defaults to `''`: while it is empty (and there are
457
+ no utility links) the navbar renders no utility strip, so no placeholder note is shown.
458
+
459
+ A view's derived `index.tsx` imports the user-owned `content.ts`/`props.ts`/
460
+ `data.ts`, so a customization changes real spec/loader output and survives `sync`.
461
+ `npx @thorprovider/storefront view add <view>` chooses the intent/variant from the
462
+ registry, writes the derived files, and regenerates the barrels; the agent edits
463
+ only the user-owned files. The scaffold templates live at the package root:
284
464
 
285
465
  ```
286
- assets/templates/app/ # shell (copied by `create`)
287
- assets/templates/views/home/ # home (copied by `create`)
288
- skills/sf-view-gen/assets/templates/views/<view>/ # the other views (copied by /sf-add-view)
466
+ assets/templates/app/ # shell (generated by `create`)
467
+ assets/templates/views/home/ # home (generated by `create`)
468
+ assets/templates/views/<view>/ # the other views (generated by `storefront view add`)
289
469
  ```
290
470
 
291
471
  ## Development
@@ -301,24 +481,13 @@ node bin/storefront.js doctor <tmp> # report the recorded version
301
481
  node scripts/render-templates.mjs <tmp> my-shop # re-render templates only (no install; fast loop)
302
482
  ```
303
483
 
304
- `scripts/render-templates.mjs` is the same renderer the `create` action uses for
305
- stage 6. It copies the templates and substitutes placeholders without touching
306
- dependencies, so iterating on a template against a persistent project (e.g.
307
- `projects/<name>/`) costs seconds and no network.
308
-
309
- `npm pack` exists to verify a different thing: **what actually ships to npm** —
310
- the `files` list, and that every asset travels inside its skill. It is only
311
- meaningful at these moments:
312
-
313
- | Moment | Action |
314
- | --- | --- |
315
- | Day-to-day development | Nothing — use the local binary above |
316
- | Changing `files`, moving assets, adding or removing directories | `npm pack --dry-run` to list what would ship |
317
- | CI, before publishing | `yarn check-assets` + `npm pack --dry-run` as a guard |
318
- | Publishing a release | `npm pack` / `yarn npm publish` — the real tarball |
319
-
320
- A generated `.tgz` goes stale the moment the tree changes, so it is not a
321
- development tool. Keep it out of the working tree.
484
+ `scripts/render-templates.mjs` is the same renderer `create` uses for stage 6; it copies
485
+ templates and substitutes placeholders without touching dependencies, so iterating on a
486
+ template costs seconds and no network. `npm pack` verifies a different thing — **what
487
+ actually ships to npm** (the `files` list, and that every asset travels inside its
488
+ skill). Use it only when `files`/assets change or before a release (`npm pack --dry-run`
489
+ to list; `yarn check-assets` + `npm pack --dry-run` in CI). A generated `.tgz` goes
490
+ stale the moment the tree changes, so keep it out of the working tree.
322
491
 
323
492
  ## Maintenance
324
493