feat/ph-consent
safeviewing older commit22fc175 · incrementalpre-PRreviewed 2026-08-01 00:30 UTC0H · 0M · 4L · 6I- Purpose
- Add GDPR-compliant analytics consent gating for PostHog session replay across both apps
- Goal
- Gate session replay behind visitor consent on apps/web and scope recording to authenticated routes on apps/platform; add revocation and persistence
- Sub-goals
- SG-1: Consent banner on public site (apps/web) gating session replay
- SG-2: Per-app replay policy — 60% sampling on web (consent-required), explicit policy on platform
- SG-3: Identity gating on consent — PostHog identify() only fires after grant
- SG-4: Route-scoped replay via SessionReplayScope — Atlas only on web, authenticated dashboard only on platform
- SG-5: Consent revocation support and persistence tests
- What
- This incremental diff clarifies comments and JSDoc across 4 files — updated layout.tsx comment to distinguish policy from activation, expanded README with 4-gate architecture and route-scoped replay table, refined PostHogProvider JSDoc to emphasize policy-vs-activation distinction, and updated isSessionReplayEnabled @param description to name both apps.
- Why
- Documentation cleanup following earlier implementation commits to make the architecture clearer for future contributors navigating the consent/replay system.
- Areas
- apps/platform+13−4apps/web+195−5packages/analytics+879−48
- Blast
- 14 files, +1091/−62 across 3 areas; touches analytics consent, replay scoping, consent UI, and privacy page
Findings · 10
correctness1
Comment-only diff — no executable paths changed
packages/analytics/src/providers/PostHogProvider.tsx
All 4 changed files contain only comment/JSDoc/documentation updates. No logic, types, control flow, or data handling was modified.
security2
localStorage consent key — origin isolation assumption
packages/analytics/src/providers/session-replay.ts
Consent is stored under 'batu.analytics.consent' in localStorage, accessible to any script on the same origin. The README additions make the 4-gate architecture clearer, but if apps/web and apps/platform ever share an origin, a revocation on one may not propagate to the other. Verify the two apps are on distinct origins, or that readStoredConsent() is called fresh on each PostHog init (it is, by the effect in PostHogProvider).
Comment-only diff — no new security surface
apps/web/src/app/layout.tsx
The sessionReplay policy object ({sampleRate:0.6, requireConsent:true}) was already present; the diff only updates the surrounding comment. No new security surface introduced.
conventions3
Comment explains WHAT, not WHY
apps/web/src/app/layout.tsx
The updated comment block describes what the code does ('This does not turn recording on', 'it is mounted on Atlas only') rather than why. Per project rules, only add a comment when the WHY is non-obvious. The non-obvious WHY (client-side sampling because PostHog sample rate is per-project and shared) is already in the last two sentences — the first half of the comment is WHAT and could be trimmed.
JSDoc @param description lists callers instead of semantics
packages/analytics/src/providers/session-replay.ts:211
The @param description 'the app declared a replay policy on the provider (apps/platform and apps/web)' enumerates current callers — WHAT — rather than the param's intent or constraints. Should describe the gate semantics abstractly.
README.md additions — no convention findings
packages/analytics/README.md
Documentation-only file; no convention rules applicable.
improvement4
Atlas-only mounting is a prose contract, not structural
apps/web/src/app/layout.tsx:71
The comment says '<SessionReplayScope /> is mounted on Atlas only' but there is no compile-time or lint guard to enforce this. A future developer adding SessionReplayScope to another route would not see a warning. Consider a named constant or TSDoc marking SessionReplayScope as atlas-only to surface the constraint at the use site.
README route-scoped replay table may drift silently
packages/analytics/README.md
The hand-written table listing where replay runs per app is valuable but will drift if a new route or app mounts SessionReplayScope. There is no test or type check tying this documentation to the code. Consider a colocated comment or type export that is grep-able rather than a prose table.
sampleRate lacks documented valid range
packages/analytics/src/providers/PostHogProvider.tsx
The JSDoc clarification that sessionReplay is 'POLICY, not activation' is helpful. The prop's sampleRate has no documented range constraint (must be 0–1). Adding a note or a branded type would prevent misconfiguration (e.g. sampleRate: 60) that would silently disable all sampling.
isSessionReplayEnabled @param names specific apps — will grow stale
packages/analytics/src/providers/session-replay.ts
Naming 'apps/platform and apps/web' in a generic utility's JSDoc creates coupling. If a third app adopts the package, the comment misleads. Describe the gate semantics abstractly instead of listing callers.
History · 6 commits
- 8c748c7safeincremental0H · 0M · 2L2026-08-01 00:40
- 22fc175safeincremental0H · 0M · 4L2026-08-01 00:30current
- f26dee1needs attentionincremental2H · 2M · 9L2026-08-01 00:24
- bf92308needs attentionincremental2H · 8M · 8L2026-07-31 23:56
- cf3e330needs attentionincremental0H · 6M · 5L2026-07-31 23:17
- 5fb4d78blockedfull4H · 8M · 9L2026-07-31 22:11