Skip to content

fix(desktop): use organization billing period for usage - #76877

Merged
trunk-io[bot] merged 10 commits into
masterfrom
posthog-code/fix-desktop-billing-period
Aug 7, 2026
Merged

fix(desktop): use organization billing period for usage#76877
trunk-io[bot] merged 10 commits into
masterfrom
posthog-code/fix-desktop-billing-period

Conversation

@adboio

@adboio adboio commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Problem

PostHog Desktop can label a per-user safety-throttle reset as the organization billing-period end when retired seats no longer provide billing periods. This makes Desktop disagree with the Billing UI.

Changes

Add a lightweight Billing-owned endpoint for the locally synchronized organization period, then have the gateway usage endpoint prefer it while retaining the legacy seat-derived period as a compatibility fallback. Keep quota limits focused on quota enforcement and remove the unrelated Tasks API ownership and generated clients.

Why: Desktop needs the same organization billing boundary as the Billing UI without putting billing metadata into the shared quota contract or calling the full external Billing payload.

How did you test this code?

  • uv run pytest tests/test_billing_period_resolver.py tests/test_quota_resolver.py tests/test_usage.py -q in services/llm-gateway - 61 passed
  • Ruff and focused mypy checks passed for changed gateway and Django files
  • The focused Django endpoint test and OpenAPI generation could not run locally because Postgres is unavailable in this fresh environment; CI will run them

Automatic notifications

  • Publish to changelog?

Docs update

No docs change. This corrects the source of an existing billing-period label.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Codex revised the implementation after architectural review. The organization period now comes from a narrow Billing-owned API, and the quota endpoint remains limited to quota state.


Created with PostHog Code

Generated-By: PostHog Code
Task-Id: 6de5f42c-0443-4002-938a-106aef1648b5
@adboio adboio self-assigned this Aug 3, 2026
@trunk-io

trunk-io Bot commented Aug 3, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

@github-actions github-actions Bot added the feature/desktop Feature Tag: Desktop label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Hey @adboio! 👋

It looks like your git author email on this PR isn't your @posthog.com address (adambowker98@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 65.58 MiB · no change

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.25 MiB · 22 files no change ███░░░░░░░ 27.7% of 4.51 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.14 MiB · 3,035 files no change ████████░░ 83.8% of 9.71 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
762 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
285.5 KiB ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
235.5 KiB src/taxonomy/core-filter-definitions-by-group.json
231.5 KiB ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/module.js
154.3 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
105.3 KiB src/lib/api.ts
94.7 KiB ../packages/quill/packages/quill/dist/index.js
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Toolbar bundle — eager 2.19 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.19 MiB · 17 files no change ████░░░░░░ 38.3% of 5.72 MiB
Deferred (lazy) 2.08 MiB · 33 files no change n/a — loads on demand
Loader dist/toolbar.js 1.1 KiB no change █░░░░░░░░░ 5.8% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
718.1 KiB dist/toolbar/toolbar-app-6ZY7LRCK.css
551.5 KiB dist/toolbar/chunk-chunk-OOWXB5XU.js
484.6 KiB dist/toolbar/chunk-chunk-HYIA72SV.js
133.6 KiB dist/toolbar/chunk-chunk-WRWH4RQO.js
131.8 KiB dist/toolbar/chunk-chunk-T5KY5WYR.js
71.0 KiB dist/toolbar/toolbar-app-NVWWGTVG.js
69.0 KiB dist/toolbar/chunk-chunk-27JL52RE.js
35.6 KiB dist/toolbar/chunk-chunk-FVSASBPN.js
20.9 KiB dist/toolbar/chunk-chunk-OWVP5BP2.js
12.2 KiB dist/toolbar/chunk-chunk-PIK3PADE.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

Dist folder size — 🔺 +6.4 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1389.36 MiB · 🔺 +6.4 KiB (+0.0%)

ℹ️ MCP UI apps size — 32 app(s), 17070.3 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 599.6 KB 187.7 KB
action 457.8 KB 187.7 KB
action-list 564.4 KB 187.7 KB
cohort 456.8 KB 187.7 KB
cohort-list 563.4 KB 187.7 KB
email-template 456.6 KB 187.7 KB
error-details 472.4 KB 187.7 KB
error-issue 457.5 KB 187.7 KB
error-issue-list 564.3 KB 187.7 KB
experiment 561.5 KB 187.7 KB
experiment-list 565.2 KB 187.7 KB
experiment-results 563.2 KB 187.7 KB
feature-flag 567.2 KB 187.7 KB
feature-flag-list 570.9 KB 187.7 KB
feature-flag-testing 461.0 KB 187.7 KB
insight-actors 562.2 KB 187.7 KB
invite-email-preview 456.0 KB 187.7 KB
llm-costs 559.5 KB 187.7 KB
session-recording 458.6 KB 187.7 KB
session-summary 463.9 KB 187.7 KB
survey 458.4 KB 187.7 KB
survey-global-stats 562.2 KB 187.7 KB
survey-list 565.1 KB 187.7 KB
survey-stats 562.2 KB 187.7 KB
trace-span 457.2 KB 187.7 KB
trace-span-list 564.3 KB 187.7 KB
workflow 457.1 KB 187.7 KB
workflow-list 563.7 KB 187.7 KB
loops-review 461.2 KB 187.7 KB
query-results 747.4 KB 187.7 KB
render-ui 828.0 KB 187.7 KB
visual-review-snapshots 461.6 KB 187.7 KB
Playwright — all passed

All tests passed.

View test results →

Backend coverage — all changed backend lines covered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ████████████████████ 100.0% (28 / 28)

All changed backend lines are covered ✅

Per-product line coverage (touched products)
Product Coverage Lines
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
batch_exports ████████░░░░░░░░░░░░ 39.5% 8,853 / 22,413
warehouse_sources_queue █████████░░░░░░░░░░░ 45.9% 90 / 196
demo ███████████░░░░░░░░░ 56.3% 1,497 / 2,661
data_tools █████████████░░░░░░░ 66.2% 90 / 136
tasks ██████████████░░░░░░ 70.4% 35,223 / 50,047
ai_gateway ███████████████░░░░░ 75.0% 9 / 12
signals █████████████████░░░ 82.5% 27,210 / 32,964
apm █████████████████░░░ 83.8% 1,220 / 1,455
cdp █████████████████░░░ 84.2% 3,914 / 4,649
notebooks █████████████████░░░ 86.4% 8,029 / 9,296
data_modeling █████████████████░░░ 86.6% 8,451 / 9,761
actions █████████████████░░░ 86.6% 717 / 828
wizard █████████████████░░░ 86.7% 1,090 / 1,257
cohorts ██████████████████░░ 87.6% 6,482 / 7,400
managed_warehouse ██████████████████░░ 87.8% 5,928 / 6,752
product_tours ██████████████████░░ 87.9% 1,303 / 1,482
data_warehouse ██████████████████░░ 88.0% 11,552 / 13,130
business_knowledge ██████████████████░░ 89.0% 4,384 / 4,928
engineering_analytics ██████████████████░░ 89.2% 6,725 / 7,537
exports ██████████████████░░ 89.3% 7,484 / 8,378
dashboards ██████████████████░░ 89.3% 5,995 / 6,710
visual_review ██████████████████░░ 89.5% 5,870 / 6,558
alerts ██████████████████░░ 90.3% 4,482 / 4,966
conversations ██████████████████░░ 90.5% 18,337 / 20,256
canvas ██████████████████░░ 90.6% 2,075 / 2,291
links ██████████████████░░ 90.6% 183 / 202
streamlit_apps ██████████████████░░ 90.7% 2,630 / 2,901
error_tracking ██████████████████░░ 91.1% 11,135 / 12,225
stamphog ██████████████████░░ 91.3% 4,505 / 4,936
slack_app ██████████████████░░ 91.5% 10,545 / 11,523
managed_migrations ███████████████████░ 92.6% 1,556 / 1,681
notifications ███████████████████░ 92.6% 1,017 / 1,098
mcp_analytics ███████████████████░ 92.7% 3,810 / 4,112
ai_observability ███████████████████░ 92.9% 17,116 / 18,426
early_access_features ███████████████████░ 92.9% 1,347 / 1,450
marketing_analytics ███████████████████░ 92.9% 14,833 / 15,967
mcp_store ███████████████████░ 92.9% 6,952 / 7,482
web_analytics ███████████████████░ 93.1% 15,963 / 17,154
surveys ███████████████████░ 93.1% 5,858 / 6,290
posthog_ai ███████████████████░ 93.3% 1,327 / 1,423
reminders ███████████████████░ 93.4% 468 / 501
product_analytics ███████████████████░ 93.5% 7,027 / 7,517
approvals ███████████████████░ 93.5% 3,491 / 3,734
legal_documents ███████████████████░ 93.8% 1,628 / 1,736
workflows ███████████████████░ 94.3% 7,951 / 8,436
endpoints ███████████████████░ 94.3% 8,771 / 9,306
tracing ███████████████████░ 94.4% 2,678 / 2,837
review_hog ███████████████████░ 94.6% 8,246 / 8,715
skills ███████████████████░ 94.8% 3,478 / 3,669
messaging ███████████████████░ 95.4% 3,401 / 3,565
replay_vision ███████████████████░ 95.7% 18,103 / 18,923
experiments ███████████████████░ 95.7% 27,865 / 29,109
logs ███████████████████░ 95.8% 11,623 / 12,135
annotations ███████████████████░ 96.2% 732 / 761
revenue_analytics ███████████████████░ 96.3% 1,887 / 1,960
growth ███████████████████░ 96.3% 4,845 / 5,031
feature_flags ███████████████████░ 96.4% 17,580 / 18,238
user_interviews ███████████████████░ 96.5% 2,638 / 2,734
access_control ███████████████████░ 96.9% 870 / 898
customer_analytics ███████████████████░ 97.1% 10,838 / 11,160
warehouse_sources ███████████████████░ 97.5% 380,177 / 390,026
data_catalog ████████████████████ 97.9% 2,676 / 2,734
analytics_platform ████████████████████ 98.1% 2,254 / 2,298
metrics ████████████████████ 98.2% 2,491 / 2,536
pulse ████████████████████ 98.4% 2,017 / 2,049
live_debugger ████████████████████ 99.2% 613 / 618
field_notes ████████████████████ 99.4% 158 / 159

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

