Skip to content

Conversation

@shige
Copy link
Member

@shige shige commented Nov 28, 2025

User description

Summary

  • Update CurrentTeam type to use v2 stripePricingPlanSubscriptionHistories instead of v1 subscriptionHistories
  • Remove the legacy subscription_histories table definition from schema
  • Add migration to drop the subscription_histories table

This is part of the Stripe v2 migration. All subscription history is now tracked in stripe_pricing_plan_subscription_histories table, making the v1 table obsolete.

Related Issue

Part of #2061

Testing

  • Verify type checking passes
  • Verify migration runs successfully in staging

PR Type

Enhancement


Description

  • Remove legacy v1 subscription_histories table from schema

  • Update CurrentTeam type to use v2 stripePricingPlanSubscriptionHistories

  • Add database migration to drop obsolete subscription_histories table

  • Remove unused Stripe type import from schema file


Diagram Walkthrough

flowchart LR
  A["v1 subscriptionHistories"] -->|"migrate to"| B["v2 stripePricingPlanSubscriptionHistories"]
  C["schema.ts"] -->|"remove table definition"| D["migration 0070"]
  E["CurrentTeam type"] -->|"update reference"| B
Loading

File Walkthrough

Relevant files
Schema changes
schema.ts
Remove v1 subscription_histories table definition               

apps/studio.giselles.ai/db/schema.ts

  • Removed subscriptionHistories table definition with all associated
    fields and indexes
  • Removed subscriptionHistoryRelations relation definition
  • Removed unused Stripe type import from stripe package
  • Kept v2 stripePricingPlanSubscriptionHistories table intact
+0/-67   
Type migration
types.ts
Update CurrentTeam type to use v2 subscription history     

apps/studio.giselles.ai/services/teams/types.ts

  • Updated import to use stripePricingPlanSubscriptionHistories instead
    of subscriptionHistories
  • Updated CurrentTeam.activeSubscriptionId type to reference v2 table's
    subscription ID type
  • Changed from subscriptionHistories.$inferInsert.id to
    stripePricingPlanSubscriptionHistories.$inferInsert.id
+4/-2     
Database migration
0070_even_living_lightning.sql
Add migration to drop v1 subscription_histories table       

apps/studio.giselles.ai/db/migrate/0070_even_living_lightning.sql

  • Added migration to drop the legacy subscription_histories table with
    CASCADE option
  • Ensures all dependent objects are removed automatically
+1/-0     
Migration metadata
0070_snapshot.json
Update migration snapshot metadata                                             

apps/studio.giselles.ai/db/migrate/meta/0070_snapshot.json

  • Generated migration snapshot reflecting current schema state after
    table removal
  • Documents all remaining tables and their structure
  • Confirms subscription_histories table is no longer present
+3433/-0
Migration journal
_journal.json
Record new migration in journal                                                   

apps/studio.giselles.ai/db/migrate/meta/_journal.json

  • Added new migration entry for 0070_even_living_lightning
  • Records migration timestamp and version information
+7/-0     

Note

Drops the deprecated subscription_histories table and switches CurrentTeam.activeSubscriptionId to use v2 stripe_pricing_plan_subscription_histories.

  • Database:
    • Drop legacy subscription_histories table via migration 0070_even_living_lightning and update migration journal/snapshot.
    • Remove subscription_histories table and its relations from apps/studio.giselles.ai/db/schema.ts.
  • Types:
    • Update CurrentTeam.activeSubscriptionId to reference stripe_pricing_plan_subscription_histories (v2).

Written by Cursor Bugbot for commit 67ab7b0. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • Chores
    • Updated database schema to restructure subscription data management infrastructure.

✏️ Tip: You can customize this high-level summary in your review settings.

Update CurrentTeam type to reference stripePricingPlanSubscriptionHistories instead of subscriptionHistories for activeSubscriptionId type inference.
Drop the legacy subscription_histories table that was used for v1 Stripe API.
All subscription history is now tracked in stripe_pricing_plan_subscription_histories.
@shige shige requested a review from Copilot November 28, 2025 08:27
@shige shige self-assigned this Nov 28, 2025
@changeset-bot
Copy link

changeset-bot bot commented Nov 28, 2025

⚠️ No Changeset found

Latest commit: 67ab7b0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

💥 An error occurred when fetching the changed packages and changesets in this PR
Some errors occurred when validating the changesets config:
The package or glob expression "giselles-ai" is specified in the `ignore` option but it is not found in the project. You may have misspelled the package name or provided an invalid glob expression. Note that glob expressions must be defined according to https://www.npmjs.com/package/micromatch.

@giselles-ai
Copy link

giselles-ai bot commented Nov 28, 2025

Finished running flow.

Step 1
🟢
On Pull Request OpenedStatus: Success Updated: Nov 28, 2025 8:27am
Step 2
🟢
Manual QAStatus: Success Updated: Nov 28, 2025 8:29am
🟢
Prompt for AI AgentsStatus: Success Updated: Nov 28, 2025 8:29am
Step 3
🟢
Create a Comment for PRStatus: Success Updated: Nov 28, 2025 8:32am
Step 4
🟢
Create Pull Request CommentStatus: Success Updated: Nov 28, 2025 8:32am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

Walkthrough

This PR removes the legacy subscription_histories table via database migration, eliminates its schema definition, and updates type references to use the new stripePricingPlanSubscriptionHistories table instead across the codebase.

Changes

Cohort / File(s) Summary
Database Migration
apps/studio.giselles.ai/db/migrate/0070_even_living_lightning.sql, apps/studio.giselles.ai/db/migrate/meta/_journal.json
SQL migration drops the subscription_histories table with CASCADE; journal entry (idx 70) appended to track the migration.
Schema & Type Updates
apps/studio.giselles.ai/db/schema.ts
Removed subscriptionHistories table definition, indices, relations, and unused Stripe type import.
Service Types
apps/studio.giselles.ai/services/teams/types.ts
Import updated from subscriptionHistories to stripePricingPlanSubscriptionHistories; CurrentTeam.activeSubscriptionId type reference updated accordingly.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify that all references to the old subscriptionHistories table have been migrated throughout the codebase (check for any remaining usages outside this diff)
  • Confirm the migration runs cleanly with CASCADE and doesn't cause unexpected dependent object deletions
  • Validate that stripePricingPlanSubscriptionHistories schema is compatible with the updated type definitions

Poem

🐰 Old tables fade like morning dew,
We hop to stripePricingPlan anew!
CASCADE clears the paths of old,
Fresh schemas sparkle, bright and bold. ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'chore: remove v1 subscription_histories table' clearly and concisely summarizes the main change—removing the legacy subscription_histories table as part of the v2 migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description comprehensively covers all required template sections with clear details about changes, testing, and related issue context.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/remove-subscription-histories-table

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

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

@vercel
Copy link

vercel bot commented Nov 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
giselle Ready Ready Preview Comment Nov 28, 2025 8:31am
ui Ready Ready Preview Comment Nov 28, 2025 8:31am

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR removes the legacy v1 subscription_histories table as part of the Stripe v2 migration, replacing all references with the v2 stripe_pricing_plan_subscription_histories table.

  • Updates TypeScript types to reference the v2 subscription histories table
  • Removes the deprecated table schema definition and relations
  • Adds database migration to drop the legacy table

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.

File Description
apps/studio.giselles.ai/services/teams/types.ts Updates CurrentTeam type to use v2 stripePricingPlanSubscriptionHistories instead of v1 subscriptionHistories
apps/studio.giselles.ai/db/schema.ts Removes the legacy subscription_histories table definition and its relations
apps/studio.giselles.ai/db/migrate/meta/_journal.json Adds migration entry for the table drop operation
apps/studio.giselles.ai/db/migrate/0070_even_living_lightning.sql Contains SQL to drop the subscription_histories table with CASCADE

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@shige
Copy link
Member Author

shige commented Nov 28, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qodo-merge-for-open-source
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Unsafe CASCADE drop

Description: The CASCADE option in DROP TABLE will automatically drop dependent objects without
explicit verification, potentially causing unintended data loss if foreign key
relationships or views depend on this table.
0070_even_living_lightning.sql [1-1]

Referred Code
DROP TABLE "subscription_histories" CASCADE;
Ticket Compliance
🟡
🎫 #2061
🟢 Backend infrastructure is updated to support the new plan structure
A migration path for existing users is defined and implemented
Users can successfully subscribe to the new Team plan
The pricing and feature pages reflect the updated Free, Pro, and new Team plans
All related documentation is updated to reflect the plan changes
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit trail: The migration drops the subscription_histories table without preserving historical audit
data, which may be required for compliance and security analysis.

Referred Code
DROP TABLE "subscription_histories" CASCADE;

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
No migration rollback: The migration lacks error handling or rollback strategy if the DROP TABLE operation fails
or if dependent data exists.

Referred Code
DROP TABLE "subscription_histories" CASCADE;

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-merge-for-open-source
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Ensure data migration is complete before dropping table

The PR includes a destructive DROP TABLE migration. It is crucial to verify that
all data from subscription_histories is migrated to the new table before this
change is deployed to prevent permanent data loss.

Examples:

apps/studio.giselles.ai/db/migrate/0070_even_living_lightning.sql [1]
DROP TABLE "subscription_histories" CASCADE;

Solution Walkthrough:

Before:

// Migration file
DROP TABLE "subscription_histories" CASCADE;

// schema.ts
// The `subscriptionHistories` table definition is removed.

// types.ts
type CurrentTeam = {
  // ...
  activeSubscriptionId: typeof stripePricingPlanSubscriptionHistories.$inferInsert.id | null;
};

After:

// Process before running the PR's migration:
// 1. Create and run a data migration script.
//    BEGIN;
//    INSERT INTO stripe_pricing_plan_subscription_histories (...)
//    SELECT ... FROM subscription_histories;
//    COMMIT;
//
// 2. Verify data integrity in the new table.
//
// 3. Deploy the PR which contains:
// Migration file
DROP TABLE "subscription_histories" CASCADE;
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly highlights the critical risk of data loss from the irreversible DROP TABLE "subscription_histories" migration, emphasizing the need for a verified data migration before deployment.

High
Possible issue
Use select type for consistency

In the CurrentTeam type, change the type inference for activeSubscriptionId from
$inferInsert to $inferSelect to correctly reflect that it represents data read
from the database.

apps/studio.giselles.ai/services/teams/types.ts [14-16]

 	activeSubscriptionId:
-		| typeof stripePricingPlanSubscriptionHistories.$inferInsert.id
+		| typeof stripePricingPlanSubscriptionHistories.$inferSelect.id
 		| null;
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly points out that for a type representing a selected database entity, drizzle-orm's $inferSelect should be used instead of $inferInsert for better type safety and consistency with other properties in the CurrentTeam type.

Low
  • More

@giselles-ai
Copy link

giselles-ai bot commented Nov 28, 2025

🔍 QA Testing Assistant by Giselle

📋 Manual QA Checklist

Based on the changes in this PR, here are the key areas to test manually:

  • Verify Migration Success: Deploy this branch to a staging/test environment and confirm that the database migration (0070_even_living_lightning.sql) runs without any errors.
  • Confirm Table Deletion: Connect to the database in the staging/test environment and verify that the subscription_histories table no longer exists.
  • General Application Stability: Log in to the application (as both paid and free plan users) and navigate through key pages to ensure no crashes or errors occur.
  • Subscription Information for a Subscribed Team: Log in as a user on a paid plan, navigate to the Billing/Subscription settings, and verify that the current plan details are displayed accurately.
  • Subscription Information for a Free Team: Log in as a user on a free plan, navigate to the Billing/Subscription settings, and verify that the free plan status is displayed correctly.
  • Feature Gating (Paid User): As a user on a paid plan, access a feature exclusive to paid subscribers and confirm you can use it.
  • Feature Gating (Free User): As a user on a free plan, attempt to access a feature exclusive to paid subscribers and confirm you are correctly blocked.

