Skip to content

feat: Add ability to purchase provider coding plans#3521

Open
jeanduplessis wants to merge 8 commits into
mainfrom
coding-plans
Open

feat: Add ability to purchase provider coding plans#3521
jeanduplessis wants to merge 8 commits into
mainfrom
coding-plans

Conversation

@jeanduplessis
Copy link
Copy Markdown
Contributor

@jeanduplessis jeanduplessis commented May 27, 2026

Summary

Adds managed MiniMax Token Plan Plus subscriptions funded by Kilo Credits and surfaced through Subscription Center and admin operations.

Why this change is needed

Kilo needs a first Coding Plans offering that can sell provider-plan access without redirecting subscribers to an upstream checkout or exposing managed provider credentials. The pilot also needs explicit billing, cleanup, and manual-revocation rules because ordinary MiniMax BYOK controls routing while Kilo remains responsible for subscription lifecycle and issued credentials.

How this is addressed

  • Defines Coding Plans and Subscription Center contracts for managed credentials, credit billing, sold-out notification intents, BYOK behavior, and operator revocation workflow.
  • Adds persistent inventory, subscription, charged-term, and availability-intent records for MiniMax Token Plan Plus.
  • Activates subscriptions atomically by charging credits, claiming encrypted inventory, and installing an ordinary personal MiniMax BYOK configuration; renewal, grace-period termination, cancellation, and account deletion preserve cleanup and revocation invariants.
  • Adds customer purchase, status, billing history, cancellation, sold-out notification, and BYOK warning surfaces within Subscription Center.
  • Adds admin inventory upload, credential validation, manual revocation operations, and scheduled billing lifecycle processing.
  • Gates the customer-facing Coding Plans UI on /subscriptions behind the CODING_PLANS_PURCHASE_ENABLED server flag (hidden by default) so the feature can ship dark; backend routers, billing lifecycle cron, and admin operations remain active regardless of the flag.

Human Verification

  • Extensive end-to-end testing and validation done locally to confirm user and admin flows and confirm MiniMax credential integration works.

Reviewer Notes

Human Reviewer Flags

  • Introduces .specs/coding-plans.md and extends Subscription Center contract for first managed provider-plan pilot.
  • Uses ordinary personal MiniMax BYOK configuration for traffic while separating routing actions from Coding Plans billing and credential revocation ownership.
  • Chooses manual MiniMax credential revocation for initial release; admin workflow may reveal credential only for pending or failed revocation work.
  • Customer-facing purchase UI ships dark: the Coding Plans tab is hidden unless CODING_PLANS_PURCHASE_ENABLED=true, so merging does not expose Coding Plans to users until the flag is enabled in the target environment.

Code Reviewer Agent

Code Reviewer Notes
  • Review atomic purchase and renewal paths carefully: credit debit, inventory assignment, installed BYOK entry, subscription state, and term recording must remain all-or-nothing.
  • Review lifecycle cleanup boundaries: user-modified/replaced BYOK credentials must survive cancellation, failed renewal, administrative termination, and soft deletion.
  • Review sensitive-data handling around encrypted inventory, validation failures, admin credential reveal, and sanitized revocation failures.
  • Review new database migration and hourly billing cron together with tRPC ownership/admin authorization boundaries.

@jeanduplessis jeanduplessis marked this pull request as ready for review May 28, 2026 13:06
Comment thread apps/web/src/lib/coding-plans/index.ts Outdated
Comment thread apps/web/src/lib/coding-plans/billing-lifecycle-cron.ts
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented May 28, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of 2 new commits found no issues: PLAN.md was removed from the PR as intended, and a stray duplicate .next/dev/dev/types/**/*.ts include path was cleaned from apps/web/tsconfig.json. All previously flagged issues remain resolved.

Resolved since previous review
  • apps/web/src/lib/coding-plans/index.ts:392 — Sequential live-API validation before DB insert replaced with pLimit(10)-bounded Promise.all. Resolved.
  • billing-lifecycle-cron.ts:244auto_top_up_triggered counter semantics documented with explicit comment. Resolved.
  • revealCredentialForManualRevocation — removed; raw credentials no longer exposed. Resolved.
Other Observations (not in diff)

Migration trailing newline: packages/db/src/migrations/0149_white_triathlon.sql ends without a trailing newline. Cosmetically inconsistent with other migration files; not blocking.

Offset-based pagination: getBillingHistory uses an integer offset as cursor. Works at current scale; may produce inconsistent pages if rows are inserted between paginated requests.

