diff --git a/.changeset/gate-group-on-person-processing.md b/.changeset/gate-group-on-person-processing.md new file mode 100644 index 0000000000..252c72ff21 --- /dev/null +++ b/.changeset/gate-group-on-person-processing.md @@ -0,0 +1,5 @@ +--- +'posthog-js': patch +--- + +Gate `$groupidentify` on person processing. Under `identified_only`, a `group()` call promotes the user to identified so the event is kept instead of dropped. Under `never`, the local group association is retained for subsequent events and feature flags, but `$groupidentify` is not sent because the server always drops it. diff --git a/packages/browser/src/__tests__/personProcessing.test.ts b/packages/browser/src/__tests__/personProcessing.test.ts index d891cb5d86..d07d82048a 100644 --- a/packages/browser/src/__tests__/personProcessing.test.ts +++ b/packages/browser/src/__tests__/personProcessing.test.ts @@ -606,8 +606,7 @@ describe('person processing', () => { expect(eventAfterGroup[0].properties.$process_person_profile).toEqual(true) }) - it('should send the $groupidentify event even if person_processing is set to never', async () => { - // Groups are separate from person processing - $groupidentify should always be sent + it('should retain the group association without sending $groupidentify if person_processing is never', async () => { // arrange const { posthog, beforeSendMock } = await setup('never') @@ -617,21 +616,19 @@ describe('person processing', () => { posthog.capture('custom event after group') // assert - // setGroupPropertiesForFlags still has a person processing check expect(mockLogger.error).toBeCalledTimes(1) expect(mockLogger.error).toHaveBeenCalledWith( 'posthog.setGroupPropertiesForFlags was called, but process_person is set to "never". This call will be ignored.' ) - // $groupidentify is sent (groups are independent of person processing) - expect(beforeSendMock).toBeCalledTimes(3) + // no $groupidentify is sent, only the two custom events + expect(beforeSendMock).toBeCalledTimes(2) const eventBeforeGroup = beforeSendMock.mock.calls[0] expect(eventBeforeGroup[0].properties.$process_person_profile).toEqual(false) - const groupIdentify = beforeSendMock.mock.calls[1] - expect(groupIdentify[0].event).toEqual('$groupidentify') - // $groupidentify doesn't set $process_person_profile since it doesn't process persons - const eventAfterGroup = beforeSendMock.mock.calls[2] + const eventAfterGroup = beforeSendMock.mock.calls[1] + expect(eventAfterGroup[0].event).toEqual('custom event after group') expect(eventAfterGroup[0].properties.$process_person_profile).toEqual(false) + expect(eventAfterGroup[0].properties.$groups).toEqual({ groupType: 'groupKey' }) }) }) diff --git a/packages/browser/src/posthog-core.ts b/packages/browser/src/posthog-core.ts index 92dae38264..10d398ffc7 100644 --- a/packages/browser/src/posthog-core.ts +++ b/packages/browser/src/posthog-core.ts @@ -3210,9 +3210,9 @@ export class PostHog implements PostHogInterface { this.register({ $groups: { ...existingGroups, [groupType]: groupKey } }) // Send $groupidentify when the group is new/changed OR when properties - // are provided. Skip only when the group already exists with the same - // key and no new properties are being set. - if (isNewGroup || groupPropertiesToSet) { + // are provided, but only when the event can be processed. The local group + // association remains useful for events and feature flags without person processing. + if ((isNewGroup || groupPropertiesToSet) && this._hasPersonProcessing()) { const groupIdentifyProperties: Properties = { $group_type: groupType, $group_key: groupKey,