eng-posthog-event-naming-convention

Category: Data Risk: Medium risk ★ 3.9 · Rating 3.9/5 (8) sboghossian/mini-claude-for-legal MIT

Rating is derived from the repo's GitHub stars and shown for reference.

network_accessautomation_control

name: eng-posthog-event-naming-convention
description: Use when instrumenting user-facing features in the legal AI product with PostHog analytics events. Defines the canonical event taxonomy, naming rules, property schemas, and anti-patterns so that all engineers emit consistent, queryable telemetry that supports product decisions, eval pipelines, and compliance audits.
license: MIT
metadata:
id: eng.posthog-event-naming-convention
category: eng
jurisdictions: [multi]
priority: P2
intent: [eng, analytics, posthog, telemetry, events]
related: [eng-supabase-edge-functions-patterns, eng-token-budget-by-tier, eval-benchmark-runner, eval-leaderboard-updater]
source: Louis — HAQQ Legal AI (github.com/sboghossian/mini-claude-for-legal)
version: "1.0"

PostHog Event Naming Convention

What it does

This skill defines the canonical naming convention, property schema, and governance rules for all PostHog analytics events emitted by the legal AI product. Consistent event naming is the prerequisite for building reliable funnels, feature-flag cohorts, and the LLM quality trend dashboard that feeds [[eval-leaderboard-updater]].

Setup / auth

PostHog project is configured via:

  • POSTHOG_KEY — public client-side key (safe to expose in frontend)
  • POSTHOG_HOST — self-hosted or app.posthog.com
  • POSTHOG_SERVER_KEY — server-side key for Edge Function calls (never expose to browser)

All events must include distinct_id (the user's UUID, never email/name in the event itself — PII goes through [[eng-pii-redaction-preprocessor]]).

Capabilities

Naming structure

<noun>_<verb>
  • Noun is the product object: session, skill, document, chat, nda, contract, user, workspace, billing, eval.
  • Verb is the past-tense action: started, completed, failed, viewed, clicked, upgraded, cancelled.
  • Always lowercase, words joined with _.
  • No "clicked" or "viewed" as the sole event name — always include a noun: button_clicked is useless; skill_selector_clicked is queryable.

Event taxonomy

Session & auth

Event When
session_started User opens the app (not page load)
session_ended Tab close or 30-min idle
user_signed_up First auth completion
user_signed_in Every subsequent auth
user_invited Invite link sent
workspace_created New workspace provisioned

Chat & skill routing

Event When
chat_message_sent User sends any message
chat_response_received First token streamed back
chat_response_completed Stream ends
skill_routed Router resolves a skill
skill_fallback_triggered No skill matched; fell back to generic
chat_error_occurred Any non-2xx from LLM

Document workflow

Event When
document_uploaded File ingestion starts
document_indexed Embedding complete
document_reviewed Review skill output delivered
document_drafted Draft skill output delivered
document_downloaded User exports a document

Billing & tier

Event When
upgrade_prompted User hits a tier gate
upgrade_completed Subscription activated
upgrade_abandoned User dismissed upgrade modal
api_key_saved BYO-key entered
token_budget_exhausted Monthly token limit hit

Eval (server-side only)

Event When
eval_run_started Benchmark run begins
eval_run_completed All rubrics scored
eval_regression_detected Any rubric dropped threshold

Required properties on every event

{
  distinct_id: string;       // user UUID
  tenant_id: string;         // workspace/firm UUID
  session_id: string;        // current session UUID
  platform: "web" | "ios" | "android";
  app_version: string;       // semver
  environment: "production" | "staging" | "local";
}

Per-event additional properties

chat_message_sent

{
  message_length_chars: number;
  language_detected: "ar" | "en" | "fr" | "mixed";
  has_attachment: boolean;
}

skill_routed

{
  skill_id: string;          // e.g. "draft-nda-unilateral"
  skill_category: string;    // e.g. "draft"
  confidence: number;        // router confidence 0-1
  latency_ms: number;
}

chat_response_completed

{
  skill_id: string;
  tokens_input: number;
  tokens_output: number;
  latency_ms: number;
  tier: "free" | "pro" | "business" | "byo";
  rubric_score?: number;     // if real-time eval enabled
}

Usage patterns

Frontend (React)

import posthog from "posthog-js";

posthog.capture("skill_routed", {
  skill_id: routedSkill.id,
  skill_category: routedSkill.category,
  confidence: routedSkill.confidence,
  latency_ms: Date.now() - startTime,
});

Edge Function (server-side)

import { PostHog } from "posthog-node";
const ph = new PostHog(Deno.env.get("POSTHOG_SERVER_KEY")!);

ph.capture({
  distinctId: userId,
  event: "eval_run_completed",
  properties: {
    tenant_id: tenantId,
    run_id: runId,
    aggregate_score: score,
    regression_detected: hasRegression,
    environment: "production",
  },
});
await ph.shutdown();

Permissions & safety

  • Never include raw user message content in event properties. Use message_length_chars instead.
  • Never include full names, email addresses, or phone numbers in event properties. Use UUIDs.
  • environment: "local" events should not appear in the production PostHog project. Gate on env var.
  • Billing events (upgrade_completed) must be cross-validated against Stripe webhook receipts.

Failure modes

Failure Impact Mitigation
Missing tenant_id Can't segment by firm Assert required properties at emit site via TypeScript type
Duplicate events Inflated metrics Deduplicate on session_id + event + timestamp in PostHog
Events in local env polluting production Noisy dashboards Set environment filter on all saved insights
PII in properties Privacy violation Lint rule: reject any property containing email, name, phone
  • [[eng-pii-redaction-preprocessor]] — ensures message content is never passed raw to analytics
  • [[eng-token-budget-by-tier]] — tier and budget data feeds chat_response_completed properties
  • [[eval-benchmark-runner]] — consumes eval_run_* events
  • [[eval-leaderboard-updater]] — reads PostHog quality-trend data