Problem
Auth claims and subscription enforcement currently resolve an account's role by querying active/trialing subscriptions ordered by created desc and selecting only the first document.
If overlapping subscriptions exist, a newer lower-tier subscription can mask an older active Pro subscription. That can under-entitle a paying user and eventually disconnect Garmin, Suunto, or COROS even though a Pro subscription remains active.
The email lifecycle now suppresses account-wide upgrade/downgrade messages when the changed subscription does not define the current membership, but the underlying claims and enforcement behavior remains unchanged.
Scope
- Define the supported account-level rule for overlapping active/trialing subscriptions.
- Prefer a fail-safe rule that never under-entitles a user who still pays for a higher tier.
- Implement one shared effective-subscription resolver.
- Use it consistently in:
functions/src/stripe/claims.ts
functions/src/schedule/enforce-subscription-limits.ts
- any other server-side entitlement checks that select one subscription
- Preserve deletion guards and recursive cleanup behavior.
- Add structured logging or diagnostics when overlapping active subscriptions are detected.
Acceptance criteria
- An account with active Basic and Pro subscriptions retains Pro claims and device sync.
- Subscription ordering or Firestore query order cannot change the effective entitlement.
- Trialing subscriptions follow the documented product rule.
- Unknown roles fail safely without granting unsupported access.
- Multi-subscription tests cover claims, enforcement, grace periods, and service disconnection.
- No connected service is disconnected while an effective Pro entitlement remains active.
- The Functions suite and build pass.
Context
Discovered while tightening the email lifecycle refresh. This is intentionally tracked separately because it changes real access and service-disconnection behavior rather than email delivery alone.
Problem
Auth claims and subscription enforcement currently resolve an account's role by querying active/trialing subscriptions ordered by
created descand selecting only the first document.If overlapping subscriptions exist, a newer lower-tier subscription can mask an older active Pro subscription. That can under-entitle a paying user and eventually disconnect Garmin, Suunto, or COROS even though a Pro subscription remains active.
The email lifecycle now suppresses account-wide upgrade/downgrade messages when the changed subscription does not define the current membership, but the underlying claims and enforcement behavior remains unchanged.
Scope
functions/src/stripe/claims.tsfunctions/src/schedule/enforce-subscription-limits.tsAcceptance criteria
Context
Discovered while tightening the email lifecycle refresh. This is intentionally tracked separately because it changes real access and service-disconnection behavior rather than email delivery alone.