Skip to content

Introduce Solo plan: rename Starter → Solo in inspector UI#1996

Merged
chelojimenez merged 2 commits intomainfrom
claude/vibrant-swirles-0ad742
May 2, 2026
Merged

Introduce Solo plan: rename Starter → Solo in inspector UI#1996
chelojimenez merged 2 commits intomainfrom
claude/vibrant-swirles-0ad742

Conversation

@chelojimenez
Copy link
Copy Markdown
Contributor

@chelojimenez chelojimenez commented May 2, 2026

Summary

  • Widens all OrganizationPlan type unions to include \"solo\" alongside \"starter\" so the inspector handles both the current backend (returning \"starter\") and the incoming backend PR that normalizes everything to \"solo\".
  • formatPlanName(\"starter\" | \"solo\")\"Solo\" so trial users and subscribers see the correct display name regardless of which value the backend currently returns.
  • getAnnualDiscountPercent reads plans.solo ?? plans.starter to work against both catalog shapes (pre and post backend deploy).
  • All plan-tier type signatures (CheckoutPlanTier, PLAN_RANK, hook params, component props) expanded to accept \"solo\".
  • Downgrade confirmation flow now sets targetPlan: \"solo\" (canonical) and passes it dynamically instead of the hardcoded \"starter\".
  • Stale pricing copy (\"$61×12 vs $588\", \"Starter subscription in place\", \"unused Starter time\") updated to Solo.

Why

Backend PR MCPJam/mcpjam-backend#182 introduces solo as the canonical replacement for starter. The rollout order is:

  1. Land and deploy this PR first — inspector now tolerates both \"starter\" and \"solo\" from any backend.
  2. Land and deploy backend PR Add docker pulls to home page #182 — backend starts returning \"solo\" (normalized), keeps plans.starter alias in catalog.
  3. Run migrateStarterToSolo migration against dev then prod (rewrites 2,255 trialPlan: \"starter\" rows).
  4. Cleanup PR: drop \"starter\" literals from backend schema/validators and remove the plans.starter alias.

Reviewer notes

  • PLAN_ORDER in OrganizationBillingSection.tsx intentionally stays as [\"free\", \"starter\", \"team\", \"enterprise\"] for now — the backend creates a plans.starter alias that mirrors plans.solo, so the compare table continues to read planCatalog.plans.starter through the transition.
  • formatPlanName(\"starter\") now returns \"Solo\" (not \"Starter\"). This is intentional — they are the same product.
  • All 3401 tests pass.

Test plan

  • Deploy to staging and verify trial org billing panel shows "Solo Trial"
  • Verify compare table column still renders correctly (reads from plans.starter alias)
  • Verify checkout deep links with ?plan=solo are accepted (new) and ?plan=starter still work (backward compat)
  • Verify downgrade from Team to Solo shows "Downgrade to Solo?" dialog and schedules correctly

🤖 Generated with Claude Code


Note

Medium Risk
Touches billing plan-selection and confirmation flows to accept a new solo tier; mistakes could mis-route upgrades/downgrades or display incorrect plan state, but changes are mostly type/copy and guard logic updates.

Overview
Updates billing-related UI and types to treat Starter → Solo as the same tier, adding solo across OrganizationPlan/checkout unions, rank/compare-table logic, and deep-link validation while keeping backward compatibility with starter.

Adjusts plan-change behavior and messaging: upgrade confirmation now triggers for starter/solo → Team, scheduled-change/downgrade dialogs render dynamic Solo copy, and discount/price formatting reads plans.solo ?? plans.starter. Tests are updated to expect Solo labels throughout.

Reviewed by Cursor Bugbot for commit d40d4c3. Bugbot is set up for automated code reviews on this repo. Configure here.

Widens all OrganizationPlan type unions to include "solo" alongside
"starter" so the inspector tolerates backend responses from both the
current backend (plan: "starter") and the incoming backend PR that
normalizes everything to "solo".

Key changes:
- formatPlanName("starter" | "solo") → "Solo" so trial users and
  subscription holders see the correct display name regardless of which
  value the backend currently returns.
- getAnnualDiscountPercent reads plans.solo ?? plans.starter so it
  works against both catalog shapes.
- All plan-tier type signatures (CheckoutPlanTier, PLAN_RANK, hook
  params, component props) expanded to accept "solo".
- Downgrade confirmation flow now sets targetPlan: "solo" (canonical)
  and passes it dynamically instead of hardcoding "starter".
- Stale pricing copy ("$61×12 vs $588", "Starter subscription in
  place", "unused Starter time") updated to Solo.
- All 3401 tests updated and passing.

This PR is safe to deploy before the backend Solo PR; it continues to
work against the current catalog (plans.starter only). The cleanup
PR dropping the plans.starter alias and "starter" literals from the
backend schema should follow after both PRs are live and the
migrateStarterToSolo migration has run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label May 2, 2026
@chelojimenez chelojimenez temporarily deployed to preview-pr-1996 May 2, 2026 02:01 — with GitHub Actions Inactive
@chelojimenez
Copy link
Copy Markdown
Contributor Author

chelojimenez commented May 2, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@dosubot dosubot Bot added the enhancement New feature or request label May 2, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 2, 2026

Internal preview

Preview URL: https://mcp-inspector-pr-1996.up.railway.app
Deployed commit: b43fcf4
PR head commit: d40d4c3
Backend target: staging fallback.
Health: ❌ Convex unreachable — see upsert-preview job logs (staging may need convex deploy)
Access is employee-only in non-production environments.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6bf72f96d2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 130 to 132
if (isHigherTier && entry.isSelfServe) {
if (plan !== "starter" && plan !== "team") {
if (plan !== "starter" && plan !== "solo" && plan !== "team") {
return { label: "Unavailable", disabled: true, variant: "outline" };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add Solo to plan-rank ordering before enabling Solo plan state

Now that OrganizationPlan includes "solo", billing states can carry currentPlan === "solo", but OrganizationBillingSection still derives rank from PLAN_ORDER that does not include solo, so getPlanRank("solo") becomes -1. That makes the CTA logic in this branch classify the Starter/Solo column as an upgrade instead of current/equivalent, which breaks compare-table actions and messaging once backend billing status starts returning solo.

Useful? React with 👍 / 👎.

Comment on lines 802 to 804
setPendingDowngradeConfirmation({
targetPlan: "starter",
targetPlan: "solo",
targetBillingInterval,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve starter downgrade target until backend accepts solo

This forces Team downgrades to set targetPlan: "solo" regardless of which low-tier alias was selected. In the rollout state where inspector ships before the backend solo mutation support, this path sends solo to startPlanChange and can fail instead of scheduling a downgrade. Keep sending starter (or runtime-map based on backend capability) until the server-side mutation accepts both tiers.

Useful? React with 👍 / 👎.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 000c9601-1f64-40ca-b1cf-0126a048bdc6

📥 Commits

Reviewing files that changed from the base of the PR and between 6bf72f9 and d40d4c3.

📒 Files selected for processing (3)
  • mcpjam-inspector/client/src/components/OrganizationsTab.tsx
  • mcpjam-inspector/client/src/components/__tests__/OrganizationsTab.billing.test.tsx
  • mcpjam-inspector/client/src/components/organization/OrganizationBillingSection.tsx
✅ Files skipped from review due to trivial changes (1)
  • mcpjam-inspector/client/src/components/tests/OrganizationsTab.billing.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • mcpjam-inspector/client/src/components/organization/OrganizationBillingSection.tsx
  • mcpjam-inspector/client/src/components/OrganizationsTab.tsx

Walkthrough

This pull request adds a new "solo" billing tier across the client: UI text/display names change from "Starter" to "Solo"; type unions (plans, checkout tiers, scheduled-plan snapshots) and component props are expanded to include "solo"; plan-ranking, pricing, and seat-limit logic treat "solo" as equivalent to "starter"; billing flows (upgrades, downgrades, confirmations, cancellation handling) and related dialogs now accept and reference "solo"; tests and mocked plan catalogs are updated to use the "Solo" display name.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

- getPlanRank: normalize "solo" → "starter" so currentPlan "solo" maps
  to rank 1 instead of -1; fixes "Current plan" CTA once backend starts
  returning plan: "solo".
- isCurrentPlan: treat "solo" and "starter" as equivalent so the Solo
  column shows "Current plan" rather than "Upgrade" for solo subscribers.
- Downgrade targetPlan/API call: revert from "solo" back to "starter" so
  the scheduled downgrade request works on the current backend (which
  only accepts "starter" until backend PR #182 lands). formatPlanName
  still renders it as "Solo" in the dialog.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d40d4c3445

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


if (
(currentPlan !== "starter" && currentPlan !== "team") ||
(currentPlan !== "starter" && currentPlan !== "solo" && currentPlan !== "team") ||
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize starter/solo before scheduled-change comparisons

By allowing currentPlan === "solo" into getScheduledBillingChangeCancellationState, this path now runs during the starter→solo migration, but the later checks still compare raw identifiers (scheduledPlan === currentPlan). If billing data mixes aliases (e.g., plan: "solo" with stripeScheduledPlan: "starter" for the same tier), the UI incorrectly treats it as a real plan change and shows a cancellation CTA/message for a non-change. Normalize starter/solo to one canonical value before equality checks in this helper.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d40d4c3. Configure here.

};

export type CheckoutPlanTier = "starter" | "team";
export type CheckoutPlanTier = "starter" | "solo" | "team";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checkout guard doesn't treat solo and starter as equivalent

Medium Severity

The guardCheckoutIntentAgainstBillingStatus function uses strict equality (effectivePlan === requestedTier) for the "already_on" check. With solo: 1 and starter: 1 now both in PLAN_RANK, a user whose effectivePlan is "solo" requesting "starter" (or vice versa) bypasses both the equality check and the > rank check, returning proceed: true. This allows an unnecessary checkout flow for an equivalent plan via deep link (e.g. ?plan=solo when already on "starter").

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d40d4c3. Configure here.

if (
currentPlan === "team" &&
targetPlan === "starter" &&
(targetPlan === "starter" || targetPlan === "solo") &&
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Downgrade flow hardcodes "starter" instead of preserving targetPlan

Medium Severity

The condition at line 799 was widened to accept targetPlan === "solo", but line 803 still hardcodes targetPlan: "starter" into the pending confirmation state. This silently discards an incoming "solo" value. Additionally, line 861 uses as "starter" type assertion on the supposedly-dynamic pendingDowngradeConfirmation.targetPlan, masking any future type mismatch. The PendingDowngradeConfirmation interface also wasn't updated to include "solo" in its targetPlan union.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d40d4c3. Configure here.

@chelojimenez chelojimenez merged commit 72649f3 into main May 2, 2026
12 checks passed
@chelojimenez chelojimenez deleted the claude/vibrant-swirles-0ad742 branch May 2, 2026 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant