Skip to content

fix(signals): return all available reviewers, not just the first 100#65029

Merged
pauldambra merged 4 commits into
masterfrom
posthog-code/signals-available-reviewers-no-cap
Jun 20, 2026
Merged

fix(signals): return all available reviewers, not just the first 100#65029
pauldambra merged 4 commits into
masterfrom
posthog-code/signals-available-reviewers-no-cap

Conversation

@pauldambra

@pauldambra pauldambra commented Jun 20, 2026

Copy link
Copy Markdown
Member

Problem

Assigning a reviewer in the Self-driving Inbox only surfaced org members whose first name fell in roughly the first half of the alphabet (up to ~"M"), and searching for anyone after that returned nothing — even for people who are definitely in the org. Reported from a Slack thread about a large org.

The cause is here: available_reviewers sorts members by first name and slices to the first 100 before returning. So any org with >100 GitHub-linked members silently loses the tail of the alphabet, and because the slice runs server-side the frontend can't page past it.

Changes

  • Drop the [:100] cap — return the full candidate set (sort retained for stable ordering). The whole member set is already loaded in memory, so the slice only ever truncated; it never saved work. Org membership is tiny in practice, so even the biggest org serialises to well under 100 KB.
  • Add a named OTel span signals.available_reviewers with candidate_count / result_count attributes, so this endpoint's latency and payload size are actually measurable next time (today there's no route-level span for it).
  • Capture a non-blocking exception when an org exceeds 1200 candidates — a tripwire telling us to add real pagination before the unpaginated payload could get large. Gated on the unfiltered request so search-as-you-type doesn't spam it.

