Move separate meter cycling to GA

Merged
PI
11d ago
+118-109

Moves separate meter cycling (a meter interval independent of the billing interval on recurring products) out of the meter_cycling_enabled organization feature flag and makes it generally available. Follows #14256, which removed the seat-based pricing flag.

What changed

  • Product service. product_service.create no longer rejects meter_interval when the flag is off. Product update had no such gate, because meter_interval can only be set on create.
  • Organization schema. meter_cycling_enabled is removed from OrganizationFeatureSettings starting with API version 2027-01. It was never in OrganizationFeatureSettingsUpdate, so there was no allowlist entry to remove.
  • Web. The product form shows the meter cycle field on any recurring product that has a metered price or a meter-credit benefit, or that already has a saved cycle. shouldShowMeterCycle loses its isEnabledForOrganization argument, and the two tests that only covered the flag are gone.
  • Tests. The flag-off rejection test is dropped. The happy-path tests in TestCreateMeterCycling no longer set the flag.
  • Generated files. The TS client (clients/packages/client/src/v1.ts, from pnpm run generate, which targets 2027-01) and docs/openapi/2027-01.openapi.json are updated.
  • Nothing in the backoffice toggled this flag.

API change

Removing meter_cycling_enabled from the feature-settings response schema is the same API change #14256 made for seat_based_pricing_enabled. One difference: that PR came before the frozen API contract (tests/test_openapi.py::test_current_openapi_frozen). To keep that test passing, the field is removed with Version(up_to=V2026_10), the way #15177 deprecated member_model_enabled. It disappears in 2027-01 and stays in the frozen 2026-04 and 2026-10 contracts, so server/tests/openapi.*.json is unchanged. Once the cleanup script has run, those older versions report the field's default, false.

Regeneration notes

  • docs/openapi/2027-01.openapi.json: running just openapi && just docs-openapi here rewrote all three docs specs with about 120k lines of unrelated drift, so I removed only the meter_cycling_enabled property by hand. That is the same change #15177 made to this file. The 2026-04 and 2026-10 docs specs are left alone, as in #15177.
  • server/emails/src/types/openapi.ts: regenerated with no diff. The email schemas are not versioned and still include the field, just as they still include member_model_enabled.
  • The generated SDKs under sdk/ are not touched, as in #14256.

Cleanup after deploy

server/scripts/drop_meter_cycling_flag.py is a copy of drop_seat_based_pricing_flag.py. It removes the meter_cycling_enabled key from organizations.feature_settings in batches, and assigns modified_at to itself so that organizations don't look freshly modified. Run it after this is deployed:

uv run python -m scripts.drop_meter_cycling_flag
# options: --batch-size 5000 --sleep-seconds 0.1

Verification

  • uv run task lint && uv run task lint_types: pass
  • pytest tests/product tests/organization tests/test_openapi.py: 975 passed
  • web: turbo run lint typecheck --filter=web, oxlint --type-aware and oxfmt --check on the changed files, and vitest on ProductForm: pass

๐Ÿค– Generated with Claude Code