Skip to content

fix: emit experience-activation (bucketing) tracking events with per-call enable_tracking control - #51

Merged
abbaseya merged 4 commits into
mainfrom
fix/bucketing-activation-events-parity
Jun 19, 2026
Merged

fix: emit experience-activation (bucketing) tracking events with per-call enable_tracking control#51
abbaseya merged 4 commits into
mainfrom
fix/bucketing-activation-events-parity

Conversation

@abbaseya

@abbaseya abbaseya commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes the cross-SDK exposure-tracking parity gap (Story 2.5 / FR40a + FR8). The released Python SDK (v1.0.0) tracked conversions only — a visitor who was bucketed but never converted produced no exposure signal, so experiment reports could show conversions without the visitor denominator. Every sibling SDK (JS reference, Android FR18, iOS FR19/FR39) already delivers a standalone bucketing/activation event; this brings the Python SDK to parity.

When a visitor buckets into a variation, the SDK now enqueues a bucketing event delivered to the metrics tracking endpoint, batched alongside conversion events in the same events array, gated per-call by a new keyword-only enable_tracking=True parameter on Context.run_experience / run_experiences.

Strictly additive — intended release bump = PATCH (1.0.1)

This is a parity defect fix, not a feature. It is strictly additive:

  • enable_tracking is keyword-only with default True — additive to the existing run_experience / run_experiences signatures (no positional breakage).
  • The ExperienceResult return value and all local-evaluation behavior are byte-for-byte unchanged — the bucketing event is a pure side effect.
  • The conversion-event wire output, ConversionEvent, and evaluation determinism are untouched.
  • No new LifecycleEvent member (BUCKETING already exists).

All commits and this PR title are fix: / test:-typed with no ! and no BREAKING CHANGE: footer, so semantic-release computes 1.0.1 (PATCH) — not a minor or major bump.

Wire shape (JS parity)

{"eventType": "bucketing", "data": {"experienceId": "<experience_id>", "variationId": "<variation_id>"}}

VisitorTrackingEvents wrapper + BucketingEvent body parity (types.gen.ts:2467-2473, :2486-2497); no timestamp, no extra keys; ids string-typed (.toString() parity). A golden-fixture parity test pins these bytes.

Behavior

  • AC#1 — one bucketing event per bucket, batched with conversions in the same per-visitor events[].
  • AC#2enable_tracking=False suppresses the enqueue; the returned ExperienceResult is identical.
  • AC#3 — dedup by (visitor_id, experience_id) via the existing DataStore boundary (bucketing: namespaced, collision-safe JSON key); at most one event per visitor-experience in scope.
  • AC#4 — a directly-constructed Context (no Tracker) is a safe no-op.
  • AC#5 — emission flows through the existing EventBus / LifecycleEvent.BUCKETING; a raising handler does not break tracking flow.
  • AC#6 — shares the existing queue/flush machinery and transport (User-Agent: ConvertAgent/1.0 unchanged); no blocking network I/O on the enqueue path.

Deferred / resolved design decision (dedup key & scope)

The JS reference (data-manager.ts:679-685) enqueues the bucketing event unconditionally when enableTracking is set and leans on the server-side enrichData flag for dedup — it has no client-side (visitor, experience) gate. Story 2.5 AC#3 is the authoritative contract and explicitly mandates client-side dedup keyed by (visitor_id, experience_id), mirroring the Story 2.3 conversion dedup. This PR honors the story: it implements an explicit client-side dedup marker (evaluate_bucketing_dedup) rather than the JS unconditional-enqueue behavior. run_experiences() emits one event per newly-bucketed qualified experience (the story Dev Notes default).

Tests / gates

  • New: tests/test_bucketing_events.py (unit, ACs 1-5) + tests/parity/test_js_bucketing_event_parity.py + tests/parity/fixtures/bucketing_event_vectors.json (golden wire-byte parity).
  • Full suite: 857 passed. mypy src/ clean. ruff check src/ tests/ clean. Parity suite green.
  • Parity-test setup deduplicated into a shared helper to respect the SonarCloud new-code-duplication gate.
  • Code review (convert-code-reviewer): clean on all primary checks (no breaking change, wire shape, enable_tracking gate, dedup, mixed-batch flush, EventBus isolation, layering, PATCH-qualifying); one duplication finding fixed.

References Story 2.5.

🤖 Generated with Claude Code


abbaseya and others added 3 commits June 19, 2026 17:26
…king

Story 2.5. Tests for the additive bucketing-event delivery path and keyword-only
enable_tracking control. Implementation follows (GREEN).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…call enable_tracking control

Closes the cross-SDK exposure-tracking parity gap (Story 2.5). A visitor that
buckets into a variation now produces a standalone bucketing/activation event
delivered to the metrics tracking endpoint, batched alongside conversion events
in the same events array, so experiment exposure is recorded instead of only
conversions.

Strictly additive: run_experience / run_experiences gain a keyword-only
enable_tracking=True parameter; the ExperienceResult return value and all
local-evaluation behavior are unchanged. Bucketing events dedup by
(visitor_id, experience_id) via the existing DataStore boundary and flow through
the existing Tracker queue/transport and the LifecycleEvent.BUCKETING EventBus.

Wire shape: {"eventType":"bucketing","data":{"experienceId","variationId"}}
(JS VisitorTrackingEvents/BucketingEvent parity; no timestamp).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code-review follow-up (Story 2.5). Extract the event-construction + payload
extraction into _serialize_event and reuse a single parametrize decorator so the
parity tests stay under the SonarCloud new-code-duplication gate. No behavior
change; 41 bucketing tests + full suite (857) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@abbaseya abbaseya self-assigned this Jun 19, 2026
@abbaseya
abbaseya requested a review from usmanabbas7 June 19, 2026 14:41
Comment thread tests/test_bucketing_events.py

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request implements Story 2.5, introducing tracking and deduplication for bucketing activation events. It adds a new BucketingEvent domain object, a BucketingEventPayload for lifecycle events, and a dedicated serializer build_bucketing_payload matching the JS-SDK wire contract. The Context evaluation methods (run_experience and run_experiences) now accept an enable_tracking keyword-only parameter to control whether bucketing events are enqueued. Deduplication logic has been added via evaluate_bucketing_dedup to ensure each visitor-experience pair produces at most one bucketing event. Extensive unit, integration, and JS-parity tests have been added to verify these changes. No review comments were provided, so there is no feedback to address.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@abbaseya abbaseya closed this Jun 19, 2026
@abbaseya abbaseya reopened this Jun 19, 2026
Empty commit to fire pull_request:synchronize so CodeQL default setup produces an
ingested analysis for this PR (its original analysis predated code scanning being
enabled, leaving the code_scanning merge-protection rule with no PR results to evaluate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@abbaseya
abbaseya merged commit ab273ec into main Jun 19, 2026
27 checks passed
@abbaseya
abbaseya deleted the fix/bucketing-activation-events-parity branch June 19, 2026 16:18
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.

2 participants