[ { "id": "BF1", "description": "Fix stale JSDoc in tenantTiers.ts — replace 'basic' references with 'pro'", "implemented": true, "prdRefs": ["BF1"] }, { "id": "BF2", "description": "Fix buildPhaseItems to use tenant's actual tier prices for scheduled reductions", "implemented": true, "prdRefs": ["BF2"] }, { "id": "BF3", "description": "Verify/add assertTierAccess(INVOICE_DESIGNER) in saveInvoiceTemplate server action", "implemented": true, "prdRefs": ["BF3"] }, { "id": "BF4", "description": "Extract shared plan-fetching logic in nextAuthOptions.ts into reusable helper", "implemented": true, "prdRefs": ["BF4"] }, { "id": "BF5", "description": "Add console.warn in tierFromStripeProduct() for unknown product names", "implemented": true, "prdRefs": ["BF5"] }, { "id": "BF6", "description": "Replace TierGate null loading state with skeleton placeholder", "implemented": true, "prdRefs": ["BF6"] }, { "id": "BF7", "description": "Migration: add FK from stripe_subscriptions.stripe_base_price_id to stripe_prices", "implemented": true, "prdRefs": ["BF7"] }, { "id": "BF8", "description": "Add JSDoc to addOns.ts marking it as intentional scaffolding", "implemented": true, "prdRefs": ["BF8"] }, { "id": "DOC1", "description": "Create docs/tier-gating-guide.md with step-by-step instructions for adding new gated features", "implemented": true, "prdRefs": ["DOC1"] }, { "id": "DOC1a", "description": "Guide covers: add to TIER_FEATURES enum, TIER_FEATURE_MAP, FEATURE_MINIMUM_TIER", "implemented": true, "prdRefs": ["DOC1"] }, { "id": "DOC1b", "description": "Guide covers: client gating with TierGate/useTierFeature, server gating with ServerTierGate/assertTierAccess", "implemented": true, "prdRefs": ["DOC1"] }, { "id": "DOC1c", "description": "Guide covers: adding display name in FEATURE_DISPLAY_NAMES, CE bypass behavior, testing", "implemented": true, "prdRefs": ["DOC1"] }, { "id": "AB1", "description": "Add annual price env vars to .env.example (PRO_BASE_ANNUAL, PRO_USER_ANNUAL, PREMIUM_BASE_ANNUAL, PREMIUM_USER_ANNUAL)", "implemented": true, "prdRefs": ["AB1"] }, { "id": "AB2", "description": "StripeService config reads annual price IDs from env", "implemented": true, "prdRefs": ["AB1"] }, { "id": "AB3", "description": "Migration: add billing_interval column to stripe_subscriptions (default 'month')", "implemented": true, "prdRefs": ["AB2"] }, { "id": "AB4", "description": "Subscription import/create tracks billing_interval from Stripe price recurring.interval", "implemented": true, "prdRefs": ["AB2"] }, { "id": "AB5", "description": "createLicenseCheckoutSession accepts interval param, uses annual price IDs when 'year'", "implemented": true, "prdRefs": ["AB3"] }, { "id": "AB6", "description": "Account page shows monthly/annual toggle with savings callout and confirmation dialog", "implemented": true, "prdRefs": ["AB4"] }, { "id": "AB7", "description": "Switching billing interval schedules change at period end via Stripe subscription schedule", "implemented": true, "prdRefs": ["AB4"] }, { "id": "AB8", "description": "upgradeTier() accepts interval parameter and uses correct annual/monthly prices", "implemented": true, "prdRefs": ["AB5"] }, { "id": "AB9", "description": "getUpgradePreview() shows both monthly and annual pricing options", "implemented": true, "prdRefs": ["AB5"] }, { "id": "TR1", "description": "Add trial_end and subscription_status to JWT token type", "implemented": true, "prdRefs": ["TR1"] }, { "id": "TR2", "description": "Fetch trial_end and subscription_status from DB on sign-in and refresh (5-min throttle)", "implemented": true, "prdRefs": ["TR1"] }, { "id": "TR3", "description": "Propagate trial_end and subscription_status from JWT to Session.user", "implemented": true, "prdRefs": ["TR1"] }, { "id": "TR4", "description": "Add isTrialing, trialDaysLeft, trialEndDate, subscriptionStatus to TierContextValue", "implemented": true, "prdRefs": ["TR2"] }, { "id": "TR5", "description": "Add isPaymentFailed derived from subscriptionStatus in TierContext", "implemented": true, "prdRefs": ["TR2"] }, { "id": "TR6", "description": "Modify checkout session to support trial_period_days param for new signups", "implemented": true, "prdRefs": ["TR3"] }, { "id": "TR7", "description": "TrialBanner component: shows '{Plan} Trial: {N} days left' in header, left of tenant badge", "implemented": true, "prdRefs": ["TR4"] }, { "id": "TR8", "description": "TrialBanner uses warning color when ≤3 days remaining", "implemented": true, "prdRefs": ["TR4"] }, { "id": "TR9", "description": "TrialBanner links to /msp/account", "implemented": true, "prdRefs": ["TR4"] }, { "id": "TR10", "description": "PaymentFailedBanner component: shows error banner when past_due/unpaid, links to billing portal", "implemented": true, "prdRefs": ["TR5"] }, { "id": "TR11", "description": "PaymentFailedBanner is not dismissible and replaces TrialBanner if both apply", "implemented": true, "prdRefs": ["TR5"] }, { "id": "TR12", "description": "Account page: Trial Status card with days remaining, progress bar, start/end dates", "implemented": true, "prdRefs": ["TR6"] }, { "id": "TR13", "description": "Account page: CTA varies by trial state — shows upcoming charge amount/date, cancel option for Premium trial, update payment for failures", "implemented": true, "prdRefs": ["TR6"] }, { "id": "TR14", "description": "handleSubscriptionUpdated webhook correctly processes trialing status (sets plan)", "implemented": true, "prdRefs": ["TR7"] }, { "id": "TR15", "description": "Payment failure at trial end triggers past_due status and payment banner", "implemented": true, "prdRefs": ["TR8"] }, { "id": "TR16", "description": "startPremiumTrial in StripeService: admin action that sets 30-day Premium trial on tenant", "implemented": true, "prdRefs": ["TR9"] }, { "id": "TR16a", "description": "startPremiumTrial for paying Pro: upgrades subscription to Premium with trial_end 30 days", "implemented": true, "prdRefs": ["TR9"] }, { "id": "TR16b", "description": "startPremiumTrial for Pro trial: ends Pro trial first, then creates Premium trial", "implemented": true, "prdRefs": ["TR9"] }, { "id": "TR17", "description": "startPremiumTrial rejects tenants already on Premium", "implemented": true, "prdRefs": ["TR9"] }, { "id": "TR18", "description": "startPremiumTrial updates tenants.plan to 'premium'", "implemented": true, "prdRefs": ["TR9"] }, { "id": "TR19", "description": "Premium trial auto-charges at end: Stripe charges for Premium, webhook confirms trialing→active, plan stays 'premium'", "implemented": true, "prdRefs": ["TR10"] }, { "id": "TR20", "description": "Cancel Premium Trial button on account page", "implemented": true, "prdRefs": ["TR10"] }, { "id": "TR20a", "description": "Premium trial charge failure: webhook handles past_due, payment failure banner shows", "implemented": true, "prdRefs": ["TR10"] }, { "id": "TR21", "description": "Premium trial request form: textarea in Account Management Plan & Tier section", "implemented": true, "prdRefs": ["TR11"] }, { "id": "TR22", "description": "Premium trial request form shown when isPro and not trialing", "implemented": true, "prdRefs": ["TR11"] }, { "id": "TR23", "description": "sendPremiumTrialRequestAction sends email to Nine Minds with tenant info + message", "implemented": true, "prdRefs": ["TR11"] }, { "id": "TR24", "description": "NineMinds extension: Trial Requests handled via email to support (no separate list view needed)", "implemented": true, "prdRefs": ["TR12"] }, { "id": "TR25", "description": "NineMinds extension: 'Start Premium Trial' action button per tenant in dropdown menu", "implemented": true, "prdRefs": ["TR12"] }, { "id": "TR26", "description": "NineMinds extension: API endpoint POST /api/v1/tenant-management/start-premium-trial", "implemented": true, "prdRefs": ["TR12"] }, { "id": "TR27", "description": "NineMinds extension: show current plan and subscription status per tenant in list", "implemented": true, "prdRefs": ["TR12"] }, { "id": "PE1", "description": "Payment failure banner in header when subscription_status is past_due or unpaid", "implemented": true, "prdRefs": ["PE2", "PE3"] }, { "id": "PE2", "description": "handleSubscriptionUpdated logs payment failure status (past_due/unpaid)", "implemented": true, "prdRefs": ["PE2"] } ]