feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(impersonate): nudge to billing add-on when impersonation is blocked - #376

Merged
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers
Jul 10, 2026
Merged

feat(impersonate): nudge to billing add-on when impersonation is blocked#376
rafa-thayto merged 3 commits into
mainfrom
rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers

Conversation

@rafa-thayto

@rafa-thaytorafa-thayto commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

whether hitting the impersonation limit nudges customers toward the add-on. Today it doesn't. This adds that nudge so the upgrade path is as frictionless as possible now that the CLI makes it easier to hit the limit.

When POST /v1/actor_tokens is rejected by billing, clerk impersonate now guides you to the billing page instead of just printing a terse error:

  • 402 (plan gate)Impersonation is available as an add-on.
  • 422 (billing-period limit)You've reached your impersonation limit this billing period.

Both attach the account-level billing page (<dashboard>/settings/billing, from getDashboardUrl(), honoring CLERK_DASHBOARD_URL) as docsUrl, and the command still exits non-zero (no session was ever created). How the URL is surfaced follows the output mode:

ConditionBehavior
Agent modeJSON error with the billing URL as docsUrl. Never opens a browser.
--printPrints the URL beneath the error. No prompt, no browser.
--yesOpens the billing page immediately (prints it if headless). No prompt.
TTY human, no --yesPrompts "Add more impersonations now?" (default Yes); opens on yes.
Non-TTY human, no --yesPrints the URL beneath the error. No prompt, no browser.

Other changes

  • Drops the (used X/Y this billing period) templating — the copy is deliberately numberless (BAPI has no quota-read endpoint, so counts can't be shown proactively or reliably).
  • Removes the now-unused limit/used fields from BillingError (impersonate was the only consumer and no longer passes them).
  • Documents the behavior in the command README.

Out of scope

  • A proactive "X/5 remaining" counter — impossible without a BAPI quota-read endpoint. This is reactive-on-failure only.

Test Plan

  • bun run format:check · bun run lint · bun run typecheck — all clean
  • bun run test — 1896 pass (adds gating-branch coverage for agent / --print / --yes / TTY-prompt / non-TTY, plus wire-level 402/422 mapping and docsUrl assertions)
  • Drove the realbapiRequest → BapiError(402/422) → nudge stack against a local stub BAPI: 402 → add-on message, 422 → limit message, both with docsUrl = <dashboard>/settings/billing; confirmed openBrowser is invoked with the billing URL on the --yes branch.
  • bun run test:e2e:opnot run locally: the runner needs the AI Enablement 1Password vault, which this environment can't access. The e2e suite has no impersonate coverage, so it wouldn't exercise this change regardless; CI will run it on the PR.

Notes

Minor bump changeset included (clerk).

When `clerk impersonate` is blocked by billing — 402 (plan gate) or 422
(billing-period limit) — surface a value-framed, numberless message and point
to the account-level billing page (`<dashboard>/settings/billing`) as docsUrl.
In an interactive terminal, offer to open the page (default Yes); `--yes` opens
it directly, while `--print` and agent mode surface the URL without opening a
browser. Every branch still exits non-zero since no session was created.
Drops the used/limit quota templating (and the now-unused BillingError
limit/used fields) since BAPI has no quota-read endpoint to make counts
reliable.
@changeset-bot

changeset-botBot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 086b8d3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
clerkMinor

Not sure what this means? Click here to learn what changesets are.

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

@coderabbitai

coderabbitaiBot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 650cd453-5a27-4982-aa0e-1c87572bb921

📥 Commits

Reviewing files that changed from the base of the PR and between d16eb38 and 086b8d3.

📒 Files selected for processing (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts

📝 Walkthrough

Walkthrough

The impersonate command now maps HTTP 402 and 422 billing failures to BillingError instances with a billing-page docsUrl, omits quota counts from messages, and conditionally prompts or opens the billing page based on execution mode and flags. BillingError no longer stores limit or used. Tests and documentation cover the updated errors and navigation behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/cli#373: Updates clerk impersonate BAPI 402/422 error handling and related tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: nudging impersonation users to the billing add-on when blocked.
Description check✅ PassedThe description is directly related to the changeset and accurately describes the impersonation billing upgrade behavior.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 7 linked repositories, but your current plan allows 1. Analyzed clerk/clerk_go, skipped clerk/dashboard, clerk/accounts, clerk/backoffice, clerk/clerk, clerk/clerk-docs, clerk/cloudflare-workers.


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

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli-core/src/commands/impersonate/impersonate.test.ts`:
- Around line 287-344: The non-TTY billing test is missing an assertion that the
upgrade nudge is skipped, so it could still pass even if nudgeToBilling()
wrongly prompts. Update the impersonate tests around BillingError handling to
assert the billing prompt/mockConfirm call count or exact calls, ensuring only
the pre-flight confirmation runs in the non-TTY case and no upgrade prompt is
shown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 266cbca4-2603-4c52-866f-481a1a365c96

📥 Commits

Reviewing files that changed from the base of the PR and between f0f8d23 and d16eb38.

📒 Files selected for processing (6)
  • .changeset/impersonate-upgrade-nudge.md
  • packages/cli-core/src/commands/impersonate/README.md
  • packages/cli-core/src/commands/impersonate/impersonate.test.ts
  • packages/cli-core/src/commands/impersonate/impersonate.ts
  • packages/cli-core/src/lib/errors.test.ts
  • packages/cli-core/src/lib/errors.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)

The prior assertion only checked that the browser never opened, which
would still pass if nudgeToBilling() incorrectly prompted in non-TTY
mode. Assert the confirm call count so a regression there is caught.
@rafa-thayto
rafa-thayto merged commit 7603dde into mainJul 10, 2026
10 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/impersonate-limit-upgrade-nudge-branch-gate-tests-impersonate-impersonate-impers branch July 10, 2026 23:13
@github-actionsgithub-actionsBot mentioned this pull request Jul 10, 2026
Sign up for freeto 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

@rafa-thayto@wyattjoh