Skip to content

docs(before-send-hook): run the hook once, on the event as sent - #151

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog/before-send-single-invocation
Draft

posthog[bot] wants to merge 1 commit into
mainfrom
posthog/before-send-single-invocation

Conversation

@posthog

@posthog posthog Bot commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Why

Two SDKs changed their before-send behavior this week, in the same direction, because the spec never said how many times the hook runs or which assembly steps must already be done when it does.

  • posthog-android#885 — the hook chain ran twice for every event captured through the stateful client (build event → run hooks → delegate to the stateless path, which rebuilt the properties and ran the hooks again). Hooks with side effects ran twice, random or uuid-keyed sampling hooks kept about half their configured rate, and hook edits to distinctId, timestamp and uuid were discarded by the rebuild. The fix queues the event the single hook pass returned, and minimizes $feature_flag_called before the hook, as the stateless path and posthog-ios already did.
  • posthog-rs#307 — groups were merged into $groups after the hook, so a hook could not see or change them, and minimized $feature_flag_called events were trimmed after the hook, so properties a hook added were silently dropped. Both steps now run before the hook, matching posthog-python.

What changed in the spec

before-send-hook gains one requirement: the hook chain runs exactly once per captured event; the event handed to it is the event that would be sent if the hook returned it unchanged (groups already merged, minimized flag-called events already trimmed); and the SDK enqueues what the hook returned instead of rebuilding it. The existing Flutter wrapper-layer divergence is carried over unchanged.

Proposed, applied and archived on this branch under openspec/changes/archive/2026-10-27-run-before-send-once-on-the-sent-event/, as AGENTS.md asks.

Uncertain / for review

  • The requirement names only the two assembly steps the SDK changes actually moved (group merge, flag-called trim) rather than a full ordered assembly list. Consent gating, sanitizers and dynamic-context building are still ordered differently across SDKs and are left alone.
  • acceptance/private/before-send-hook.feature is untouched: the harness has no steps for invocation counts, group merging, or minimal flag-called events. The four new scenarios live in spec.md only and have not been run against an adapter.
  • openspec validate --specs --strict was not run — the OpenSpec CLI is not installed in this environment.

Created with PostHog Desktop

🤖 Generated with Claude Code

posthog-android ran the hook chain twice per stateful capture, and posthog-rust merged groups and trimmed minimized $feature_flag_called events after the hook. Both now match posthog-ios and posthog-python. Specify the single invocation and the event shape the hook sees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 4237a2aa-9610-4c4f-a024-1ae7a64b0d75
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