Skip to content

Subscription restructure: Basic + Operator ($49) + Architect ($400), deprecate Unlimited for existing users #6734

@beastoin

Description

@beastoin

Context

We are restructuring subscription plans from the legacy 3-tier system to a new 3-tier system:

Old plan New plan Price Status
Basic (free) Basic (free) $0 Stays
(new) Operator $49/mo New tier
Pro Architect $400/mo Renamed + repriced (was "Pro")
Unlimited (legacy) $19.99/mo Deprecated — keep for existing subscribers only, block new purchases

For existing Unlimited subscribers: keep their plan active, continue to display plan info, but show a deprecation notice encouraging them to try the Operator plan. Do NOT force-migrate or cancel.

For new users: Unlimited must not appear in the purchase catalog. Only Basic, Operator, and Architect are available for purchase.

Current state

Nik shipped 8 PRs (Apr 11–17) that partially implemented the new plan system on desktop:

A full audit was completed (ryo + mon, Apr 17) covering backend, mobile, desktop, and Stripe. 18 flaws found (4 critical, 4 high, 8 medium, 2 low). The flaws relevant to this issue are called out below.

What needs to happen

1. Stripe setup

  • Create Operator Stripe products and prices: monthly ($49) and annual ($490 or whatever the annual discount is). Set STRIPE_OPERATOR_MONTHLY_PRICE_ID and STRIPE_OPERATOR_ANNUAL_PRICE_ID in prod + dev Helm values.
    • Fixes F17 (CRITICAL): Operator price IDs are currently NOT configured in any Helm values. The plan shows in the desktop catalog but checkout sends None price IDs → guaranteed Stripe failure.
  • Create Architect Stripe products and prices: monthly ($400) and annual pricing. Update STRIPE_PRO_MONTHLY_PRICE_ID and STRIPE_PRO_ANNUAL_PRICE_ID in prod + dev Helm values to point at the new $400 prices.
  • Add Pro/Architect price IDs to pusher Helm values.
    • Fixes F18 (CRITICAL): Pusher only has Unlimited price IDs. Pro/Architect subscribers can't be resolved.
  • Keep existing Unlimited price IDs (STRIPE_UNLIMITED_MONTHLY_PRICE_ID, STRIPE_UNLIMITED_ANNUAL_PRICE_ID) — existing subscribers' renewals must continue to work.

2. Backend

  • Plan catalog (utils/subscription.py):

    • Purchase catalog returns only: Basic, Operator ($49), Architect ($400).
    • Unlimited must NOT appear in the purchase catalog for any client.
    • Existing Unlimited subscribers continue to see their plan on the /v1/users/me/subscription endpoint with a deprecated: true flag and a deprecation_message string (e.g., "Your Unlimited plan is being retired. Try the new Operator plan — same great features at $49/mo.").
    • PR fix(backend): hide legacy plans from purchase catalog #6731 already hides legacy plans from catalog — verify this covers Unlimited fully.
  • Architect pricing: update the Architect/Pro plan definition to reflect $400/mo (verify current definition matches or needs update).

  • Operator env var separation:

    • Fixes F4 (HIGH): PLUS_CHAT_QUESTIONS_PER_MONTH currently controls both Operator and Unlimited caps. Split into OPERATOR_CHAT_QUESTIONS_PER_MONTH (for Operator) and keep PLUS_CHAT_QUESTIONS_PER_MONTH (for legacy Unlimited). They may start at the same value (500) but must be independently tunable.
  • Version gating cleanup:

    • Currently Operator/Architect are gated to macOS v0.11.324+ only. Mobile always sees legacy names.
    • Once mobile is updated (step 4), remove the version gate or extend it to include the new mobile version that supports the new plan names.

3. Desktop (macOS)

  • Desktop already shows Operator and Architect in the catalog (Nik's PRs).
  • Add deprecation notice for existing Unlimited subscribers on the plan settings page: show a banner/card encouraging migration to Operator.
  • Fix F3 (HIGH): Unify the two conflicting free-tier limits. Floating bar uses 50/week (local), backend uses 30/month (server). Pick one source of truth — the server — and have the floating bar call the server quota endpoint instead of maintaining a separate local counter.
  • Fix F8 (MEDIUM): Add operator to the Swift plan enum before backend starts returning it without the backward-compat unlimited alias. If enum mismatch, Codable decode crashes.

4. Mobile (Flutter)

  • Update plan catalog UI (plans_sheet.dart):
    • Show: Basic (free), Operator ($49/mo), Architect ($400/mo).
    • Do NOT show Unlimited for new users.
    • For existing Unlimited subscribers: show current plan card with deprecation notice + "Try Operator" CTA.
  • Update plan names in subscription.dart: add operator to PlanType enum. Update display strings.
    • Fixes F16 (LOW): PlanType.pro currently unused in UI — now it maps to Architect.
  • Fix F9 (MEDIUM): Plan feature bullets are hardcoded. Either fetch from backend catalog or update the hardcoded strings to match the new Operator/Architect feature sets.

5. Deprecation notice for Unlimited subscribers

Across all surfaces (backend API response, mobile UI, desktop UI):

  • Message: "Your Unlimited plan is being retired. Switch to the Operator plan — [feature highlights]. Your current plan will continue to work in the meantime."
  • CTA: "Try Operator" button that opens the Operator checkout flow.
  • No forced migration. Users keep Unlimited until they voluntarily switch or we announce a sunset date.

Out of scope (separate issues)

These flaws from the audit are real but orthogonal to the plan restructuring:

  • F1 (CRITICAL): No IAP on mobile — Stripe checkout bypasses App Store/Play Store. Needs its own issue + legal review.
  • F2 (CRITICAL): Kill-switch defaults OFF — caps are display-only. Needs ops decision on when to enable.
  • F5 (HIGH): Transcription/memory limits informational only.
  • F6 (HIGH): Stripe price ID validation non-blocking.
  • F7–F15 (MEDIUM/LOW): Various enforcement gaps, backward-compat fragility, display inconsistencies.

Acceptance criteria

  • Stripe products and prices created for Operator ($49/mo) and Architect ($400/mo)
  • All price IDs configured in prod + dev Helm values (backend + backend-listen + pusher)
  • Operator checkout works end-to-end on desktop (macOS v0.11.324+)
  • Architect checkout works end-to-end on desktop
  • Unlimited plan hidden from purchase catalog on ALL clients (desktop, mobile, web)
  • Existing Unlimited subscribers see their plan + deprecation notice + "Try Operator" CTA
  • Operator and Unlimited chat caps independently configurable via separate env vars
  • Mobile app shows Basic / Operator / Architect (after app update)
  • Desktop plan enum includes operator — no Codable decode crash
  • No regression: existing Unlimited subscribers can still use their plan, renewals work, webhook handling intact

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    p1Priority: Critical (score 22-29)

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions