You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.").
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.
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.
Context
We are restructuring subscription plans from the legacy 3-tier system to a new 3-tier system:
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
STRIPE_OPERATOR_MONTHLY_PRICE_IDandSTRIPE_OPERATOR_ANNUAL_PRICE_IDin prod + dev Helm values.Noneprice IDs → guaranteed Stripe failure.STRIPE_PRO_MONTHLY_PRICE_IDandSTRIPE_PRO_ANNUAL_PRICE_IDin prod + dev Helm values to point at the new $400 prices.STRIPE_UNLIMITED_MONTHLY_PRICE_ID,STRIPE_UNLIMITED_ANNUAL_PRICE_ID) — existing subscribers' renewals must continue to work.2. Backend
Plan catalog (
utils/subscription.py):/v1/users/me/subscriptionendpoint with adeprecated: trueflag and adeprecation_messagestring (e.g., "Your Unlimited plan is being retired. Try the new Operator plan — same great features at $49/mo.").Architect pricing: update the Architect/Pro plan definition to reflect $400/mo (verify current definition matches or needs update).
Operator env var separation:
PLUS_CHAT_QUESTIONS_PER_MONTHcurrently controls both Operator and Unlimited caps. Split intoOPERATOR_CHAT_QUESTIONS_PER_MONTH(for Operator) and keepPLUS_CHAT_QUESTIONS_PER_MONTH(for legacy Unlimited). They may start at the same value (500) but must be independently tunable.Version gating cleanup:
3. Desktop (macOS)
operatorto the Swift plan enum before backend starts returning it without the backward-compatunlimitedalias. If enum mismatch, Codable decode crashes.4. Mobile (Flutter)
plans_sheet.dart):subscription.dart: addoperatortoPlanTypeenum. Update display strings.PlanType.procurrently unused in UI — now it maps to Architect.5. Deprecation notice for Unlimited subscribers
Across all surfaces (backend API response, mobile UI, desktop UI):
Out of scope (separate issues)
These flaws from the audit are real but orthogonal to the plan restructuring:
Acceptance criteria
operator— no Codable decode crashReferences
utils/subscription.py,routers/payment.py,models/users.pymodels/subscription.dart,pages/settings/widgets/plans_sheet.dartAPIClient.swift,SettingsPage.swift,FloatingBarUsageLimiter.swift