Skip to content

feat(cli): handle plan-gate denials centrally - #7441

Merged
pamelachia merged 7 commits into
developfrom
pamela/growth-1050-central-upgrade-event
Nov 11, 2026
Merged

pamelachia merged 7 commits into
developfrom
pamela/growth-1050-central-upgrade-event

Conversation

@pamelachia

@pamelachia pamelachia commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

I moved plan-gate handling out of each command into shared code. Since #7271, mapHttpError attaches entitlement from the Management API's entitlement_required body, so the upgrade hint, the cli_upgrade_suggested event and the plan_limit classification now all come from that one field, with no per-command wiring and no extra requests.

Changed:

  • One hint and one event per gated denial, wired or not: the text error renderer prints the upgrade hint before the error message, and withCommandTelemetry captures cli_upgrade_suggested (same name, same {feature_key, org_slug} payload) after the command, just before cli_command_executed. Commands that never had wiring, such as branches unpause, now get both. vanity-subdomains check-availability now fires the event too; its no-event exception only existed for parity with the removed Go CLI.
  • No per-command gate code or follow-up lookups: I deleted the gateMapError/suggestUpgrade wrapping at branches create/update, the four domains commands and the three vanity-subdomains commands. branches update no longer fetches the project and org entitlements after a 4xx, since the persistent-branch 402 carries the envelope. Only the SSO commands keep the entitlements lookup.
  • plan_limit from the envelope: an error carrying entitlement classifies as plan_limit whatever its class declares, so gated denials from branches unpause, domains get/create/activate/reverify and vanity-subdomains get move from api_status to plan_limit (fingerprint suffix :api_status becomes :plan_limit).

Note: the event moved from mid-handler to post-run. Payload and identity stay the same; it uses the same capture context as cli_command_executed.

Linear

  • part of GROWTH-1050

@pamelachia pamelachia self-assigned this Nov 2, 2026
@pamelachia pamelachia changed the title feat(cli): fire the plan-gate upgrade event from the command wrapper feat(cli): render the plan-gate upgrade hint and event centrally Nov 2, 2026
withCommandTelemetry captures cli_upgrade_suggested once, before
cli_command_executed, when a failure carries the entitlement_required
envelope's entitlement. Commands with no per-command wiring (branches
unpause) now fire it too. suggestUpgrade keeps the event only on its
entitlements-lookup fallback. vanity-subdomains check-availability keeps
its no-event exception through a new trackUpgradeSuggested option.

Errors carrying an entitlement classify as plan_limit, whether or not
their class declares the field.
The text layer's fail prints the upgrade hint before the error message
whenever the error carries the entitlement_required envelope's
entitlement, so commands with no per-command wiring (branches unpause)
print it too. suggestUpgrade prints only on its entitlements-lookup
fallback. Handler tests assert the failure carries the entitlement.
The persistent-branch 402 carries the entitlement_required envelope, so the
central hint, event and plan_limit cover it. A 4xx without the envelope no
longer triggers the follow-up project and entitlements requests.
@pamelachia
pamelachia force-pushed the pamela/growth-1050-central-upgrade-event branch from 979e26a to 82f5088 Compare November 9, 2026 06:26
@pamelachia pamelachia changed the title feat(cli): render the plan-gate upgrade hint and event centrally feat(cli): handle plan-gate denials centrally Nov 9, 2026
@pamelachia

pamelachia commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Manual check from source against a Free-plan project (<ref>, org <org>), run on the earlier head that also added a source param to the link. That change is now dropped; the request count, the hint and the entitlement field don't depend on it.

  • branches create gate-probe --project-ref <ref> --yes --debug: one POST /v1/projects/<ref>/branches (402), then the hint printed once with the org's billing URL. Passed.
  • Same command with --output-format json: error.entitlement is {"feature":"branching_limit","upgrade_url":".../org/<org>/billing..."}. Passed.
  • branches update <branch> --persistent not run live: Pro and Team orgs have the persistent-branch entitlement and Free orgs can't create branches, so only a Free org that still has a branch reaches this 402, and no test org is in that state. update.integration.test.ts covers one PATCH carrying the 402 envelope.

@pamelachia
pamelachia force-pushed the pamela/growth-1050-central-upgrade-event branch from 82f5088 to 33a4c0e Compare November 9, 2026 08:09
@pamelachia
pamelachia marked this pull request as ready for review November 10, 2026 14:23
@pamelachia
pamelachia requested a review from a team as a code owner November 10, 2026 14:23
@github-actions

Copy link
Copy Markdown
Contributor

CLI benchmark (Lightweight Commands): no changes found

Commit ad90dd9 against develop at 529024b · 10 runs each · report only, never blocks merging

Command Workload Before After Change
supabase --version version 390 ms 328 ms no real change (-16%, within normal variation)
supabase --help help 397 ms 369 ms no real change (-7%, within normal variation)
supabase init init 323 ms 307 ms no real change
supabase migration new bench migration-new 277 ms 262 ms no real change (-5%, within normal variation)
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 280 ms 292 ms no real change
supabase projects list projects-list 256 ms 265 ms no real change
supabase functions list --project-ref replayfixtureprojref functions-list 254 ms 246 ms no real change

API calls: unchanged for all 7 commands.

Binary size: 134.9 MB, down 4.0 KB from 134.9 MB.

Details: timing ranges, API calls per command, slowest phases

Timing ranges

Command Workload Before range After range Change Verdict
supabase --version version 276-530 ms 298-429 ms -62 ms (-16%) within noise
supabase --help help 317-504 ms 330-487 ms -28 ms (-7%) within noise
supabase init init 309-407 ms 287-367 ms -15 ms (-5%) within noise
supabase migration new bench migration-new 235-318 ms 229-312 ms -15 ms (-5%) within noise
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 251-314 ms 269-339 ms +12 ms (+4%) within noise
supabase projects list projects-list 237-366 ms 236-327 ms +9 ms (+4%) within noise
supabase functions list --project-ref replayfixtureprojref functions-list 225-292 ms 221-365 ms -9 ms (-3%) within noise

API calls per command

Command Workload Before After Endpoint changes
supabase --version version 0 0
supabase --help help 0 0
supabase init init 0 0
supabase migration new bench migration-new 0 0
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 2 2
supabase projects list projects-list 1 1
supabase functions list --project-ref replayfixtureprojref functions-list 2 2

Slowest phases

No command moved beyond noise, so there are no phase deltas.

Workflow run

Comment /bench to run the lightweight benchmark (this comment) again on the latest commit, or /bench full to also run the integration-heavy benchmark, which posts in its own comment.

@github-actions github-actions 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.

🤖 AI Review

Verified both Claude findings against the checked-out code. Confirmed the SSO 404 upgrade-hint and telemetry regression, plus a defensive validation inconsistency limited to malformed entitlement objects. Codex's independent review completed with no findings.

Findings

Severity Location Category Sources Claim
🟡 MINOR apps/cli/src/command-internal/upgrade-suggest.ts:83 error-handling claude SSO 404 responses carrying an entitlement_required envelope lose the upgrade hint and cli_upgrade_suggested event. suggestUpgrade now returns without emitting either, but the list/add/update/remove 404 handlers discard mapped.entitlement when constructing their specialized errors. upgradeSuggested still classifies these failures as plan_limit.
⚪ NIT apps/cli/src/shared/telemetry/error-actionability.ts:1152 correctness claude The classifier accepts any non-null object-valued entitlement as a plan gate, while error normalization requires non-empty feature and upgrade_url strings. A malformed entitlement can therefore produce plan_limit classification without an upgrade hint or cli_upgrade_suggested event.

Stats

Claude findings: 2 · Codex findings: 0 · Confirmed: 2 · Refuted: 0 · Uncertain: 0


Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: auto · Workflow run

This review runs once per PR. A maintainer can request another with a /ai-review comment.

@Coly010 Coly010 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.

Approving, thanks! One optional ask: the SSO handlers build SsoListSamlDisabledError on a 404 without forwarding entitlement. If a 404 ever carried an entitlement_required envelope, suggestUpgrade would return true and nothing would print or fire. Unlikely, so a one-line forward or a test pinning it would do, but feel free to skip it.

The plan_limit reclassification also shifts those fingerprint suffixes, so worth a heads up to whoever watches the dashboards.

@pamelachia

pamelachia commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor Author

@Coly010 thanks for the review! Skipping the SSO forward: the Management API's plan gate never responds 404, so those 404 branches can't carry the envelope (details in #7441 (comment)).

On the fingerprint shift: I checked what reads these fields. Two dashboards group by them and neither filters on api_status or plan_limit, so the rows move and nothing breaks.

@pamelachia
pamelachia added this pull request to the merge queue Nov 11, 2026
Merged via the queue into develop with commit e8e440b Nov 11, 2026
80 checks passed
@pamelachia
pamelachia deleted the pamela/growth-1050-central-upgrade-event branch November 11, 2026 05:17
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.

2 participants