Skip to content

feat(billing): usage-based billing cutover#3482

Closed
adboio wants to merge 4 commits into
mainfrom
posthog-code/usage-based-billing-cutover
Closed

feat(billing): usage-based billing cutover#3482
adboio wants to merge 4 commits into
mainfrom
posthog-code/usage-based-billing-cutover

Conversation

@adboio

@adboio adboio commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Problem

PostHog Code is cutting over from seat-based plans to usage-based billing (PostHog/billing#2030, PostHog/posthog#70951). The app still speaks the seat era everywhere: anonymous models fetches that can't see the free-tier model gate, a premium default model that would 403 for free orgs on their first message, "Upgrade to Pro for 40× more usage" copy, per-seat plan cards, and no acknowledgment of the billing change.

Changes

Everything new activates off the posthog-code-usage-billing feature flag and the gateway's own signals (flip together with LLM_GATEWAY_POSTHOG_CODE_MODEL_GATE_ENABLED); flag off keeps the seat-era behavior unchanged, which is also the rollback lever.

  • Model gate: every gateway models fetch is authenticated and honors the allowed/restriction_reason marks from feat(llm-gateway): gate posthog_code models for orgs not billed for Code usage posthog#70951 — restricted models render locked with an upgrade gate in all pickers, session defaults never land on a restricted model, and the cloud harness falls back to an allowed model. GLM stays pickable as the free-tier model regardless of its rollout flag.
  • Errors → upgrade gate: the gateway's model-gate 403 and org-limit 429 are classified (classifyGatewayLimitError) and open a cause-aware modal (add payment method / raise billing limit / free valve) instead of surfacing raw errors or tearing sessions down; helper prompts (commit messages, titles) proactively route to the free-tier model for unbilled orgs.
  • Billing UI: one-time blocking billing-change announcement (acknowledgment stamped on the person profile), usage-based Plan & usage page (free-tier vs billed states), titlebar free-tier meter keyed on the new code_usage_billed field, and seat fetch/auto-provision skipped since seats are retired.

Cutover companion: PostHog/posthog#71315 adds code_usage_billed to the gateway usage endpoint (the app treats it as optional, so either side can ship first).

Suggested follow-through on PostHog/posthog#70951 while it's open: attach structured codes to the gate/limit error bodies so new clients can stop substring-matching server copy.

How did you test this?

Automated only: new unit tests for the error classifier (incl. the ACP "Internal error:" wrapping that would otherwise classify the 403 as fatal), authed/marked models fetch + pickAllowedModel, helper free-tier routing (proactive + 403 backstop + bare-detail parsing), codex restricted-model config options, and gateway usage schema fields. Full typecheck across all touched packages, Biome clean, host-boundary check clean; targeted suites green (agent 49 + 90, core sessions 304, harness posthog-provider 82, UI selector tests).

Not covered by automation: visual pass over the new modals/plans page states (flag is off by default, so nothing renders differently until flip).

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code

adboio added 2 commits July 15, 2026 15:24
Classify the gateway's usage-based billing denials (free-tier model gate 403, org credit-bucket 429, per-user valve 429) in shared error helpers and route sessions to the cause-aware upgrade gate instead of error states. Authenticate every gateway models fetch and honor the new allowed/restriction_reason marks: adapters annotate restricted models on session config options, never auto-select a restricted model as session default, and the cloud harness drops restricted models and falls back to an allowed default. Helper prompts route to the free-tier model proactively off the usage endpoint's code_usage_billed bit (403 retry as staleness backstop).

Generated-By: PostHog Code
Task-Id: bfbcd747-a365-429f-a5dc-622e2c2f7edf
…ng flag

Behind the new flag: cause-aware usage limit modal (model gate / org limit / free-tier valve) with add-payment-method CTAs replacing the Pro-upgrade copy, a one-time blocking billing-change announcement recorded on the person profile, locked rendering + upgrade gate for plan-restricted models in every picker (GLM stays pickable as the free-tier model), a usage-based Plan & usage page (free-tier vs billed states, org-limit callout), free-tier gating of the titlebar usage card off code_usage_billed, and seat fetch/auto-provision skipped everywhere since seats are retired. Flag off keeps the seat-era UI byte-for-byte.

Generated-By: PostHog Code
Task-Id: bfbcd747-a365-429f-a5dc-622e2c2f7edf
@trunk-io

trunk-io Bot commented Jul 15, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit ed5567d.

adboio added 2 commits July 15, 2026 15:41
Move usage-limit modal copy derivation into core (pure, unit-tested) and thin the component; persist the billing announcement via the shared electronStorage-backed persist middleware with a hydration guard; gate seat fetch/provision once in the seat store instead of at three call sites; share a GLM-visibility hook and a models-cache validity helper; name the code_usage_billed tri-state with isCodeUsageUnbilled; merge the session service's duplicated limit branches.

Generated-By: PostHog Code
Task-Id: bfbcd747-a365-429f-a5dc-622e2c2f7edf
Matches the gateway field rename: the bit is the org's subscription status (payment method on file), and "billed" next to is_pro read ambiguously. Mechanical rename across the schema, gateway service, limit-content derivation, and billing UI; is_pro semantics untouched.

Generated-By: PostHog Code
Task-Id: bfbcd747-a365-429f-a5dc-622e2c2f7edf
@adboio adboio closed this Jul 15, 2026
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