✨ Prompt for AI Agents

Use the following prompts with Cursor or Claude Code to automate E2E testing:

📝 E2E Test Generation Prompt
You are an expert QA engineer. Based on the context below, generate a new suite of E2E tests using Playwright to verify that the recent backend refactoring of our subscription system has not introduced any user-facing regressions.

### 1. Context Summary

- **PR Description:** This pull request is a backend-only change as part of a larger Stripe v2 migration. It removes the legacy `subscription_histories` (v1) database table and updates all code to use the new `stripe_pricing_plan_subscription_histories` (v2) table. The `CurrentTeam` type was updated to reflect this change.
- **User Impact:** There should be **zero** user-facing changes. This is a pure refactor. However, any part of the application that displays subscription information or gates features based on a user's plan is at risk of regression.
- **Critical Paths to Test:**
    1. The Team Settings / Billing page, where users view their current plan and status.
    2. Any UI elements that display the current plan (e.g., user dropdown menu, sidebars).
    3. Feature Gating: Access to features that are exclusive to paid plans.
    4. The upgrade/downgrade workflow initiation points.

### 2. Test Scenarios

Your primary goal is to create regression tests that confirm the application behaves identically to how it did before this change. You must test various subscription states by setting up the appropriate data in the **new** `stripe_pricing_plan_subscription_histories` table.

Create a new test file named `tests/e2e/billing-regression.spec.ts`.

#### Scenario 1: Active Paid Subscription ("Pro" Plan)

- **Description:** A user belonging to a team with an active, paid subscription logs in.
- **Steps:**
    1. **Setup:** Before the test, programmatically create a team and populate the database to simulate an active "Pro" subscription. This involves adding records to `teams`, `stripe_billing_cadence_histories`, and `stripe_pricing_plan_subscription_histories` with a `servicing_status` of 'active'.
    2. Log in as a user from this team.
    3. Navigate to the billing/subscription settings page (e.g., `/settings/billing`).
    4. **Assert:** The page correctly displays the plan name as "Pro" (or your equivalent paid tier name).
    5. **Assert:** The plan status is shown as "Active".
    6. **Assert:** The "Manage Subscription" button (leading to Stripe Portal) is visible and enabled.
    7. Navigate to a known pro-only feature page.
    8. **Assert:** The user can access the feature and does not see an "Upgrade" paywall.

#### Scenario 2: Free Plan Subscription

- **Description:** A user belonging to a team on the default "Free" plan logs in.
- **Steps:**
    1. **Setup:** Ensure a team exists with its `plan` column set to 'free' and no corresponding active entries in the Stripe v2 tables.
    2. Log in as a user from this team.
    3. Navigate to the billing/subscription settings page.
    4. **Assert:** The page correctly displays the plan name as "Free".
    5. **Assert:** The page displays an "Upgrade" button or call-to-action.
    6. Navigate to a known pro-only feature page.
    7. **Assert:** The user is blocked by a paywall/upgrade modal.
    8. **Assert:** The user cannot access the pro feature's functionality.

#### Scenario 3: Canceled (but still active) Subscription

- **Description:** A user whose paid subscription has been canceled but has not yet reached the end of the billing period.
- **Steps:**
    1. **Setup:** Create a team with a subscription in the database where `servicing_status` is 'active' but `canceled_at` is set to a non-null timestamp in the `stripe_pricing_plan_subscription_histories` table.
    2. Log in as a user from this team.
    3. Navigate to the billing/subscription settings page.
    4. **Assert:** The page displays the correct plan name ("Pro").
    5. **Assert:** A clear message indicates the subscription is active but will be canceled on a specific date (e.g., "Your plan will be canceled on [date]").
    6. **Assert:** The user can still access pro-only features until the period ends.

### 3. Playwright Implementation Instructions

