@funnelsgrove/cli 0.1.15 → 0.1.18

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 (293) hide show
  1. package/contracts/step-contract-v2.json +733 -0
  2. package/dist/analyticsOutput.d.ts +30 -15
  3. package/dist/analyticsOutput.js +45 -28
  4. package/dist/apiClient.d.ts +85 -0
  5. package/dist/apiClient.js +225 -8
  6. package/dist/cli.d.ts +149 -1
  7. package/dist/cli.js +1232 -71
  8. package/dist/diagnosticOutput.d.ts +42 -0
  9. package/dist/diagnosticOutput.js +83 -0
  10. package/dist/docsOutput.d.ts +19 -0
  11. package/dist/docsOutput.js +73 -0
  12. package/dist/docsTransaction.d.ts +81 -0
  13. package/dist/docsTransaction.js +1755 -0
  14. package/dist/funnelValidation.d.ts +14 -0
  15. package/dist/funnelValidation.js +297 -0
  16. package/dist/localSync.d.ts +79 -0
  17. package/dist/localSync.js +400 -29
  18. package/dist/projectValidator.d.ts +27 -0
  19. package/dist/projectValidator.js +208 -0
  20. package/dist/reskin.js +27 -1
  21. package/dist/stepContractMigration.d.ts +71 -0
  22. package/dist/stepContractMigration.js +1323 -0
  23. package/dist/templateDocs.js +8 -25
  24. package/docs-release-history.json +66 -0
  25. package/legacy-docs-catalog.json +1012 -0
  26. package/package.json +13 -4
  27. package/template_docs/.funnelsgrove-docs.json +144 -0
  28. package/template_docs/AGENTS.md +30 -58
  29. package/template_docs/CLAUDE.md +0 -2
  30. package/template_docs/docs/funnelsgrove/START-HERE.md +73 -0
  31. package/template_docs/docs/funnelsgrove/contracts/analytics-events.md +59 -0
  32. package/template_docs/docs/funnelsgrove/contracts/content-answers.md +44 -0
  33. package/template_docs/docs/funnelsgrove/contracts/flow-routing.md +41 -0
  34. package/template_docs/docs/funnelsgrove/contracts/payments.md +42 -0
  35. package/template_docs/docs/funnelsgrove/contracts/step-metadata.md +57 -0
  36. package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
  37. package/template_docs/docs/funnelsgrove/qa/analytics.md +43 -0
  38. package/template_docs/docs/funnelsgrove/qa/local.md +42 -0
  39. package/template_docs/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
  40. package/template_docs/docs/funnelsgrove/qa/publish.md +32 -0
  41. package/template_docs/docs/funnelsgrove/recipes/add-experiment.md +34 -0
  42. package/template_docs/docs/funnelsgrove/recipes/add-step.md +33 -0
  43. package/template_docs/docs/funnelsgrove/recipes/edit-step.md +31 -0
  44. package/template_docs/docs/funnelsgrove/steps/INDEX.md +26 -0
  45. package/template_docs/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
  46. package/template_docs/docs/funnelsgrove/steps/checkout.md +120 -0
  47. package/template_docs/docs/funnelsgrove/steps/form_input.md +115 -0
  48. package/template_docs/docs/funnelsgrove/steps/intro_hero.md +110 -0
  49. package/template_docs/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
  50. package/template_docs/docs/funnelsgrove/steps/paywall_offer.md +115 -0
  51. package/template_docs/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
  52. package/template_docs/docs/funnelsgrove/steps/purchase_completed.md +117 -0
  53. package/template_docs/docs/funnelsgrove/steps/single_step_choice.md +122 -0
  54. package/template_docs/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
  55. package/template_docs/docs/funnelsgrove/steps/social_proof.md +110 -0
  56. package/template_docs/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
  57. package/template_docs/docs/funnelsgrove/steps/subscription_management.md +112 -0
  58. package/template_docs/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
  59. package/template_docs/docs/funnelsgrove/steps/upsell_offer.md +112 -0
  60. package/template_docs/docs/funnelsgrove/steps/value_prop_story.md +110 -0
  61. package/template_docs/funnel-docs.config.json +43 -0
  62. package/template_scaffold/.env.example +41 -0
  63. package/template_scaffold/.funnelsgrove-contract-tools.json +19 -0
  64. package/template_scaffold/.funnelsgrove-docs.json +144 -0
  65. package/template_scaffold/.funnelsgrove-scaffold.json +1078 -0
  66. package/template_scaffold/AGENTS.md +42 -0
  67. package/template_scaffold/CLAUDE.md +1 -0
  68. package/template_scaffold/PLAN.md +81 -0
  69. package/template_scaffold/PRODUCT_SENSE.md +28 -0
  70. package/template_scaffold/context/build-context.md +34 -0
  71. package/template_scaffold/docs/AGENT_FUNNEL_IMPLEMENTATION_SPEC.md +5 -0
  72. package/template_scaffold/docs/ARCHITECTURE.md +5 -0
  73. package/template_scaffold/docs/FLOW_CONFIG_AND_ROUTING.md +5 -0
  74. package/template_scaffold/docs/PAYWALL_AND_PAYMENTS.md +5 -0
  75. package/template_scaffold/docs/README.md +5 -0
  76. package/template_scaffold/docs/funnelsgrove/START-HERE.md +73 -0
  77. package/template_scaffold/docs/funnelsgrove/contracts/analytics-events.md +59 -0
  78. package/template_scaffold/docs/funnelsgrove/contracts/content-answers.md +44 -0
  79. package/template_scaffold/docs/funnelsgrove/contracts/flow-routing.md +41 -0
  80. package/template_scaffold/docs/funnelsgrove/contracts/payments.md +42 -0
  81. package/template_scaffold/docs/funnelsgrove/contracts/step-metadata.md +57 -0
  82. package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
  83. package/template_scaffold/docs/funnelsgrove/qa/analytics.md +43 -0
  84. package/template_scaffold/docs/funnelsgrove/qa/local.md +42 -0
  85. package/template_scaffold/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
  86. package/template_scaffold/docs/funnelsgrove/qa/publish.md +32 -0
  87. package/template_scaffold/docs/funnelsgrove/recipes/add-experiment.md +34 -0
  88. package/template_scaffold/docs/funnelsgrove/recipes/add-step.md +33 -0
  89. package/template_scaffold/docs/funnelsgrove/recipes/edit-step.md +31 -0
  90. package/template_scaffold/docs/funnelsgrove/steps/INDEX.md +26 -0
  91. package/template_scaffold/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
  92. package/template_scaffold/docs/funnelsgrove/steps/checkout.md +120 -0
  93. package/template_scaffold/docs/funnelsgrove/steps/form_input.md +115 -0
  94. package/template_scaffold/docs/funnelsgrove/steps/intro_hero.md +110 -0
  95. package/template_scaffold/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
  96. package/template_scaffold/docs/funnelsgrove/steps/paywall_offer.md +115 -0
  97. package/template_scaffold/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
  98. package/template_scaffold/docs/funnelsgrove/steps/purchase_completed.md +117 -0
  99. package/template_scaffold/docs/funnelsgrove/steps/single_step_choice.md +122 -0
  100. package/template_scaffold/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
  101. package/template_scaffold/docs/funnelsgrove/steps/social_proof.md +110 -0
  102. package/template_scaffold/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
  103. package/template_scaffold/docs/funnelsgrove/steps/subscription_management.md +112 -0
  104. package/template_scaffold/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
  105. package/template_scaffold/docs/funnelsgrove/steps/upsell_offer.md +112 -0
  106. package/template_scaffold/docs/funnelsgrove/steps/value_prop_story.md +110 -0
  107. package/template_scaffold/docs/references/funnel-sdk-user-answers.md +5 -0
  108. package/template_scaffold/eslint.config.mjs +16 -0
  109. package/template_scaffold/funnel-agent-docs.test.ts +699 -0
  110. package/template_scaffold/funnel-docs.config.json +43 -0
  111. package/template_scaffold/funnel.config.json +5 -0
  112. package/template_scaffold/gitignore.template +5 -0
  113. package/template_scaffold/next-env.d.ts +6 -0
  114. package/template_scaffold/next.config.ts +53 -0
  115. package/template_scaffold/package-lock.json +7737 -0
  116. package/template_scaffold/package.json +37 -0
  117. package/template_scaffold/package.test.ts +23 -0
  118. package/template_scaffold/public/claimbee/bee.png +0 -0
  119. package/template_scaffold/public/claimbee/device-iphone.png +0 -0
  120. package/template_scaffold/public/claimbee/device-macbook.png +0 -0
  121. package/template_scaffold/public/claimbee/device-none.png +0 -0
  122. package/template_scaffold/public/claimbee/device-watch.png +0 -0
  123. package/template_scaffold/public/claimbee/glow.svg +3 -0
  124. package/template_scaffold/public/claimbee/steps-sprite-alt.png +0 -0
  125. package/template_scaffold/public/claimbee/steps-sprite-default.png +0 -0
  126. package/template_scaffold/public/claimbee/testimonial-avatar.png +0 -0
  127. package/template_scaffold/public/claimbee/testimonial-star.svg +3 -0
  128. package/template_scaffold/public/paywall/ai-claim-assistant.png +0 -0
  129. package/template_scaffold/public/paywall/claim-history.png +0 -0
  130. package/template_scaffold/public/paywall/clear-progress.png +0 -0
  131. package/template_scaffold/public/paywall/hero.png +0 -0
  132. package/template_scaffold/public/paywall/money-alerts.png +0 -0
  133. package/template_scaffold/public/paywall/money-back-badge.png +0 -0
  134. package/template_scaffold/public/paywall/paywall-image.png +0 -0
  135. package/template_scaffold/public/paywall/r1.png +0 -0
  136. package/template_scaffold/public/paywall/r2.png +0 -0
  137. package/template_scaffold/public/paywall/r3.png +0 -0
  138. package/template_scaffold/public/paywall/r4.png +0 -0
  139. package/template_scaffold/public/paywall/r5.png +0 -0
  140. package/template_scaffold/public/paywall/r6.png +0 -0
  141. package/template_scaffold/public/paywall/special-offer-gift.png +0 -0
  142. package/template_scaffold/public/please/bee.png +0 -0
  143. package/template_scaffold/public/please/device-iphone.png +0 -0
  144. package/template_scaffold/public/please/device-macbook.png +0 -0
  145. package/template_scaffold/public/please/device-none.png +0 -0
  146. package/template_scaffold/public/please/device-watch.png +0 -0
  147. package/template_scaffold/public/please/glow.svg +3 -0
  148. package/template_scaffold/public/please/steps-sprite-alt.png +0 -0
  149. package/template_scaffold/public/please/steps-sprite-default.png +0 -0
  150. package/template_scaffold/public/please/testimonial-avatar.png +0 -0
  151. package/template_scaffold/public/please/testimonial-star.svg +3 -0
  152. package/template_scaffold/public/upsell/upgrade-hero.png +0 -0
  153. package/template_scaffold/src/app/[stepId]/page.test.tsx +59 -0
  154. package/template_scaffold/src/app/[stepId]/page.tsx +62 -0
  155. package/template_scaffold/src/app/globals.css +49 -0
  156. package/template_scaffold/src/app/layout.test.ts +26 -0
  157. package/template_scaffold/src/app/layout.tsx +61 -0
  158. package/template_scaffold/src/app/page.tsx +8 -0
  159. package/template_scaffold/src/app/sdk/page.tsx +673 -0
  160. package/template_scaffold/src/app/subscription-dashboard/page.tsx +274 -0
  161. package/template_scaffold/src/assets/claimbee/bee.png +0 -0
  162. package/template_scaffold/src/assets/claimbee/device-iphone.png +0 -0
  163. package/template_scaffold/src/assets/claimbee/device-macbook.png +0 -0
  164. package/template_scaffold/src/assets/claimbee/device-none.png +0 -0
  165. package/template_scaffold/src/assets/claimbee/device-watch.png +0 -0
  166. package/template_scaffold/src/assets/claimbee/glow.svg +3 -0
  167. package/template_scaffold/src/assets/claimbee/steps-sprite-alt.png +0 -0
  168. package/template_scaffold/src/assets/claimbee/steps-sprite-default.png +0 -0
  169. package/template_scaffold/src/assets/claimbee/testimonial-avatar.png +0 -0
  170. package/template_scaffold/src/assets/claimbee/testimonial-star.svg +3 -0
  171. package/template_scaffold/src/components/FunnelEditorPanel.tsx +194 -0
  172. package/template_scaffold/src/components/FunnelFlow.test.tsx +17 -0
  173. package/template_scaffold/src/components/FunnelFlow.tsx +114 -0
  174. package/template_scaffold/src/components/FunnelStepImage.test.ts +15 -0
  175. package/template_scaffold/src/components/FunnelStepImage.tsx +28 -0
  176. package/template_scaffold/src/components/step/FunnelStepHeader.tsx +35 -0
  177. package/template_scaffold/src/config/billing.plans.test.ts +143 -0
  178. package/template_scaffold/src/config/billing.plans.ts +69 -0
  179. package/template_scaffold/src/config/billing.test.plans.ts +32 -0
  180. package/template_scaffold/src/config/experiments.generated.ts +4 -0
  181. package/template_scaffold/src/config/experiments.test.ts +12 -0
  182. package/template_scaffold/src/config/experiments.ts +9 -0
  183. package/template_scaffold/src/config/funnel.config.ts +12 -0
  184. package/template_scaffold/src/config/funnel.manifest.test.ts +166 -0
  185. package/template_scaffold/src/config/funnel.manifest.ts +139 -0
  186. package/template_scaffold/src/config/offer-sets.generated.ts +4 -0
  187. package/template_scaffold/src/contract/funnel-project.validation.test.ts +80 -0
  188. package/template_scaffold/src/contract/funnel-project.validation.ts +44 -0
  189. package/template_scaffold/src/contract/funnel-validator.vite.config.ts +11 -0
  190. package/template_scaffold/src/contract/golden/cancellation_offer.fixture.ts +29 -0
  191. package/template_scaffold/src/contract/golden/checkout.fixture.ts +37 -0
  192. package/template_scaffold/src/contract/golden/form_input.fixture.ts +28 -0
  193. package/template_scaffold/src/contract/golden/golden-fixtures.test.ts +190 -0
  194. package/template_scaffold/src/contract/golden/intro_hero.fixture.ts +27 -0
  195. package/template_scaffold/src/contract/golden/multi_select_choice.fixture.ts +37 -0
  196. package/template_scaffold/src/contract/golden/paywall_offer.fixture.ts +30 -0
  197. package/template_scaffold/src/contract/golden/progress_interstitial.fixture.ts +28 -0
  198. package/template_scaffold/src/contract/golden/purchase_completed.fixture.ts +33 -0
  199. package/template_scaffold/src/contract/golden/single_step_choice.fixture.ts +36 -0
  200. package/template_scaffold/src/contract/golden/single_step_choice_emoji.fixture.ts +36 -0
  201. package/template_scaffold/src/contract/golden/social_proof.fixture.ts +27 -0
  202. package/template_scaffold/src/contract/golden/subscription_handoff.fixture.ts +30 -0
  203. package/template_scaffold/src/contract/golden/subscription_management.fixture.ts +29 -0
  204. package/template_scaffold/src/contract/golden/summary_confirmation.fixture.ts +27 -0
  205. package/template_scaffold/src/contract/golden/upsell_offer.fixture.ts +29 -0
  206. package/template_scaffold/src/contract/golden/value_prop_story.fixture.ts +27 -0
  207. package/template_scaffold/src/contract/validate-funnel.cli.test.ts +180 -0
  208. package/template_scaffold/src/contract/validate-funnel.cli.ts +199 -0
  209. package/template_scaffold/src/runtime/checkout-runtime-config.test.ts +60 -0
  210. package/template_scaffold/src/runtime/checkout-runtime-config.ts +26 -0
  211. package/template_scaffold/src/runtime/funnel-runtime.test.ts +25 -0
  212. package/template_scaffold/src/runtime/funnel-runtime.ts +118 -0
  213. package/template_scaffold/src/runtime/step-content-context.test.ts +38 -0
  214. package/template_scaffold/src/runtime/step-content-context.ts +65 -0
  215. package/template_scaffold/src/runtime/step-registry.ts +66 -0
  216. package/template_scaffold/src/runtime/step-variable-values.ts +31 -0
  217. package/template_scaffold/src/runtime/use-funnel-flow-controller.integration.test.ts +203 -0
  218. package/template_scaffold/src/runtime/use-funnel-flow-controller.test.ts +76 -0
  219. package/template_scaffold/src/runtime/use-funnel-flow-controller.ts +53 -0
  220. package/template_scaffold/src/steps/content/email-capture.content.ts +29 -0
  221. package/template_scaffold/src/steps/content/manage-subscription.content.ts +62 -0
  222. package/template_scaffold/src/steps/content/paywall.content.ts +191 -0
  223. package/template_scaffold/src/steps/content/step-01.content.ts +23 -0
  224. package/template_scaffold/src/steps/content/step-02.content.ts +77 -0
  225. package/template_scaffold/src/steps/content/step-03.content.ts +40 -0
  226. package/template_scaffold/src/steps/content/step-04.content.ts +49 -0
  227. package/template_scaffold/src/steps/content/step-32-paywall.content.ts +352 -0
  228. package/template_scaffold/src/steps/content/subscription-started.content.ts +68 -0
  229. package/template_scaffold/src/steps/editor/email-capture.editor.ts +58 -0
  230. package/template_scaffold/src/steps/editor/manage-subscription.editor.ts +179 -0
  231. package/template_scaffold/src/steps/editor/paywall.editor.ts +232 -0
  232. package/template_scaffold/src/steps/editor/step-01.editor.ts +22 -0
  233. package/template_scaffold/src/steps/editor/step-02.editor.ts +41 -0
  234. package/template_scaffold/src/steps/editor/step-03.editor.ts +58 -0
  235. package/template_scaffold/src/steps/editor/step-04.editor.ts +98 -0
  236. package/template_scaffold/src/steps/editor/step-32-paywall.editor.ts +478 -0
  237. package/template_scaffold/src/steps/editor/subscription-started.editor.ts +131 -0
  238. package/template_scaffold/src/steps/index.ts +25 -0
  239. package/template_scaffold/src/steps/paywall-checkout-options.test.ts +104 -0
  240. package/template_scaffold/src/steps/paywall-checkout-options.ts +130 -0
  241. package/template_scaffold/src/steps/pricing/paywall-b.pricing.ts +12 -0
  242. package/template_scaffold/src/steps/pricing/paywall.pricing.ts +12 -0
  243. package/template_scaffold/src/steps/pricing/shared-plan-order.ts +17 -0
  244. package/template_scaffold/src/steps/pricing/upsell-form.pricing.ts +16 -0
  245. package/template_scaffold/src/steps/shared-header-contract.test.ts +27 -0
  246. package/template_scaffold/src/steps/step-01.test.ts +38 -0
  247. package/template_scaffold/src/steps/step-01.tsx +108 -0
  248. package/template_scaffold/src/steps/step-02.test.ts +43 -0
  249. package/template_scaffold/src/steps/step-02.tsx +140 -0
  250. package/template_scaffold/src/steps/step-03.test.ts +15 -0
  251. package/template_scaffold/src/steps/step-03.tsx +262 -0
  252. package/template_scaffold/src/steps/step-04.test.ts +32 -0
  253. package/template_scaffold/src/steps/step-04.tsx +673 -0
  254. package/template_scaffold/src/steps/step-31-email-capture.test.ts +22 -0
  255. package/template_scaffold/src/steps/step-31-email-capture.tsx +195 -0
  256. package/template_scaffold/src/steps/step-32-paywall.test.ts +770 -0
  257. package/template_scaffold/src/steps/step-32-paywall.tsx +2253 -0
  258. package/template_scaffold/src/steps/step-32b-paywall-b.tsx +35 -0
  259. package/template_scaffold/src/steps/step-33-subscription-started.test.ts +26 -0
  260. package/template_scaffold/src/steps/step-33-subscription-started.tsx +86 -0
  261. package/template_scaffold/src/steps/step-34-upsell-form.test.ts +27 -0
  262. package/template_scaffold/src/steps/step-35-manage-subscription.tsx +46 -0
  263. package/template_scaffold/src/steps/step-content.registry.ts +45 -0
  264. package/template_scaffold/src/steps/step-image-migration.test.ts +20 -0
  265. package/template_scaffold/src/steps/styles/index.css +2 -0
  266. package/template_scaffold/src/steps/styles/shared/final-flow-shared.css +55 -0
  267. package/template_scaffold/src/steps/styles/shared/funnel-base.css +543 -0
  268. package/template_scaffold/src/steps/styles/shared/info-shared.css +139 -0
  269. package/template_scaffold/src/steps/styles/shared/progress-shared.css +117 -0
  270. package/template_scaffold/src/steps/styles/shared/question-shared.css +76 -0
  271. package/template_scaffold/src/steps/styles/shared/responsive-container.css +176 -0
  272. package/template_scaffold/src/steps/types.ts +7 -0
  273. package/template_scaffold/src/steps/use-claimbee-step-content.ts +15 -0
  274. package/template_scaffold/src/theme/theme.ts +81 -0
  275. package/template_scaffold/tsconfig.json +38 -0
  276. package/template_scaffold/vitest.config.ts +32 -0
  277. package/template_validation/.funnelsgrove-contract-tools.json +19 -0
  278. package/template_validation/src/contract/funnel-project.validation.ts +44 -0
  279. package/template_validation/src/contract/funnel-validator.vite.config.ts +11 -0
  280. package/template_validation/src/contract/validate-funnel.cli.ts +199 -0
  281. package/template_docs/docs/ab-experiments.md +0 -111
  282. package/template_docs/docs/analytics.md +0 -65
  283. package/template_docs/docs/editing-flow.md +0 -136
  284. package/template_docs/docs/editing-step.md +0 -125
  285. package/template_docs/docs/editor-and-content.md +0 -58
  286. package/template_docs/docs/funnel-runtime-architecture.md +0 -125
  287. package/template_docs/docs/meta-pixel-conversions-api.md +0 -50
  288. package/template_docs/docs/payment-plans-and-discounts.md +0 -106
  289. package/template_docs/docs/publishing-and-versioning.md +0 -117
  290. package/template_docs/docs/qa-checklist.md +0 -80
  291. package/template_docs/docs/sdk-api-endpoints.md +0 -78
  292. package/template_docs/docs/step-ui-guidelines.md +0 -110
  293. package/template_docs/docs/theme.md +0 -35