Files Reviewed (66 files)
  • .specs/coding-plans.md
  • .specs/subscription-center.md
  • apps/web/public/logos/minimax.svg
  • apps/web/src/app/(app)/components/PersonalAppSidebar.tsx
  • apps/web/src/app/(app)/subscriptions/coding-plans/[subscriptionId]/page.tsx
  • apps/web/src/app/(app)/subscriptions/page.tsx
  • apps/web/src/app/admin/coding-plans/CodingPlansOperationsContent.tsx
  • apps/web/src/app/admin/coding-plans/page.tsx
  • apps/web/src/app/admin/components/AppSidebar.tsx
  • apps/web/src/app/api/cron/coding-plans-billing/route.ts
  • apps/web/src/components/organizations/byok/BYOKKeysManager.tsx
  • apps/web/src/components/profile/kilo-pass/KiloPassSubscribeCard.tsx
  • apps/web/src/components/profile/kilo-pass/KiloPassTierCard.tsx
  • apps/web/src/components/subscriptions/AvailableProductCard.tsx
  • apps/web/src/components/subscriptions/BillingHistoryTable.tsx
  • apps/web/src/components/subscriptions/DetailPageHeader.tsx
  • apps/web/src/components/subscriptions/PersonalSubscriptions.tsx
  • apps/web/src/components/subscriptions/SubscriptionCard.tsx
  • apps/web/src/components/subscriptions/SubscriptionGroup.tsx
  • apps/web/src/components/subscriptions/SubscriptionStatusBadge.tsx
  • apps/web/src/components/subscriptions/TerminalToggle.tsx
  • apps/web/src/components/subscriptions/coding-plans/CodingPlanDetail.tsx
  • apps/web/src/components/subscriptions/coding-plans/CodingPlansGroup.tsx
  • apps/web/src/components/subscriptions/coding-plans/MiniMaxPlanIcon.tsx
  • apps/web/src/components/subscriptions/helpers.test.ts
  • apps/web/src/components/subscriptions/helpers.ts
  • apps/web/src/components/subscriptions/kilo-pass/KiloPassDetail.tsx
  • apps/web/src/components/subscriptions/kilo-pass/KiloPassGroup.tsx
  • apps/web/src/components/subscriptions/kiloclaw/KiloClawDetail.tsx
  • apps/web/src/components/subscriptions/kiloclaw/KiloClawGroup.tsx
  • apps/web/src/components/subscriptions/kiloclaw/KiloClawSubscribeCard.tsx
  • apps/web/src/components/ui/alert.tsx
  • apps/web/src/lib/ai-gateway/byok/coding-plan-entitlement.test.ts
  • apps/web/src/lib/ai-gateway/byok/index.ts
  • apps/web/src/lib/ai-gateway/byok/types.ts
  • apps/web/src/lib/coding-plans/billing-lifecycle-cron.ts
  • apps/web/src/lib/coding-plans/billing-lifecycle-cron.test.ts
  • apps/web/src/lib/coding-plans/index.ts — fixed (p-limit concurrency bound)
  • apps/web/src/lib/coding-plans/index.test.ts
  • apps/web/src/lib/coding-plans/inventory-validation.ts
  • apps/web/src/lib/coding-plans/inventory-validation.test.ts
  • apps/web/src/lib/coding-plans/pricing.ts
  • apps/web/src/lib/coding-plans/revocation.ts
  • apps/web/src/lib/config.server.ts
  • apps/web/src/lib/constants.ts
  • apps/web/src/lib/user/index.test.ts
  • apps/web/src/lib/user/index.ts
  • apps/web/src/routers/byok-router.test.ts
  • apps/web/src/routers/byok-router.ts
  • apps/web/src/routers/coding-plans-router.test.ts
  • apps/web/src/routers/coding-plans-router.ts
  • apps/web/src/routers/root-router.ts
  • apps/web/tsconfig.json — stray duplicate include removed
  • apps/web/vercel.json
  • dev/seed/coding-plans/available-credentials.ts
  • dev/seed/coding-plans/occupied-minimax-byok.ts
  • packages/db/src/migrations/0149_white_triathlon.sql
  • packages/db/src/migrations/meta/0149_snapshot.json
  • packages/db/src/migrations/meta/_journal.json
  • packages/db/src/schema-types.ts
  • packages/db/src/schema.ts
  • PLAN.md — removed from PR

Reviewed by claude-4.6-sonnet-20260217 · 354,261 tokens

Review guidance: REVIEW.md from base branch main

- Add upstream_plan_id to the coding plan key inventory (migration 0148)
  and parse managed MiniMax credentials in <api key>::<plan id> format.
- Clear encrypted_api_key when a credential enters revocation_pending,
  including on GDPR soft-delete, so revoked keys are not retained.
- Gate the Coding Plans tab on /subscriptions behind
  CODING_PLANS_PURCHASE_ENABLED; hidden by default for a dark launch.
- Update billing lifecycle, revocation, inventory validation, the
  coding-plans router, and admin operations surfaces accordingly.
The counter intentionally measures triggered auto-top-up attempts (not
successful charges), matching spec rule 5.5. Document that a best-effort
maybePerformAutoTopUp failure still counts as a triggered attempt.
Comment thread apps/web/src/lib/coding-plans/index.ts Outdated
Replace the serial validation loop in uploadKeysToInventory with a
p-limit(10) fan-out so large inventory uploads finish well within the
request budget without firing one unbounded burst at the MiniMax API.
Behavior is unchanged: malformed entries fail before validation, any
invalid credential aborts the upload, and nothing is inserted on failure.
PLAN.md was a development planning artifact and does not belong in the
repo root (the designated location for plans is .plans/). Drop it so it
is not part of this PR's changes.
This duplicated glob with a doubled dev/dev segment was auto-written into
tsconfig.json by the Next dev server and committed by mistake. It points
at a nonexistent directory and duplicates the existing .next/dev/types
entry, so removing it restores tsconfig to match main.
summary: CodingPlanCronSummary
): Promise<void> {
const rows = await database
.select({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here. Probably want to add a limit just in case

}
}

async function processRenewal(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There's a lot of implicit business logic in this function. It would be nice to have some explainer comments walking through the various scenarios

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants