Skip to content

fix(signals): state the org-wide PR allowance where PRs are turned on - #75897

Draft
posthog[bot] wants to merge 1 commit into
masterfrom
posthog-code/signals-org-wide-pr-allowance-copy
Draft

fix(signals): state the org-wide PR allowance where PRs are turned on#75897
posthog[bot] wants to merge 1 commit into
masterfrom
posthog-code/signals-org-wide-pr-allowance-copy

Conversation

@posthog

@posthog posthog Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Problem

The free PR tier and the PR usage limit are allocated per organization. The switch that spends them is per project. Nothing on screen connected the two.

period_billable_credits_for_org and credited_refund_credits_for_org in products/signals/backend/billing.py both roll up across every team in the org, and custom_limits_usd is org-level too. But the only place we said so was two hover tooltips on the usage widget, and hover text only reaches someone who is already suspicious. Meanwhile SignalTeamConfig.autostart_enabled is team-scoped, and the panel that renders it never mentioned billing at all.

So you flip a per-project switch, get an org-level bill, and the free allowance you thought you had per project turns out to be shared. That reaches us as billing surprise via support, which is the most expensive way to find out.

Why: a customer asked why they were charged when they expected the PRs to be free. They had PRs across two projects and reasonably read the free tier as per project.

Changes

Copy and layout only. No billing logic touched.

SelfDrivingSection.tsx – the panel with the switch now states the scope where the decision gets made, and points at the Usage section further down the same rail:

rail

InboxUsageWidget.tsx – both tooltips are gone. The org-wide scope and the free-tier figure are visible copy under the card title. The bar and the count are plain elements again, so the tabIndex that existed only to make the tooltip keyboard-reachable is no longer needed.

Billable usage Free plan
usage-billable usage-free

Two more places promised a free allowance without saying whose it is, so they get the scope too:

  • The PR limit modal: the limit is written to org-level custom_limits_usd, so its description and free-tier line now say so.
  • The onboarding pricing line, which is where the expectation gets set in the first place.

Note

Deliberately out of scope: whether the free tier should be per project is a pricing call, not an engineering one. Separately, a team with no SignalTeamConfig row defaults to Priority.P4, so a brand-new team autostarts PRs at every priority. That deserves a product decision rather than a quiet change in a copy PR.

How did you test this code?

No automated tests added. Asserting a string back to itself catches nothing a reviewer reading the diff doesn't.

What I actually ran:

  • pnpm --filter=@posthog/frontend typescript:check – clean for these files. The only remaining errors in this environment come from unbuilt workspace packages (@posthog/hogvm) and are present on master too.
  • pnpm --filter=@posthog/frontend fix (Oxlint + Oxfmt) and hogli ci:preflight --fix – both clean.
  • Rendered the affected surfaces in Storybook and screenshotted them (above) to confirm the added lines fit the narrow rail without pushing the layout around, in both the billable and free-plan states. That is also how I checked "See Usage below" is accurate: the Usage section sits below in the same column in both the rail and stacked layouts.

Existing InboxUsageWidget stories cover the states and now render the new line.

Automatic notifications

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

Docs update

Not needed. No documented workflow, API, or setting changes.

🤖 Agent context

Autonomy: Fully autonomous

Claude (Claude Code, Opus 5) wrote this from a PostHog Inbox report. I confirmed the report's claim before changing anything: read both _for_org billing helpers, SignalTeamConfig, and inboxUsageLogic, which already carries a comment noting usage and refunds are org-wide. Invoked /writing-user-facing-copy.

Decisions along the way:

  • The report suggested a link from the self-driving panel to the usage widget. I used a plain reference to the Usage section instead. Both widgets render in the same AgentSetupColumn a short scroll apart, so an in-page anchor would mean an id plus scrollIntoView plumbing, or a hash the SPA router would have to tolerate, to replace a sentence that already works.
  • Widened past the two files the report named. The modal that sets the limit and the onboarding line that promises "first 3 PRs each month are free" are the same defect: a free allowance stated without its scope. Onboarding is arguably where the wrong expectation forms.
  • Used text-secondary rather than the fainter text-tertiary for the new lines. The failure mode here was copy nobody read, so fine print would have reproduced the tooltip problem at a different opacity.

The report also flagged that there is no instrumentation on either surface, so the confusion can only be sized by support volume. I did not add capture calls here to keep this reviewable as a pure copy change.


Created with PostHog Code from an inbox report

The free PR tier and the usage limit are allocated per organization, but the
switch that spends them (autostart_enabled) is per project, and the only place
the org-wide scope was stated was two hover tooltips on the usage widget. Move
that scope into visible copy: on the PR generation panel, where someone turns
generation on, and on the usage widget next to the count and the free-tier
figure. Also name the scope in the onboarding pricing line and the PR limit
modal, since both promise a free allowance without saying whose it is.

No billing logic changes.

Generated-By: PostHog Code
Task-Id: a67a2ca9-4915-4b63-924f-477621b3f775
@trunk-io

trunk-io Bot commented Jul 31, 2026

Copy link
Copy Markdown

Merging to master 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 31, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Bundle size — 🔺 +314 B (+0.0%)

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

Total: 65.43 MiB · 🔺 +314 B (+0.0%)

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.12 MiB · 3,023 files no change ████████░░ 83.6% 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.3 KiB ../node_modules/.pnpm/posthog-js@1.409.2/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.4 KiB ../node_modules/.pnpm/posthog-js@1.409.2/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.2 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
717.8 KiB dist/toolbar/toolbar-app-2QFKEBT7.css
551.1 KiB dist/toolbar/chunk-chunk-5FDJUV5A.js
484.6 KiB dist/toolbar/chunk-chunk-QCKKTYHY.js
133.6 KiB dist/toolbar/chunk-chunk-UDBJ4EGL.js
131.8 KiB dist/toolbar/chunk-chunk-T5KY5WYR.js
71.0 KiB dist/toolbar/toolbar-app-DBANC7W5.js
69.0 KiB dist/toolbar/chunk-chunk-27JL52RE.js
35.6 KiB dist/toolbar/chunk-chunk-QFZQEUXB.js
20.9 KiB dist/toolbar/chunk-chunk-HWIVFWBZ.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 — 🔺 +1.2 KiB (+0.0%)

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

Total: 1370.99 MiB · 🔺 +1.2 KiB (+0.0%)

@posthog

posthog Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

👋 Visual changes detected for this PR.

Review and approve in PostHog Visual Review

If these changes are unexpected, they may be caused by a flaky test or a broken snapshot on master. Don't approve — rerun the job or wait for a fix.

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.

0 participants