Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
30 KiB
30 KiB
Scratchpad — Solo Tier
- Plan slug:
solo-tier - Created:
2026-03-26
What This Is
Keep a lightweight, continuously-updated log of discoveries and decisions made while implementing this plan.
Decisions
- (2026-03-26) AI is a cross-tier add-on, not a tier feature. Even Pro users must purchase AI separately. This is the first real activation of the
ADD_ONSenum which was previously scaffolded but empty. - (2026-03-26) Mobile app access is gated to Pro+ via the
MOBILE_ACCESStier feature. Block happens at OTT exchange inmobileAuthService.ts, so Solo users never receive API tokens. - (2026-03-26) No DB migration needed —
tenants.planis already an unconstrained text column,tenant_addonstable already exists (migration20260303100000). - (2026-03-26)
resolveTier()keeps defaulting to'pro'for null/invalid — Solo is opt-in only, existing tenants are unaffected. - (2026-03-26) No AI grandfathering needed — feature is brand new, no existing users have it.
- (2026-03-26) Solo trial: 7 days for new signups.
- (2026-03-26) Solo->Pro trial: available to established Solo customers (past their 7-day trial), duration 15–30 days TBD. Prevents trial stacking (can't trial Pro while still on Solo trial).
- (2026-03-26) Settings tabs stay visible but show
<FeatureUpgradeNotice>instead of content — user can see what they're missing. Sidebar items withrequiredFeatureare hidden entirely.
Discoveries / Constraints
- (2026-03-26) Current tier system: only
proandpremiumexist. 3 features gated at premium (ENTRA_SYNC, CIPP, TEAMS_INTEGRATION). No rank system — plan uses map lookup. - (2026-03-26)
ADD_ONSenum is empty,tenantHasAddOn()exists but is unused at runtime.tenant_addonstable exists in DB. - (2026-03-26)
TierContextValuehas: tier, isPro, isPremium, hasFeature, trial state, premium trial state, subscription status. No isSolo, no addOns. - (2026-03-26)
MenuIteminterface has norequiredFeaturefield. Sidebar currently filters by RBAC permissions and PostHog feature flags only. - (2026-03-26) Mobile auth:
exchangeOttForSession()validates OTT + rate limiting but has zero tier checks. Solo users could currently auth to mobile. - (2026-03-26)
getTierPriceIds()returns{ basePriceId: string; userPriceId: string }— return type needs updating to allowuserPriceId: nullfor Solo. - (2026-03-26) Upgrade flow gated behind
'tier-upgrade-flow'feature flag in AccountManagement. - (2026-03-26) Root
package.jsondoes not definenpm run test:unit; focused validation currently uses package-localvitestcommands (for example fromserver/). - (2026-03-26)
TierAccessErrornow carriesstatusCode=403/code='TIER_ACCESS_DENIED'so existing API middleware can serialize tier denials without route-specific error adapters. - (2026-03-26) EE tests that import
server/src/lib/tier-gating/*behave better when those shared modules avoid the@/lib/featuresalias and use local relative imports instead. - (2026-03-26)
npx tsc -p ee/server/tsconfig.json --noEmitis currently blocked byee/server/src/components/settings/account/AccountManagement.tsxmissing display names for the newly-added tier features; that aligns with still-pending F067 rather than the RMM gating changes. - (2026-03-26) Some
DefaultLayoutVitest suites currently fail during module resolution for unrelated@alga-psa/core/context/DocumentsCrossFeatureContextimports pulled in by package components; narrow layout validation is currently more reliable via targeted sidebar tests. - (2026-03-26)
mobileAuth.test.tsneeded an explicit partial@alga-psa/authmock once mobile tier gating was added; otherwiseApiKeyServiceresolved as missing under Vitest's mocked module graph. - (2026-03-26) After adding Solo Stripe price support,
npx tsc -p ee/server/tsconfig.json --noEmitstill only fails on the pre-existing F067FEATURE_DISPLAY_NAMESgap inAccountManagement.tsx; no new Stripe typing regressions surfaced.
Commands / Runbooks
- Build shared packages:
npm run build:shared - Run unit tests:
npm run test:unit - Run focused server unit tests from
server/:npx vitest run src/lib/tier-gating/assertTierAccess.test.ts - Run focused EE SSO unit tests from
ee/server/:npx vitest run src/__tests__/unit/ssoActions.test.ts src/__tests__/unit/auth/getLinkedSsoProvidersAction.test.ts - Run focused EE Stripe pricing tests from
ee/server/:npx vitest run src/__tests__/unit/stripeTierMapping.test.ts src/__tests__/unit/stripeService.tierPricing.test.ts - Run focused mobile auth unit tests from
server/:npx vitest run src/test/unit/mobileAuth.test.ts - Test Solo locally: Set tenant plan to 'solo' in DB:
UPDATE tenants SET plan = 'solo' WHERE tenant = '<id>' - Test AI add-on: Insert into tenant_addons:
INSERT INTO tenant_addons (tenant, addon_key, activated_at) VALUES ('<id>', 'ai_assistant', NOW())
Links / References
- Source plan:
.ai/tiers/solo-tier-implementation-plan.md - Tier constants:
packages/types/src/constants/tenantTiers.ts - Feature constants:
packages/types/src/constants/tierFeatures.ts - Add-on constants:
packages/types/src/constants/addOns.ts - Tier context:
server/src/context/TierContext.tsx - Tier gating:
server/src/lib/tier-gating/assertTierAccess.ts - Menu config:
server/src/config/menuConfig.ts - Sidebar:
server/src/components/layout/SidebarWithFeatureFlags.tsx - Settings page:
server/src/components/settings/SettingsPage.tsx - Mobile auth:
server/src/lib/mobileAuth/mobileAuthService.ts - Stripe service:
ee/server/src/lib/stripe/StripeService.ts - Stripe tier mapping:
ee/server/src/lib/stripe/stripeTierMapping.ts - License actions:
ee/server/src/lib/actions/license-actions.ts - Account management:
ee/server/src/components/settings/account/AccountManagement.tsx - User actions:
packages/users/src/actions/user-actions/userActions.ts - Grandfathering plan:
.ai/tiers/early-adopters-grandfathering.md
Resolved Questions
- (2026-03-26) No AI grandfathering — nobody has it yet, it's new. Addon-only from day one for all tiers.
- (2026-03-26) Solo gets 7-day free trial for new purchases.
- (2026-03-26) Established Solo customers (past their 7-day trial) can trial Pro features for 15–30 days (duration TBD). Not available during Solo trial.
- (2026-03-26) AI add-on pricing TBD — implement env var plumbing but price IDs won't be set at launch.
Open Questions
- Solo->Pro trial duration: 15 or 30 days? (Awaiting final decision)
- (2026-03-26) Completed F001 feature: Add 'solo' to TENANT_TIERS array so it becomes ['solo', 'pro', 'premium']
- (2026-03-26) Completed F002 feature: Update TenantTier type to include 'solo'
- (2026-03-26) Completed F003 feature: Add TIER_LABELS entry: solo -> 'Solo'
- (2026-03-26) Completed F004 feature: Add TIER_RANK record: { solo: 0, pro: 1, premium: 2 }
- (2026-03-26) Completed F005 feature: Add tierAtLeast(tier, minimum) helper using TIER_RANK comparison
- (2026-03-26) Completed F006 feature: Add INTEGRATIONS to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F007 feature: Add EXTENSIONS to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F008 feature: Add MANAGED_EMAIL to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F009 feature: Add SSO to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F010 feature: Add ADVANCED_ASSETS to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F011 feature: Add CLIENT_PORTAL_ADMIN to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F012 feature: Add WORKFLOW_DESIGNER to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F013 feature: Add MOBILE_ACCESS to TIER_FEATURES enum with min tier 'pro'
- (2026-03-26) Completed F014 feature: Rewrite tierHasFeature() to use TIER_RANK comparison instead of map lookup
- (2026-03-26) Completed F015 feature: Derive TIER_FEATURE_MAP from FEATURE_MINIMUM_TIER + TIER_RANK
- (2026-03-26) Completed F016 feature: Add AI_ASSISTANT to ADD_ONS enum
- (2026-03-26) Completed F017 feature: Add ADD_ON_LABELS record with AI_ASSISTANT -> 'AI Assistant'
- (2026-03-26) Completed F018 feature: Add ADD_ON_DESCRIPTIONS with marketing-friendly description for AI_ASSISTANT
- (2026-03-26) Completed F019 feature: resolveTier() returns 'pro' for null/invalid input (unchanged behavior, Solo is opt-in)
- (2026-03-26) Completed F020 feature: isValidTier('solo') returns true
- (2026-03-26) Completed F021 feature: Create getActiveAddOns(tenantId) service querying tenant_addons for non-expired add-ons
- (2026-03-26) Completed F022 feature: Create assertAddOnAccess(addOn) that throws if tenant lacks the add-on
- (2026-03-26) Completed F023 feature: assertAddOnAccess bypasses check in CE edition (all add-ons unlocked)
- (2026-03-26) Completed F025 feature: Add isSolo boolean to TierContextValue (tier === 'solo')
- (2026-03-26) Completed F026 feature: Add addOns array to TierContextValue populated from session/DB
- (2026-03-26) Completed F027 feature: Add hasAddOn(addOn) helper to TierContextValue
- (2026-03-26) Completed F028 feature: CE edition bypasses add-on checks (hasAddOn returns true for all)
- (2026-03-26) Completed F029 feature: Add requiredFeature?: TIER_FEATURES to MenuItem interface in menuConfig
- (2026-03-26) Completed F030 feature: Annotate Extensions menu item with requiredFeature: TIER_FEATURES.EXTENSIONS
- (2026-03-26) Completed F031 feature: Annotate Workflow Editor subitem with requiredFeature: TIER_FEATURES.WORKFLOW_DESIGNER
- (2026-03-26) Completed F032 feature: SidebarWithFeatureFlags filters out menu items where requiredFeature is set and tier lacks it
- (2026-03-26) Completed F033 feature: Sidebar filtering applies recursively to subItems
- (2026-03-26) Completed F034 feature: Add requiredFeature?: TIER_FEATURES to TabContent type in SettingsPage
- (2026-03-26) Completed F035 feature: Tag Integrations settings tab with TIER_FEATURES.INTEGRATIONS
- (2026-03-26) Completed F036 feature: Tag Extensions settings tab with TIER_FEATURES.EXTENSIONS
- (2026-03-26) Completed F037 feature: Tag Email settings tab with TIER_FEATURES.MANAGED_EMAIL
- (2026-03-26) Completed F038 feature: Render FeatureUpgradeNotice instead of content when tab's requiredFeature is gated
- (2026-03-26) Completed F039 feature: Gated settings tabs remain visible and clickable (not hidden)
- (2026-03-26) Completed F040 feature: Settings sidebar items remain visible regardless of tier (no requiredFeature filtering in settings mode)
- (2026-03-26) Completed F041 feature: Added
INTEGRATIONStier enforcement to shared Entra API guard, Teams package status flow, and calendar OAuth callbacks; introduced tenant-aware tier lookup for callback routes that run without an authenticated web session. - (2026-03-26) Completed F042 feature: Added
EXTENSIONStier enforcement to EE extension install/uninstall API routes so Solo tenants receive a structured 403 instead of reaching the registry actions. - (2026-03-26) Completed F043 feature: Added
MANAGED_EMAILtier enforcement to the managed-email server actions (get/updateEmailSettings,getEmailProviders, and EE managed-domain actions) so Solo tenants cannot bypass the gated Email settings tab. - (2026-03-26) Completed F044 feature: Added
SSOtier enforcement to EE SSO/OAuth actions by guarding the shared SSO permission helper, SSO preferences/account-link actions, provider-option loading, and tenant-scoped linked-provider lookups. - (2026-03-26) Completed F045 feature: Added
ADVANCED_ASSETStier enforcement to authenticated RMM/NinjaOne server actions plus the NinjaOne OAuth callback and webhook routes, so Solo tenants cannot connect, sync, or operate RMM-managed assets through EE handlers. - (2026-03-26) Completed F046 feature: Added
assertAddOnAccess(ADD_ONS.AI_ASSISTANT)to the non-stream chat completions route so AI chat requests now return 403 unless the tenant has the AI add-on and the existing feature flag is enabled. - (2026-03-26) Completed F047 feature: Added tenant-scoped AI add-on enforcement for the document-assist API by introducing
assertTenantAddOnAccess(tenantId, addOn)and using it in the sessionless document-assist route before AI generation. - (2026-03-26) Completed F048 feature: Threaded
hasAddOn(ADD_ONS.AI_ASSISTANT)intoDefaultLayoutand the EERightSidebarso keyboard shortcuts, Quick Ask, and sidebar rendering all shut off when the AI add-on is inactive. - (2026-03-26) Completed F049 feature: Added
MOBILE_ACCESStier enforcement insideexchangeOttForSession()so Solo tenants are rejected with a 403 before mobile API keys and refresh tokens are issued. - (2026-03-26) Completed F050 feature: Mobile auth exchange now returns the explicit upgrade message
"Mobile app access requires Pro or higher"through the shared API error response when Solo tenants attempt to sign in on mobile. - (2026-03-26) Completed F051 feature: Added
alga-psa-solo -> soloto the Stripe product-tier map so Solo subscriptions and webhooks can resolve the tenant plan without special-case fallback logic. - (2026-03-26) Completed F052 feature: Extended Stripe tier pricing to support Solo base-only prices so
getTierPriceIds('solo')returns{ basePriceId, userPriceId: null }for monthly and annual billing while Pro/Premium keep base+per-user pricing. - (2026-03-26) Completed F053 feature: Updated Stripe checkout session creation to emit a single flat-rate line item for Solo pricing while preserving the existing base-plus-per-user checkout payload for Pro and Premium tiers.
- (2026-03-26) Completed F054 feature: Made Stripe checkout-completed webhook handling explicitly derive
licensed_user_count = 1for Solo subscriptions instead of relying on the base-price item quantity by coincidence. - (2026-03-26) Completed F055 feature: Reused the Solo license-count normalization in
customer.subscription.updatedhandling so renewals and later subscription updates keeplicensed_user_count = 1for Solo tenants. - (2026-03-26) Completed F056 feature: Verified
upgradeTier()now handles Solo -> Pro in place by deleting the Solo flat-rate item, adding Pro base + per-user items, and updating the stored Stripe item ids and tenant plan. - (2026-03-26) Completed F057 feature: Added
downgradeTier()with a hard validation that Solo downgrades are only allowed when exactly one active internal user remains. - (2026-03-26) Completed F058 feature: Implemented the Pro -> Solo Stripe mutation to remove the existing per-user/base items, add the Solo flat-rate item, and rewrite the stored subscription pointers back to single-item mode.
- (2026-03-26) Completed F059 feature: Added
purchaseAddOn()forAI_ASSISTANT, creating an embedded Stripe checkout session with add-on metadata so AI billing can run separately from tier subscription changes. - (2026-03-26) Completed F060 feature: Added
cancelAddOn()forAI_ASSISTANT, cancelling the stored add-on Stripe subscription referenced fromtenant_addonsmetadata instead of touching the core license subscription. - (2026-03-26) Completed F061 feature: Added add-on-aware Stripe webhook handling that activates
tenant_addonsrows for AI subscriptions instead of importing them as license subscriptions. - (2026-03-26) Completed F062 feature: Extended the add-on-aware Stripe webhook path to expire
tenant_addonsrows when AI subscriptions are cancelled or otherwise leave an active/trialing state. - (2026-03-26) Completed F063 feature: Documented the new Solo and AI Stripe price IDs in the root
.env.exampleso local/test billing setups expose the same configuration surface as the updated Stripe service. - (2026-03-26) Completed F064 feature: Added server-side Solo enforcement to
addUser()so a second active internal user is rejected even if the UI is bypassed. - (2026-03-26) Completed F065 feature: The Solo user-cap enforcement now returns the explicit upgrade message
"Solo plan is limited to 1 user. Upgrade to Pro to add more users."instead of the generic license-limit text. - (2026-03-26) Completed F066 feature: User Management now disables the MSP create-user button for Solo tenants once the single-user cap is reached and shows the same upgrade guidance inline in the button area.
- (2026-03-26) Completed T001 test: isValidTier('solo') returns true
- (2026-03-26) Completed T002 test: isValidTier('pro') and isValidTier('premium') still return true
- (2026-03-26) Completed T003 test: isValidTier('invalid') returns false
- (2026-03-26) Completed T004 test: TENANT_TIERS contains ['solo', 'pro', 'premium'] in order
- (2026-03-26) Completed T005 test: TIER_LABELS.solo equals 'Solo'
- (2026-03-26) Completed T006 test: TIER_RANK maps solo=0, pro=1, premium=2
- (2026-03-26) Completed T007 test: tierAtLeast('solo', 'solo') returns true
- (2026-03-26) Completed T008 test: tierAtLeast('solo', 'pro') returns false
- (2026-03-26) Completed T009 test: tierAtLeast('pro', 'solo') returns true
- (2026-03-26) Completed T010 test: tierAtLeast('premium', 'pro') returns true
- (2026-03-26) Completed T011 test: tierAtLeast('pro', 'premium') returns false
- (2026-03-26) Completed T012 test: resolveTier('solo') returns { tier: 'solo', isMisconfigured: false }
- (2026-03-26) Completed T013 test: resolveTier(null) defaults to { tier: 'pro', isMisconfigured: false }
- (2026-03-26) Completed T014 test: resolveTier('invalid') defaults to pro with isMisconfigured: true
- (2026-03-26) Completed T015 test: tierHasFeature('solo', INTEGRATIONS) returns false
- (2026-03-26) Completed T016 test: tierHasFeature('pro', INTEGRATIONS) returns true
- (2026-03-26) Completed T017 test: tierHasFeature('premium', INTEGRATIONS) returns true
- (2026-03-26) Completed T018 test: tierHasFeature('solo', EXTENSIONS) returns false
- (2026-03-26) Completed T019 test: tierHasFeature('pro', EXTENSIONS) returns true
- (2026-03-26) Completed T020 test: tierHasFeature('solo', MANAGED_EMAIL) returns false
- (2026-03-26) Completed T021 test: tierHasFeature('solo', SSO) returns false
- (2026-03-26) Completed T022 test: tierHasFeature('solo', ADVANCED_ASSETS) returns false
- (2026-03-26) Completed T023 test: tierHasFeature('solo', CLIENT_PORTAL_ADMIN) returns false
- (2026-03-26) Completed T024 test: tierHasFeature('solo', WORKFLOW_DESIGNER) returns false
- (2026-03-26) Completed T025 test: tierHasFeature('solo', MOBILE_ACCESS) returns false
- (2026-03-26) Completed T026 test: tierHasFeature('pro', MOBILE_ACCESS) returns true
- (2026-03-26) Completed T027 test: tierHasFeature('solo', ENTRA_SYNC) returns false (premium feature)
- (2026-03-26) Completed T028 test: tierHasFeature('pro', ENTRA_SYNC) returns false (premium feature)
- (2026-03-26) Completed T029 test: tierHasFeature('premium', ENTRA_SYNC) returns true
- (2026-03-26) Completed T030 test: TIER_FEATURE_MAP['solo'] is empty array (no features at solo level)
- (2026-03-26) Completed T031 test: TIER_FEATURE_MAP['pro'] contains all 8 pro-minimum features
- (2026-03-26) Completed T032 test: TIER_FEATURE_MAP['premium'] contains all 11 features (8 pro + 3 premium)
- (2026-03-26) Completed T033 test: ADD_ONS.AI_ASSISTANT equals 'ai_assistant'
- (2026-03-26) Completed T034 test: ADD_ON_LABELS[AI_ASSISTANT] equals 'AI Assistant'
- (2026-03-26) Completed T035 test: tenantHasAddOn(['ai_assistant'], ADD_ONS.AI_ASSISTANT) returns true
- (2026-03-26) Completed T036 test: tenantHasAddOn([], ADD_ONS.AI_ASSISTANT) returns false
- (2026-03-26) Completed T037 test: getActiveAddOns returns only non-expired add-ons from tenant_addons table
- (2026-03-26) Completed T038 test: getActiveAddOns returns empty array when no add-ons exist for tenant
- (2026-03-26) Completed T039 test: getActiveAddOns excludes expired add-ons (expires_at < now)
- (2026-03-26) Completed T040 test: assertAddOnAccess throws when tenant lacks the requested add-on
- (2026-03-26) Completed T041 test: assertAddOnAccess passes when tenant has the requested add-on
- (2026-03-26) Completed T042 test: assertAddOnAccess bypasses check in CE edition (never throws)
- (2026-03-26) Completed T043 test: assertAddOnAccess works for Solo tier with AI add-on active
- (2026-03-26) Completed T044 test: assertAddOnAccess works for Pro tier with AI add-on active
- (2026-03-26) Completed T045 test: assertAddOnAccess works for Premium tier with AI add-on active
- (2026-03-26) Completed T046 test: TierContext provides isSolo=true when tier is solo
- (2026-03-26) Completed T047 test: TierContext provides isSolo=false when tier is pro
- (2026-03-26) Completed T048 test: TierContext provides addOns array from session/DB
- (2026-03-26) Completed T049 test: TierContext hasAddOn returns true when add-on is in addOns array
- (2026-03-26) Completed T050 test: TierContext hasAddOn returns false when add-on is not in addOns array
- (2026-03-26) Completed T051 test: CE edition: hasAddOn always returns true regardless of addOns array
- (2026-03-26) Completed T052 test: CE edition: hasFeature always returns true regardless of tier
- (2026-03-26) Completed T053 test: MenuItem interface accepts optional requiredFeature field
- (2026-03-26) Completed T054 test: Sidebar hides Extensions item for Solo tier
- (2026-03-26) Completed T055 test: Sidebar hides Workflow Editor subitem for Solo tier
- (2026-03-26) Completed T056 test: Sidebar shows Extensions item for Pro tier
- (2026-03-26) Completed T057 test: Sidebar shows all items for Premium tier
- (2026-03-26) Completed T058 test: Sidebar items without requiredFeature are always shown
- (2026-03-26) Completed T059 test: Settings Integrations tab shows FeatureUpgradeNotice for Solo tier
- (2026-03-26) Completed T060 test: Settings Extensions tab shows FeatureUpgradeNotice for Solo tier
- (2026-03-26) Completed T061 test: Settings Email tab shows FeatureUpgradeNotice for Solo tier
- (2026-03-26) Completed T062 test: Settings tabs remain visible and clickable even when gated
- (2026-03-26) Completed T063 test: Settings Integrations tab shows normal content for Pro tier
- (2026-03-26) Completed T064 test: Settings sidebar items are never filtered by tier (all visible)
- (2026-03-26) Completed T065 test:
assertTierAccess(TIER_FEATURES.INTEGRATIONS)throwsTierAccessErrorfor Solo tenants withrequiredTier='pro' - (2026-03-26) Completed T066 test:
assertTierAccess(TIER_FEATURES.INTEGRATIONS)succeeds for Pro tenants without changing Premium-only gating - (2026-03-26) Completed T067 test:
assertTierAccess(TIER_FEATURES.EXTENSIONS)throwsTierAccessErrorfor Solo tenants so extension API denials match the route guard - (2026-03-26) Completed T107 test: npm run build:shared compiles with all new types/features/add-ons
- (2026-03-26) Completed F024 feature: Add STRIPE_AI_ADDON_PRICE_ID and STRIPE_AI_ADDON_ANNUAL_PRICE_ID env vars to Stripe config
- (2026-03-26) Completed F067 feature: Extend
FEATURE_DISPLAY_NAMESinAccountManagementto cover all new Pro-gated Solo exclusions, which also clears the remainingee/serverTypeScript exhaustiveness error - (2026-03-26) Completed F068 feature: Add Solo-specific account messaging and adjust the empty-feature-state copy so Solo tenants see upgrade guidance instead of the old Pro-only text
- (2026-03-26) Completed F069 feature: Extend the existing tier-upgrade confirmation flow so Solo tenants get an
Upgrade to Procard and Pro/Premium upgrades share the same preview dialog - (2026-03-26) Completed F070 feature: Surface a
Downgrade to Soloconfirmation path for Pro tenants whenlicenseInfo.active_licenses === 1, matching the Stripe-side one-user validation - (2026-03-26) Completed F071 feature: Add a dedicated
AI Assistantaccount-management card with embedded checkout start flow so add-on purchasing is separate from tier upgrades and available on any tier - (2026-03-26) Completed F072 feature: Render the active AI Assistant state with an
Activebadge, usage copy, and a cancel confirmation flow when the tenant already has the add-on - (2026-03-26) Completed F073 feature:
upgradeTierAction()already accepted'pro', and the Solo account page now drives it through the shared upgrade preview/confirm flow for Solo -> Pro upgrades - (2026-03-26) Completed F074 feature: Add
downgradeTierAction()as the EE server-action wrapper over Stripe's Pro -> Solo downgrade path, preserving the active-user-count validation fromStripeService.downgradeTier() - (2026-03-26) Completed F075 feature: New Solo subscription checkouts now pass
trialDays=7intocreateLicenseCheckoutSession()when no active subscription exists, so only first-time Solo purchases get the free trial by default - (2026-03-26) Completed F076 feature: Add a self-service Solo -> Pro trial flow backed by Stripe subscription metadata (
solo_pro_trial_*), TierContext effective-tier checks, and account-page trial UI so established Solo customers get Pro feature access without changing billing immediately - (2026-03-26) Decision: Implement Solo -> Pro trial as 30 days. The PRD leaves 15 vs 30 days open; 30 days matches the existing Premium trial pattern and is isolated to a single constant (
SOLO_PRO_TRIAL_DAYS) for later adjustment. - (2026-03-26) Completed F077 feature: Block Solo -> Pro trial stacking by rejecting
stripe_subscriptions.status === 'trialing'instartSoloProTrial()and hiding the CTA unlesssubscriptionStatus === 'active' - (2026-03-26) Completed F078 feature: Keep the tenant's persisted plan as
soloduring the Pro trial and only elevate to an effective Pro tier whilesolo_pro_trial_endis still in the future, so expiry automatically reverts feature access back to Solo if the user never converts - (2026-03-26) Completed F079 feature: Add a
Try Pro freeCTA plus confirmation dialog to Account Management for established Solo customers, and hide it during the initial Solo trial or once a Pro trial is already active - (2026-03-26) Completed T068 test: assertAddOnAccess(AI_ASSISTANT) throws for tenant without AI add-on
- (2026-03-26) Completed T069 test: assertAddOnAccess(AI_ASSISTANT) passes for Solo tenant with AI add-on
- (2026-03-26) Completed T070 test: exchangeOttForSession rejects Solo tenant with 403 status
- (2026-03-26) Completed T071 test: exchangeOttForSession 403 response includes upgrade message
- (2026-03-26) Completed T072 test: exchangeOttForSession succeeds for Pro tenant
- (2026-03-26) Completed T073 test: exchangeOttForSession succeeds for Premium tenant
- (2026-03-26) Completed T074 test: stripeTierMapping maps 'alga-psa-solo' to 'solo'
- (2026-03-26) Completed T075 test: getTierPriceIds('solo') returns basePriceId with userPriceId: null
- (2026-03-26) Completed T076 test: getTierPriceIds('pro') still returns both basePriceId and userPriceId
- (2026-03-26) Completed T077 test: Solo checkout creates session with single line item (base price only)
- (2026-03-26) Completed T078 test: Pro checkout still creates session with two line items (base + per-user)
- (2026-03-26) Completed T079 test: handleCheckoutCompleted sets licensed_user_count=1 for Solo tier
- (2026-03-26) Completed T080 test: handleSubscriptionUpdated sets licensed_user_count=1 for Solo tier
- (2026-03-26) Completed T081 test: upgradeTier Solo->Pro adds per-user line item to subscription
- (2026-03-26) Completed T082 test: upgradeTier Solo->Pro swaps base price from Solo to Pro
- (2026-03-26) Completed T083 test: downgradeTier Pro->Solo succeeds when active user count = 1
- (2026-03-26) Completed T084 test: downgradeTier Pro->Solo fails when active user count > 1
- (2026-03-26) Completed T085 test: downgradeTier Pro->Solo removes per-user line item
- (2026-03-26) Completed T086 test: downgradeTier Pro->Solo swaps base price from Pro to Solo
- (2026-03-26) Completed T087 test: purchaseAddOn(AI_ASSISTANT) creates checkout for AI add-on subscription item
- (2026-03-26) Completed T088 test: cancelAddOn(AI_ASSISTANT) removes AI line item from subscription
- (2026-03-26) Completed T089 test: Stripe webhook inserts tenant_addons row when AI subscription activates
- (2026-03-26) Completed T090 test: Stripe webhook deactivates tenant_addons row when AI subscription is cancelled
- (2026-03-26) Completed T091 test: addUser action rejects when plan='solo' and used >= 1 internal users
- (2026-03-26) Completed T092 test: addUser action returns correct error message for Solo limit
- (2026-03-26) Completed T093 test: addUser action allows adding first user on Solo plan
- (2026-03-26) Completed T094 test: addUser action allows adding users on Pro plan without Solo limit
- (2026-03-26) Completed T095 test: Add User button is disabled when isSolo in user management UI
- (2026-03-26) Completed T096 test: Add User button shows upgrade CTA when disabled for Solo
- (2026-03-26) Completed T097 test: FEATURE_DISPLAY_NAMES has entries for all 8 new tier features
- (2026-03-26) Completed T098 test: AccountManagement shows Solo-specific messaging for solo tier
- (2026-03-26) Completed T099 test: AccountManagement shows 'Upgrade to Pro' card for Solo tenants
- (2026-03-26) Completed T100 test: AccountManagement shows 'Downgrade to Solo' for Pro tenants with 1 user
- (2026-03-26) Completed T101 test: AccountManagement hides 'Downgrade to Solo' for Pro tenants with 2+ users
- (2026-03-26) Completed T102 test: AccountManagement shows 'Add AI Assistant' card when add-on not active
- (2026-03-26) Completed T103 test: AccountManagement shows 'AI Assistant (active)' when add-on is active
- (2026-03-26) Completed T104 test: AI add-on card appears for Solo tier (not just Pro/Premium)
- (2026-03-26) Completed T105 test: upgradeTierAction supports Solo -> Pro flow
- (2026-03-26) Completed T106 test: downgradeTierAction validates user count before downgrading
- (2026-03-26) Completed T108 test: Solo checkout session includes trialDays=7
- (2026-03-26) Completed T109 test: Pro checkout does not include trial unless explicitly requested
- (2026-03-26) Completed T110 test: Solo->Pro trial activates for established Solo customer (subscriptionStatus='active', past trial)
- (2026-03-26) Completed T111 test: Solo->Pro trial blocked when Solo customer is still in their 7-day trial (subscriptionStatus='trialing')
- (2026-03-26) Completed T112 test: Solo->Pro trial grants access to all Pro tier features during trial period
- (2026-03-26) Completed T113 test: Solo->Pro trial reverts to Solo when trial expires without conversion
- (2026-03-26) Completed T114 test: 'Try Pro free' CTA shown for established Solo customers in AccountManagement
- (2026-03-26) Completed T115 test: 'Try Pro free' CTA hidden when Solo customer is still in their trial