Skip to content

feat: grant legacy pro enterprise feature access - #5912

Open
simplesagar wants to merge 1 commit into
mainfrom
feat/legacy-pro-enterprise-access
Open

feat: grant legacy pro enterprise feature access#5912
simplesagar wants to merge 1 commit into
mainfrom
feat/legacy-pro-enterprise-access

Conversation

@simplesagar

@simplesagar simplesagar commented Aug 31, 2026

Copy link
Copy Markdown
Member

Summary

  • add a dry-run-by-default offline backfill that grants Legacy Pro organizations the shared enterprise-access entitlement bundle
  • preserve explicitly disabled entitlements, lock and recheck tier eligibility before writes, and make reruns idempotent
  • require target-bound apply confirmations and document the operator workflow
  • generalize the entitlement seeder naming so enterprise trials, PAYG activation, and the Pro backfill share one source of truth

Motivation

Legacy Pro organizations should receive enterprise-level feature access without changing their billing behavior or resource limits. An application data backfill follows the repository's migration rules while preserving administrator choices represented by soft-deleted feature rows.


Summary by cubic

Adds a dry-run-by-default backfill that grants Legacy Pro organizations the shared enterprise-access entitlement bundle without changing their billing or resource limits.

New Features

  • Adds the pro-entitlements command under server/cmd/tools/migrations; it defaults to a non-writing preview and requires explicit environment, target, and subcommand confirmations to apply.
  • Locks each organization, rechecks gram_account_type before writing, and preserves soft-deleted features as administrator disables so reruns stay idempotent.
  • Documents the operator workflow, including the required GRAM_DATABASE_URL target and error-and-rerun recovery, in PRO_ENTITLEMENTS_BACKFILL.md.

Refactors

  • Renames SeedPaygEntitlementsTx and EnterpriseTrialBundle to SeedEnterpriseAccessEntitlementsTx and EnterpriseAccessBundle so enterprise trials, PAYG activation, and the Pro backfill share one seeder.
  • Application feature caches may hold pre-backfill values for up to their normal TTL after an apply.

Written for commit 4c5bf32. Summary will update on new commits.

Review in cubic

@simplesagar
simplesagar requested a review from a team as a code owner August 31, 2026 19:13
@simplesagar simplesagar added the enhancement New feature or request label Aug 31, 2026
@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 4c5bf32

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.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Running ultrareview automatically — Adds a new database backfill granting enterprise entitlements to all Pro organizations, touching core entitlement logic and the shared seeder used in Stripe checkout; a bug here could mass-grant or mass-revoke feature access.. I'll post findings when complete.

@blacksmith-sh

This comment has been minimized.

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

Ultrareview completed in 12m 9s

4 issues found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/cmd/tools/migrations/pro_entitlements_cmd.go">

<violation number="1" location="server/cmd/tools/migrations/pro_entitlements_cmd.go:90">
P2: For a large Pro population, `ListProOrganizations` materializes every ID before processing any row, so memory usage grows with the full table and an interruption forces a full rescan. Page the source with a keyset cursor and process each page.</violation>

<violation number="2" location="server/cmd/tools/migrations/pro_entitlements_cmd.go:96">
P2: After this command commits an entitlement, existing Redis feature entries are never refreshed, so gated requests can continue seeing `Enabled=false` for up to 15 minutes; the raw transaction also bypasses the advisory lock that prevents a cache fill from republishing stale state. Acquire the canonical per-feature locks before `Begin` and refresh every inserted feature after commit.

(Based on your team's feedback about serializing product-feature cache updates.)</violation>

<violation number="3" location="server/cmd/tools/migrations/pro_entitlements_cmd.go:152">
P1: Custom agent: **Flag Security Vulnerabilities**

When `GRAM_DATABASE_URL` sets `sslmode=disable` or permits a plaintext fallback, this call opens the privileged PostgreSQL connection without TLS. It can expose database credentials and entitlement writes to a network attacker even when `-confirm-target` matches. Parse the pool config and require TLS with plaintext fallbacks removed before connecting.</violation>
</file>

<file name="server/cmd/tools/migrations/pro_entitlements_cmd_test.go">

<violation number="1" location="server/cmd/tools/migrations/pro_entitlements_cmd_test.go:76">
P2: The successful apply transaction is not exercised here: the apply test only checks that the orchestration callback receives `true`. Add a Pro `migrateProOrganization(..., true)` test that asserts `Commit` and not `Rollback`, so writes cannot silently stop being committed.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

}
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
pool, err := pgxpool.New(ctx, cfg.dbURL)

@cubic-dev-ai cubic-dev-ai Bot Aug 31, 2026

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.

P1: Custom agent: Flag Security Vulnerabilities

When GRAM_DATABASE_URL sets sslmode=disable or permits a plaintext fallback, this call opens the privileged PostgreSQL connection without TLS. It can expose database credentials and entitlement writes to a network attacker even when -confirm-target matches. Parse the pool config and require TLS with plaintext fallbacks removed before connecting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/cmd/tools/migrations/pro_entitlements_cmd.go, line 152:

<comment>When `GRAM_DATABASE_URL` sets `sslmode=disable` or permits a plaintext fallback, this call opens the privileged PostgreSQL connection without TLS. It can expose database credentials and entitlement writes to a network attacker even when `-confirm-target` matches. Parse the pool config and require TLS with plaintext fallbacks removed before connecting.</comment>

<file context>
@@ -0,0 +1,169 @@
+	}
+	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
+	defer stop()
+	pool, err := pgxpool.New(ctx, cfg.dbURL)
+	if err != nil {
+		fmt.Fprintln(stdout, "migration failed: connect postgres")
</file context>
Fix with cubic

}

func backfillProEntitlements(ctx context.Context, db proEntitlementsDB, apply bool) (proEntitlementsReport, error) {
organizationIDs, err := featurerepo.New(db).ListProOrganizations(ctx)

@cubic-dev-ai cubic-dev-ai Bot Aug 31, 2026

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.

P2: For a large Pro population, ListProOrganizations materializes every ID before processing any row, so memory usage grows with the full table and an interruption forces a full rescan. Page the source with a keyset cursor and process each page.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/cmd/tools/migrations/pro_entitlements_cmd.go, line 90:

<comment>For a large Pro population, `ListProOrganizations` materializes every ID before processing any row, so memory usage grows with the full table and an interruption forces a full rescan. Page the source with a keyset cursor and process each page.</comment>

<file context>
@@ -0,0 +1,169 @@
+}
+
+func backfillProEntitlements(ctx context.Context, db proEntitlementsDB, apply bool) (proEntitlementsReport, error) {
+	organizationIDs, err := featurerepo.New(db).ListProOrganizations(ctx)
+	if err != nil {
+		return proEntitlementsReport{}, fmt.Errorf("list pro organizations: %w", err)
</file context>
Fix with cubic

}

return backfillProOrganizations(organizationIDs, apply, func(organizationID string, apply bool) (int, error) {
tx, err := db.Begin(ctx)

@cubic-dev-ai cubic-dev-ai Bot Aug 31, 2026

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.

P2: After this command commits an entitlement, existing Redis feature entries are never refreshed, so gated requests can continue seeing Enabled=false for up to 15 minutes; the raw transaction also bypasses the advisory lock that prevents a cache fill from republishing stale state. Acquire the canonical per-feature locks before Begin and refresh every inserted feature after commit.

(Based on your team's feedback about serializing product-feature cache updates.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/cmd/tools/migrations/pro_entitlements_cmd.go, line 96:

<comment>After this command commits an entitlement, existing Redis feature entries are never refreshed, so gated requests can continue seeing `Enabled=false` for up to 15 minutes; the raw transaction also bypasses the advisory lock that prevents a cache fill from republishing stale state. Acquire the canonical per-feature locks before `Begin` and refresh every inserted feature after commit.

(Based on your team's feedback about serializing product-feature cache updates.) </comment>

<file context>
@@ -0,0 +1,169 @@
+	}
+
+	return backfillProOrganizations(organizationIDs, apply, func(organizationID string, apply bool) (int, error) {
+		tx, err := db.Begin(ctx)
+		if err != nil {
+			return 0, fmt.Errorf("begin organization transaction: %w", err)
</file context>
Fix with cubic

require.Equal(t, proEntitlementsReport{Organizations: 2, FeaturesAdded: 4}, report)
}

func TestBackfillProOrganizationsAppliesEachOrganization(t *testing.T) {

@cubic-dev-ai cubic-dev-ai Bot Aug 31, 2026

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.

P2: The successful apply transaction is not exercised here: the apply test only checks that the orchestration callback receives true. Add a Pro migrateProOrganization(..., true) test that asserts Commit and not Rollback, so writes cannot silently stop being committed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/cmd/tools/migrations/pro_entitlements_cmd_test.go, line 76:

<comment>The successful apply transaction is not exercised here: the apply test only checks that the orchestration callback receives `true`. Add a Pro `migrateProOrganization(..., true)` test that asserts `Commit` and not `Rollback`, so writes cannot silently stop being committed.</comment>

<file context>
@@ -0,0 +1,105 @@
+	require.Equal(t, proEntitlementsReport{Organizations: 2, FeaturesAdded: 4}, report)
+}
+
+func TestBackfillProOrganizationsAppliesEachOrganization(t *testing.T) {
+	t.Parallel()
+	var seeded []string
</file context>
Fix with cubic

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants