@funnelsgrove/cli 0.1.14 → 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.
- package/contracts/step-contract-v2.json +733 -0
- package/dist/analyticsOutput.d.ts +30 -15
- package/dist/analyticsOutput.js +45 -28
- package/dist/apiClient.d.ts +85 -0
- package/dist/apiClient.js +225 -8
- package/dist/cli.d.ts +149 -1
- package/dist/cli.js +1232 -71
- package/dist/diagnosticOutput.d.ts +42 -0
- package/dist/diagnosticOutput.js +83 -0
- package/dist/docsOutput.d.ts +19 -0
- package/dist/docsOutput.js +73 -0
- package/dist/docsTransaction.d.ts +81 -0
- package/dist/docsTransaction.js +1755 -0
- package/dist/funnelValidation.d.ts +14 -0
- package/dist/funnelValidation.js +297 -0
- package/dist/localSync.d.ts +79 -0
- package/dist/localSync.js +428 -53
- package/dist/projectValidator.d.ts +27 -0
- package/dist/projectValidator.js +208 -0
- package/dist/reskin.js +27 -1
- package/dist/stepContractMigration.d.ts +71 -0
- package/dist/stepContractMigration.js +1323 -0
- package/dist/templateDocs.js +8 -25
- package/docs-release-history.json +66 -0
- package/legacy-docs-catalog.json +1012 -0
- package/package.json +13 -4
- package/template_docs/.funnelsgrove-docs.json +144 -0
- package/template_docs/AGENTS.md +30 -58
- package/template_docs/CLAUDE.md +0 -2
- package/template_docs/docs/funnelsgrove/START-HERE.md +73 -0
- package/template_docs/docs/funnelsgrove/contracts/analytics-events.md +59 -0
- package/template_docs/docs/funnelsgrove/contracts/content-answers.md +44 -0
- package/template_docs/docs/funnelsgrove/contracts/flow-routing.md +41 -0
- package/template_docs/docs/funnelsgrove/contracts/payments.md +42 -0
- package/template_docs/docs/funnelsgrove/contracts/step-metadata.md +57 -0
- package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
- package/template_docs/docs/funnelsgrove/qa/analytics.md +43 -0
- package/template_docs/docs/funnelsgrove/qa/local.md +42 -0
- package/template_docs/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
- package/template_docs/docs/funnelsgrove/qa/publish.md +32 -0
- package/template_docs/docs/funnelsgrove/recipes/add-experiment.md +34 -0
- package/template_docs/docs/funnelsgrove/recipes/add-step.md +33 -0
- package/template_docs/docs/funnelsgrove/recipes/edit-step.md +31 -0
- package/template_docs/docs/funnelsgrove/steps/INDEX.md +26 -0
- package/template_docs/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
- package/template_docs/docs/funnelsgrove/steps/checkout.md +120 -0
- package/template_docs/docs/funnelsgrove/steps/form_input.md +115 -0
- package/template_docs/docs/funnelsgrove/steps/intro_hero.md +110 -0
- package/template_docs/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
- package/template_docs/docs/funnelsgrove/steps/paywall_offer.md +115 -0
- package/template_docs/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
- package/template_docs/docs/funnelsgrove/steps/purchase_completed.md +117 -0
- package/template_docs/docs/funnelsgrove/steps/single_step_choice.md +122 -0
- package/template_docs/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
- package/template_docs/docs/funnelsgrove/steps/social_proof.md +110 -0
- package/template_docs/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
- package/template_docs/docs/funnelsgrove/steps/subscription_management.md +112 -0
- package/template_docs/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
- package/template_docs/docs/funnelsgrove/steps/upsell_offer.md +112 -0
- package/template_docs/docs/funnelsgrove/steps/value_prop_story.md +110 -0
- package/template_docs/funnel-docs.config.json +43 -0
- package/template_scaffold/.env.example +41 -0
- package/template_scaffold/.funnelsgrove-contract-tools.json +19 -0
- package/template_scaffold/.funnelsgrove-docs.json +144 -0
- package/template_scaffold/.funnelsgrove-scaffold.json +1078 -0
- package/template_scaffold/AGENTS.md +42 -0
- package/template_scaffold/CLAUDE.md +1 -0
- package/template_scaffold/PLAN.md +81 -0
- package/template_scaffold/PRODUCT_SENSE.md +28 -0
- package/template_scaffold/context/build-context.md +34 -0
- package/template_scaffold/docs/AGENT_FUNNEL_IMPLEMENTATION_SPEC.md +5 -0
- package/template_scaffold/docs/ARCHITECTURE.md +5 -0
- package/template_scaffold/docs/FLOW_CONFIG_AND_ROUTING.md +5 -0
- package/template_scaffold/docs/PAYWALL_AND_PAYMENTS.md +5 -0
- package/template_scaffold/docs/README.md +5 -0
- package/template_scaffold/docs/funnelsgrove/START-HERE.md +73 -0
- package/template_scaffold/docs/funnelsgrove/contracts/analytics-events.md +59 -0
- package/template_scaffold/docs/funnelsgrove/contracts/content-answers.md +44 -0
- package/template_scaffold/docs/funnelsgrove/contracts/flow-routing.md +41 -0
- package/template_scaffold/docs/funnelsgrove/contracts/payments.md +42 -0
- package/template_scaffold/docs/funnelsgrove/contracts/step-metadata.md +57 -0
- package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
- package/template_scaffold/docs/funnelsgrove/qa/analytics.md +43 -0
- package/template_scaffold/docs/funnelsgrove/qa/local.md +42 -0
- package/template_scaffold/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
- package/template_scaffold/docs/funnelsgrove/qa/publish.md +32 -0
- package/template_scaffold/docs/funnelsgrove/recipes/add-experiment.md +34 -0
- package/template_scaffold/docs/funnelsgrove/recipes/add-step.md +33 -0
- package/template_scaffold/docs/funnelsgrove/recipes/edit-step.md +31 -0
- package/template_scaffold/docs/funnelsgrove/steps/INDEX.md +26 -0
- package/template_scaffold/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
- package/template_scaffold/docs/funnelsgrove/steps/checkout.md +120 -0
- package/template_scaffold/docs/funnelsgrove/steps/form_input.md +115 -0
- package/template_scaffold/docs/funnelsgrove/steps/intro_hero.md +110 -0
- package/template_scaffold/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
- package/template_scaffold/docs/funnelsgrove/steps/paywall_offer.md +115 -0
- package/template_scaffold/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
- package/template_scaffold/docs/funnelsgrove/steps/purchase_completed.md +117 -0
- package/template_scaffold/docs/funnelsgrove/steps/single_step_choice.md +122 -0
- package/template_scaffold/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
- package/template_scaffold/docs/funnelsgrove/steps/social_proof.md +110 -0
- package/template_scaffold/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
- package/template_scaffold/docs/funnelsgrove/steps/subscription_management.md +112 -0
- package/template_scaffold/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
- package/template_scaffold/docs/funnelsgrove/steps/upsell_offer.md +112 -0
- package/template_scaffold/docs/funnelsgrove/steps/value_prop_story.md +110 -0
- package/template_scaffold/docs/references/funnel-sdk-user-answers.md +5 -0
- package/template_scaffold/eslint.config.mjs +16 -0
- package/template_scaffold/funnel-agent-docs.test.ts +699 -0
- package/template_scaffold/funnel-docs.config.json +43 -0
- package/template_scaffold/funnel.config.json +5 -0
- package/template_scaffold/gitignore.template +5 -0
- package/template_scaffold/next-env.d.ts +6 -0
- package/template_scaffold/next.config.ts +53 -0
- package/template_scaffold/package-lock.json +7737 -0
- package/template_scaffold/package.json +37 -0
- package/template_scaffold/package.test.ts +23 -0
- package/template_scaffold/public/claimbee/bee.png +0 -0
- package/template_scaffold/public/claimbee/device-iphone.png +0 -0
- package/template_scaffold/public/claimbee/device-macbook.png +0 -0
- package/template_scaffold/public/claimbee/device-none.png +0 -0
- package/template_scaffold/public/claimbee/device-watch.png +0 -0
- package/template_scaffold/public/claimbee/glow.svg +3 -0
- package/template_scaffold/public/claimbee/steps-sprite-alt.png +0 -0
- package/template_scaffold/public/claimbee/steps-sprite-default.png +0 -0
- package/template_scaffold/public/claimbee/testimonial-avatar.png +0 -0
- package/template_scaffold/public/claimbee/testimonial-star.svg +3 -0
- package/template_scaffold/public/paywall/ai-claim-assistant.png +0 -0
- package/template_scaffold/public/paywall/claim-history.png +0 -0
- package/template_scaffold/public/paywall/clear-progress.png +0 -0
- package/template_scaffold/public/paywall/hero.png +0 -0
- package/template_scaffold/public/paywall/money-alerts.png +0 -0
- package/template_scaffold/public/paywall/money-back-badge.png +0 -0
- package/template_scaffold/public/paywall/paywall-image.png +0 -0
- package/template_scaffold/public/paywall/r1.png +0 -0
- package/template_scaffold/public/paywall/r2.png +0 -0
- package/template_scaffold/public/paywall/r3.png +0 -0
- package/template_scaffold/public/paywall/r4.png +0 -0
- package/template_scaffold/public/paywall/r5.png +0 -0
- package/template_scaffold/public/paywall/r6.png +0 -0
- package/template_scaffold/public/paywall/special-offer-gift.png +0 -0
- package/template_scaffold/public/please/bee.png +0 -0
- package/template_scaffold/public/please/device-iphone.png +0 -0
- package/template_scaffold/public/please/device-macbook.png +0 -0
- package/template_scaffold/public/please/device-none.png +0 -0
- package/template_scaffold/public/please/device-watch.png +0 -0
- package/template_scaffold/public/please/glow.svg +3 -0
- package/template_scaffold/public/please/steps-sprite-alt.png +0 -0
- package/template_scaffold/public/please/steps-sprite-default.png +0 -0
- package/template_scaffold/public/please/testimonial-avatar.png +0 -0
- package/template_scaffold/public/please/testimonial-star.svg +3 -0
- package/template_scaffold/public/upsell/upgrade-hero.png +0 -0
- package/template_scaffold/src/app/[stepId]/page.test.tsx +59 -0
- package/template_scaffold/src/app/[stepId]/page.tsx +62 -0
- package/template_scaffold/src/app/globals.css +49 -0
- package/template_scaffold/src/app/layout.test.ts +26 -0
- package/template_scaffold/src/app/layout.tsx +61 -0
- package/template_scaffold/src/app/page.tsx +8 -0
- package/template_scaffold/src/app/sdk/page.tsx +673 -0
- package/template_scaffold/src/app/subscription-dashboard/page.tsx +274 -0
- package/template_scaffold/src/assets/claimbee/bee.png +0 -0
- package/template_scaffold/src/assets/claimbee/device-iphone.png +0 -0
- package/template_scaffold/src/assets/claimbee/device-macbook.png +0 -0
- package/template_scaffold/src/assets/claimbee/device-none.png +0 -0
- package/template_scaffold/src/assets/claimbee/device-watch.png +0 -0
- package/template_scaffold/src/assets/claimbee/glow.svg +3 -0
- package/template_scaffold/src/assets/claimbee/steps-sprite-alt.png +0 -0
- package/template_scaffold/src/assets/claimbee/steps-sprite-default.png +0 -0
- package/template_scaffold/src/assets/claimbee/testimonial-avatar.png +0 -0
- package/template_scaffold/src/assets/claimbee/testimonial-star.svg +3 -0
- package/template_scaffold/src/components/FunnelEditorPanel.tsx +194 -0
- package/template_scaffold/src/components/FunnelFlow.test.tsx +17 -0
- package/template_scaffold/src/components/FunnelFlow.tsx +114 -0
- package/template_scaffold/src/components/FunnelStepImage.test.ts +15 -0
- package/template_scaffold/src/components/FunnelStepImage.tsx +28 -0
- package/template_scaffold/src/components/step/FunnelStepHeader.tsx +35 -0
- package/template_scaffold/src/config/billing.plans.test.ts +143 -0
- package/template_scaffold/src/config/billing.plans.ts +69 -0
- package/template_scaffold/src/config/billing.test.plans.ts +32 -0
- package/template_scaffold/src/config/experiments.generated.ts +4 -0
- package/template_scaffold/src/config/experiments.test.ts +12 -0
- package/template_scaffold/src/config/experiments.ts +9 -0
- package/template_scaffold/src/config/funnel.config.ts +12 -0
- package/template_scaffold/src/config/funnel.manifest.test.ts +166 -0
- package/template_scaffold/src/config/funnel.manifest.ts +139 -0
- package/template_scaffold/src/config/offer-sets.generated.ts +4 -0
- package/template_scaffold/src/contract/funnel-project.validation.test.ts +80 -0
- package/template_scaffold/src/contract/funnel-project.validation.ts +44 -0
- package/template_scaffold/src/contract/funnel-validator.vite.config.ts +11 -0
- package/template_scaffold/src/contract/golden/cancellation_offer.fixture.ts +29 -0
- package/template_scaffold/src/contract/golden/checkout.fixture.ts +37 -0
- package/template_scaffold/src/contract/golden/form_input.fixture.ts +28 -0
- package/template_scaffold/src/contract/golden/golden-fixtures.test.ts +190 -0
- package/template_scaffold/src/contract/golden/intro_hero.fixture.ts +27 -0
- package/template_scaffold/src/contract/golden/multi_select_choice.fixture.ts +37 -0
- package/template_scaffold/src/contract/golden/paywall_offer.fixture.ts +30 -0
- package/template_scaffold/src/contract/golden/progress_interstitial.fixture.ts +28 -0
- package/template_scaffold/src/contract/golden/purchase_completed.fixture.ts +33 -0
- package/template_scaffold/src/contract/golden/single_step_choice.fixture.ts +36 -0
- package/template_scaffold/src/contract/golden/single_step_choice_emoji.fixture.ts +36 -0
- package/template_scaffold/src/contract/golden/social_proof.fixture.ts +27 -0
- package/template_scaffold/src/contract/golden/subscription_handoff.fixture.ts +30 -0
- package/template_scaffold/src/contract/golden/subscription_management.fixture.ts +29 -0
- package/template_scaffold/src/contract/golden/summary_confirmation.fixture.ts +27 -0
- package/template_scaffold/src/contract/golden/upsell_offer.fixture.ts +29 -0
- package/template_scaffold/src/contract/golden/value_prop_story.fixture.ts +27 -0
- package/template_scaffold/src/contract/validate-funnel.cli.test.ts +180 -0
- package/template_scaffold/src/contract/validate-funnel.cli.ts +199 -0
- package/template_scaffold/src/runtime/checkout-runtime-config.test.ts +60 -0
- package/template_scaffold/src/runtime/checkout-runtime-config.ts +26 -0
- package/template_scaffold/src/runtime/funnel-runtime.test.ts +25 -0
- package/template_scaffold/src/runtime/funnel-runtime.ts +118 -0
- package/template_scaffold/src/runtime/step-content-context.test.ts +38 -0
- package/template_scaffold/src/runtime/step-content-context.ts +65 -0
- package/template_scaffold/src/runtime/step-registry.ts +66 -0
- package/template_scaffold/src/runtime/step-variable-values.ts +31 -0
- package/template_scaffold/src/runtime/use-funnel-flow-controller.integration.test.ts +203 -0
- package/template_scaffold/src/runtime/use-funnel-flow-controller.test.ts +76 -0
- package/template_scaffold/src/runtime/use-funnel-flow-controller.ts +53 -0
- package/template_scaffold/src/steps/content/email-capture.content.ts +29 -0
- package/template_scaffold/src/steps/content/manage-subscription.content.ts +62 -0
- package/template_scaffold/src/steps/content/paywall.content.ts +191 -0
- package/template_scaffold/src/steps/content/step-01.content.ts +23 -0
- package/template_scaffold/src/steps/content/step-02.content.ts +77 -0
- package/template_scaffold/src/steps/content/step-03.content.ts +40 -0
- package/template_scaffold/src/steps/content/step-04.content.ts +49 -0
- package/template_scaffold/src/steps/content/step-32-paywall.content.ts +352 -0
- package/template_scaffold/src/steps/content/subscription-started.content.ts +68 -0
- package/template_scaffold/src/steps/editor/email-capture.editor.ts +58 -0
- package/template_scaffold/src/steps/editor/manage-subscription.editor.ts +179 -0
- package/template_scaffold/src/steps/editor/paywall.editor.ts +232 -0
- package/template_scaffold/src/steps/editor/step-01.editor.ts +22 -0
- package/template_scaffold/src/steps/editor/step-02.editor.ts +41 -0
- package/template_scaffold/src/steps/editor/step-03.editor.ts +58 -0
- package/template_scaffold/src/steps/editor/step-04.editor.ts +98 -0
- package/template_scaffold/src/steps/editor/step-32-paywall.editor.ts +478 -0
- package/template_scaffold/src/steps/editor/subscription-started.editor.ts +131 -0
- package/template_scaffold/src/steps/index.ts +25 -0
- package/template_scaffold/src/steps/paywall-checkout-options.test.ts +104 -0
- package/template_scaffold/src/steps/paywall-checkout-options.ts +130 -0
- package/template_scaffold/src/steps/pricing/paywall-b.pricing.ts +12 -0
- package/template_scaffold/src/steps/pricing/paywall.pricing.ts +12 -0
- package/template_scaffold/src/steps/pricing/shared-plan-order.ts +17 -0
- package/template_scaffold/src/steps/pricing/upsell-form.pricing.ts +16 -0
- package/template_scaffold/src/steps/shared-header-contract.test.ts +27 -0
- package/template_scaffold/src/steps/step-01.test.ts +38 -0
- package/template_scaffold/src/steps/step-01.tsx +108 -0
- package/template_scaffold/src/steps/step-02.test.ts +43 -0
- package/template_scaffold/src/steps/step-02.tsx +140 -0
- package/template_scaffold/src/steps/step-03.test.ts +15 -0
- package/template_scaffold/src/steps/step-03.tsx +262 -0
- package/template_scaffold/src/steps/step-04.test.ts +32 -0
- package/template_scaffold/src/steps/step-04.tsx +673 -0
- package/template_scaffold/src/steps/step-31-email-capture.test.ts +22 -0
- package/template_scaffold/src/steps/step-31-email-capture.tsx +195 -0
- package/template_scaffold/src/steps/step-32-paywall.test.ts +770 -0
- package/template_scaffold/src/steps/step-32-paywall.tsx +2253 -0
- package/template_scaffold/src/steps/step-32b-paywall-b.tsx +35 -0
- package/template_scaffold/src/steps/step-33-subscription-started.test.ts +26 -0
- package/template_scaffold/src/steps/step-33-subscription-started.tsx +86 -0
- package/template_scaffold/src/steps/step-34-upsell-form.test.ts +27 -0
- package/template_scaffold/src/steps/step-35-manage-subscription.tsx +46 -0
- package/template_scaffold/src/steps/step-content.registry.ts +45 -0
- package/template_scaffold/src/steps/step-image-migration.test.ts +20 -0
- package/template_scaffold/src/steps/styles/index.css +2 -0
- package/template_scaffold/src/steps/styles/shared/final-flow-shared.css +55 -0
- package/template_scaffold/src/steps/styles/shared/funnel-base.css +543 -0
- package/template_scaffold/src/steps/styles/shared/info-shared.css +139 -0
- package/template_scaffold/src/steps/styles/shared/progress-shared.css +117 -0
- package/template_scaffold/src/steps/styles/shared/question-shared.css +76 -0
- package/template_scaffold/src/steps/styles/shared/responsive-container.css +176 -0
- package/template_scaffold/src/steps/types.ts +7 -0
- package/template_scaffold/src/steps/use-claimbee-step-content.ts +15 -0
- package/template_scaffold/src/theme/theme.ts +81 -0
- package/template_scaffold/tsconfig.json +38 -0
- package/template_scaffold/vitest.config.ts +32 -0
- package/template_validation/.funnelsgrove-contract-tools.json +19 -0
- package/template_validation/src/contract/funnel-project.validation.ts +44 -0
- package/template_validation/src/contract/funnel-validator.vite.config.ts +11 -0
- package/template_validation/src/contract/validate-funnel.cli.ts +199 -0
- package/template_docs/docs/ab-experiments.md +0 -55
- package/template_docs/docs/analytics.md +0 -65
- package/template_docs/docs/editing-flow.md +0 -125
- package/template_docs/docs/editing-step.md +0 -125
- package/template_docs/docs/editor-and-content.md +0 -58
- package/template_docs/docs/funnel-runtime-architecture.md +0 -125
- package/template_docs/docs/meta-pixel-conversions-api.md +0 -50
- package/template_docs/docs/payment-plans-and-discounts.md +0 -106
- package/template_docs/docs/publishing-and-versioning.md +0 -117
- package/template_docs/docs/qa-checklist.md +0 -80
- package/template_docs/docs/sdk-api-endpoints.md +0 -78
- package/template_docs/docs/step-ui-guidelines.md +0 -110
- package/template_docs/docs/theme.md +0 -35
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# `intro_hero`
|
|
2
|
+
|
|
3
|
+
## Use when / do not use when
|
|
4
|
+
|
|
5
|
+
Use for the opening promise or a low-friction entry screen. Do not use for a questionnaire, reveal, offer, or checkout.
|
|
6
|
+
|
|
7
|
+
## Exact metadata contract
|
|
8
|
+
|
|
9
|
+
<!-- funnelsgrove:generated:start contract-v3/step/intro_hero -->
|
|
10
|
+
### Generated contract facts — do not edit
|
|
11
|
+
|
|
12
|
+
- Step contract version: `3`
|
|
13
|
+
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
|
|
14
|
+
- Type: `intro_hero`
|
|
15
|
+
- Family: `content`
|
|
16
|
+
- Description: Opening promise or low-friction entry screen.
|
|
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: 'welcome',
|
|
48
|
+
name: 'welcome',
|
|
49
|
+
path: '/welcome',
|
|
50
|
+
filePath: 'src/steps/step-welcome.tsx',
|
|
51
|
+
componentKey: 'stepWelcome',
|
|
52
|
+
type: 'intro_hero',
|
|
53
|
+
title: 'Welcome',
|
|
54
|
+
} as const satisfies FunnelManifestStep;
|
|
55
|
+
|
|
56
|
+
const componentMeta = {
|
|
57
|
+
id: 'welcome',
|
|
58
|
+
name: 'welcome',
|
|
59
|
+
type: 'intro_hero',
|
|
60
|
+
figmaNodeId: 'golden:intro-hero',
|
|
61
|
+
title: 'Welcome',
|
|
62
|
+
description: 'Opening promise and low-friction entry.',
|
|
63
|
+
} as const satisfies FunnelStepMeta;
|
|
64
|
+
|
|
65
|
+
export const goldenFixture = {
|
|
66
|
+
stepType: 'intro_hero',
|
|
67
|
+
manifestStep,
|
|
68
|
+
componentMeta,
|
|
69
|
+
actionOwner: 'shared-shell',
|
|
70
|
+
} as const;
|
|
71
|
+
```
|
|
72
|
+
<!-- funnelsgrove:generated:end contract-v3/step/intro_hero -->
|
|
73
|
+
|
|
74
|
+
## Interaction and persisted answer shape
|
|
75
|
+
|
|
76
|
+
This content screen does not persist an answer. Put its primary progression action in the shared shell.
|
|
77
|
+
|
|
78
|
+
## Automatic lifecycle events
|
|
79
|
+
|
|
80
|
+
The flow controller owns the applicable automatic view, start, completion, and exit events. `step_engaged` is registered as non-automatic and is not emitted by default. Only an explicit, centralized, tested engagement policy may emit it through the approved controller/helper boundary; step components never emit it manually. Preview suppresses lifecycle delivery.
|
|
81
|
+
|
|
82
|
+
## Required semantic conversions
|
|
83
|
+
|
|
84
|
+
None. A hero CTA is navigation, not a conversion event.
|
|
85
|
+
|
|
86
|
+
## Approved shared helpers
|
|
87
|
+
|
|
88
|
+
Use `useFunnel()` only when the view needs controller state; use the shared action bar for normal Continue behavior.
|
|
89
|
+
|
|
90
|
+
## MUST rules
|
|
91
|
+
|
|
92
|
+
- Keep the component and manifest metadata identical.
|
|
93
|
+
- Keep the opening action low-friction and let the manifest choose the next step.
|
|
94
|
+
|
|
95
|
+
## NEVER rules
|
|
96
|
+
|
|
97
|
+
- Never write answers or call an analytics provider.
|
|
98
|
+
- Never hard-code the next route.
|
|
99
|
+
|
|
100
|
+
## Golden template or fixture
|
|
101
|
+
|
|
102
|
+
See the [embedded canonical fixture](#golden-fixture-source) generated from `intro_hero.fixture.ts`.
|
|
103
|
+
|
|
104
|
+
## Validation command
|
|
105
|
+
|
|
106
|
+
Run `fgrove validate`.
|
|
107
|
+
|
|
108
|
+
## Manual QA and completion criteria
|
|
109
|
+
|
|
110
|
+
Confirm the screen starts the funnel once, the CTA advances through manifest routing, and back/exit behavior records the correct lifecycle outcome.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# `multi_select_choice`
|
|
2
|
+
|
|
3
|
+
## Use when / do not use when
|
|
4
|
+
|
|
5
|
+
Use when zero or more options can be selected before an explicit Continue action. Do not use for single-selection or implicit selection-to-advance behavior.
|
|
6
|
+
|
|
7
|
+
## Exact metadata contract
|
|
8
|
+
|
|
9
|
+
<!-- funnelsgrove:generated:start contract-v3/step/multi_select_choice -->
|
|
10
|
+
### Generated contract facts — do not edit
|
|
11
|
+
|
|
12
|
+
- Step contract version: `3`
|
|
13
|
+
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
|
|
14
|
+
- Type: `multi_select_choice`
|
|
15
|
+
- Family: `choice`
|
|
16
|
+
- Description: Multiple options are selected before explicit completion.
|
|
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: `email-capture`, `checkout`, `subscription-management`, `subscription-handoff`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`
|
|
24
|
+
|
|
25
|
+
#### Choice contract
|
|
26
|
+
|
|
27
|
+
- Cardinality: `many`
|
|
28
|
+
- Presentation: `standard`
|
|
29
|
+
- Completion: `explicit`
|
|
30
|
+
- Allow empty config: `true`
|
|
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: 'priorities', allowEmpty: true } as const;
|
|
50
|
+
|
|
51
|
+
const manifestStep = {
|
|
52
|
+
id: 'priorities',
|
|
53
|
+
name: 'priorities',
|
|
54
|
+
path: '/priorities',
|
|
55
|
+
filePath: 'src/steps/step-priorities.tsx',
|
|
56
|
+
componentKey: 'stepPriorities',
|
|
57
|
+
type: 'multi_select_choice',
|
|
58
|
+
choice,
|
|
59
|
+
title: 'Choose your priorities',
|
|
60
|
+
} as const satisfies FunnelManifestStep;
|
|
61
|
+
|
|
62
|
+
const componentMeta = {
|
|
63
|
+
id: 'priorities',
|
|
64
|
+
name: 'priorities',
|
|
65
|
+
type: 'multi_select_choice',
|
|
66
|
+
choice,
|
|
67
|
+
figmaNodeId: 'golden:multi-select-choice',
|
|
68
|
+
title: 'Choose your priorities',
|
|
69
|
+
description: 'Select any priorities that apply.',
|
|
70
|
+
} as const satisfies FunnelStepMeta;
|
|
71
|
+
|
|
72
|
+
export const goldenFixture = {
|
|
73
|
+
stepType: 'multi_select_choice',
|
|
74
|
+
manifestStep,
|
|
75
|
+
componentMeta,
|
|
76
|
+
options: [
|
|
77
|
+
{ id: 'sleep', label: 'Better sleep' },
|
|
78
|
+
{ id: 'focus', label: 'More focus' },
|
|
79
|
+
],
|
|
80
|
+
persistedAnswer: ['sleep', 'focus'],
|
|
81
|
+
emptyAnswer: [],
|
|
82
|
+
helper: 'useStepChoices',
|
|
83
|
+
} as const;
|
|
84
|
+
```
|
|
85
|
+
<!-- funnelsgrove:generated:end contract-v3/step/multi_select_choice -->
|
|
86
|
+
|
|
87
|
+
## Interaction and persisted answer shape
|
|
88
|
+
|
|
89
|
+
Declare `choice.answerKey` and the intended `allowEmpty` value. Persist an array of stable option IDs; an allowed empty selection is `[]`.
|
|
90
|
+
|
|
91
|
+
## Automatic lifecycle events
|
|
92
|
+
|
|
93
|
+
`useStepChoices` coordinates array persistence and explicit completion. Preview suppresses provider delivery.
|
|
94
|
+
|
|
95
|
+
## Required semantic conversions
|
|
96
|
+
|
|
97
|
+
None.
|
|
98
|
+
|
|
99
|
+
## Approved shared helpers
|
|
100
|
+
|
|
101
|
+
Use `useStepChoices` for toggles, saving, completion, and routing.
|
|
102
|
+
|
|
103
|
+
## MUST rules
|
|
104
|
+
|
|
105
|
+
- Preserve stable IDs and exact array shape.
|
|
106
|
+
- Make Continue state match `allowEmpty`.
|
|
107
|
+
|
|
108
|
+
## NEVER rules
|
|
109
|
+
|
|
110
|
+
- Never store comma-separated text, labels, or option objects.
|
|
111
|
+
- Never auto-advance on the first selection.
|
|
112
|
+
|
|
113
|
+
## Golden template or fixture
|
|
114
|
+
|
|
115
|
+
See the [embedded canonical fixture](#golden-fixture-source) generated from `multi_select_choice.fixture.ts`.
|
|
116
|
+
|
|
117
|
+
## Validation command
|
|
118
|
+
|
|
119
|
+
Run `fgrove validate`.
|
|
120
|
+
|
|
121
|
+
## Manual QA and completion criteria
|
|
122
|
+
|
|
123
|
+
Test select, deselect, reload, and empty-state behavior; confirm one ID array persists and explicit Continue completes once.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# `paywall_offer`
|
|
2
|
+
|
|
3
|
+
## Use when / do not use when
|
|
4
|
+
|
|
5
|
+
Use only for a monetization screen that presents purchasable plans. Do not use for a loading, calculation, reveal, or pre-offer bridge.
|
|
6
|
+
|
|
7
|
+
## Exact metadata contract
|
|
8
|
+
|
|
9
|
+
<!-- funnelsgrove:generated:start contract-v3/step/paywall_offer -->
|
|
10
|
+
### Generated contract facts — do not edit
|
|
11
|
+
|
|
12
|
+
- Step contract version: `3`
|
|
13
|
+
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
|
|
14
|
+
- Type: `paywall_offer`
|
|
15
|
+
- Family: `commerce`
|
|
16
|
+
- Description: A monetization offer that presents purchasable plans.
|
|
17
|
+
- Action owner: `step`
|
|
18
|
+
- Analytics role: `paywall`
|
|
19
|
+
- Terminal: `false`
|
|
20
|
+
- Required kind: `paywall`
|
|
21
|
+
- Allowed kinds: `paywall`
|
|
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
|
+
- `paywall`: id=`paywall`, name=`paywall`; type=`paywall_offer`; kind=`paywall`; primary=`true`; unique=`true`
|
|
40
|
+
- `paywall-variant`: idPattern=`^paywall-[a-z0-9-]+$`, namePattern=`^paywall-[a-z0-9-]+$`; type=`paywall_offer`; kind=`paywall`; primary=`false`; unique=`false`
|
|
41
|
+
|
|
42
|
+
#### Golden fixture source
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
|
|
46
|
+
|
|
47
|
+
const manifestStep = {
|
|
48
|
+
id: 'paywall',
|
|
49
|
+
name: 'paywall',
|
|
50
|
+
path: '/paywall',
|
|
51
|
+
filePath: 'src/steps/step-paywall.tsx',
|
|
52
|
+
componentKey: 'stepPaywall',
|
|
53
|
+
type: 'paywall_offer',
|
|
54
|
+
kind: 'paywall',
|
|
55
|
+
title: 'Choose your plan',
|
|
56
|
+
} as const satisfies FunnelManifestStep;
|
|
57
|
+
|
|
58
|
+
const componentMeta = {
|
|
59
|
+
id: 'paywall',
|
|
60
|
+
name: 'paywall',
|
|
61
|
+
type: 'paywall_offer',
|
|
62
|
+
kind: 'paywall',
|
|
63
|
+
figmaNodeId: 'golden:paywall-offer',
|
|
64
|
+
title: 'Choose your plan',
|
|
65
|
+
description: 'Present purchasable plans.',
|
|
66
|
+
} as const satisfies FunnelStepMeta;
|
|
67
|
+
|
|
68
|
+
export const goldenFixture = {
|
|
69
|
+
stepType: 'paywall_offer',
|
|
70
|
+
manifestStep,
|
|
71
|
+
componentMeta,
|
|
72
|
+
presentsPurchasablePlans: true,
|
|
73
|
+
semanticHelpers: ['trackCheckoutStarted', 'trackPaymentInfoSubmitted', 'trackCheckoutCompleted'],
|
|
74
|
+
} as const;
|
|
75
|
+
```
|
|
76
|
+
<!-- funnelsgrove:generated:end contract-v3/step/paywall_offer -->
|
|
77
|
+
|
|
78
|
+
## Interaction and persisted answer shape
|
|
79
|
+
|
|
80
|
+
Resolve offers and plans through shared payments configuration. Do not persist plan data as a choice answer.
|
|
81
|
+
|
|
82
|
+
## Automatic lifecycle events
|
|
83
|
+
|
|
84
|
+
The controller owns view/start/completion/exit lifecycle. Closing or leaving without purchase is an exit, not a completion.
|
|
85
|
+
|
|
86
|
+
## Required semantic conversions
|
|
87
|
+
|
|
88
|
+
Use named helpers for checkout started, payment info, and checkout completed at their real boundaries with one stable event ID.
|
|
89
|
+
|
|
90
|
+
## Approved shared helpers
|
|
91
|
+
|
|
92
|
+
Use shared plan resolution, checkout, wallet, and `trackCheckoutStarted`, `trackPaymentInfoSubmitted`, and `trackCheckoutCompleted` helpers.
|
|
93
|
+
|
|
94
|
+
## MUST rules
|
|
95
|
+
|
|
96
|
+
- Keep the primary paywall identity exact; variants use contract-compliant distinct identities.
|
|
97
|
+
- Present at least one purchasable plan.
|
|
98
|
+
- Read prices, discounts, provider IDs, and payment mode from shared configuration.
|
|
99
|
+
|
|
100
|
+
## NEVER rules
|
|
101
|
+
|
|
102
|
+
- Never classify a reveal/interstitial as a paywall.
|
|
103
|
+
- Never put secrets, provider IDs, amount math, or provider analytics in JSX.
|
|
104
|
+
|
|
105
|
+
## Golden template or fixture
|
|
106
|
+
|
|
107
|
+
See the [embedded canonical fixture](#golden-fixture-source) generated from `paywall_offer.fixture.ts`.
|
|
108
|
+
|
|
109
|
+
## Validation command
|
|
110
|
+
|
|
111
|
+
Run `fgrove validate`.
|
|
112
|
+
|
|
113
|
+
## Manual QA and completion criteria
|
|
114
|
+
|
|
115
|
+
Verify plans and discounts, every payment method, decline/retry, checkout event metadata/deduplication, cancel/exit, and manifest routing after success.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# `progress_interstitial`
|
|
2
|
+
|
|
3
|
+
## Use when / do not use when
|
|
4
|
+
|
|
5
|
+
Use for progress, calculation, loading, reveal, or transition. Do not use for a screen that presents purchasable plans: that is a paywall offer.
|
|
6
|
+
|
|
7
|
+
## Exact metadata contract
|
|
8
|
+
|
|
9
|
+
<!-- funnelsgrove:generated:start contract-v3/step/progress_interstitial -->
|
|
10
|
+
### Generated contract facts — do not edit
|
|
11
|
+
|
|
12
|
+
- Step contract version: `3`
|
|
13
|
+
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
|
|
14
|
+
- Type: `progress_interstitial`
|
|
15
|
+
- Family: `transition`
|
|
16
|
+
- Description: Progress, calculation, loading, reveal, or transition screen.
|
|
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: 'building-plan',
|
|
48
|
+
name: 'building-plan',
|
|
49
|
+
path: '/building-plan',
|
|
50
|
+
filePath: 'src/steps/step-building-plan.tsx',
|
|
51
|
+
componentKey: 'stepBuildingPlan',
|
|
52
|
+
type: 'progress_interstitial',
|
|
53
|
+
title: 'Building your plan',
|
|
54
|
+
} as const satisfies FunnelManifestStep;
|
|
55
|
+
|
|
56
|
+
const componentMeta = {
|
|
57
|
+
id: 'building-plan',
|
|
58
|
+
name: 'building-plan',
|
|
59
|
+
type: 'progress_interstitial',
|
|
60
|
+
figmaNodeId: 'golden:progress-interstitial',
|
|
61
|
+
title: 'Building your plan',
|
|
62
|
+
description: 'Show progress while preparing the next screen.',
|
|
63
|
+
} as const satisfies FunnelStepMeta;
|
|
64
|
+
|
|
65
|
+
export const goldenFixture = {
|
|
66
|
+
stepType: 'progress_interstitial',
|
|
67
|
+
manifestStep,
|
|
68
|
+
componentMeta,
|
|
69
|
+
semanticHelpers: [],
|
|
70
|
+
isPaywall: false,
|
|
71
|
+
} as const;
|
|
72
|
+
```
|
|
73
|
+
<!-- funnelsgrove:generated:end contract-v3/step/progress_interstitial -->
|
|
74
|
+
|
|
75
|
+
## Interaction and persisted answer shape
|
|
76
|
+
|
|
77
|
+
This transition does not persist a choice answer. An automatic delay or explicit Continue may complete it through the controller.
|
|
78
|
+
|
|
79
|
+
## Automatic lifecycle events
|
|
80
|
+
|
|
81
|
+
The controller distinguishes completion from abandonment and suppresses provider delivery in preview.
|
|
82
|
+
|
|
83
|
+
## Required semantic conversions
|
|
84
|
+
|
|
85
|
+
None. A reveal or “your plan is ready” transition is not a paywall or checkout conversion.
|
|
86
|
+
|
|
87
|
+
## Approved shared helpers
|
|
88
|
+
|
|
89
|
+
Use shared progress/action primitives and manifest routing.
|
|
90
|
+
|
|
91
|
+
## MUST rules
|
|
92
|
+
|
|
93
|
+
- Keep automatic completion cancellable when the visit becomes stale.
|
|
94
|
+
- Classify by actual interaction, not by funnel position.
|
|
95
|
+
|
|
96
|
+
## NEVER rules
|
|
97
|
+
|
|
98
|
+
- Never set paywall identity or emit checkout semantics.
|
|
99
|
+
- Never hard-code a paywall destination.
|
|
100
|
+
|
|
101
|
+
## Golden template or fixture
|
|
102
|
+
|
|
103
|
+
See the [embedded canonical fixture](#golden-fixture-source) generated from `progress_interstitial.fixture.ts`.
|
|
104
|
+
|
|
105
|
+
## Validation command
|
|
106
|
+
|
|
107
|
+
Run `fgrove validate`.
|
|
108
|
+
|
|
109
|
+
## Manual QA and completion criteria
|
|
110
|
+
|
|
111
|
+
Confirm timer/manual completion fires once, leaving early records an exit, and no paywall or checkout event appears.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# `purchase_completed`
|
|
2
|
+
|
|
3
|
+
## Use when / do not use when
|
|
4
|
+
|
|
5
|
+
Use for the canonical terminal after an initial subscription or one-time purchase flow. Do not use for a generic handoff, receipt, renewal, or pre-purchase summary. Entering this screen does not prove that the backend verified payment.
|
|
6
|
+
|
|
7
|
+
## Exact metadata contract
|
|
8
|
+
|
|
9
|
+
<!-- funnelsgrove:generated:start contract-v3/step/purchase_completed -->
|
|
10
|
+
### Generated contract facts — do not edit
|
|
11
|
+
|
|
12
|
+
- Step contract version: `3`
|
|
13
|
+
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
|
|
14
|
+
- Type: `purchase_completed`
|
|
15
|
+
- Family: `terminal`
|
|
16
|
+
- Description: The canonical terminal shown after a verified initial purchase.
|
|
17
|
+
- Action owner: `step`
|
|
18
|
+
- Analytics role: `purchase-completed`
|
|
19
|
+
- Terminal: `true`
|
|
20
|
+
- Required kind: `subscription-handoff`
|
|
21
|
+
- Allowed kinds: `subscription-handoff`
|
|
22
|
+
- Allowed semantic events: `funnel_completed`
|
|
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
|
+
- `subscription-started`: id=`subscription-started`, name=`subscription-started`; type=`purchase_completed`; kind=`subscription-handoff`; unique=`true`
|
|
40
|
+
|
|
41
|
+
#### Golden fixture source
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import type { FunnelContextValue, FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';
|
|
45
|
+
|
|
46
|
+
const terminalCompletionHelper = 'completeFunnel' as const satisfies keyof FunnelContextValue;
|
|
47
|
+
|
|
48
|
+
const manifestStep = {
|
|
49
|
+
id: 'subscription-started',
|
|
50
|
+
name: 'subscription-started',
|
|
51
|
+
path: '/subscription-started',
|
|
52
|
+
filePath: 'src/steps/step-subscription-started.tsx',
|
|
53
|
+
componentKey: 'stepSubscriptionStarted',
|
|
54
|
+
type: 'purchase_completed',
|
|
55
|
+
kind: 'subscription-handoff',
|
|
56
|
+
title: 'Purchase completed',
|
|
57
|
+
} as const satisfies FunnelManifestStep;
|
|
58
|
+
|
|
59
|
+
const componentMeta = {
|
|
60
|
+
id: 'subscription-started',
|
|
61
|
+
name: 'subscription-started',
|
|
62
|
+
type: 'purchase_completed',
|
|
63
|
+
kind: 'subscription-handoff',
|
|
64
|
+
figmaNodeId: 'golden:purchase-completed',
|
|
65
|
+
title: 'Purchase completed',
|
|
66
|
+
description: 'Complete the funnel after purchase and continue into the app.',
|
|
67
|
+
} as const satisfies FunnelStepMeta;
|
|
68
|
+
|
|
69
|
+
export const goldenFixture = {
|
|
70
|
+
stepType: 'purchase_completed',
|
|
71
|
+
manifestStep,
|
|
72
|
+
componentMeta,
|
|
73
|
+
helper: 'runtime handoff helper',
|
|
74
|
+
semanticHelpers: [terminalCompletionHelper],
|
|
75
|
+
forbiddenBrowserEvents: ['purchase_completed', 'registration_completed'],
|
|
76
|
+
} as const;
|
|
77
|
+
```
|
|
78
|
+
<!-- funnelsgrove:generated:end contract-v3/step/purchase_completed -->
|
|
79
|
+
|
|
80
|
+
## Interaction and persisted answer shape
|
|
81
|
+
|
|
82
|
+
The runtime terminal/handoff helper owns completion and destination construction. No choice answer is persisted.
|
|
83
|
+
|
|
84
|
+
## Automatic lifecycle events
|
|
85
|
+
|
|
86
|
+
`completeFunnel(stepId)` atomically claims the active terminal visit, emits the normal step completion and one deduplicated `funnel_completed`, then flushes before outbound handoff. Preview/editor still claims the local completion outcome while suppressing persistence and provider delivery.
|
|
87
|
+
|
|
88
|
+
## Required semantic conversions
|
|
89
|
+
|
|
90
|
+
The browser may request lifecycle-only funnel completion. The business conversion `purchase_completed` is trusted-server-only after the backend verifies a successful initial subscription or one-time payment. `registration_completed` remains a separate later app-link claim.
|
|
91
|
+
|
|
92
|
+
## Approved shared helpers
|
|
93
|
+
|
|
94
|
+
Use `completeFunnel` through the runtime handoff surface. The reserved terminal passes `completionMode="funnel"` to `SubscriptionHandoffScreen`; the shared component defaults to `completionMode="step"` for non-terminal handoffs.
|
|
95
|
+
|
|
96
|
+
## MUST rules
|
|
97
|
+
|
|
98
|
+
- Preserve the reserved `subscription-started` identity.
|
|
99
|
+
- Complete the funnel once before the app handoff.
|
|
100
|
+
- Keep terminal completion mode explicit; only the current `purchase_completed` terminal may call `completeFunnel`.
|
|
101
|
+
|
|
102
|
+
## NEVER rules
|
|
103
|
+
|
|
104
|
+
- Never emit `purchase_completed` in browser code.
|
|
105
|
+
- Never treat terminal reach, `funnel_completed`, a pending payment, or a renewal as purchase verification.
|
|
106
|
+
|
|
107
|
+
## Golden template or fixture
|
|
108
|
+
|
|
109
|
+
See the [embedded canonical fixture](#golden-fixture-source) generated from `purchase_completed.fixture.ts`.
|
|
110
|
+
|
|
111
|
+
## Validation command
|
|
112
|
+
|
|
113
|
+
Run `fgrove validate`.
|
|
114
|
+
|
|
115
|
+
## Manual QA and completion criteria
|
|
116
|
+
|
|
117
|
+
Confirm funnel completion is deduplicated, the trusted purchase event requires backend verification, preview is silent, and the app destination works.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# `single_step_choice`
|
|
2
|
+
|
|
3
|
+
## Use when / do not use when
|
|
4
|
+
|
|
5
|
+
Use when exactly one standard option completes the step on selection. Do not use for emoji-led choices, multi-select, or a separate form submission.
|
|
6
|
+
|
|
7
|
+
## Exact metadata contract
|
|
8
|
+
|
|
9
|
+
<!-- funnelsgrove:generated:start contract-v3/step/single_step_choice -->
|
|
10
|
+
### Generated contract facts — do not edit
|
|
11
|
+
|
|
12
|
+
- Step contract version: `3`
|
|
13
|
+
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
|
|
14
|
+
- Type: `single_step_choice`
|
|
15
|
+
- Family: `choice`
|
|
16
|
+
- Description: Exactly one standard 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: `standard`
|
|
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: 'routine' } as const;
|
|
50
|
+
|
|
51
|
+
const manifestStep = {
|
|
52
|
+
id: 'routine',
|
|
53
|
+
name: 'routine',
|
|
54
|
+
path: '/routine',
|
|
55
|
+
filePath: 'src/steps/step-routine.tsx',
|
|
56
|
+
componentKey: 'stepRoutine',
|
|
57
|
+
type: 'single_step_choice',
|
|
58
|
+
choice,
|
|
59
|
+
title: 'Choose a routine',
|
|
60
|
+
} as const satisfies FunnelManifestStep;
|
|
61
|
+
|
|
62
|
+
const componentMeta = {
|
|
63
|
+
id: 'routine',
|
|
64
|
+
name: 'routine',
|
|
65
|
+
type: 'single_step_choice',
|
|
66
|
+
choice,
|
|
67
|
+
figmaNodeId: 'golden:single-step-choice',
|
|
68
|
+
title: 'Choose a routine',
|
|
69
|
+
description: 'Select exactly one routine.',
|
|
70
|
+
} as const satisfies FunnelStepMeta;
|
|
71
|
+
|
|
72
|
+
export const goldenFixture = {
|
|
73
|
+
stepType: 'single_step_choice',
|
|
74
|
+
manifestStep,
|
|
75
|
+
componentMeta,
|
|
76
|
+
options: [
|
|
77
|
+
{ id: 'daily', label: 'Every day' },
|
|
78
|
+
{ id: 'weekdays', label: 'Weekdays' },
|
|
79
|
+
],
|
|
80
|
+
persistedAnswer: 'daily',
|
|
81
|
+
helper: 'useStepChoices',
|
|
82
|
+
} as const;
|
|
83
|
+
```
|
|
84
|
+
<!-- funnelsgrove:generated:end contract-v3/step/single_step_choice -->
|
|
85
|
+
|
|
86
|
+
## Interaction and persisted answer shape
|
|
87
|
+
|
|
88
|
+
Declare `choice.answerKey` in both metadata copies. Persist one stable option ID as a scalar string, never the label or an object.
|
|
89
|
+
|
|
90
|
+
## Automatic lifecycle events
|
|
91
|
+
|
|
92
|
+
`useStepChoices` coordinates the answer write with the controller-owned completion lifecycle. Preview keeps provider delivery suppressed.
|
|
93
|
+
|
|
94
|
+
## Required semantic conversions
|
|
95
|
+
|
|
96
|
+
None. Selecting an option is answer state plus automatic lifecycle completion.
|
|
97
|
+
|
|
98
|
+
## Approved shared helpers
|
|
99
|
+
|
|
100
|
+
Use `useStepChoices`; let it complete and route after the stable ID is persisted.
|
|
101
|
+
|
|
102
|
+
## MUST rules
|
|
103
|
+
|
|
104
|
+
- Give every option a stable ID that survives copy changes.
|
|
105
|
+
- Keep the manifest and component `choice.answerKey` identical.
|
|
106
|
+
|
|
107
|
+
## NEVER rules
|
|
108
|
+
|
|
109
|
+
- Never call answer SDK methods directly from the choice component.
|
|
110
|
+
- Never store a display label, index, or whole option object.
|
|
111
|
+
|
|
112
|
+
## Golden template or fixture
|
|
113
|
+
|
|
114
|
+
See the [embedded canonical fixture](#golden-fixture-source) generated from `single_step_choice.fixture.ts`.
|
|
115
|
+
|
|
116
|
+
## Validation command
|
|
117
|
+
|
|
118
|
+
Run `fgrove validate`.
|
|
119
|
+
|
|
120
|
+
## Manual QA and completion criteria
|
|
121
|
+
|
|
122
|
+
Select every option once; confirm exactly one stable ID is saved and each selection completes/routes only once.
|