- **Test File:** `tests/e2e/billing-regression.spec.ts`
- **Test Structure:** Use `test.describe('Billing and Subscription Regression', () => { ... })` to group the tests. Use `test.beforeEach` to handle user login.
- **Data Setup:** This is the most critical part. Create a helper function or use a pre-test script hook to manipulate the database. You must seed the **new v2 tables**, not the old v1 table that was just removed.
    ```typescript
    // Example of a conceptual test setup helper (adapt to your project's test utils)
    import { test, expect } from '@playwright/test';
    import { createTestUserAndTeam, setupSubscription } from '../test-utils'; // Assuming these helpers exist

    test.describe('Billing and Subscription Regression', () => {
      let user;
      let team;

      test.beforeEach(async ({ page }) => {
        // This is a placeholder; use your actual login utility
        const { testUser, testTeam } = await createTestUserAndTeam();
        user = testUser;
        team = testTeam;
      });

      test('should correctly display an active paid plan', async ({ page }) => {
        // ARRANGE: Set up the database state for an active "Pro" subscription for the team
        await setupSubscription(team.id, {
          plan: 'pro',
          servicingStatus: 'active',
          collectionStatus: 'current',
        });

        // ACT: Log in and navigate
        await login(page, user.email, user.password); // Assuming a login function exists
        await page.goto('/settings/billing');

        // ASSERT
        await expect(page.getByTestId('current-plan-name')).toHaveText('Pro');
        await expect(page.getByTestId('current-plan-status')).toHaveText('Active');
        // ... more assertions for manage subscription button and feature access
      });

      test('free plan users should see an upgrade paywall for pro features', async ({ page }) => {
        // ARRANGE: Ensure a free team is set up (or use the default from beforeEach if applicable)
        // ACT: Log in and navigate to a pro-only feature
        await login(page, user.email, user.password);
        await page.goto('/some-pro-feature-page'); // Replace with an actual pro feature page

        // ASSERT
        await expect(page.getByTestId('feature-paywall')).toBeVisible(); // Assuming a paywall element exists
        await expect(page.getByTestId('upgrade-plan-button')).toBeVisible(); // Assuming an upgrade button exists
      });
    });
    ```
- **Selectors:** Since no UI code was provided, use plausible `data-testid` attributes. If they don't exist, create them as a standard.
    - Billing page plan name: `data-testid="current-plan-name"`
    - Billing page plan status: `data-testid="current-plan-status"`
    - Upgrade button on free plan: `data-testid="upgrade-plan-button"`
    - Paywall on pro feature: `data-testid="feature-paywall"`
- **Assertions:** Use `expect` from `@playwright/test`. Verify text content, visibility (`toBeVisible()`), and count (`toHaveCount(0)` for inaccessible elements).

### 4. MCP Integration Guidelines

- **Tagging:** Add tags to your tests to allow for targeted runs.
    ```typescript
    test('should correctly display an active paid plan @billing @regression', async ({ page }) => {
      // ...
    });
    ```
- **Execution Command:** The tests can then be run in CI/locally using a command like:
    ```bash
    # Run all tests in the new billing regression suite
    mcp playwright:test -- tests/e2e/billing-regression.spec.ts

    # Run only tests tagged with @billing
    mcp playwright:test -- --grep "@billing"
    ```

### 5. CI-Ready Code Requirements

- **Idempotency:** Tests must be independent and clean up after themselves or rely on fresh state for each run. The `beforeEach` setup should ensure this.
- **Clarity:** Use clear, descriptive names for tests (e.g., `test('free plan users should see an upgrade paywall for pro features')`).
- **Maintainability:** Encapsulate common logic (like login, data setup) into utility functions within your testing framework.
- **Error Handling:** Ensure tests fail clearly and report which assertion failed. Playwright handles this by default. Avoid generic `try/catch` blocks that might swallow assertion failures.

@shige shige requested a review from toyamarinyon November 28, 2025 12:49
Base automatically changed from feat/stripe-v2-remove-v1-api to main December 1, 2025 06:51
@shige
Copy link
Member Author

shige commented Dec 2, 2025

Thank you! 🚀

@shige shige merged commit a96bf53 into main Dec 2, 2025
11 checks passed
@shige shige deleted the chore/remove-subscription-histories-table branch December 2, 2025 01:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants