Skip to content

feat(slack_app): capture analytics event on @PostHog mention#61338

Merged
oliverb123 merged 6 commits into
masterfrom
posthog-code/slack-mention-analytics-event
Jun 3, 2026
Merged

feat(slack_app): capture analytics event on @PostHog mention#61338
oliverb123 merged 6 commits into
masterfrom
posthog-code/slack-mention-analytics-event

Conversation

@oliverb123
Copy link
Copy Markdown
Contributor

Problem

We have no product-analytics event for when someone interacts with the @PostHog Slack bot. The bot handles Slack app_mention events in products/slack_app/backend/api.py, but that path only emits structlog application logs — nothing is captured into PostHog. So questions like "how often is the bot mentioned?", "how many are first-touch vs. follow-ups in a thread?", and "who is using it?" can't be answered from events today.

Changes

Adds a posthog code slack mention received event, captured at the single chokepoint where a mention is confirmed actionable and routed to the coding-agent workflow (_start_mention_workflow).

The Slack thread is the session:

  • slack_session_id = <team>:<channel>:<thread_ts>
  • is_first_message_in_session — true when the mention has no thread_ts or its thread_ts equals its own ts (i.e. it opens the thread)
  • session_message_count1 for a first message; otherwise a best-effort count of thread replies via conversations_replies
  • user identification — the acting Slack user is resolved to a PostHog User, so distinct_id is the real person's distinct id (with $set analytics metadata, and groups for org/project), per PostHog's server-side capture convention. When no PostHog user can be resolved, it falls back to a stable slack:<team>:<user> distinct id and sets posthog_user_identified=false.

Capture is fully wrapped in try/except — analytics is best-effort and can never break Slack event handling.

How did you test this code?

I'm an agent (PostHog Code). I added unit tests in products/slack_app/backend/tests/test_mention_analytics.py covering: first-message identification + count, follow-up thread counting, the unresolved-user fallback distinct id, and that a capture failure is swallowed.

I could not run the test suite in this environment — it has no flox/hogli, no pytest/django installed, and constrained disk. I verified both files parse (ast.parse) and that the change matches the existing patterns in the file (_resolve_posthog_user_from_event, groups() from posthog.event_usage, the structlog/best-effort idiom). A human should run hogli test products/slack_app/backend/tests/test_mention_analytics.py and lint (ruff check/format) before merge.

Automatic notifications

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

Docs update

No docs changes.

🤖 Agent context

Authored by PostHog Code (Claude Opus) on request to add an analytics event tracking @PostHog Slack mentions, including first-message-in-session, session message count, a session identifier, and user identification.

Key decisions:

  • Placement: chose _start_mention_workflow as the single emit point — it fires for genuinely handled mentions (after ignore/scope/project checks), including repo-picker follow-ups, rather than for every raw webhook (which includes ignored edits and unroutable events). This keeps the event meaningful and guarantees a resolved target integration is available.
  • Session model: reused the existing thread semantics (thread_ts) already used throughout the file as the session key, rather than inventing a new identifier.
  • Message count: kept it best-effort with a bounded conversations_replies call (only for follow-ups) to avoid extra Slack API cost/latency on the common first-message case.
  • User identity: reused the existing _resolve_posthog_user_from_event helper so attribution maps to a real PostHog user where possible, matching the server-side posthoganalytics.capture(distinct_id=user.distinct_id, ... groups=groups(...)) convention.

Adds a `posthog code slack mention received` product-analytics event captured whenever the @PostHog Slack bot is mentioned and routed to the coding-agent workflow.

The Slack thread is treated as the session: `thread_ts` identifies it, and a mention whose `thread_ts` is absent or equal to its own `ts` is the session's first message. The event carries `is_first_message_in_session`, `session_message_count` (1 for the first message; otherwise a best-effort count of thread replies), `slack_session_id`, and Slack context (team/channel/thread/user). The acting Slack user is resolved to a PostHog `User` so the event is attributed to a real person via their `distinct_id` (with `$set` analytics metadata); when no PostHog user can be resolved it falls back to a stable `slack:<team>:<user>` distinct id. Capture is best-effort and wrapped so analytics can never break Slack event handling.

Generated-By: PostHog Code
Task-Id: 65cd121b-ce0a-4fb1-be75-b14924d0f938
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

Hey @oliverb123! 👋

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

@oliverb123 oliverb123 marked this pull request as ready for review June 3, 2026 09:38
Copilot AI review requested due to automatic review settings June 3, 2026 09:38
Satisfies ruff isort ordering for the new imports and restructures the distinct_id resolution so it narrows cleanly to str for mypy.

Generated-By: PostHog Code
Task-Id: 65cd121b-ce0a-4fb1-be75-b14924d0f938
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 3, 2026

Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
products/slack_app/backend/api.py:1667-1671
The `identified and posthog_user is not None` condition is redundant. `identified` is computed as `bool(posthog_user and posthog_user.distinct_id)`, which already guarantees `posthog_user` is not None when `identified` is True. The extra guard cannot trigger in practice.

```suggestion
        distinct_id = (
            posthog_user.distinct_id
            if identified
            else f"slack:{slack_team_id}:{slack_user_id or 'unknown'}"
        )
```

### Issue 2 of 3
products/slack_app/backend/api.py:1634
**Synchronous Slack API call in the webhook handler**

