feat(capture): v1 wire serialization transforms (capture v1, 2/6)#702
Draft
eli-r-ph wants to merge 2 commits into
Draft
feat(capture): v1 wire serialization transforms (capture v1, 2/6)#702eli-r-ph wants to merge 2 commits into
eli-r-ph wants to merge 2 commits into
Conversation
Contributor
|
Reviews (1): Last reviewed commit: "feat(capture): add v1 wire serialization..." | Re-trigger Greptile |
Contributor
posthog-python Compliance ReportDate: 2026-06-28 00:55:07 UTC ✅ All Tests Passed!45/45 tests passed Capture Tests✅ 29/29 tests passed View Details
Feature_Flags Tests✅ 16/16 tests passed View Details
|
This was referenced Jun 27, 2026
531fb62 to
33ebdfb
Compare
7d4b037 to
419d8ac
Compare
33ebdfb to
b508cf9
Compare
419d8ac to
41c6948
Compare
Add posthog/capture_v1.py with the pure (no-I/O) transform layer for /i/v1/analytics/events: - to_v1_event(): lifts sentinel properties into the typed options object (with the $ignore_sent_at -> disable_skew_correction rename), promotes $session_id/$window_id to top-level fields, relocates top-level $set/$set_once into properties (v1 has no top-level form), and strips $lib/$lib_version (server injects them from PostHog-Sdk-Info). Options are coerced to native JSON types or omitted, since a wrong type would 400 the whole batch. Pure: the input message is not mutated. - build_v1_batch_body(): the api_key/sent_at-free envelope with a tz-aware RFC3339 created_at. - Shared constants: path, required header names, result codes, retryable/ terminal status sets. Stacked on the capture_mode scaffolding; still inert (nothing calls these yet).
41c6948 to
d6d4aa2
Compare
b508cf9 to
b139d02
Compare
Hold the coercer function directly in _OPTION_SENTINELS instead of a stringly-typed name keyed through a side _COERCERS dict, removing a KeyError foot-gun and tightening the types.
d6d4aa2 to
4ee420a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
Second PR in the stacked Capture V1 series (stacked on #701). Adds the pure, no-I/O serialization layer that turns a legacy-shaped queued message into a
/i/v1/analytics/eventswire event. Still inert — nothing calls these functions yet (transport + wiring come in later PRs).New module
posthog/capture_v1.py:to_v1_event(msg)— dict-to-dict transform proven against the server contract (rust/capture/src/v1/analytics/types.rs) and posthog-go'scapture_v1.go:optionsobject, including the$ignore_sent_at->disable_skew_correctionrename.$session_id/$window_idto top-level string fields.$set/$set_onceintoproperties— v1 has no top-level form, and the legacyset()/set_once()builders emit them at the top level, so without this person-property updates would silently vanish.$lib/$lib_version(the server injects them from the requiredPostHog-Sdk-Infoheader).optionsstrictly; a wrong type (e.g.cookieless_mode: "true") 400s the entire batch, so a bad value is dropped rather than forwarded.build_v1_batch_body(events, historical_migration)— theapi_key/sent_at-free envelope with a tz-aware RFC3339created_at(historical_migrationomitted when false).💚 How did you test it?
New
posthog/test/test_capture_v1.py(47 cases, parameterized): bool/string coercion across all variants; per-sentinel lift + rename + coercion; bad-coercion omitted-but-removed; top-level string sentinels;options == {}when empty;$libstripping; non-wire keys (e.g.type) not leaked; input-not-mutated; top-level$set/$set_oncerelocation incl. merge-precedence with an existingproperties.$set;$groupsleft untouched; tz-aware/passthrough/default timestamps; envelope shape + RFC3339created_at+historical_migrationtoggling.ruff format/checkclean;mypyclean on the new module; regeneratedreferences/public_api_snapshot.txt.📝 Checklist
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Authored with Cursor (Claude Opus 4.8) per the agreed plan. Architecture note: posthog-go builds v1 events from typed message structs, but posthog-python's queue holds already-assembled legacy dicts, so this is a dict-to-dict transform applied at send time — the wire shape and sentinel table are identical to go's. The strict-type/coerce-or-omit and
$setrelocation behaviors were verified directly against the RustEvent/Optionsstructs and their serde tests.