Subscription tier not synced properly - displays "Free" after admin changes #58
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
When an admin changes a user's subscription tier (Free/Pro/Enterprise) via the admin UI, the user's settings page continues to display "Free" instead of the updated tier. The subscription feature (added in #55) has a sync gap between the admin update and the frontend display.
Related
Current Behavior
Expected Behavior
subscriptions.tiercolumnuser_profiles.subscription_tiercolumnRoot Cause Hypothesis
The admin endpoint likely updates one database location but not both, or the
syncTierToUserProfile()method is not called during admin updates. The frontend reads fromsubscriptions.tierviauseSubscription()hook.Files to Investigate
Backend:
backend/src/features/subscriptions/domain/subscriptions.service.ts- sync logicbackend/src/features/subscriptions/data/subscriptions.repository.ts- DB operationsFrontend:
frontend/src/pages/SettingsPage.tsx(lines 414-445) - "Current Plan:" displayfrontend/src/features/subscription/hooks/useSubscription.ts- data fetchingAcceptance Criteria
subscriptions.tieranduser_profiles.subscription_tierTest Plan
Admin Update Flow:
Grace Period Downgrade:
Notifications:
Plan: Subscription Tier Sync Fix
Phase: Planning | Agent: Planner | Status: AWAITING_REVIEW
Root Cause Analysis
Problem: When admin changes a user's subscription tier, the Settings page still shows "Free" instead of the updated tier.
Root Cause: Admin tier change updates
user_profiles.subscription_tierbut NOTsubscriptions.tier. The frontenduseSubscription()hook reads fromsubscriptions.tier, causing the display mismatch.Evidence:
backend/src/features/user-profile/domain/user-profile.service.ts:150- Only updatesuser_profilestablebackend/src/features/subscriptions/domain/subscriptions.service.ts:42-58- GET /subscriptions reads fromsubscriptionstableMilestones
Milestone 1: Fix Core Sync Issue (Critical)
Goal: Admin tier changes sync both database tables
Files to modify:
backend/src/features/subscriptions/domain/subscriptions.service.tsadminOverrideTier(userId: string, newTier: SubscriptionTier)methodsubscriptions.tierdirectlysyncTierToUserProfile()for consistencybackend/src/features/subscriptions/api/subscriptions.routes.tsbackend/src/features/admin/api/users.controller.ts:264userProfileService.updateSubscriptionTier()subscriptionsService.adminOverrideTier()backend/src/features/admin/api/users.controller.ts(constructor)Acceptance:
subscriptions.tieruser_profiles.subscription_tierMilestone 2: Verify Grace Period & Vehicle Selection
Goal: Confirm existing grace period and vehicle selection flows work correctly
Files to verify:
backend/src/features/subscriptions/jobs/grace-period.job.tsfrontend/src/features/subscription/components/VehicleSelectionDialog.tsxfrontend/src/App.tsxor equivalentAcceptance:
Milestone 3: Notifications Implementation
Goal: Notify users when their tier changes
Files to create:
backend/src/features/notifications/(new feature)domain/notifications.service.ts- Core notification logicdomain/notifications.types.ts- Typesdata/notifications.repository.ts- DB storage for in-app notificationsapi/notifications.controller.ts- GET /notifications endpointapi/notifications.routes.ts- Route registrationmigrations/001_notifications_table.sql- In-app notification storagebackend/src/features/notifications/domain/email-templates/tier-change.template.ts- Email template for tier changesfrontend/src/features/notifications/components/NotificationBell.tsx- Header notification iconcomponents/NotificationList.tsx- Dropdown notification listhooks/useNotifications.ts- React Query hookIntegration points:
NotificationsService.sendTierChangeNotification()from:SubscriptionsService.adminOverrideTier()SubscriptionsService.upgradeSubscription()SubscriptionsService.downgradeSubscription()grace-period.job.ts(auto-downgrade)Acceptance:
Milestone 4: Testing & Validation
Goal: Comprehensive test coverage and mobile/desktop validation
Tests to add:
backend/src/features/subscriptions/tests/admin-tier-override.test.ts- Unit tests for new adminOverrideTier methodintegration/admin-tier-sync.integration.test.ts- E2E admin tier change flowbackend/src/features/notifications/tests/notifications.service.test.ts- Unit testsintegration/tier-notifications.integration.test.ts- E2E notification flowManual validation:
Acceptance:
File Change Summary
Total: ~6 files modified, ~16 files created
Risks & Mitigations
Risk: User has no subscription record when admin changes tier
adminOverrideTier()creates subscription record if missingRisk: Email delivery failures
Risk: Notification feature scope creep
Execution Order
Verdict: AWAITING_REVIEW | Next: QR plan-completeness review
QR Review: Plan Completeness
Phase: Plan-Review | Agent: Quality Reviewer | Status: PASS
Acceptance Criteria Coverage
Result: All 7 acceptance criteria mapped to milestones.
Plan Structure Review
Findings
[SUGGESTION]: M3 Scope May Be Large
[SUGGESTION]: Consider Creating Notification Feature as Separate Issue
Considered But Not Flagged
VERDICT: PASS
Plan covers all acceptance criteria with clear milestones, file lists, and acceptance checkboxes. Suggestions are non-blocking improvements.
Verdict: PASS | Next: TW plan-scrub
TW Review: Plan Scrub
Phase: Plan-Review | Agent: Technical Writer | Status: PASS
Documentation Quality Check
Terminology Consistency
subscriptions.tieruser_profiles.subscription_tieradminOverrideTier()Clarity Improvements (Minor)
M1 acceptance checkbox wording - Could be more specific:
subscriptions.tier"subscriptions.tier"M3 file paths - Uses placeholder format:
backend/src/features/notifications/(new feature)Structure Assessment
VERDICT: PASS
Plan documentation is clear, consistent, and actionable. Minor suggestions do not affect executability.
Verdict: PASS | Next: QR plan-code
QR Review: Plan Code
Phase: Plan-Review | Agent: Quality Reviewer | Status: PASS_WITH_CONCERNS
RULE 0: Production Reliability
[HIGH] RULE 0: Transaction Safety for Dual-Table Updates
adminOverrideTier()method (proposed)subscriptions.tieranduser_profiles.subscription_tierbut doesn't specify transaction wrappingRULE 1: Project Conformance
RULE 2: Structural Quality
[SHOULD_FIX] RULE 2: Cross-Feature Dependency Direction
Findings Summary
Considered But Not Flagged
VERDICT: PASS_WITH_CONCERNS
Plan is executable but M1 implementation MUST include:
Verdict: PASS_WITH_CONCERNS | Next: QR plan-docs
QR Review: Plan Docs
Phase: Plan-Review | Agent: Quality Reviewer | Status: PASS
Documentation Updates Required
CLAUDE.md Impact Assessment
backend/src/features/subscriptions/CLAUDE.mdbackend/src/features/admin/CLAUDE.mdbackend/src/features/notifications/CLAUDE.mdfrontend/src/features/notifications/CLAUDE.mdPlan Documentation Quality
Findings
No blocking documentation issues. Plan includes sufficient detail for implementation.
[SUGGESTION]: Add Doc Update Checklist to M4
Considered But Not Flagged
VERDICT: PASS
Plan documentation is adequate. Suggestion to add explicit doc update checklist is non-blocking.
Plan Review Cycle Complete
Overall: PLAN APPROVED
Implementation Notes:
Verdict: PASS | Next: Execute M1 (branch: issue-58-subscription-tier-sync)
Milestone: M1 Complete
Phase: Execution | Agent: Developer | Status: PASS
Changes Implemented
subscriptions.repository.tsupdateTierByUserId()andcreateForAdminOverride()with transaction supportsubscriptions.service.tsadminOverrideTier()method with atomic dual-table updateusers.controller.tsupdateTier()to usesubscriptionsService.adminOverrideTier()Implementation Details
Verification
subscriptions.tieruser_profiles.subscription_tier2c0cbd5- fix: sync subscription tier on admin override (refs #58)Verdict: PASS | Next: M2 - Verify Grace Period & Vehicle Selection
Milestone: M2 Verification Complete
Phase: Execution | Agent: Developer | Status: PASS_WITH_FINDINGS
Verification Results
Grace Period Job (
grace-period.job.ts)WHERE grace_period_end < NOW()WHERE user_id = $1, should beWHERE auth0_sub = $1Fix Applied: Changed line 87 from
WHERE user_id = $1toWHERE auth0_sub = $1VehicleSelectionDialog (
VehicleSelectionDialog.tsx)DowngradeFlow (
DowngradeFlow.tsx)Auto-Prompt on Login After Grace Period Downgrade
Gap Analysis
Acceptance Criteria Gap: "User prompted to select 2 vehicles on next login after downgrade to free"
This functionality does not currently exist. The VehicleSelectionDialog is only shown during manual downgrade flows, not after automatic grace period expiration.
Recommendation: This is a new feature that should be tracked separately or added to M3/M4. Implementation would require:
Commits Pending
grace-period.job.tsfix staged (GPG signing issue preventing commit)Verdict: PASS_WITH_FINDINGS | Next: M3 - Notifications Implementation (or address vehicle selection gap)
Execution Summary
Phase: Execution | Agent: Developer | Status: COMPLETE (pending push)
Commits Ready (local branch)
2c0cbd5- fix: sync subscription tier on admin override (refs #58)adminOverrideTier()to SubscriptionsService with transaction wrappingupdateTierByUserId()andcreateForAdminOverride()8c86d8d- fix: correct user_profiles column name in grace-period job (refs #58)user_idinstead ofauth0_sub)Acceptance Criteria Status
subscriptions.tieruser_profiles.subscription_tierFollow-up Issues Created
Next Steps
Push branch and create PR. SSH authentication issue blocking automated push.
Verdict: COMPLETE | Next: Push and create PR