← all branches

feat/ph-consent

needs attentionviewing older commit
cf3e330 · incrementalpre-PRreviewed 2026-07-31 23:17 UTC0H · 6M · 5L
The branch
Purpose
Add per-app session replay policy since PostHog project-level settings are shared between both apps.
Goal
Refactor sessionReplay from two booleans to a structured ReplayPolicy with sampleRate + requireConsent; add 60% sampling for public site.
Sub-goals
  • SG-1: Add ReplayPolicy interface to packages/analytics
  • SG-2: Gate apps/web at 60% sampling + consent required
  • SG-3: Wire apps/platform for 100% recording, no consent gate
  • SG-4: Preserve fail-closed semantics (env → consent → sampling order)
  • SG-5: Update tests to cover new functions
The changes (whole branch)
What
Consolidated sessionReplay + requireConsent props into Partial<ReplayPolicy>. Added normalizeReplayPolicy, isInReplaySample, shouldStartReplay. Module-level activeReplayPolicy singleton to thread policy into grantAnalyticsConsent.
Why
PostHog sampling is per-project; both apps share one project. Client-side policy is the only way to differentiate per app.
Areas
packages/analytics+36618apps/web+1161apps/platform+73
Blast
9 files, +626/−42; analytics infrastructure only — no domain entities, DB, or API contracts changed.
Auth routes on apps/platform covered by no-consent-required policy grantAnalyticsConsent() exported API signature changed — no semver bump
ci· No open PR — CI signals unavailablecoderabbit· No .coderabbit.yaml in repo

Findings · 11

correctness1

low

activeReplayPolicy stays null after fast-refresh remount in dev

packages/analytics/src/providers/PostHogProvider.tsx:134

Fast refresh resets activeReplayPolicy to null; posthog.__loaded guard short-circuits the useEffect. Dev-only — not a production bug.

security3

medium

Platform auth routes recorded with no consent gate for unauthenticated visitors

apps/platform/src/app/[locale]/layout.tsx:96

Login/password-reset/invite pages are under the requireConsent:false provider. Unauthenticated visitors have not accepted Batu's ToS.

medium

grantAnalyticsConsent() param removal is a silent behavioral break for external callers who passed false

packages/analytics/src/providers/PostHogProvider.tsx:66

Old API accepted grantAnalyticsConsent(sessionReplay=true); passing false suppressed recording. No active regression in this repo but JSDoc breaking-change note or semver bump is warranted.

low

DEFAULT_REPLAY_POLICY requireConsent:false is unsafe default for partial policy objects

packages/analytics/src/providers/session-replay.ts:208

A future app omitting requireConsent from a partial policy inherits no-consent-required.

conventions2

medium

isSessionReplayEnabled and isReplayAllowedWithConsent first-param is vestigial inside shouldStartReplay

packages/analytics/src/providers/session-replay.ts

shouldStartReplay passes hardcoded true to both helpers — policy !== null already serves the app-opt-in check.

medium

Module-level activeReplayPolicy has no dev-mode warning when null at consent time

packages/analytics/src/providers/PostHogProvider.tsx

If grantAnalyticsConsent() is called before the provider mounts, replay silently doesn't start with no signal.

tests3

medium

isInReplaySample: sessionStorage persistence path not tested

packages/analytics/src/providers/__tests__/session-replay.test.ts

The stable-session-decision guarantee (stored in/out) is structurally untested. Needs jsdom/happy-dom env.

medium

normalizeReplayPolicy: partial object with only requireConsent (no sampleRate) untested

packages/analytics/src/providers/__tests__/session-replay.test.ts

The sampleRate nullish-coalesce fallback path is not covered.

low

grantAnalyticsConsent and denyAnalyticsConsent have zero test coverage

packages/analytics/src/providers/PostHogProvider.tsx

The only code paths calling posthog.startSessionRecording / stopSessionRecording are untested.

improvement2

low

shouldStartReplay: hardcoded true for helpers' first param is undocumented

packages/analytics/src/providers/session-replay.ts

Intent (policy !== null already covers app opt-in) should be documented inline.

low

Integration tests never exercise non-1 sampleRate through shouldStartReplay

packages/analytics/src/providers/__tests__/session-replay.test.ts

60% web sampling tested at isInReplaySample unit level only.

History · 6 commits

  1. 8c748c7safeincremental0H · 0M · 2L2026-08-01 00:40
  2. 22fc175safeincremental0H · 0M · 4L2026-08-01 00:30
  3. f26dee1needs attentionincremental2H · 2M · 9L2026-08-01 00:24
  4. bf92308needs attentionincremental2H · 8M · 8L2026-07-31 23:56
  5. cf3e330needs attentionincremental0H · 6M · 5L2026-07-31 23:17current
  6. 5fb4d78blockedfull4H · 8M · 9L2026-07-31 22:11