Hermes 284313f908
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
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

30 KiB
Raw Blame History

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_ONS enum which was previously scaffolded but empty.
  • (2026-03-26) Mobile app access is gated to Pro+ via the MOBILE_ACCESS tier feature. Block happens at OTT exchange in mobileAuthService.ts, so Solo users never receive API tokens.
  • (2026-03-26) No DB migration needed — tenants.plan is already an unconstrained text column, tenant_addons table already exists (migration 20260303100000).
  • (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 1530 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 with requiredFeature are hidden entirely.

Discoveries / Constraints

  • (2026-03-26) Current tier system: only pro and premium exist. 3 features gated at premium (ENTRA_SYNC, CIPP, TEAMS_INTEGRATION). No rank system — plan uses map lookup.
  • (2026-03-26) ADD_ONS enum is empty, tenantHasAddOn() exists but is unused at runtime. tenant_addons table exists in DB.
  • (2026-03-26) TierContextValue has: tier, isPro, isPremium, hasFeature, trial state, premium trial state, subscription status. No isSolo, no addOns.
  • (2026-03-26) MenuItem interface has no requiredFeature field. 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 allow userPriceId: null for Solo.
  • (2026-03-26) Upgrade flow gated behind 'tier-upgrade-flow' feature flag in AccountManagement.
  • (2026-03-26) Root package.json does not define npm run test:unit; focused validation currently uses package-local vitest commands (for example from server/).
  • (2026-03-26) TierAccessError now carries statusCode=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/features alias and use local relative imports instead.
  • (2026-03-26) npx tsc -p ee/server/tsconfig.json --noEmit is currently blocked by ee/server/src/components/settings/account/AccountManagement.tsx missing display names for the newly-added tier features; that aligns with still-pending F067 rather than the RMM gating changes.
  • (2026-03-26) Some DefaultLayout Vitest suites currently fail during module resolution for unrelated @alga-psa/core/context/DocumentsCrossFeatureContext imports pulled in by package components; narrow layout validation is currently more reliable via targeted sidebar tests.
  • (2026-03-26) mobileAuth.test.ts needed an explicit partial @alga-psa/auth mock once mobile tier gating was added; otherwise ApiKeyService resolved as missing under Vitest's mocked module graph.
  • (2026-03-26) After adding Solo Stripe price support, npx tsc -p ee/server/tsconfig.json --noEmit still only fails on the pre-existing F067 FEATURE_DISPLAY_NAMES gap in AccountManagement.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())
  • 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 1530 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 INTEGRATIONS tier 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 EXTENSIONS tier 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_EMAIL tier 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 SSO tier 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_ASSETS tier 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) into DefaultLayout and the EE RightSidebar so 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_ACCESS tier enforcement inside exchangeOttForSession() 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 -> solo to 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 = 1 for 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.updated handling so renewals and later subscription updates keep licensed_user_count = 1 for 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() for AI_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() for AI_ASSISTANT, cancelling the stored add-on Stripe subscription referenced from tenant_addons metadata instead of touching the core license subscription.
  • (2026-03-26) Completed F061 feature: Added add-on-aware Stripe webhook handling that activates tenant_addons rows 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_addons rows 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.example so 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) throws TierAccessError for Solo tenants with requiredTier='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) throws TierAccessError for 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_NAMES in AccountManagement to cover all new Pro-gated Solo exclusions, which also clears the remaining ee/server TypeScript 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 Pro card and Pro/Premium upgrades share the same preview dialog
  • (2026-03-26) Completed F070 feature: Surface a Downgrade to Solo confirmation path for Pro tenants when licenseInfo.active_licenses === 1, matching the Stripe-side one-user validation
  • (2026-03-26) Completed F071 feature: Add a dedicated AI Assistant account-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 Active badge, 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 from StripeService.downgradeTier()
  • (2026-03-26) Completed F075 feature: New Solo subscription checkouts now pass trialDays=7 into createLicenseCheckoutSession() 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' in startSoloProTrial() and hiding the CTA unless subscriptionStatus === 'active'
  • (2026-03-26) Completed F078 feature: Keep the tenant's persisted plan as solo during the Pro trial and only elevate to an effective Pro tier while solo_pro_trial_end is 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 free CTA 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