Skip to content

fix(ui): the paid org gate must reach the user with a way to pay - #350

Merged
cvince merged 1 commit into
mainfrom
fix/cap-619-paid-org-gate-prompt
Aug 31, 2026
Merged

fix(ui): the paid org gate must reach the user with a way to pay#350
cvince merged 1 commit into
mainfrom
fix/cap-619-paid-org-gate-prompt

Conversation

@cvince

@cvince cvince commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Creating an org is now a paywall, not a hard cap — it's gated on the Team tier. Both error renderers still treated it as the old one-per-account rule.

The bug

renderQuotaExceeded (terminal) and quotaExceeded (page) each special-cased kind: 'organization' with the same reasoning — "the 1-org-per-user cap is a hard rule, not a paywall" — and returned early. That branch told the user "Each Capy account can own one organization", pointed them at asking for an invite, and deliberately withheld upgrade_url, on the grounds that an upgrade link would be an offer that couldn't be taken.

That reasoning was correct for the old model and is now exactly backwards. The service computes a correct upgrade link, and the CLI threw it away — so the paid gate reached the user with no way to pay.

The fix

Both surfaces now offer the upgrade. The terminal's organization branch no longer returns early and goes through the shared paywall render; the page carries upgrade_url as a fact and offers paying first, with the invite route as the alternative rather than the only exit. Project and member refusals now surface the URL on the page too, which they previously only did in the terminal.

Capy Business was the single paid tier and is retired under Free / Solo / Team; it no longer appears in any user-facing string.

Copy

All new CLI wording is minimal-neutral and marked COPY-FLAG. The service strings and the behaviour ("prompt users to pay") are approved; this file's wording is not yet. The renderers still take their specific reason from the service message rather than inventing one, which the added tests pin.

Verification

bun run typecheck    → clean, exit 0
bun test tests/ui/commandErrorScreen.test.ts → 31 pass / 0 fail

Existing expectations are unchanged — 27 passed before, 31 after. Nothing previously asserted the organization branch's text, so the four added tests are new coverage rather than adjusted expectations.

Pairs with the service side in capysc/capy#573. Neither half is releasable alone.

Creating an org used to be a hard one-per-account cap, so both renderers
special-cased kind='organization': they said 'each account can own one
organization', pointed at capy invite, and deliberately withheld the
upgrade link — an offer that could not be taken. CAP-550/CAP-592 turned
that cap into a Team-tier paywall, which inverts the requirement: the
link IS the way out now, and withholding it strands a user who is
willing to pay.

Both surfaces carried the mistake independently, so both change:
errorScreen's organization branch no longer returns early and now goes
through the shared paywall render, and commandErrorScreen carries the
upgrade URL as a fact and offers paying first, with the invite route as
the alternative rather than the only exit.

'Capy Business' is retired under Free/Solo/Team and no longer appears in
any user-facing string. All new CLI wording is minimal-neutral and
COPY-FLAGged — Vince approved the service strings and the behaviour, not
this copy.

Existing test expectations are unchanged (27 pass before, 31 after); the
four added tests assert the property, since nothing previously covered
the organization branch's text at all.

Also replaces a let/reassign pair in renderQuotaExceeded with an
early-return quotaCopyFor() helper.
@cvince
cvince merged commit 602eacc into main Aug 31, 2026
5 checks passed
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.

1 participant