A paired frontend change (PostHog/code#2800) forwards the picker's search box to this endpoint's query param so search hits the server.

How did you test this code?

I'm an agent (PostHog Code). I added automated tests in test_signal_report_api.py (TestAvailableReviewersAPI) covering: all members returned past the old 100 cap, server-side search filtering, the threshold capture firing above 1200 / staying silent below it and on search requests, and the empty-org / unknown-team paths. I could not run the suite in my environment (no configured Postgres/ClickHouse dev env); python -m py_compile and ruff check both pass, and CI will execute the tests.

Automatic notifications

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

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Investigation started from a Slack thread about the reviewer picker stopping at "M". Traced the data flow front-to-back, then confirmed via the warehouse that org membership is small enough that the full list fits comfortably in one response (well under 100 KB even for the largest orgs) — which ruled out pagination in favour of simply removing the cap, with a 1200 tripwire for the future. Tooling: PostHog Code agent with the PostHog MCP (HogQL execute-sql, APM span queries). No repo skills were invoked.


Created with PostHog Code from a Slack thread

The available_reviewers endpoint sorted org members by first name and sliced
to the first 100, so any org with more than 100 GitHub-linked members silently
lost everyone alphabetically after roughly "M" — both in the default list and
when searching, since the slice happened before the response was returned.

Drop the cap and return the full candidate set (sort retained for stable
ordering). Org membership is tiny in practice (p99 ~11, largest org ~470), so
even the biggest org serialises to well under 100 KB.

Add a named OTel span ("signals.available_reviewers") with candidate/result
counts so the endpoint's latency and size are measurable going forward, and
capture a non-blocking exception when an org exceeds 1200 candidates — a signal
to add real pagination before the unpaginated payload ever gets large.

Generated-By: PostHog Code
Task-Id: d1b4a6ce-de4c-43a6-98a0-4cb45275dde0
@github-actions

Copy link
Copy Markdown
Contributor

Hey @pauldambra! 👋

It looks like your git author email on this PR isn't your @posthog.com address (paul.dambra@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. 🙂

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
products/signals/backend/views.py:971-985
**Threshold capture fires on every unfiltered request, not once**

The comment says "a search-as-you-type session reports at most once," but that only prevents the search sub-requests from firing — every bare (no-`query`) call to the endpoint from an org over 1200 members will call `capture_exception`, which also invokes `logger.exception`. If a picker is opened repeatedly by multiple users, this fires once per open. `posthoganalytics.capture_exception` does not deduplicate events the way Sentry does, and since the message embeds `candidate_count` (which can drift), events won't necessarily fold together. The OTel span already records `candidate_count` on every request, so an alert on that attribute would provide the same signal without the repeated error-log noise.

Reviews (1): Last reviewed commit: "fix(signals): return all available revie..." | Re-trigger Greptile

Comment thread products/signals/backend/views.py
…er org per day

Per review feedback: capture_exception logs an exception and isn't
deduplicated, so the previous guard (skip only search sub-requests) still
fired on every unfiltered request from a >threshold org. Throttle with a
24h cache key so an over-threshold org reports at most once per day, and
correct the misleading comment. The span's candidate_count attribute is
still recorded on every request for metric-based alerting.

Generated-By: PostHog Code
Task-Id: d1b4a6ce-de4c-43a6-98a0-4cb45275dde0
@pauldambra pauldambra marked this pull request as ready for review June 20, 2026 16:43
@assign-reviewers-posthog assign-reviewers-posthog Bot requested a review from a team June 20, 2026 16:43

@pauldambra pauldambra left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note

🤖 Automated comment by QA Swarm — not written by a human

QA Swarm review complete. See inline comments.

Comment thread products/signals/backend/views.py
Comment thread products/signals/backend/views.py

Copy link
Copy Markdown
Member Author

Note

🤖 Automated comment by QA Swarm — not written by a human

Multi-perspective review: qa-team (correctness + tests), paul-reviewer, xp-reviewer, security-audit

Verdict: ✅ APPROVE

Pragmatic, well-scoped, well-tested fix. Removing the [:100] cap is the right call, the server-side search and stable sort are preserved, and the over-threshold tripwire is correctly throttled to once-per-org-per-day via cache.add (atomic, per-team key, 24h TTL). No blocking findings; only LOW/NIT observations.

Key findings

  • 🟢 LOWcapture_exception is used as a non-error capacity tripwire; it surfaces in error dashboards and triggers logger.exception. Throttling makes volume a non-issue, but the OTel span's candidate_count attribute already provides the same signal without error-view noise. Worth considering dropping the exception capture in favour of a span/metric alert.
  • NIT — Tests don't assert the span attributes, and the empty-org (None{}) path is untested.

Already addressed (not re-raised)

The Greptile P2 thread about the threshold capture firing on every unfiltered request is fixed by commit 6363a52: the cache.add(...) throttle ensures at most one report per org per day. Verified against the current code and the test_threshold_capture_deduplicated_across_requests / test_threshold_not_triggered_by_search_requests tests.

Convergence

  • The capture_exception-as-tripwire observation was raised independently by the xp (DX/semantics) and paul (pragmatism) perspectives — higher confidence, still non-blocking.

Security

Org-scoped via self.team.id, gated by task:read; cache key and exception payload are per-team (team_id, candidate_count only — no PII, no cross-tenant leakage). No IDOR / SSRF / injection / prompt-injection surface introduced.

Reviewer summaries

Reviewer Assessment
🔍 qa-team Behavioural change is well-covered; minor untested span/empty-org paths only.
👤 paul Lightweight, correctly-scoped fix with a sensible documented threshold; no over-engineering.
📐 xp Excellent why-not-what comments; only quibble is exception-as-capacity-signal semantics.
🛡 security-audit No new attack surface; tenant scoping and payload contents are clean.

Automated by QA Swarm — not a human review

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "fix(signals): throttle reviewer paginati..." | Re-trigger Greptile

From qa-swarm review feedback: add coverage for the available_reviewers
empty paths — an org with no GitHub-linked members and an unknown team
(helper returns None) both return an empty 200 response.

Generated-By: PostHog Code
Task-Id: d1b4a6ce-de4c-43a6-98a0-4cb45275dde0

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6363a5296a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread products/signals/backend/views.py
@pauldambra pauldambra added the stamphog Request AI review from stamphog label Jun 20, 2026 — with PostHog

@stamphog stamphog Bot 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.

The functional change is sound and well-tested, but there is an intentionally unresolved review thread about code comments embedding private operational scale data (p99/largest-org member counts) and change history — both prohibited by project guidelines. The author's own triage reply explicitly left it open for a human to decide before merge.

@stamphog stamphog Bot removed the stamphog Request AI review from stamphog label Jun 20, 2026

@andrewm4894 andrewm4894 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.

yeah we have this limit to 100 thing in a few places and can have strange side effects - ty!

@pauldambra pauldambra merged commit 35b6dc0 into master Jun 20, 2026
227 checks passed

Copy link
Copy Markdown
Member Author

Merge activity

@pauldambra pauldambra deleted the posthog-code/signals-available-reviewers-no-cap branch June 20, 2026 17:23
@deployment-status-posthog

deployment-status-posthog Bot commented Jun 20, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-06-20 18:07 UTC Run
prod-us ✅ Deployed 2026-06-20 18:18 UTC Run
prod-eu ✅ Deployed 2026-06-20 18:19 UTC Run

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.

2 participants