Skip to content

feat(analytics): hierarchical groups support - #476

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

tylerjroach wants to merge 16 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): return null from getCompany when there is no company
  • feat(flags): send $user_id and $company_id in hierarchical groups flag requests
  • feat(analytics): reject list groups in trackWithGroups on hierarchical groups projects

🤖 Generated with Claude Code

tylerjroach and others added 4 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>
…g requests

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

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: 4/5

[Medium impact] The changes affect identity, event, and profile flows only for apps that enable hierarchical groups.

The PR still needs the earlier fix that prevents caller-supplied distinct_id values from surviving on identified hierarchical-groups events.

Findings

  1. P1 Caller identity still survives ▶

Reviews (12) · Last reviewed commit: "refactor: leave deviceId off the Mixpane..." · Reviewed by Greptile

Comment thread javascript/mixpanel-main.js Resolved
Comment thread index.js Resolved
Comment thread javascript/mixpanel-main.js Resolved
… user delete on opt out

On hierarchical groups projects in JavaScript mode, a /groups write keyed by
$company_id now uses its own group id as $company_id instead of the currently
identified company, so a saved getCompany() handle keeps writing to its
company after identify or reset. Opting out now clears pending /groups
updates before queuing the user delete, so only the delete is flushed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread javascript/mixpanel-main.js Resolved
tylerjroach and others added 2 commits November 4, 2026 14:26
The hierarchical groups opt-out clears the /groups queue before queuing
the user delete. A batch already sending then removed the first records
from the new queue when it finished, which could drop the delete unsent.
The queue now counts clears, and a batch sent before a clear leaves the
queue alone. Only the hierarchical groups opt-out clears a queue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A batch rejected with a 400 removed the first queued record even after a
clear, which could drop the hierarchical groups opt-out delete. It now
leaves the queue alone when a clear happened while it was sending, as a
sent batch already does.

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
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread index.js Resolved
Comment thread javascript/mixpanel-main.js Resolved
tylerjroach and others added 6 commits November 4, 2026 16:21
identify with an identity object called the bridge in a later Promise
callback, so a track() made right after it reached the native SDK first
and was recorded under the previous identity. Call it in the executor,
as the string overload does.

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

optOutTracking set the opt-out flag only after clearing the /groups queue and
sending the $user_id profile delete, so calls made meanwhile were still tracked
and a profile update could be sent alongside the delete. The flag is now set
first, and the delete is queued and sent through a core path that skips the
opt-out checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Original ID Merge projects, and projects with ID Merge off, still identify
users by distinct id, so the getter stays supported. Its docs now say not to
use it on hierarchical groups projects and to prefer the identity getter where
possible. Deprecation suppressions added for it are removed.

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

Demo and example apps must not ship a real project token. Set
YOUR_HG_PROJECT_TOKEN to a project with hierarchical groups enabled to run the
page, as the other demo pages do with their own placeholder.

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

On a hierarchical groups project the user is identified by $user_id and
$company_id, which the request already carries, so the first-time event body
no longer sends a distinct_id. Other projects send it as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a hierarchical groups project an identified user is $user_id and
$company_id, so events leave out distinct_id rather than send the bare
user id. Anonymous events still send the $device: distinct id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +143 to +145
...(userId && companyIdentityProps.$company_id
? {}
: { distinct_id: this.mixpanelPersistent.getDistinctId(token) }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Caller identity still survives

In JavaScript mode, an identified hierarchical-groups user can still send distinct_id through event properties or saved super properties. Leaving it out of identityProps does not remove the earlier value when the objects are merged. Previously, the SDK overwrote that value. These events now carry conflicting identity fields instead of only $user_id and $company_id.

Remove distinct_id from the merged properties before freezing the event, and test both property sources.

Knowledge Base Used: Analytics events and identity

tylerjroach and others added 2 commits November 6, 2026 10:05
Ingestion rejects $identify without a distinct_id, and a hierarchical
groups project ties the device to the user from the $device_id,
$user_id and $company_id every later event carries. So identify (and
optInTrackingWithIdentity) track no $identify there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SDK owns the device id, so the identity apps pass to identify() has only userId and companyId. getIdentity() returns a ReportedMixpanelIdentity that adds deviceId.

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