Skip to content

docs(signal): brainstorm + plan for internal usage analytics (NES-1613)#9104

Draft
edmonday wants to merge 3 commits into
mainfrom
edmondshen/nes-1613-brainstorm-signal-usage-analytics-approach
Draft

docs(signal): brainstorm + plan for internal usage analytics (NES-1613)#9104
edmonday wants to merge 3 commits into
mainfrom
edmondshen/nes-1613-brainstorm-signal-usage-analytics-approach

Conversation

@edmonday
Copy link
Copy Markdown
Contributor

Summary

Brainstorm + deepened implementation plan for Signal — internal admin-app usage analytics (NES-1613).

This PR is docs-only — no code changes. It captures team-aligned product decisions and a deepened technical plan so reviewers can challenge the design before Phase 1 implementation begins.

Files:

  • docs/brainstorms/2026-04-29-signal-usage-analytics-requirements.md — locked product decisions from the brainstorm
  • docs/plans/2026-04-29-001-feat-signal-usage-analytics-plan.md — implementation plan (with v2 deepen-pass corrections + v3 schema iteration)

Headline decisions

  • Storage: new `libs/prisma/signals` migrate-managed domain; queried via `api-analytics` GraphQL surface (analytics domain is introspect-only per `.claude/rules/backend/database-schema-changes.md`).
  • Privacy: deterministic daily salt via `HMAC_SHA256(SIGNALS_SALT_SECRET, utc_date)` — no salt persisted in the DB. Rotating the env secret invalidates all historical hashes (kill switch).
  • Hash: `HMAC-SHA256(salt, host || \x1f || ip || \x1f || normalisedUA)` with `ua-parser-js` normalisation + `isbot` filter.
  • Schema: `appName`, `eventType`, `route`, polymorphic `entityType`+`entityId`, `userHash`, `sessionId`, coarse UA buckets (`deviceType`/`browserFamily`/`osFamily`), `clientEventId` (`@unique`), free-form `description` (2KB cap + PII-key denylist).
  • Wire: `eventType` is `String!` + server allowlist (forward-compat across deploys).
  • Auth: mirror `apis/api-media/{yoga,builder}.ts` (`useForwardedJWT` + discriminated context union); read scope restricted to `admin` role.
  • Client: `useTrackEvent` fail-closed during `useUser()` loading; `clientEventId` defeats Apollo dedup; internal-user exclusion via Doppler-managed `NEXT_PUBLIC_INTERNAL_USER_EMAILS`.
  • v1 events: `journey_create_clicked`, `journey_create_from_template`, four named editor overlay events, `ai_translation_language_picked`.
  • Out of scope (v1): cross-day distinct-user counts (privacy/utility trade), dwell time, cursor pagination, JSON-path filtering, country/region.
  • Retention: 180 days; daily DELETE job.

Open decisions (need team input before Phase 1)

  • D1. Confirm the new `libs/prisma/signals` Prisma domain (vs. provisioning the table in the Plausible DB and re-introspecting).
  • D2. Read auth: extend `api-analytics` to accept gateway Firebase JWT for the `events` query.
  • D3. Mutation auth: gateway-JWT + per-user rate limit.
  • D4. Verify Ken's "admin UI language already tracked" claim before adding `ai_translation_language_picked`.

Test plan

  • Reviewers walk the brainstorm + plan and surface blockers/concerns
  • Resolve D1–D4 with the team
  • On approval, this branch can stay merged as docs and Phase 1 work opens against a fresh feature branch

🤖 Generated with Claude Code

edmonday and others added 2 commits April 29, 2026 02:03
Captures the brainstorm decisions and deepened implementation plan for
NES-1613 (Signal — internal admin-app usage analytics).

Highlights:
- Storage: own table behind api-analytics GraphQL surface; new
  libs/prisma/signals migrate-managed domain (analytics domain is
  introspect-only per .claude/rules/backend/database-schema-changes.md).
- Privacy: deterministic daily salt via HMAC(SIGNALS_SALT_SECRET, utc_date)
  — no salt in the events DB; rotating the secret is the kill switch.
- Hash: HMAC-SHA256(salt, host || \x1f || ip || \x1f || normalisedUA),
  with ua-parser normalisation and isbot filter.
- Wire: eventType is String! + server allowlist (forward-compat across
  deploys); description has 2KB cap + recursive PII-key denylist.
- Auth: gateway-JWT path mirroring api-media (useForwardedJWT +
  discriminated context union); read scope restricted to admin role.
- Client: useTrackEvent fail-closed on useUser() loading; clientEventId
  nonce defeats Apollo mutation dedup.
- v1 events: journey_create_clicked, journey_create_from_template,
  editor_overlay_opened (4 overlays), ai_translation_language_picked.

Open decisions (D1–D4) flagged for team sign-off before Phase 1.

Refs: NES-1613

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Schema iteration after team review of cross-app needs and query ergonomics:

- appName: multi-app slicing from day 1
- route: which page produced the event
- entityType + entityId: polymorphic primary entity (replaces per-app columns
  like journeyId; reuses cleanly across journeys-admin, videos-admin, etc.)
- sessionId: per-tab UUID in sessionStorage; survives mobile IP rotation
- deviceType, browserFamily, osFamily: coarse UA buckets persisted from the
  same ua-parser call already used for the hash input
- clientEventId: persisted with @unique for free idempotency

Editor overlay events split from one + description.overlay into four named
event types: faster queries (no JSONB path), cleaner allowlist.

Indexes: [appName, eventType, createdAt], [entityType, entityId, createdAt],
[userHash, createdAt].

Success criterion rewritten — cross-day distinct-user counting is intentionally
broken by the daily-rotating salt (privacy/utility trade); team agreed it's
non-load-bearing for usage decisions. Replaced with daily distinct + total
counts sliced by app/device/route/entity.

Out-of-scope additions: dwell time, cross-day distinct counts, cursor pagination,
country/region, appVersion. Schema can absorb dwell time later via heartbeat
events using existing columns — no migration needed.

Retention: 180-day default with a daily DELETE job.

useTrackEvent reshaped to capture appName, route, entity, sessionId in the
mutation variables.

Refs: NES-1613

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@linear
Copy link
Copy Markdown

linear Bot commented Apr 29, 2026

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 137281be-ec51-4683-9bfa-13df38d756ce

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch edmondshen/nes-1613-brainstorm-signal-usage-analytics-approach

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

Fails
🚫 Please assign someone to merge this PR.
🚫 Please request a reviewer for this PR.
Warnings
⚠️ ❗ Big PR (785 changes)

(change count - 785): Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.

Generated by 🚫 dangerJS against 42b818c

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 29, 2026

View your CI Pipeline Execution ↗ for commit c342580

Command Status Duration Result
nx run-many --target=prisma-generate --all --pa... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-29 04:36:28 UTC

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 29, 2026

View your CI Pipeline Execution ↗ for commit c342580

Command Status Duration Result
nx affected --target=subgraph-check --base=6a36... ✅ Succeeded <1s View ↗
nx affected --target=extract-translations --bas... ✅ Succeeded <1s View ↗
nx affected --target=lint --base=6a36e5120e4b67... ✅ Succeeded <1s View ↗
nx affected --target=type-check --base=6a36e512... ✅ Succeeded <1s View ↗
nx run-many --target=codegen --all --parallel=3 ✅ Succeeded 1s View ↗
nx run-many --target=prisma-generate --all --pa... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-29 04:40:29 UTC

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