@@ -0,0 +1,122 @@
1
+ # `single_step_choice_emoji`
2
+
3
+ ## Use when / do not use when
4
+
5
+ Use when exactly one emoji-led option completes on selection. Do not use when emoji is merely decorative on a standard list or when multiple answers are allowed.
6
+
7
+ ## Exact metadata contract
8
+
9
+ <!-- funnelsgrove:generated:start contract-v3/step/single_step_choice_emoji -->
10
+ ### Generated contract facts — do not edit
11
+
12
+ - Step contract version: `3`
13
+ - Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
14
+ - Type: `single_step_choice_emoji`
15
+ - Family: `choice`
16
+ - Description: Exactly one emoji-led option is selected.
17
+ - Action owner: `step`
18
+ - Analytics role: _none_
19
+ - Terminal: `false`
20
+ - Required kind: _none_
21
+ - Allowed kinds: _None_
22
+ - Allowed semantic events: _None_
23
+ - Forbidden capabilities: `email-capture`, `checkout`, `subscription-management`, `subscription-handoff`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`
24
+
25
+ #### Choice contract
26
+
27
+ - Cardinality: `one`
28
+ - Presentation: `emoji`
29
+ - Completion: `selection`
30
+ - Allow empty config: `false`
31
+
32
+ #### Automatic lifecycle events
33
+
34
+ `first_step_clicked`, `first_step_viewed`, `funnel_started`, `step_completed`, `step_exited`, `step_started`
35
+
36
+ #### Approved capability owners
37
+
38
+ - `choice-write` must be owned by `useStepChoices`.
39
+
40
+ #### Reserved identities
41
+
42
+ _No reserved identity for this type._
43
+
44
+ #### Golden fixture source
45
+
46
+ ```ts
47
+ import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
48
+
49
+ const choice = { answerKey: 'goalFeeling' } as const;
50
+
51
+ const manifestStep = {
52
+ id: 'goal-feeling',
53
+ name: 'goal-feeling',
54
+ path: '/goal-feeling',
55
+ filePath: 'src/steps/step-goal-feeling.tsx',
56
+ componentKey: 'stepGoalFeeling',
57
+ type: 'single_step_choice_emoji',
58
+ choice,
59
+ title: 'How do you want to feel?',
60
+ } as const satisfies FunnelManifestStep;
61
+
62
+ const componentMeta = {
63
+ id: 'goal-feeling',
64
+ name: 'goal-feeling',
65
+ type: 'single_step_choice_emoji',
66
+ choice,
67
+ figmaNodeId: 'golden:single-step-choice-emoji',
68
+ title: 'How do you want to feel?',
69
+ description: 'Select exactly one emoji-led feeling.',
70
+ } as const satisfies FunnelStepMeta;
71
+
72
+ export const goldenFixture = {
73
+ stepType: 'single_step_choice_emoji',
74
+ manifestStep,
75
+ componentMeta,
76
+ options: [
77
+ { id: 'focused', label: 'Focused', emoji: '🎯' },
78
+ { id: 'calm', label: 'Calm', emoji: '🌿' },
79
+ ],
80
+ persistedAnswer: 'focused',
81
+ helper: 'useStepChoices',
82
+ } as const;
83
+ ```
84
+ <!-- funnelsgrove:generated:end contract-v3/step/single_step_choice_emoji -->
85
+
86
+ ## Interaction and persisted answer shape
87
+
88
+ Declare `choice.answerKey`; every option includes an emoji and stable ID. Persist the selected ID as a scalar string.
89
+
90
+ ## Automatic lifecycle events
91
+
92
+ `useStepChoices` coordinates persistence, controller completion, and manifest routing. Preview suppresses provider delivery.
93
+
94
+ ## Required semantic conversions
95
+
96
+ None.
97
+
98
+ ## Approved shared helpers
99
+
100
+ Use `useStepChoices` and the shared emoji-choice rendering pattern.
101
+
102
+ ## MUST rules
103
+
104
+ - Provide meaningful emoji data for every option.
105
+ - Keep IDs stable when labels or emoji change.
106
+
107
+ ## NEVER rules
108
+
109
+ - Never persist emoji or labels as the answer.
110
+ - Never bypass the choice helper.
111
+
112
+ ## Golden template or fixture
113
+
114
+ See the [embedded canonical fixture](#golden-fixture-source) generated from `single_step_choice_emoji.fixture.ts`.
115
+
116
+ ## Validation command
117
+
118
+ Run `fgrove validate`.
119
+
120
+ ## Manual QA and completion criteria
121
+
122
+ Confirm keyboard and pointer selection both save one stable ID, show the intended emoji, and advance once.
@@ -0,0 +1,110 @@
1
+ # `social_proof`
2
+
3
+ ## Use when / do not use when
4
+
5
+ Use for testimonials, reviews, credibility, or evidence. Do not use for general benefits, a survey, or an offer.
6
+
7
+ ## Exact metadata contract
8
+
9
+ <!-- funnelsgrove:generated:start contract-v3/step/social_proof -->
10
+ ### Generated contract facts — do not edit
11
+
12
+ - Step contract version: `3`
13
+ - Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
14
+ - Type: `social_proof`
15
+ - Family: `content`
16
+ - Description: Testimonials, reviews, credibility, or evidence.
17
+ - Action owner: `shared-shell`
18
+ - Analytics role: _none_
19
+ - Terminal: `false`
20
+ - Required kind: _none_
21
+ - Allowed kinds: _None_
22
+ - Allowed semantic events: _None_
23
+ - Forbidden capabilities: `choice-write`, `email-capture`, `checkout`, `subscription-management`, `subscription-handoff`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`
24
+
25
+ #### Choice contract
26
+
27
+ _Not a choice type._
28
+
29
+ #### Automatic lifecycle events
30
+
31
+ `first_step_clicked`, `first_step_viewed`, `funnel_started`, `step_completed`, `step_exited`, `step_started`
32
+
33
+ #### Approved capability owners
34
+
35
+ _No special capability is allowed._
36
+
37
+ #### Reserved identities
38
+
39
+ _No reserved identity for this type._
40
+
41
+ #### Golden fixture source
42
+
43
+ ```ts
44
+ import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
45
+
46
+ const manifestStep = {
47
+ id: 'member-results',
48
+ name: 'member-results',
49
+ path: '/member-results',
50
+ filePath: 'src/steps/step-member-results.tsx',
51
+ componentKey: 'stepMemberResults',
52
+ type: 'social_proof',
53
+ title: 'Member results',
54
+ } as const satisfies FunnelManifestStep;
55
+
56
+ const componentMeta = {
57
+ id: 'member-results',
58
+ name: 'member-results',
59
+ type: 'social_proof',
60
+ figmaNodeId: 'golden:social-proof',
61
+ title: 'Member results',
62
+ description: 'Show attributable testimonials and evidence.',
63
+ } as const satisfies FunnelStepMeta;
64
+
65
+ export const goldenFixture = {
66
+ stepType: 'social_proof',
67
+ manifestStep,
68
+ componentMeta,
69
+ actionOwner: 'shared-shell',
70
+ } as const;
71
+ ```
72
+ <!-- funnelsgrove:generated:end contract-v3/step/social_proof -->
73
+
74
+ ## Interaction and persisted answer shape
75
+
76
+ This content screen does not persist an answer. Standard progression belongs to the shared shell.
77
+
78
+ ## Automatic lifecycle events
79
+
80
+ The controller owns lifecycle tracking and suppresses provider delivery in preview.
81
+
82
+ ## Required semantic conversions
83
+
84
+ None.
85
+
86
+ ## Approved shared helpers
87
+
88
+ Use shared content/image components and the shared action bar.
89
+
90
+ ## MUST rules
91
+
92
+ - Keep proof content attributable and builder-editable where required.
93
+ - Keep metadata parity.
94
+
95
+ ## NEVER rules
96
+
97
+ - Never turn proof interactions into invented conversion events.
98
+ - Never call providers directly.
99
+
100
+ ## Golden template or fixture
101
+
102
+ See the [embedded canonical fixture](#golden-fixture-source) generated from `social_proof.fixture.ts`.
103
+
104
+ ## Validation command
105
+
106
+ Run `fgrove validate`.
107
+
108
+ ## Manual QA and completion criteria
109
+
110
+ Confirm all proof content and assets render accessibly at supported breakpoints and Continue follows manifest routing.
@@ -0,0 +1,113 @@
1
+ # `subscription_handoff`
2
+
3
+ ## Use when / do not use when
4
+
5
+ Use for a non-terminal app, store, or deep-link handoff. Do not use for the canonical post-purchase registration-completion terminal.
6
+
7
+ ## Exact metadata contract
8
+
9
+ <!-- funnelsgrove:generated:start contract-v3/step/subscription_handoff -->
10
+ ### Generated contract facts — do not edit
11
+
12
+ - Step contract version: `3`
13
+ - Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
14
+ - Type: `subscription_handoff`
15
+ - Family: `commerce`
16
+ - Description: A non-terminal app, store, or deep-link handoff.
17
+ - Action owner: `step`
18
+ - Analytics role: _none_
19
+ - Terminal: `false`
20
+ - Required kind: `subscription-handoff`
21
+ - Allowed kinds: `subscription-handoff`
22
+ - Allowed semantic events: _None_
23
+ - Forbidden capabilities: `choice-write`, `email-capture`, `checkout`, `subscription-management`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`
24
+
25
+ #### Choice contract
26
+
27
+ _Not a choice type._
28
+
29
+ #### Automatic lifecycle events
30
+
31
+ `first_step_clicked`, `first_step_viewed`, `funnel_started`, `step_completed`, `step_exited`, `step_started`
32
+
33
+ #### Approved capability owners
34
+
35
+ - `subscription-handoff` must be owned by `runtime-handoff-helper`.
36
+
37
+ #### Reserved identities
38
+
39
+ _No reserved identity for this type._
40
+
41
+ #### Golden fixture source
42
+
43
+ ```ts
44
+ import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
45
+
46
+ const manifestStep = {
47
+ id: 'open-app',
48
+ name: 'open-app',
49
+ path: '/open-app',
50
+ filePath: 'src/steps/step-open-app.tsx',
51
+ componentKey: 'stepOpenApp',
52
+ type: 'subscription_handoff',
53
+ kind: 'subscription-handoff',
54
+ title: 'Continue in the app',
55
+ } as const satisfies FunnelManifestStep;
56
+
57
+ const componentMeta = {
58
+ id: 'open-app',
59
+ name: 'open-app',
60
+ type: 'subscription_handoff',
61
+ kind: 'subscription-handoff',
62
+ figmaNodeId: 'golden:subscription-handoff',
63
+ title: 'Continue in the app',
64
+ description: 'Continue through a non-terminal app handoff.',
65
+ } as const satisfies FunnelStepMeta;
66
+
67
+ export const goldenFixture = {
68
+ stepType: 'subscription_handoff',
69
+ manifestStep,
70
+ componentMeta,
71
+ helper: 'runtime handoff helper',
72
+ isRegistrationCompletion: false,
73
+ } as const;
74
+ ```
75
+ <!-- funnelsgrove:generated:end contract-v3/step/subscription_handoff -->
76
+
77
+ ## Interaction and persisted answer shape
78
+
79
+ Build the outbound destination through the runtime handoff helper. This screen persists no choice answer.
80
+
81
+ ## Automatic lifecycle events
82
+
83
+ Leaving through the handoff is not funnel registration completion. The controller owns the step outcome.
84
+
85
+ ## Required semantic conversions
86
+
87
+ None. This non-terminal handoff must not emit funnel or registration completion.
88
+
89
+ ## Approved shared helpers
90
+
91
+ Use the runtime subscription-handoff helper with the safe default `SubscriptionHandoffScreen` completion mode of `step`; never opt a non-terminal handoff into `completionMode="funnel"`.
92
+
93
+ ## MUST rules
94
+
95
+ - Treat the destination as configuration/runtime data.
96
+ - Keep non-terminal semantics explicit.
97
+
98
+ ## NEVER rules
99
+
100
+ - Never emit trusted `purchase_completed`, `registration_completed`, or terminal `funnel_completed`.
101
+ - Never build an unvalidated deep link by string concatenation.
102
+
103
+ ## Golden template or fixture
104
+
105
+ See the [embedded canonical fixture](#golden-fixture-source) generated from `subscription_handoff.fixture.ts`.
106
+
107
+ ## Validation command
108
+
109
+ Run `fgrove validate`.
110
+
111
+ ## Manual QA and completion criteria
112
+
113
+ Test installed/uninstalled destinations, query propagation, return behavior, and absence of completion conversions.
@@ -0,0 +1,112 @@
1
+ # `subscription_management`
2
+
3
+ ## Use when / do not use when
4
+
5
+ Use to inspect or manage an existing subscription. Do not use for an acquisition paywall, cancellation save offer, or app handoff.
6
+
7
+ ## Exact metadata contract
8
+
9
+ <!-- funnelsgrove:generated:start contract-v3/step/subscription_management -->
10
+ ### Generated contract facts — do not edit
11
+
12
+ - Step contract version: `3`
13
+ - Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
14
+ - Type: `subscription_management`
15
+ - Family: `commerce`
16
+ - Description: A screen for managing or inspecting an existing subscription.
17
+ - Action owner: `step`
18
+ - Analytics role: _none_
19
+ - Terminal: `false`
20
+ - Required kind: `manage-subscription`
21
+ - Allowed kinds: `manage-subscription`
22
+ - Allowed semantic events: _None_
23
+ - Forbidden capabilities: `choice-write`, `email-capture`, `checkout`, `subscription-handoff`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`
24
+
25
+ #### Choice contract
26
+
27
+ _Not a choice type._
28
+
29
+ #### Automatic lifecycle events
30
+
31
+ `first_step_clicked`, `first_step_viewed`, `funnel_started`, `step_completed`, `step_exited`, `step_started`
32
+
33
+ #### Approved capability owners
34
+
35
+ - `subscription-management` must be owned by `runtime-subscription-helpers`.
36
+
37
+ #### Reserved identities
38
+
39
+ - `manage-subscription`: id=`manage-subscription`, name=`manage-subscription`; type=`subscription_management`; kind=`manage-subscription`; unique=`true`
40
+
41
+ #### Golden fixture source
42
+
43
+ ```ts
44
+ import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
45
+
46
+ const manifestStep = {
47
+ id: 'manage-subscription',
48
+ name: 'manage-subscription',
49
+ path: '/manage-subscription',
50
+ filePath: 'src/steps/step-manage-subscription.tsx',
51
+ componentKey: 'stepManageSubscription',
52
+ type: 'subscription_management',
53
+ kind: 'manage-subscription',
54
+ title: 'Manage subscription',
55
+ } as const satisfies FunnelManifestStep;
56
+
57
+ const componentMeta = {
58
+ id: 'manage-subscription',
59
+ name: 'manage-subscription',
60
+ type: 'subscription_management',
61
+ kind: 'manage-subscription',
62
+ figmaNodeId: 'golden:subscription-management',
63
+ title: 'Manage subscription',
64
+ description: 'Inspect and manage an existing subscription.',
65
+ } as const satisfies FunnelStepMeta;
66
+
67
+ export const goldenFixture = {
68
+ stepType: 'subscription_management',
69
+ manifestStep,
70
+ componentMeta,
71
+ helper: 'runtime subscription helpers',
72
+ } as const;
73
+ ```
74
+ <!-- funnelsgrove:generated:end contract-v3/step/subscription_management -->
75
+
76
+ ## Interaction and persisted answer shape
77
+
78
+ Load and mutate subscription state through runtime subscription helpers. This screen does not persist funnel choice answers.
79
+
80
+ ## Automatic lifecycle events
81
+
82
+ The controller owns step lifecycle; subscription APIs own account-state changes.
83
+
84
+ ## Required semantic conversions
85
+
86
+ None unless a separately defined server-owned subscription event exists.
87
+
88
+ ## Approved shared helpers
89
+
90
+ Use runtime subscription-management helpers and manifest routing.
91
+
92
+ ## MUST rules
93
+
94
+ - Preserve the reserved management identity.
95
+ - Refresh visible state after a successful management action.
96
+
97
+ ## NEVER rules
98
+
99
+ - Never expose secrets or call a provider with browser credentials.
100
+ - Never relabel this screen as a paywall.
101
+
102
+ ## Golden template or fixture
103
+
104
+ See the [embedded canonical fixture](#golden-fixture-source) generated from `subscription_management.fixture.ts`.
105
+
106
+ ## Validation command
107
+
108
+ Run `fgrove validate`.
109
+
110
+ ## Manual QA and completion criteria
111
+
112
+ Test loading, empty state, action success/failure, refresh, back/exit, and exact reserved metadata.
@@ -0,0 +1,110 @@
1
+ # `summary_confirmation`
2
+
3
+ ## Use when / do not use when
4
+
5
+ Use for a summary, review, or confirmation before the next action. Do not use for choice collection, a progress reveal, or an offer.
6
+
7
+ ## Exact metadata contract
8
+
9
+ <!-- funnelsgrove:generated:start contract-v3/step/summary_confirmation -->
10
+ ### Generated contract facts — do not edit
11
+
12
+ - Step contract version: `3`
13
+ - Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
14
+ - Type: `summary_confirmation`
15
+ - Family: `content`
16
+ - Description: A summary, review, or confirmation before the next action.
17
+ - Action owner: `either`
18
+ - Analytics role: _none_
19
+ - Terminal: `false`
20
+ - Required kind: _none_
21
+ - Allowed kinds: _None_
22
+ - Allowed semantic events: _None_
23
+ - Forbidden capabilities: `choice-write`, `email-capture`, `checkout`, `subscription-management`, `subscription-handoff`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`
24
+
25
+ #### Choice contract
26
+
27
+ _Not a choice type._
28
+
29
+ #### Automatic lifecycle events
30
+
31
+ `first_step_clicked`, `first_step_viewed`, `funnel_started`, `step_completed`, `step_exited`, `step_started`
32
+
33
+ #### Approved capability owners
34
+
35
+ _No special capability is allowed._
36
+
37
+ #### Reserved identities
38
+
39
+ _No reserved identity for this type._
40
+
41
+ #### Golden fixture source
42
+
43
+ ```ts
44
+ import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
45
+
46
+ const manifestStep = {
47
+ id: 'review-plan',
48
+ name: 'review-plan',
49
+ path: '/review-plan',
50
+ filePath: 'src/steps/step-review-plan.tsx',
51
+ componentKey: 'stepReviewPlan',
52
+ type: 'summary_confirmation',
53
+ title: 'Review your plan',
54
+ } as const satisfies FunnelManifestStep;
55
+
56
+ const componentMeta = {
57
+ id: 'review-plan',
58
+ name: 'review-plan',
59
+ type: 'summary_confirmation',
60
+ figmaNodeId: 'golden:summary-confirmation',
61
+ title: 'Review your plan',
62
+ description: 'Review existing answers before continuing.',
63
+ } as const satisfies FunnelStepMeta;
64
+
65
+ export const goldenFixture = {
66
+ stepType: 'summary_confirmation',
67
+ manifestStep,
68
+ componentMeta,
69
+ actionOwner: 'either',
70
+ } as const;
71
+ ```
72
+ <!-- funnelsgrove:generated:end contract-v3/step/summary_confirmation -->
73
+
74
+ ## Interaction and persisted answer shape
75
+
76
+ Read existing answers through runtime state. Only edit an answer through its owning input/choice flow.
77
+
78
+ ## Automatic lifecycle events
79
+
80
+ The controller owns completion and exit. Preview suppresses provider delivery.
81
+
82
+ ## Required semantic conversions
83
+
84
+ None.
85
+
86
+ ## Approved shared helpers
87
+
88
+ Use runtime answer readers, shared action UI, and manifest routing.
89
+
90
+ ## MUST rules
91
+
92
+ - Render stable answer meaning, not internal object dumps.
93
+ - Keep confirmation and edit routes manifest-owned.
94
+
95
+ ## NEVER rules
96
+
97
+ - Never invent a completion conversion for viewing a summary.
98
+ - Never mutate answer storage directly.
99
+
100
+ ## Golden template or fixture
101
+
102
+ See the [embedded canonical fixture](#golden-fixture-source) generated from `summary_confirmation.fixture.ts`.
103
+
104
+ ## Validation command
105
+
106
+ Run `fgrove validate`.
107
+
108
+ ## Manual QA and completion criteria
109
+
110
+ Confirm empty/populated answers, edit-return behavior, accessibility, and exactly-once completion through the controller.
@@ -0,0 +1,112 @@
1
+ # `upsell_offer`
2
+
3
+ ## Use when / do not use when
4
+
5
+ Use for a post-purchase or upgrade offer. Do not use for the primary plan-selection paywall or a non-purchasable value screen.
6
+
7
+ ## Exact metadata contract
8
+
9
+ <!-- funnelsgrove:generated:start contract-v3/step/upsell_offer -->
10
+ ### Generated contract facts — do not edit
11
+
12
+ - Step contract version: `3`
13
+ - Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
14
+ - Type: `upsell_offer`
15
+ - Family: `commerce`
16
+ - Description: A post-purchase or upgrade offer.
17
+ - Action owner: `step`
18
+ - Analytics role: _none_
19
+ - Terminal: `false`
20
+ - Required kind: `upsell`
21
+ - Allowed kinds: `upsell`
22
+ - Allowed semantic events: `checkout_started`, `add_payment_info`, `checkout_completed`
23
+ - Forbidden capabilities: `choice-write`, `email-capture`, `subscription-management`, `subscription-handoff`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`
24
+
25
+ #### Choice contract
26
+
27
+ _Not a choice type._
28
+
29
+ #### Automatic lifecycle events
30
+
31
+ `first_step_clicked`, `first_step_viewed`, `funnel_started`, `step_completed`, `step_exited`, `step_started`
32
+
33
+ #### Approved capability owners
34
+
35
+ - `checkout` must be owned by `named-payment-helpers`.
36
+
37
+ #### Reserved identities
38
+
39
+ _No reserved identity for this type._
40
+
41
+ #### Golden fixture source
42
+
43
+ ```ts
44
+ import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
45
+
46
+ const manifestStep = {
47
+ id: 'premium-upgrade',
48
+ name: 'premium-upgrade',
49
+ path: '/premium-upgrade',
50
+ filePath: 'src/steps/step-premium-upgrade.tsx',
51
+ componentKey: 'stepPremiumUpgrade',
52
+ type: 'upsell_offer',
53
+ kind: 'upsell',
54
+ title: 'Add premium support',
55
+ } as const satisfies FunnelManifestStep;
56
+
57
+ const componentMeta = {
58
+ id: 'premium-upgrade',
59
+ name: 'premium-upgrade',
60
+ type: 'upsell_offer',
61
+ kind: 'upsell',
62
+ figmaNodeId: 'golden:upsell-offer',
63
+ title: 'Add premium support',
64
+ description: 'Offer an optional post-purchase upgrade.',
65
+ } as const satisfies FunnelStepMeta;
66
+
67
+ export const goldenFixture = {
68
+ stepType: 'upsell_offer',
69
+ manifestStep,
70
+ componentMeta,
71
+ semanticHelpers: ['trackCheckoutStarted', 'trackPaymentInfoSubmitted', 'trackCheckoutCompleted'],
72
+ } as const;
73
+ ```
74
+ <!-- funnelsgrove:generated:end contract-v3/step/upsell_offer -->
75
+
76
+ ## Interaction and persisted answer shape
77
+
78
+ Resolve the upgrade through shared payment configuration. Accept and decline both flow through controller-owned routes.
79
+
80
+ ## Automatic lifecycle events
81
+
82
+ The controller owns lifecycle completion/exit; the payment boundary owns semantic checkout events.
83
+
84
+ ## Required semantic conversions
85
+
86
+ Call `trackCheckoutStarted`, `trackPaymentInfoSubmitted`, and `trackCheckoutCompleted` only at their matching real boundaries.
87
+
88
+ ## Approved shared helpers
89
+
90
+ Use named payment helpers and manifest routing.
91
+
92
+ ## MUST rules
93
+
94
+ - Keep offer identity and pricing configuration outside the component.
95
+ - Make decline behavior explicit in the manifest.
96
+
97
+ ## NEVER rules
98
+
99
+ - Never emit a completed checkout before provider success.
100
+ - Never call provider analytics directly.
101
+
102
+ ## Golden template or fixture
103
+
104
+ See the [embedded canonical fixture](#golden-fixture-source) generated from `upsell_offer.fixture.ts`.
105
+
106
+ ## Validation command
107
+
108
+ Run `fgrove validate`.
109
+
110
+ ## Manual QA and completion criteria
111
+
112
+ Test accept, decline, provider failure, retry, event deduplication, and both manifest routes.