feat/ph-consent
needs attentionviewing older commitcf3e330 · incrementalpre-PRreviewed 2026-07-31 23:17 UTC0H · 6M · 5L- 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
- 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+366−18apps/web+116−1apps/platform+7−3
- Blast
- 9 files, +626/−42; analytics infrastructure only — no domain entities, DB, or API contracts changed.
Findings · 11
correctness1
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
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.
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.
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
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.
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
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.
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.
grantAnalyticsConsent and denyAnalyticsConsent have zero test coverage
packages/analytics/src/providers/PostHogProvider.tsx
The only code paths calling posthog.startSessionRecording / stopSessionRecording are untested.
improvement2
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.
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
- 8c748c7safeincremental0H · 0M · 2L2026-08-01 00:40
- 22fc175safeincremental0H · 0M · 4L2026-08-01 00:30
- f26dee1needs attentionincremental2H · 2M · 9L2026-08-01 00:24
- bf92308needs attentionincremental2H · 8M · 8L2026-07-31 23:56
- cf3e330needs attentionincremental0H · 6M · 5L2026-07-31 23:17current
- 5fb4d78blockedfull4H · 8M · 9L2026-07-31 22:11