# Migrate a funnel to step contract v3

<!-- funnelsgrove:generated:start contract-v3/migration/step-contract-v3 -->
Step contract version: `3`

Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`

Supported read versions: `1`, `2`, `3`. Authoring and publish target version `3`. Contract v2 remains readable with `complete_registration`; migration to v3 changes that terminal type to `purchase_completed`. New v3 instructions must not author `complete_registration`.

| Contract | Match | ID | Name | Type | Kind | Primary | Unique |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `email-capture` | `exact` | `email-capture` | `email-capture` | `form_input` | — | — | `true` |
| `manage-subscription` | `exact` | `manage-subscription` | `manage-subscription` | `subscription_management` | `manage-subscription` | — | `true` |
| `paywall` | `exact` | `paywall` | `paywall` | `paywall_offer` | `paywall` | `true` | `true` |
| `paywall-variant` | `pattern` | `^paywall-[a-z0-9-]+$` | `^paywall-[a-z0-9-]+$` | `paywall_offer` | `paywall` | `false` | `false` |
| `subscription-started` | `exact` | `subscription-started` | `subscription-started` | `purchase_completed` | `subscription-handoff` | — | `true` |

### Package release order

Release `@funnelsgrove/sdk` `0.4.0` first, then `@funnelsgrove/runtime` `0.20.1`, then `@funnelsgrove/analytics` `0.3.0`, then `@funnelsgrove/payments` `0.22.4`. The production deploy verifies the zero-traffic API candidate, publishes and verifies `@funnelsgrove/cli` `0.1.247`, and only then promotes the candidate to production traffic. The serving API must never advertise an unpublished preferred CLI. Publishing packages and deploying production remain separately approved operational actions.
<!-- funnelsgrove:generated:end contract-v3/migration/step-contract-v3 -->

## Version-last policy

1. Inventory every step, component metadata export, manifest entry, answer key, route, branch, experiment, and custom analytics call.
2. Reclassify by behavior using the [step-type index](../steps/INDEX.md).
3. Restore exact reserved identities and manifest/component parity.
4. Move choice writes, email capture, payment events, handoff, routing, and lifecycle work to approved shared owners.
5. Add/update fixtures and focused tests.
6. Run `fgrove validate` and all [local](../qa/local.md), [analytics](../qa/analytics.md), and payment QA that applies.
7. Change `stepContractVersion` only after every earlier step passes.

Do not use the version field to silence diagnostics. Preserve legacy stable IDs and answer keys unless a separately planned data/analytics migration owns the change.

## v2 compatibility

Contract v2 funnels remain readable with the `complete_registration` terminal type. The v2-to-v3 migration preserves the `subscription-started` identity, route, file, transitions, and all unrelated metadata while changing only the contract version and terminal type to `purchase_completed`. New v3 authoring must never generate `complete_registration`.

The migration does not turn browser terminal reach into a trusted conversion. Only the backend emits the v3 `purchase_completed` analytics event after verifying a successful initial subscription or one-time payment. `registration_completed` remains the separate later app-link event.
