Skip to content

feat(analytics): hierarchical groups support - #201

Draft
tylerjroach wants to merge 19 commits into
masterfrom
hierarchical-groups
Draft

tylerjroach wants to merge 19 commits into
masterfrom
hierarchical-groups

Conversation

@tylerjroach

Copy link
Copy Markdown
Contributor

Adds hierarchical groups (HG) support: a MixpanelIdentity carrying user and company ids, HG-aware profile updates, and company-bound group writes. HG behavior is gated on the SDK's hierarchical groups init flag; projects without it behave as before.

Draft, opened for an early automated review. Not ready to merge.

Commits

  • feat(analytics): hierarchical groups support
  • feat(analytics): refuse alias on hierarchical groups projects
  • feat(analytics): refuse merge on hierarchical groups projects
  • feat(flags): resolve local assignment keys from context_value_template
  • feat(analytics): raise for unsupported group_update operations on hierarchical groups projects
  • feat(analytics): add company_* profile methods to for_company clients
  • chore(analytics): point the company-only identity error at the company profile API
  • revert(analytics): drop the company profile methods from for_company clients
  • feat(analytics): infer the company for $company_id group profile updates
  • feat(analytics): restore the company profile on for_company clients
  • feat(flags): build a flag context from a MixpanelIdentity
  • feat(analytics): refuse a user from another company on a for_company client
  • feat(analytics): send a user without a company as given on hierarchical groups
  • Document MixpanelIdentity on every people method that accepts one

🤖 Generated with Claude Code

tylerjroach and others added 14 commits November 2, 2026 09:58
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rarchical groups projects

$add, $append and $merge were logged and still sent. They now raise UnsupportedOperationError
before anything is sent, like the people operations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y profile API

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

The company profile is updated through the existing group methods instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings back the company profile methods in place of inferring the company from $company_id
group writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MixpanelIdentity.to_flag_context puts the ids under the keys flags read,
the groups at the top level and the custom properties under
custom_properties, so callers do not hand-write $user_id and $company_id.

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

A people update through a client bound with for_company used the
identity's company and ignored the bound one, so a mismatched identity
silently updated the other company's user. It now raises ValueError and
sends nothing.

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

An identity with a user id but no company or device id threw on every
call. It is valid to send, like a plain distinct id, so events now send
it as given. User profile updates still need the company and fail with
the existing company-required error.

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

greptile-apps Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The reviewed changes appear safe to merge; the previous wrong-profile issue is fixed.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Profile update] --> B{Hierarchical groups enabled?}
    B -->|No| C[Send to /engage]
    B -->|Yes| D[Read company and user]
    D --> E{Profile fields agree?}
    E -->|No| F[Raise ValueError; send nothing]
    E -->|Yes| G[Send user group update to /groups]
Loading

Reviews (6) · Last reviewed commit: "fix: refuse people_update profile keys t..." · Reviewed by Greptile

Comment thread mixpanel/__init__.py Resolved
Comment thread mixpanel/__init__.py Resolved
Comment thread mixpanel/__init__.py Resolved
Comment thread mixpanel/__init__.py Resolved
Comment thread README.md Resolved
@codecov

codecov Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.71950% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.08%. Comparing base (091b056) to head (7ba9c31).

Files with missing lines Patch % Lines
mixpanel/__init__.py 98.80% 1 Missing and 1 partial ⚠️
mixpanel/flags/local_feature_flags.py 96.15% 0 Missing and 1 partial ⚠️
mixpanel/flags/test_remote_feature_flags.py 98.73% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #201      +/-   ##
==========================================
+ Coverage   96.39%   98.08%   +1.68%     
==========================================
  Files          15       17       +2     
  Lines        2888     4287    +1399     
  Branches      176      255      +79     
==========================================
+ Hits         2784     4205    +1421     
+ Misses         67       45      -22     
  Partials       37       37              
Flag Coverage Δ
openfeature-provider 46.96% <20.51%> (-4.29%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…l groups profile updates

- A user profile update for an identity without a company now uses the
  for_company client's bound company, and the identity's or bound company
  wins over a $company_id in meta. Unbound clients still fall back to meta.
- A None or empty $company_id no longer counts as a company, so the update
  raises CompanyRequiredError instead of being sent.
- Unsupported operations are checked on the record after meta is merged, so
  meta cannot send $add, $append or $merge to /groups.
- A for_company client no longer shuts down the flag providers it shares
  with its parent when used as a context manager.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tylerjroach tylerjroach changed the title Hierarchical groups support feat(analytics): hierarchical groups support Nov 4, 2026
tylerjroach and others added 3 commits November 4, 2026 16:20
Move the local_feature_flags import in the flags tests to the top of the
file (ruff PLC0415), and exclude "{" from the placeholder capture so a
template full of "{" can't make matching slow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Public docs are the source of truth for hierarchical groups usage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on keys on hierarchical groups

- Exposure events on hierarchical groups keep $user_id when the flag context
  has no $company_id; the skip rule ($company_id or distinct_id) is unchanged.
- MixpanelIdentity and for_company accept string and integer ids (not bool)
  and refuse other types with ValueError; for_company(0) is accepted and the
  bound company is compared as a string.
- people_update on hierarchical groups no longer treats $company_id,
  $group_key or $group_id as unknown operations.
- track() docstring: distinct_id is also sent when properties carry one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread mixpanel/__init__.py Resolved
…d distinct id

On a hierarchical groups client, $group_key, $group_id or $company_id in a
people_update message or meta could retarget the update away from the user
named by the combined $distinct_id. Values that repeat the parsed identity are
accepted; conflicting ones raise ValueError and nothing is sent.

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

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.

1 participant