Skip to content

docs(before-send-hook): order calls while an async hook runs - #166

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog/order-calls-around-async-before-send
Draft

posthog[bot] wants to merge 1 commit into
mainfrom
posthog/order-calls-around-async-before-send

Conversation

@posthog

@posthog posthog Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Why

before-send-hook says under Concurrency & ordering guarantees that hooks "run synchronously with event preparation" and that the chain "does not batch or reorder events". That holds for every SDK except posthog-flutter, whose Dart-layer beforeSend may return a Future — and the spec never said what an SDK owes callers when the hook can complete asynchronously.

In posthog-flutter 5.x, capture(), screen() and captureException() awaited the hook before handing the call to posthog-js or the native SDK, so a reset(), identify(), register(), group(), optIn() or optOut() made right after an un-awaited capture reached the SDK first and the event went out under the next user's identity, registered properties, groups and consent.

Source: PostHog/posthog-flutter#672 (merged 2026-10-30), which adds ordered hand-over and notes in its own follow-ups that this spec is the thing out of date.

What changed in the spec

before-send-hook gains one requirement, Ordered hand-over while a before-send hook can complete asynchronously:

  • Where a hook may complete asynchronously, calls are handed to the underlying SDK in the order the app made them; a later event-producing or state-changing call, and a later getter, waits behind the pending event.
  • A call made from inside a running hook does not wait on that hook, so a hook that re-captures (renaming a $screen) cannot deadlock.
  • The hold is bounded (posthog-flutter: 10s from the capture, not per hook). Expiry releases later calls; the event is still handed over when the chain finishes and is never dropped for exceeding the bound.
  • A synchronous hook chain satisfies the requirement with no queueing.

The Concurrency & ordering guarantees prose is narrowed to the SDKs synchronous execution is true of.

Proposed, applied and archived on this branch under openspec/changes/archive/2026-11-01-order-calls-around-async-before-send/, as AGENTS.md asks.

Uncertain / for review

  • The requirement does not say an SDK should accept an asynchronous hook. posthog-flutter#686 proposes a synchronous beforeSend for 6, which closes the window entirely; the spec notes that as the convergent answer rather than requiring either shape.
  • Platform coverage is recorded as shipped: web, iOS, Android and macOS order calls; the Dart-only Windows and Linux path does not. Whether that gap is intended to persist past 6 is a question for a human.
  • acceptance/private/before-send-hook.feature is untouched: the harness has no steps for call ordering or for an asynchronous hook, so the three scenarios live in spec.md only and have not been run against an adapter.
  • Touches the same file as open draft docs(before-send-hook): run the hook once, on the event as sent #151. Whichever merges second needs a trivial rebase.

Validation

openspec validate --specs --strict --no-interactive: 32 passed, 36 failed — identical to main's totals, all pre-existing "requirement text is very long" warnings. The change itself validates with only that same length warning.


Created with PostHog Desktop

🤖 Generated with Claude Code

The spec said hooks run synchronously and never reorder events. Flutter's
Dart-layer beforeSend may return a Future, and posthog-flutter 5.x now hands
calls to the underlying SDK in the order the app made them so an event keeps
the identity, registered properties, groups and consent it was captured under.

Adds the ordering requirement, the no-deadlock rule for a hook that calls back
into the SDK, and the bounded wait that releases later calls without dropping
the pending event. Narrows the synchronous-execution prose to the SDKs it
describes.

Generated-By: PostHog Desktop
Task-Id: bb18dc36-dad3-4323-9385-4fa5bacc5ed7
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