`conversations_replies` makes a real HTTP roundtrip on every follow-up mention, adding its latency to the time before the handler returns 202. Slack's 3-second retry window is already shared with Temporal workflow submission and user-info resolution; a slow or rate-limited `conversations_replies` response narrows that window further. Since `session_message_count` is a best-effort metric, consider moving this call outside the request path (e.g., deferring it into the analytics properties assembled by the Temporal workflow) or accepting `None` as the count for follow-ups when latency budget is tight.

### Issue 3 of 3
products/slack_app/backend/tests/test_mention_analytics.py:1-99
**Tests not parameterised**

The codebase prefers parameterised tests. The four methods here share the same `_report_slack_mention_received` entry point and differ only in event shape and mock return values. Scenarios like first-message vs. follow-up (`is_first_message_in_session`, `session_message_count`) and resolved vs. unresolved user (`distinct_id`, `posthog_user_identified`) are natural axes for `@pytest.mark.parametrize`, which would also make it easy to add the missing case where `thread_ts == ts` (thread root is the mention itself) without adding another full test method.

Reviews (1): Last reviewed commit: "chore(slack_app): fix import order and n..." | Re-trigger Greptile

Comment thread products/slack_app/backend/api.py
Comment thread products/slack_app/backend/api.py Outdated
Comment thread products/slack_app/backend/tests/test_mention_analytics.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds server-side product analytics for actionable app_mention events handled by the PostHog Code Slack app, so mentions can be measured (session/thread semantics, first vs follow-up, user attribution) without affecting Slack event handling.

Changes:

  • Captures a new posthog code slack mention received event at the _start_mention_workflow chokepoint, including session/thread identifiers and best-effort thread message counts.
  • Resolves the acting Slack user to a PostHog User when possible and falls back to a stable Slack-derived distinct_id when not.
  • Adds unit tests covering first-message detection, follow-up counting, fallback distinct id behavior, and best-effort failure swallowing.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
products/slack_app/backend/api.py Adds analytics capture helper(s) and hooks capture into mention workflow start.
products/slack_app/backend/tests/test_mention_analytics.py Adds unit tests for the new analytics capture behavior and failure handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread products/slack_app/backend/api.py Outdated
Comment thread products/slack_app/backend/api.py
Comment thread products/slack_app/backend/api.py
Pass send_feature_flags=True to posthoganalytics.capture so the acting user's active feature flags are recorded on the mention event, matching the convention in posthog/event_usage.py report_user_action. Module-level capture is correct here since this runs in the synchronous Slack webhook view, not a Celery task (where ph_scoped_capture would be required).

Generated-By: PostHog Code
Task-Id: 65cd121b-ce0a-4fb1-be75-b14924d0f938
Addresses trusted code-review feedback (Greptile, Copilot):

- Bound the Slack client timeout when counting thread messages so the best-effort count cannot eat into Slack's webhook retry window on a slow or rate-limited response.
- Add integration/channel/thread context to both best-effort warning logs so failures are debuggable without a stack trace.
- Validate the Slack user id with an isinstance check, consistent with the channel/ts handling, instead of stringifying arbitrary values.
- Parameterize the analytics tests and add the case where the thread root is the mention itself (thread_ts == ts).

Generated-By: PostHog Code
Task-Id: 65cd121b-ce0a-4fb1-be75-b14924d0f938
@tests-posthog
Copy link
Copy Markdown
Contributor

tests-posthog Bot commented Jun 3, 2026

⏭️ Skipped snapshot commit because branch advanced to 5ff0548 while workflow was testing 7478165.

The new commit will trigger its own snapshot update workflow.

If you expected this workflow to succeed: This can happen due to concurrent commits. To get a fresh workflow run, either:

  • Merge master into your branch, or
  • Push an empty commit: git commit --allow-empty -m 'trigger CI' && git push

@tests-posthog
Copy link
Copy Markdown
Contributor

tests-posthog Bot commented Jun 3, 2026

Query snapshots: Backend query snapshots updated

Changes: 1 snapshots (1 modified, 0 added, 0 deleted)

What this means:

  • Query snapshots have been automatically updated to match current output
  • These changes reflect modifications to database queries or schema

Next steps:

  • Review the query changes to ensure they're intentional
  • If unexpected, investigate what caused the query to change

Review snapshot changes →

@oliverb123 oliverb123 requested a review from a team June 3, 2026 10:42
@oliverb123 oliverb123 enabled auto-merge (squash) June 3, 2026 10:45
return ROUTE_HANDLED_LOCALLY


def _count_session_thread_messages(integration: Integration, channel: str | None, thread_ts: str | None) -> int | None:
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.

nit: can we move these out of the api file

@oliverb123 oliverb123 merged commit 00e0a87 into master Jun 3, 2026
217 checks passed
@oliverb123 oliverb123 deleted the posthog-code/slack-mention-analytics-event branch June 3, 2026 10:59
@deployment-status-posthog
Copy link
Copy Markdown

deployment-status-posthog Bot commented Jun 3, 2026

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-06-03 11:41 UTC Run
prod-us ✅ Deployed 2026-06-03 12:05 UTC Run
prod-eu ✅ Deployed 2026-06-03 12:09 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.

4 participants