@trunk-io

trunk-io Bot commented Aug 3, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

adboio added 2 commits August 5, 2026 21:42
Generated-By: PostHog Code
Task-Id: 6de5f42c-0443-4002-938a-106aef1648b5
Generated-By: PostHog Code
Task-Id: 6de5f42c-0443-4002-938a-106aef1648b5
@adboio
adboio marked this pull request as ready for review August 6, 2026 02:01
@posthog

posthog Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🦔 ReviewHog reviewed this pull request

Found 2 must fix, 2 should fix, 0 consider.

Published 4 findings (view the review).

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested review from a team August 6, 2026 02:01
@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
### Issue 1
services/llm-gateway/src/llm_gateway/services/billing_period_resolver.py:44-46
**Billing period uses mismatched scope**

When a user's current team belongs to a different organization than their current-organization preference, this request sends no team identifier, so Django returns the preference organization's period and the gateway caches it under the requested team, causing Desktop to display the wrong billing-period end for up to the cache TTL.

### Issue 2
services/llm-gateway/src/llm_gateway/services/billing_period_resolver.py:45-52
**Malformed responses bypass fallback**

If the billing endpoint or an intermediary returns a successful response with invalid JSON, `response.json()` raises outside the error handler and propagates through `asyncio.gather`, causing `/v1/usage` to return HTTP 500 instead of using the legacy seat-derived fallback.

```suggestion
        try:
            response = await self._http.get(url, headers={"Authorization": auth_header}, timeout=2.0)
            response.raise_for_status()
            data = response.json()
        except (httpx.HTTPError, json.JSONDecodeError):
            logger.warning("billing_period_fetch_failed", team_id=team_id, exc_info=True)
            return None
```

### Issue 3
services/llm-gateway/src/llm_gateway/services/billing_period_resolver.py:18-21
**Resolver violates Python conventions**

The new same-typed frozen dataclass remains positional, `upstream_auth_header` is imported inside the resolver function, and the public billing endpoint has no matching documentation update. This permits silently swapped period boundaries, obscures the module dependency, and leaves the new API undocumented; make the dataclass keyword-only, move the import to module scope, and add the corresponding API documentation.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(billing): type synchronized billing ..." | Re-trigger Greptile

Comment thread services/llm-gateway/src/llm_gateway/services/billing_period_resolver.py Outdated
Comment thread services/llm-gateway/src/llm_gateway/services/billing_period_resolver.py Outdated
@veria-ai

veria-ai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

@posthog

posthog Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

ReviewHog Alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ReviewHog Report

Changes

Issues: 4 issues

Files (4)
  • ee/api/billing.py
  • services/llm-gateway/src/llm_gateway/api/usage.py
  • services/llm-gateway/src/llm_gateway/main.py
  • services/llm-gateway/src/llm_gateway/services/billing_period_resolver.py

Comment thread ee/api/billing.py Outdated
Comment thread ee/api/billing.py Outdated

@richardsolomou richardsolomou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could we test organization-period precedence when the seat has a different billing period? the current no-seat case still passes if precedence is accidentally reversed.

Generated-By: PostHog Code
Task-Id: 6de5f42c-0443-4002-938a-106aef1648b5
@adboio
adboio requested a review from richardsolomou August 6, 2026 13:02
Generated-By: PostHog Code
Task-Id: 6de5f42c-0443-4002-938a-106aef1648b5
adboio and others added 4 commits August 6, 2026 09:34
Generated-By: PostHog Code
Task-Id: 6de5f42c-0443-4002-938a-106aef1648b5
Use the synchronized organization billing period's named fields after its tuple-to-dataclass migration.

Generated-By: PostHog Code
Task-Id: 19e58639-375a-4f6b-95a9-cccad9135d76
Generated-By: PostHog Code
Task-Id: 19e58639-375a-4f6b-95a9-cccad9135d76
@trunk-io
trunk-io Bot merged commit 9f02882 into master Aug 7, 2026
263 checks passed
@trunk-io
trunk-io Bot deleted the posthog-code/fix-desktop-billing-period branch August 7, 2026 15:44
@deployment-status-posthog

deployment-status-posthog Bot commented Aug 7, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-08-07 16:29 UTC Run
prod-us ✅ Deployed 2026-08-07 16:47 UTC Run
prod-eu ✅ Deployed 2026-08-07 16:47 UTC Run

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

Labels

feature/desktop Feature Tag: Desktop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants