Skip to content

chore: merge main into v1 (DO NOT SQUASH, lands by fast-forward) - #1236

Open
eli-r-ph wants to merge 247 commits into
v1from
eli/v1-sync-main-20261111
Open

eli-r-ph wants to merge 247 commits into
v1from
eli/v1-sync-main-20261111

Conversation

@eli-r-ph

@eli-r-ph eli-r-ph commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

v1 is missing main's 7.83.2-7.87.1: emails from code, the MCP spec alignment, $experiment_exposure (#1154), flags, errors and AI fixes, #1209 and #1211, and the Markdown reference tooling. This repo only squash-merges, and squashed syncs leave the merge base with main at October, so the 8.0 release PR (#1016) keeps conflicting (86 files).

Warning

Do not squash or merge this PR with the button. After approval it lands by fast-forwarding v1 to this exact head, which keeps both merge commits. Squashing drops them, and #1016 keeps conflicting.

Changes:

💚 How did you test it?

  • Every file that differs from v1 was changed on main since the last sync, apart from capture_event.py above.
  • ruff, mypy and the public API check pass; the snapshot changes only by main's email API.
  • 7,378 tests pass. The 3 fork tests in test_metrics_autocapture.py fail locally on pristine v1 too (macOS fork).
  • Removing the pin fails the sync and async attribution tests.
  • Merging this head into current main has 0 conflicts.

📝 Checklist

  • All commits are signed (see Commit signing)
  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file

No changeset: main's changes shipped in 7.83.2-7.87.1, and 8.0's changesets are on v1.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Claude Code (Claude Opus 5.5).

🤖 Generated with Claude Code

dustinbyrne and others added 30 commits October 7, 2026 10:16
* ci: reuse Python SDK upgrade PRs unless queued

* ci: protect reused upgrade PRs from stale and queued writes
* feat(error-tracking): standardize exception metadata

* fix(error-tracking): preserve explicit trace context

* fix(error-tracking): restrict exception group traversal

* fix(error-tracking): deprecate reserved property overrides

* fix(error-tracking): bound exception group traversal
* feat(asgi): add error tracking middleware

* chore(asgi): type exception capture metadata

* fix(asgi): make tracing header trust explicit

* refactor(asgi): expose request properties terminology
* feat(mcp): resolve fresh low-level tool ownership

Add resolve_original_tool for raw MCP SDK 1.x and 2.x servers. Prefer ownership learned from a served listing and keep unknown ownership fail-open.

Generated-By: PostHog Desktop
Task-Id: db1a8a08-fe03-43b6-9e49-705e8b1f375e

* fix(mcp): preserve listed argument ownership

Generated-By: PostHog Desktop
Task-Id: db1a8a08-fe03-43b6-9e49-705e8b1f375e

* fix(mcp): preserve arguments for unknown schemas

Generated-By: PostHog Desktop
Task-Id: db1a8a08-fe03-43b6-9e49-705e8b1f375e

* fix(mcp): preserve low-level tool schemas

Generated-By: PostHog Desktop
Task-Id: db1a8a08-fe03-43b6-9e49-705e8b1f375e
* feat(flask): add error tracking integration

* fix(flask): use canonical exception metadata

* chore(flask): type exception capture metadata

* refactor(flask): expose request properties terminology

* fix(flask): preserve exception privacy settings
* feat(drf): add automatic error tracking integration

* fix(drf): align exception metadata with SDK spec

* chore(drf): type exception capture metadata

* fix(drf): honor Django request filtering

* test(drf): describe context data as properties

* feat(drf): support exception capture opt-out

* chore(drf): update public API snapshot

* fix(drf): align exception capture configuration

* test(drf): preserve omitted setting default
feat(mcp): add interface property to server events

Generated-By: PostHog Desktop
Task-Id: 651bf7d9-01f4-4d75-8583-868e29fdd314
* Align ASGI and Flask integration defaults

* feat(integrations): align request middleware configuration
…1042)

Bumps the uv group with 1 update in the /examples/example-ai-llamaindex directory: [banks](https://github.com/masci/banks).
Bumps the uv group with 2 updates in the /examples/example-ai-pydantic-ai directory: [pydantic-ai](https://github.com/pydantic/pydantic-ai) and [pydantic-ai-slim](https://github.com/pydantic/pydantic-ai).


Updates `banks` from 2.4.5 to 2.5.1
- [Release notes](https://github.com/masci/banks/releases)
- [Commits](masci/banks@v2.4.5...v2.5.1)

Updates `pydantic-ai` from 1.102.0 to 1.107.7
- [Release notes](https://github.com/pydantic/pydantic-ai/releases)
- [Changelog](https://github.com/pydantic/pydantic-ai/blob/main/docs/changelog.md)
- [Commits](pydantic/pydantic-ai@v1.102.0...v1.107.7)

Updates `pydantic-ai-slim` from 1.102.0 to 1.107.7
- [Release notes](https://github.com/pydantic/pydantic-ai/releases)
- [Changelog](https://github.com/pydantic/pydantic-ai/blob/main/docs/changelog.md)
- [Commits](pydantic/pydantic-ai@v1.102.0...v1.107.7)

---
updated-dependencies:
- dependency-name: banks
  dependency-version: 2.5.1
  dependency-type: indirect
  dependency-group: uv
- dependency-name: pydantic-ai
  dependency-version: 1.107.7
  dependency-type: direct:production
  dependency-group: uv
- dependency-name: pydantic-ai-slim
  dependency-version: 1.107.7
  dependency-type: indirect
  dependency-group: uv
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: span processor request context

* fix: span processor review feedback

* fix: span processor review follow-ups
* fix(ai): mark cancelled async LLM calls as errors

* test: fix Python 3.10 cancellation and FastMCP compatibility
releaser-posthog-python Bot and others added 24 commits November 10, 2026 02:17
* fix(ai): report thinking tokens from the Anthropic wrapper

Map usage.output_tokens_details.thinking_tokens to reasoning_tokens
($ai_reasoning_tokens) for non-streaming and streaming responses.
The streaming value is cumulative on message_delta, so it is replaced
rather than summed.

* fix(ai): emit reasoning tokens for Anthropic streaming events

capture_streaming_event copied only the cache fields for Anthropic and
skipped the shared optional-token path, so $ai_reasoning_tokens never
reached $ai_generation for streams.
…1212)

Month and year lookbacks jumped straight to the target and clamped once. The SDK spec requires one unit at a time with a clamp after every step, so 2m on March 31 is January 28, and 4y on February 29 is February 28.
Bumps the github-actions group with 2 updates: [github/codeql-action/init](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.39.1 to 4.39.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@9b32540...1fc58d0)

Updates `github/codeql-action/analyze` from 4.39.1 to 4.39.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@9b32540...1fc58d0)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…1221)

* ci: notify Slack when release jobs fail, are cancelled, or never run

* ci: drop in-job Slack failure replies now covered by the notifier

* ci: accept a lone thread artifact and gate the notifier on trusted runs
* docs: add the Markdown reference adapter

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

* docs: render the reference in one Python script

Drops the Node renderer, its tests, the page check and the sdk-specs
module. The generated pages are unchanged byte for byte.

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

* docs: write the latest reference as its own file, with the site's front matter

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

* docs: leave indexing to the site

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

* docs: render the reference with griffe2md

Replaces the 539-line renderer with griffe2md, a dev dependency.

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

* docs: write signatures with griffe and mark async methods

griffe2md 1.5.1's signature template gives an unannotated parameter the
previous parameter's annotation, so Client(...) showed `debug: str = False`.
It also drops `async`. Swap its format_signature filter for griffe's own
Function.signature(), prefixed with `async` for coroutines.

Also document OptionalSetArgs and CodeEmailArgs in Types, since set,
set_once and Emails.send reference them.

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

* docs: map capabilities in docs/capabilities.json instead of docstrings

The posthog-capability lines showed in IDE hover help. The docstrings
read as they do on main again, Category sections included, and the
generator drops those sections from the reference.

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

* docs: list v1's context option methods in SDK utils

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

* test: check the reference generator's signatures

Covers the two output bugs review reproduced: unannotated parameters getting the
previous parameter's annotation, and coroutines missing async. Also checks the
keyword-only marker. griffe and griffe2md join the test extra so CI runs them.

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

* docs: leave private parameters out of reference signatures

Client(...) listed _use_ai_lane and _enable_multimodal_capture.

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

* docs: keep whole summaries and Returns, and call constructors by class name

- Summary tables keep a docstring's first paragraph, not its first line.
- A Returns section is one return value, even when its description wraps.
- A constructor's signature reads `Client(...)`, not `__init__(...)`.

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

* docs: fix three docstring examples

- The context example tags with `tag`, since `tag_context` doesn't exist.
- The `capture_exception` example passes `distinct_id` by keyword, since it's keyword-only.
- Two reST `Example::` blocks become Markdown, which the reference renders.

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

* docs: add the steps to a first event to the Python getting-started intro

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs: generate the 7.x Markdown reference

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

* docs: regenerate the 7.x reference and its latest file

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

* docs: regenerate the 7.x pages

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

* docs: regenerate the 7.x pages with griffe2md

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

* docs: regenerate the 7.x reference from current main

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

* docs: regenerate the 7 and reference pages

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

* docs: regenerate the 7 and reference pages from the capability map

The Category boxes are gone; nothing else changed.

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

* docs: regenerate the 7.x reference without private parameters

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

* docs: regenerate the 7.x reference with whole summaries and Returns

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

* docs: regenerate the Python 7.x reference with the getting-started intro

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs: generate the experimental 8 reference and migration page

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

* docs: regenerate the experimental 8 reference and migration page

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

* docs: regenerate the 8 and migration pages

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

* docs: regenerate the 8 and migration pages with griffe2md

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

* docs: regenerate the 8 and migration pages from v1

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

* docs: regenerate the 8 page from v1 with the capability map

The Category boxes are gone; nothing else changed.

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

* docs: regenerate the 8.x reference without private parameters

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

* docs: regenerate the 8.x reference with whole summaries and Returns

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

* docs: regenerate the Python 8 reference with the getting-started intro

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…neration (#1185)

* docs: generate the Markdown reference on release and remove HogRef generation

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

* ci: add a workflow that regenerates the reference by hand

The release's reference step is best-effort, so a failure never blocks a
release. generate-references.yml reruns it from main on demand and commits
references/ when anything changed. Same shape as posthog-js's
generate-references.yml.

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

* ci: install the dev extra in the manual reference run, and don't let the sdk-specs checkout block a release

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

* ci: warn when the SDK reference doesn't regenerate

The reference step is best-effort, so a failure leaves the release green. A
::warning:: annotation now says so and points to generate-references.yml.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1225)

* chore(references): keep only the newest HogRef JSON per major

Strapi's HogRef cron downloads every file in references/ every 15
minutes. With 194 versioned files here and over 1,000 across the JS
packages, it uses up its GitHub token's hourly budget, and other syncs
on the same token fail. Strapi already stores every version, so the
files are only needed for new releases.

Keep 6.9.3, 7.86.6 and latest. New releases still add their file.

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

* chore(references): keep the latest patch of each minor that changed the reference

Restore the latest patch of every minor whose reference differs from the
release before it, plus the newest release of each major. Minors that
only bumped the version number stay deleted.

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

* chore(references): keep only references for versions people install

Of the files the previous commit kept, keep only those whose versions get
at least 1% of the package's weekly downloads, plus the newest release
of each major. A file also counts downloads of later minors that share
its reference.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1222)

Bumps the uv group with 1 update in the /examples/example-ai-llamaindex directory: [nltk](https://github.com/nltk/nltk).
Bumps the uv group with 2 updates in the /examples/example-ai-crewai directory: [litellm](https://github.com/BerriAI/litellm) and [oauthlib](https://github.com/oauthlib/oauthlib).
Bumps the uv group with 4 updates in the /examples/example-ai-pydantic-ai directory: [authlib](https://github.com/authlib/authlib), [fastmcp](https://github.com/PrefectHQ/fastmcp), [joserfc](https://github.com/authlib/joserfc) and [pyasn1](https://github.com/pyasn1/pyasn1).
Bumps the uv group with 1 update in the /examples/example-ai-gemini directory: [pyasn1](https://github.com/pyasn1/pyasn1).
Bumps the uv group with 1 update in the /examples/example-ai-litellm directory: [litellm](https://github.com/BerriAI/litellm).
Bumps the uv group with 1 update in the /examples/example-ai-langgraph directory: [langgraph-sdk](https://github.com/langchain-ai/langgraph).
Bumps the uv group with 1 update in the /examples/example-ai-langchain directory: [langgraph-sdk](https://github.com/langchain-ai/langgraph).
Bumps the uv group with 1 update in the /examples/example-ai-dspy directory: [litellm](https://github.com/BerriAI/litellm).


Updates `nltk` from 3.10.3 to 3.10.4
- [Release notes](https://github.com/nltk/nltk/releases)
- [Changelog](https://github.com/nltk/nltk/blob/develop/ChangeLog)
- [Commits](nltk/nltk@v3.10.3...v3.10.4)

Updates `litellm` from 1.88.6 to 1.100.4
- [Release notes](https://github.com/BerriAI/litellm/releases)
- [Commits](BerriAI/litellm@v1.88.6...v1.100.4)

Updates `oauthlib` from 4.0.0 to 4.0.1
- [Release notes](https://github.com/oauthlib/oauthlib/releases)
- [Changelog](https://github.com/oauthlib/oauthlib/blob/v4.0.1/CHANGELOG.rst)
- [Commits](oauthlib/oauthlib@v4.0.0...v4.0.1)

Updates `authlib` from 1.7.2 to 1.9.1
- [Release notes](https://github.com/authlib/authlib/releases)
- [Commits](authlib/authlib@v1.7.2...v1.9.1)

Updates `fastmcp` from 3.4.2 to 3.4.10
- [Release notes](https://github.com/PrefectHQ/fastmcp/releases)
- [Changelog](https://github.com/PrefectHQ/fastmcp/blob/v3.4.10/docs/changelog.mdx)
- [Commits](PrefectHQ/fastmcp@v3.4.2...v3.4.10)

Updates `joserfc` from 1.7.1 to 1.7.6
- [Release notes](https://github.com/authlib/joserfc/releases)
- [Changelog](https://github.com/authlib/joserfc/blob/main/docs/changelog.rst)
- [Commits](authlib/joserfc@1.7.1...1.7.6)

Updates `pyasn1` from 0.6.4 to 0.6.5
- [Release notes](https://github.com/pyasn1/pyasn1/releases)
- [Changelog](https://github.com/pyasn1/pyasn1/blob/main/CHANGES.rst)
- [Commits](pyasn1/pyasn1@v0.6.4...v0.6.5)

Updates `pyasn1` from 0.6.4 to 0.6.5
- [Release notes](https://github.com/pyasn1/pyasn1/releases)
- [Changelog](https://github.com/pyasn1/pyasn1/blob/main/CHANGES.rst)
- [Commits](pyasn1/pyasn1@v0.6.4...v0.6.5)

Updates `litellm` from 1.88.6 to 1.100.4
- [Release notes](https://github.com/BerriAI/litellm/releases)
- [Commits](BerriAI/litellm@v1.88.6...v1.100.4)

Updates `langgraph-sdk` from 0.4.4 to 0.4.6
- [Release notes](https://github.com/langchain-ai/langgraph/releases)
- [Commits](langchain-ai/langgraph@0.4.4...0.4.6)

Updates `langgraph-sdk` from 0.4.4 to 0.4.12
- [Release notes](https://github.com/langchain-ai/langgraph/releases)
- [Commits](langchain-ai/langgraph@0.4.4...0.4.6)

Updates `litellm` from 1.88.6 to 1.100.4
- [Release notes](https://github.com/BerriAI/litellm/releases)
- [Commits](BerriAI/litellm@v1.88.6...v1.100.4)

---
updated-dependencies:
- dependency-name: nltk
  dependency-version: 3.10.4
  dependency-type: indirect
  dependency-group: uv
- dependency-name: litellm
  dependency-version: 1.100.4
  dependency-type: direct:production
  dependency-group: uv
- dependency-name: oauthlib
  dependency-version: 4.0.1
  dependency-type: indirect
  dependency-group: uv
- dependency-name: authlib
  dependency-version: 1.9.1
  dependency-type: indirect
  dependency-group: uv
- dependency-name: fastmcp
  dependency-version: 3.4.10
  dependency-type: indirect
  dependency-group: uv
- dependency-name: joserfc
  dependency-version: 1.7.6
  dependency-type: indirect
  dependency-group: uv
- dependency-name: pyasn1
  dependency-version: 0.6.5
  dependency-type: indirect
  dependency-group: uv
- dependency-name: pyasn1
  dependency-version: 0.6.5
  dependency-type: indirect
  dependency-group: uv
- dependency-name: litellm
  dependency-version: 1.100.4
  dependency-type: direct:production
  dependency-group: uv
- dependency-name: langgraph-sdk
  dependency-version: 0.4.6
  dependency-type: indirect
  dependency-group: uv
- dependency-name: langgraph-sdk
  dependency-version: 0.4.12
  dependency-type: indirect
  dependency-group: uv
- dependency-name: litellm
  dependency-version: 1.100.4
  dependency-type: direct:production
  dependency-group: uv
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… errors, AI, references)

Real merge of main at f66e696 (the commit before #1154), so v1's merge
base with main moves. $experiment_exposure (#1154) and later come in a
second merge.

v1 already carries main through 7.83.1 and its reference update
(06e5611) from the squashed sync #1175, so every path is resolved
against 06e5611, not the old merge base: paths main has not changed
since keep v1's version, paths v1 has not changed take main's, and the
rest are a three-way merge of v1, 06e5611 and main. references/ ends
equal to main's (18 HogRef files; the 163 #1225 removed stay removed).

Resolutions:
- posthog/capture_v1.py, posthog/test/test_capture_v1.py: stay removed.
  main's only change is #1178 (7.x opt-in v1 retries on 404/429); v1
  retries those since #1132. AGENTS.md keeps v1's capture note.
- posthog/client.py: v1's super_properties/super_options docs plus
  main's migrated_from doc (emails).
- posthog/ai/anthropic/anthropic_async.py: keep v1 without
  `from posthog import setup`; #1146's logging import is kept.
- sdk_compliance_adapter: v1's capability list plus #1179's
  feature_flag_payloads and its /get_feature_flag_payload route; harness
  image stays 1.26.0 (the harness bump is separate); workflow keeps v1's
  jobs with main's action pins.
- examples crewai/dspy/litellm: v1's local posthog source plus #1222's
  versions (litellm 1.100.4, oauthlib 4.0.1); locks regenerated.
- references/public_api_snapshot.txt: regenerated; the only changes are
  main's email API and migrated_from.

Test-only edits to tests main added, for v1's API: AI wrapper tests read
capture_ai (#1189, #1205, #1211); #1210's test stubs _send_v1_batch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…flag cache URLs)

Second real merge, of main at c60e7a6. #1154 sends $experiment_exposure
for config version 2 experiment splits and pins their flag attribution so
no super property changes or adds it.

Resolutions (#1154 into v1's layered pipeline):
- The pin rides in _EventDefaults and applies at the end of
  _fill_event_defaults, after every layer and the library pair, then the
  property allowlist; main applies it after its super property merge and
  before the allowlist. One place serves both clients: the sync client
  passes it to _build_event_defaults, and AsyncPosthog, which fills
  defaults later in _process_event, passes it through _event_defaults.
- main's own enrichment block (super property merge, $release_id,
  $is_server) stays out: v1's layers replace it.
- _enqueue and capture take v1's layer arguments plus #1154's
  pinned_properties and before_send_drops; v1's _report_capture_failure
  stays. args.py keeps v1's _capture_trace_context next to #1154's fields.

Test-only edits to #1154's tests, for v1:
- Senders are v1's (_send_v1_batch, async_send_v1_batch); capture_mode is
  gone.
- The parity recorders keep the event in before_send (v1 reads flag
  events there) instead of returning an event UUID, which v1's hook
  rejects; returning the event is what makes capture accept it.
- The async attribution test records events in before_send, after the
  pin, not from _prepare_event, which on v1 runs before defaults fill and
  would pass without the pin. Removing the pin fails it, and the sync
  tests.
- Config version 1 calls: super properties fill every key the event
  leaves unset and the event's own values win (8.0's rule), instead of
  1.x's override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run with --major 8, so the 7.x and latest pages stay as main generated
them. The 8.x page now documents 8.0 as merged here (emails, per-event
$lib, MCP and AI options, Celery context options); the migration page
matches docs/migration-7.x-to-8.0.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@eli-r-ph eli-r-ph self-assigned this Nov 11, 2026
@eli-r-ph
eli-r-ph requested a review from a team November 11, 2026 21:05
main's benchmark harness (#1134, #1135) drove 7.x's legacy sender: the
client passed gzip=False and capture_mode="v0", and the loopback server
answered /batch with {"status":1}. 8.0 removed both options and only
speaks capture v1, so every benchmark worker failed on this branch, and
would on main after 8.0 merges.

- workload.py and run.py: drop gzip, capture_mode and
  POSTHOG_CAPTURE_MODE; requests are uncompressed capture v1.
- server.py: receive /i/v1/analytics/events and answer with a result per
  event uuid, so the SDK reads each event as delivered. The delivery
  checks are unchanged.
- test_benchmarks.py posts its synthetic batch to the v1 path; README
  describes the v1 transport.

benchmarks/test_benchmarks.py: 28 tests pass (18 errored before).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

SDK performance: posthog-python

1 change worth reviewing: 1 observed regression.

Flags require at least a 5% change in the median and all six pairs agreeing on direction. This is a noise filter, not proof of a performance change or a CI gate.

Test Measurement Before PR With PR Change
Capture and deliver events (32-byte payload, burst) Time per operation 80.98 µs/op 86.26 µs/op 6.5% more time
View all results
Test Measurement Before PR With PR Change Assessment
Capture and deliver events (32-byte payload, burst)
Shard: capture
Managed heap after settling — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​; head:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Managed heap after settling — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​; head:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​
Capture and deliver events (32-byte payload, steady)
Shard: capture
Managed heap after settling — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​; head:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Managed heap after settling — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​; head:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​
Queue an event (32-byte payload)
Shard: capture
Allocated memory per operation — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​; head:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​
Queue an event (32-byte payload)
Shard: capture
Allocations per operation — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​; head:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​
Queue an event (4,096-byte payload)
Shard: capture
Allocated memory per operation — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​; head:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​
Queue an event (4,096-byte payload)
Shard: capture
Allocations per operation — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​; head:​ Background preparation has no public completion boundary before flush; see capture.​drain for full-pipeline allocations.​
Evaluate 1,000 flags under concurrent load
Shard: flags-memory
Managed heap after settling — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​; head:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​
Refresh 1,000 flag definitions
Shard: flags-memory
Managed heap after settling — — — unavailable; base:​ unsupported; head:​ unsupported; base:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​; head:​ CPython has no uninstrumented total managed-heap byte counter; tracing would distort this RSS pass.​
Capture and deliver events (32-byte payload, burst)
Shard: capture
Allocated memory per operation 131.85 KiB/op 133.2 KiB/op 1% more memory Not flagged
Capture and deliver events (32-byte payload, burst)
Shard: capture
Allocations per operation 1,325.53 count/op 1,337.53 count/op 0.9% more Not flagged
Capture and deliver events (32-byte payload, burst)
Shard: capture
Time per operation 80.98 µs/op 86.26 µs/op 6.5% more time Worth reviewing: observed regression
Capture and deliver events (32-byte payload, burst)
Shard: capture
Idle process memory (RSS) 37.55 MiB 37.61 MiB 0.1% more memory Not flagged
Capture and deliver events (32-byte payload, burst)
Shard: capture
Observed peak process memory (RSS) 40 MiB 40.04 MiB <0.1% more memory Not flagged
Capture and deliver events (32-byte payload, burst)
Shard: capture
Process memory after settling (RSS) 40 MiB 40.04 MiB <0.1% more memory Not flagged
Capture and deliver events (32-byte payload, burst)
Shard: capture
Process CPU time per operation 80.89 µs/op 86.82 µs/op 7.3% more time Not flagged
Capture and deliver events (32-byte payload, burst)
Shard: capture
Lifetime peak process memory (RSS) 40.2 MiB 40.22 MiB <0.1% more memory Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Allocated memory per operation 183.93 KiB/op 185.28 KiB/op 0.7% more memory Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Allocations per operation 1,317.52 count/op 1,329.52 count/op 0.9% more Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Time per operation 115.13 µs/op 111.93 µs/op 2.8% less time Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Idle process memory (RSS) 41.03 MiB 41.29 MiB 0.6% more memory Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Observed peak process memory (RSS) 46.81 MiB 47.04 MiB 0.5% more memory Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Process memory after settling (RSS) 46.81 MiB 47.04 MiB 0.5% more memory Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Process CPU time per operation 109.07 µs/op 104.72 µs/op 4% less time Not flagged
Capture and deliver events (4,096-byte payload, burst)
Shard: capture
Lifetime peak process memory (RSS) 51.3 MiB 51.51 MiB 0.4% more memory Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Allocated memory per operation 136.92 KiB/op 138.27 KiB/op 1% more memory Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Allocations per operation 1,370.44 count/op 1,382.42 count/op 0.9% more Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Time per operation 96.89 µs/op 100.23 µs/op 3.4% more time Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Idle process memory (RSS) 37.39 MiB 37.57 MiB 0.5% more memory Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Observed peak process memory (RSS) 37.58 MiB 37.73 MiB 0.4% more memory Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Process memory after settling (RSS) 37.58 MiB 37.73 MiB 0.4% more memory Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Process CPU time per operation 95.81 µs/op 99.59 µs/op 3.9% more time Not flagged
Capture and deliver events (32-byte payload, steady)
Shard: capture
Lifetime peak process memory (RSS) 39.46 MiB 39.46 MiB No observed change Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Allocated memory per operation 189.1 KiB/op 190.45 KiB/op 0.7% more memory Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Allocations per operation 1,362.37 count/op 1,374.37 count/op 0.9% more Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Time per operation 113.45 µs/op 113.59 µs/op 0.1% more time Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Idle process memory (RSS) 40.83 MiB 41.01 MiB 0.4% more memory Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Observed peak process memory (RSS) 41.33 MiB 41.48 MiB 0.3% more memory Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Process memory after settling (RSS) 40.91 MiB 41.06 MiB 0.4% more memory Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Process CPU time per operation 107.58 µs/op 107.39 µs/op 0.2% less time Not flagged
Capture and deliver events (4,096-byte payload, steady)
Shard: capture
Lifetime peak process memory (RSS) 41.23 MiB 41.37 MiB 0.3% more memory Not flagged
Queue an event (32-byte payload)
Shard: capture
Caller-thread CPU time per operation 59.79 µs/op 58.56 µs/op 2.1% less time Not flagged
Queue an event (32-byte payload)
Shard: capture
Time per operation 61.02 µs/op 58.55 µs/op 4% less time Not flagged
Queue an event (4,096-byte payload)
Shard: capture
Caller-thread CPU time per operation 62.62 µs/op 66.82 µs/op 6.7% more time Not flagged
Queue an event (4,096-byte payload)
Shard: capture
Time per operation 62.62 µs/op 66.82 µs/op 6.7% more time Not flagged
Evaluate all 10 flags
Shard: flags-scaling
Allocated memory per operation 198.17 KiB/op 200.98 KiB/op 1.4% more memory Not flagged
Evaluate all 10 flags
Shard: flags-scaling
Allocations per operation 2,276.33 count/op 2,339.33 count/op 2.8% more Not flagged
Evaluate all 10 flags
Shard: flags-scaling
Time per operation 88.79 µs/op 88.16 µs/op 0.7% less time Not flagged
Evaluate all 10 flags
Shard: flags-scaling
Process CPU time per operation 88.79 µs/op 88.16 µs/op 0.7% less time Not flagged
Evaluate all 100 flags
Shard: flags-scaling
Allocated memory per operation 1.9 MiB/op 1.93 MiB/op 1.4% more memory Not flagged
Evaluate all 100 flags
Shard: flags-scaling
Allocations per operation 22,383.56 count/op 23,013.56 count/op 2.8% more Not flagged
Evaluate all 100 flags
Shard: flags-scaling
Time per operation 851.38 µs/op 858.97 µs/op 0.9% more time Not flagged
Evaluate all 100 flags
Shard: flags-scaling
Process CPU time per operation 851.41 µs/op 858.64 µs/op 0.8% more time Not flagged
Evaluate all 1,000 flags
Shard: flags-scaling
Allocated memory per operation 18.97 MiB/op 19.24 MiB/op 1.4% more memory Not flagged
Evaluate all 1,000 flags
Shard: flags-scaling
Allocations per operation 223,354.94 count/op 229,654.94 count/op 2.8% more Not flagged
Evaluate all 1,000 flags
Shard: flags-scaling
Time per operation 8.6 ms/op 8.54 ms/op 0.6% less time Not flagged
Evaluate all 1,000 flags
Shard: flags-scaling
Process CPU time per operation 8.6 ms/op 8.54 ms/op 0.6% less time Not flagged
Evaluate 1,000 flags under concurrent load
Shard: flags-memory
Idle process memory (RSS) 43.47 MiB 43.07 MiB 0.9% less memory Not flagged
Evaluate 1,000 flags under concurrent load
Shard: flags-memory
Observed peak process memory (RSS) 43.75 MiB 43.38 MiB 0.9% less memory Not flagged
Evaluate 1,000 flags under concurrent load
Shard: flags-memory
Process memory after settling (RSS) 43.75 MiB 43.38 MiB 0.9% less memory Not flagged
Evaluate 1,000 flags under concurrent load
Shard: flags-memory
Lifetime peak process memory (RSS) 43.7 MiB 43.88 MiB 0.4% more memory Not flagged
Refresh 1,000 flag definitions
Shard: flags-memory
Idle process memory (RSS) 43.12 MiB 43.06 MiB 0.1% less memory Not flagged
Refresh 1,000 flag definitions
Shard: flags-memory
Observed peak process memory (RSS) 43.12 MiB 43.07 MiB 0.1% less memory Not flagged
Refresh 1,000 flag definitions
Shard: flags-memory
Process memory after settling (RSS) 43.12 MiB 43.07 MiB 0.1% less memory Not flagged
Refresh 1,000 flag definitions
Shard: flags-memory
Lifetime peak process memory (RSS) 42.98 MiB 42.86 MiB 0.3% less memory Not flagged
Evaluate one boolean flag
Shard: flags-single
Allocated memory per operation 14.74 KiB/op 15.06 KiB/op 2.2% more memory Not flagged
Evaluate one boolean flag
Shard: flags-single
Allocations per operation 166.76 count/op 173.76 count/op 4.2% more Not flagged
Evaluate one boolean flag
Shard: flags-single
Time per operation 9.81 µs/op 9.75 µs/op 0.6% less time Not flagged
Evaluate one boolean flag
Shard: flags-single
Process CPU time per operation 9.81 µs/op 9.75 µs/op 0.6% less time Not flagged
Evaluate a cohort-filtered flag
Shard: flags-single
Allocated memory per operation 22.81 KiB/op 23.14 KiB/op 1.4% more memory Not flagged
Evaluate a cohort-filtered flag
Shard: flags-single
Allocations per operation 263.27 count/op 270.27 count/op 2.7% more Not flagged
Evaluate a cohort-filtered flag
Shard: flags-single
Time per operation 12.98 µs/op 13.04 µs/op 0.4% more time Not flagged
Evaluate a cohort-filtered flag
Shard: flags-single
Process CPU time per operation 12.98 µs/op 13.04 µs/op 0.4% more time Not flagged
Evaluate a flag (early match)
Shard: flags-single
Allocated memory per operation 35.4 KiB/op 35.73 KiB/op 0.9% more memory Not flagged
Evaluate a flag (early match)
Shard: flags-single
Allocations per operation 380.27 count/op 387.27 count/op 1.8% more Not flagged
Evaluate a flag (early match)
Shard: flags-single
Time per operation 18.49 µs/op 18.52 µs/op 0.2% more time Not flagged
Evaluate a flag (early match)
Shard: flags-single
Process CPU time per operation 18.49 µs/op 18.52 µs/op 0.2% more time Not flagged
Evaluate a flag (late match)
Shard: flags-single
Allocated memory per operation 53.28 KiB/op 53.61 KiB/op 0.6% more memory Not flagged
Evaluate a flag (late match)
Shard: flags-single
Allocations per operation 569.27 count/op 576.27 count/op 1.2% more Not flagged
Evaluate a flag (late match)
Shard: flags-single
Time per operation 26.2 µs/op 26.39 µs/op 0.7% more time Not flagged
Evaluate a flag (late match)
Shard: flags-single
Process CPU time per operation 26.2 µs/op 26.39 µs/op 0.7% more time Not flagged
Evaluate a flag (no match)
Shard: flags-single
Allocated memory per operation 51.83 KiB/op 52.16 KiB/op 0.6% more memory Not flagged
Evaluate a flag (no match)
Shard: flags-single
Allocations per operation 549.77 count/op 556.77 count/op 1.3% more Not flagged
Evaluate a flag (no match)
Shard: flags-single
Time per operation 24.91 µs/op 25.27 µs/op 1.5% more time Not flagged
Evaluate a flag (no match)
Shard: flags-single
Process CPU time per operation 24.91 µs/op 25.27 µs/op 1.5% more time Not flagged
Evaluate a property-filtered flag
Shard: flags-single
Allocated memory per operation 17.52 KiB/op 17.85 KiB/op 1.9% more memory Not flagged
Evaluate a property-filtered flag
Shard: flags-single
Allocations per operation 191.27 count/op 198.27 count/op 3.7% more Not flagged
Evaluate a property-filtered flag
Shard: flags-single
Time per operation 10.68 µs/op 10.72 µs/op 0.4% more time Not flagged
Evaluate a property-filtered flag
Shard: flags-single
Process CPU time per operation 10.68 µs/op 10.72 µs/op 0.4% more time Not flagged
Evaluate a regex-filtered flag
Shard: flags-single
Allocated memory per operation 18.91 KiB/op 19.24 KiB/op 1.7% more memory Not flagged
Evaluate a regex-filtered flag
Shard: flags-single
Allocations per operation 203.77 count/op 210.77 count/op 3.4% more Not flagged
Evaluate a regex-filtered flag
Shard: flags-single
Time per operation 11.45 µs/op 11.45 µs/op <0.1% more time Not flagged
Evaluate a regex-filtered flag
Shard: flags-single
Process CPU time per operation 11.45 µs/op 11.45 µs/op <0.1% more time Not flagged
Evaluate a rollout flag
Shard: flags-single
Allocated memory per operation 13.72 KiB/op 14.05 KiB/op 2.4% more memory Not flagged
Evaluate a rollout flag
Shard: flags-single
Allocations per operation 153.76 count/op 160.76 count/op 4.6% more Not flagged
Evaluate a rollout flag
Shard: flags-single
Time per operation 9.16 µs/op 9.11 µs/op 0.5% less time Not flagged
Evaluate a rollout flag
Shard: flags-single
Process CPU time per operation 9.16 µs/op 9.11 µs/op 0.5% less time Not flagged
Evaluate a multivariate flag
Shard: flags-single
Allocated memory per operation 14.89 KiB/op 15.22 KiB/op 2.2% more memory Not flagged
Evaluate a multivariate flag
Shard: flags-single
Allocations per operation 170.77 count/op 177.77 count/op 4.1% more Not flagged
Evaluate a multivariate flag
Shard: flags-single
Time per operation 10.08 µs/op 10.04 µs/op 0.4% less time Not flagged
Evaluate a multivariate flag
Shard: flags-single
Process CPU time per operation 10.08 µs/op 10.04 µs/op 0.4% less time Not flagged

Values are medians of paired measurements. Not flagged does not mean unchanged. Unavailable means a measurement could not be provided, not zero. Estimated allocation measurements retain their instrumentation uncertainty. RSS measures the whole process, not just SDK allocations; observed peaks are sampled checkpoints. Paired ranges in the diagnostics describe variability, not confidence intervals.

Each shard uses its own runner; compare Before PR against With PR within a row, not across shards.

Compared 4f60c3392bc93a53ede2e61d9079447ce0d950a8 → 80563b2fb1dc6dc874d36e30eb673202f7269d1e.

Full report and diagnostics

102 metric rows; 102 rows shown in all results. Omitted or shortened data remain in comparison.json, including per-runner environments, resource limits, measurement methods, samples and checkpoints.

@eli-r-ph
eli-r-ph marked this pull request as ready for review November 11, 2026 22:01
@veria-ai

veria-ai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

PR overview

No security issues were found in this pull request.

Reviews

This branch has not been deployed

No deployments
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.