# ADR-0012: Analytics Taxonomy and Event Naming

**Status**: Accepted **Date**: 2026-02-16 **Authors**: OSHUN Platform
Engineering, OSHUN Data/Analytics Engineering **Reviewers**: Domain Leads (Tara,
Veritas, Nyx, Arete), OSHUN Product **Supersedes**: N/A **Superseded by**: N/A

## Context and Problem Statement

OSHUN needs one analytics language across:

- mobile shell (iOS/Android)
- website/PWA shell
- domain adapters (Tara, Veritas, Nyx, Arete)

Current repo patterns are inconsistent:

- OSHUN metrics draft defines shell KPI events as `oshun.*` names.
- Veritas web behavior tracking uses snake_case event names (`article_view`,
  `article_complete`, `search_query`, etc.).
- Shared monitoring infra accepts generic `eventType: string` with no enforced
  naming taxonomy.
- Domain contracts in `libs/contracts/src/events/*` already use dot-delimited
  `<domain>.<entity>.<action>` naming.

Without a single taxonomy, we get fragmented dashboards, weak KPI comparability
across surfaces, and expensive data normalization work.

## Decision Drivers

- **Comparability** of product metrics across iOS, Android, web, and PWA.
- **Cross-domain consistency** for Tara, Veritas, Nyx, and Arete reporting.
- **Governance** with versioned event contracts and validation.
- **Implementation pragmatism** by reusing existing event styles where possible.
- **Privacy/compliance** through explicit consent and PII controls.
- **Observability** with low-cardinality, query-friendly event names.

## Considered Options

### Option 1: Keep Domain-Specific Naming, Normalize in Warehouse

**Description**: each domain keeps current names; BI layer remaps later.

**Pros**:

- ✅ Minimal short-term engineering work in apps
- ✅ High domain autonomy

**Cons**:

- ❌ Expensive and brittle downstream mapping
- ❌ Slower KPI iteration and conflicting metric definitions
- ❌ Harder enforcement of quality and consent rules

### Option 2: One Flat Global Event List

**Description**: enforce one long list of event names with no namespace
layering.

**Pros**:

- ✅ Simple initial schema
- ✅ Easy to start tracking quickly

**Cons**:

- ❌ Name collisions over time
- ❌ Poor ownership boundaries by shell/domain
- ❌ Hard to evolve and reason about event families

### Option 3: Canonical Dot-Delimited Taxonomy with Namespaces (Chosen)

**Description**: enforce `<namespace>.<entity>.<action>` canonical naming and a
standard envelope; allow adapter-level mapping from legacy names.

**Pros**:

- ✅ Aligns with existing domain contract style (`veritas.article.published`)
- ✅ Clear ownership boundaries (`oshun`, `tara`, `veritas`, `nyx`, `arete`)
- ✅ Query-friendly and scalable naming model
- ✅ Supports gradual migration from existing snake_case trackers

**Cons**:

- ❌ Requires migration/adaptation for existing behavior trackers
- ❌ Requires governance and linting discipline

## Decision Outcome

**Chosen option**: Option 3 - canonical dot-delimited taxonomy.

### Canonical Event Name Contract (Normative)

Event names MUST use:

- format: `<namespace>.<entity>.<action>`
- lowercase ascii only
- dot-delimited segments
- snake_case words within each segment if needed
- past-tense or completed-action verb in `action` where applicable

Examples:

- `oshun.app.opened`
- `oshun.domain.opened`
- `oshun.search.executed`
- `veritas.article.viewed`
- `tara.session.completed`
- `arete.checkin.completed`

Disallowed examples:

- `AppOpened` (camelCase)
- `article_view` (missing namespace/entity separation)
- `oshun.article` (no action)
- `oshun..opened` (empty segment)

### Namespace Ownership

- `oshun.*`: shell-level and cross-domain normalized KPI events.
- `tara.*`, `veritas.*`, `nyx.*`, `arete.*`: domain-native product events.
- `system.*`: optional non-product telemetry (ingestion, pipeline, SDK health).

### Required Event Envelope (Normative)

Every analytics event MUST include:

- `event_id` (globally unique id)
- `event_name` (canonical name)
- `event_version` (integer, starts at `1`)
- `occurred_at` (ISO-8601 UTC timestamp)
- `platform` (`ios`, `android`, `web`, `pwa`)
- `surface` (`mobile_shell`, `web_shell`, `domain_app`, `bff`)
- `session_id`
- `domain_context` (`oshun_shell`, `tara`, `veritas`, `nyx`, `arete`)
- `consent_analytics` (boolean gate)

User identity fields:

- `user_id` when authenticated
- `anonymous_id` when unauthenticated
- at least one of `user_id` or `anonymous_id` is required

### Property Standards

- Use snake_case property names.
- Stable enum properties MUST be documented in contract schemas.
- High-cardinality free text should be minimized and explicitly justified.
- PII (email, phone, full name, raw IP) MUST NOT be sent in event properties.

### Event Versioning Rules

- Additive, backward-compatible property additions: keep same `event_name`,
  increment optional schema metadata as needed.
- Breaking changes (rename/remove semantic meaning): increment `event_version`.
- Name changes require dual-write period and mapping documentation.

### Taxonomy Families (Normative)

OSHUN analytics events MUST be categorized into these families:

- `lifecycle`: app/session open, close, resume
- `auth`: signin, signup, logout, refresh, revocation outcomes
- `navigation`: route transitions, deep-link opens, domain launches
- `engagement`: views, reads, saves, shares, follows
- `value_action`: normalized high-value outcomes for KPI computation
- `search`: query, filter, result open, zero-result
- `notification`: received, opened, dismissed, actioned
- `performance`: web vitals, startup, hydration, launch latency
- `error`: user-visible failures and recoveries
- `commerce` (if enabled): paywall views, conversion steps, purchase outcomes

### Canonical OSHUN KPI Events

The following normalized events are mandatory for OSHUN KPI computation:

- `oshun.app.opened`
- `oshun.auth.completed`
- `oshun.profile.initialized`
- `oshun.domain.opened`
- `oshun.value_action.completed`
- `oshun.item.saved`
- `oshun.item.resumed`
- `oshun.search.executed`
- `oshun.notification.opened`

`oshun.value_action.completed` MUST include:

- `domain_context`
- `value_action_type`
- `completion_quality` (`partial`, `full`)
- `entity_id` (or equivalent domain entity reference)

### Legacy-to-Canonical Mapping Requirements

Adapter layers MUST map legacy names to canonical names before ingestion into
OSHUN KPI datasets.

Initial mapping requirements include:

| Legacy Event                                  | Canonical Event                |
| --------------------------------------------- | ------------------------------ |
| `oshun.app_opened`                            | `oshun.app.opened`             |
| `oshun.auth_completed`                        | `oshun.auth.completed`         |
| `oshun.profile_initialized`                   | `oshun.profile.initialized`    |
| `oshun.domain_card_opened`                    | `oshun.domain.opened`          |
| `oshun.value_action_completed`                | `oshun.value_action.completed` |
| `oshun.item_saved`                            | `oshun.item.saved`             |
| `oshun.item_resumed`                          | `oshun.item.resumed`           |
| `oshun.search_executed`                       | `oshun.search.executed`        |
| `oshun.notification_opened`                   | `oshun.notification.opened`    |
| `article_view` (Veritas behavior tracker)     | `veritas.article.viewed`       |
| `article_complete` (Veritas behavior tracker) | `veritas.article.completed`    |
| `search_query` (Veritas behavior tracker)     | `veritas.search.executed`      |

### Consent and Privacy Rules

- Events with `consent_analytics=false` MUST be excluded from analytics KPI
  views.
- Consent state transitions must be audited as separate `oshun.privacy.*` events
  where policy permits.
- Anonymous pre-consent events may be used only for strictly necessary
  operational telemetry, not product analytics KPIs.

### Implementation Guidance

### SDK and Contracts

- Implement shared types/contracts in `libs/oshun/analytics` (planned).
- Validate names and required envelope fields at SDK boundary.
- Provide helper APIs to reduce ad hoc string event usage.

### Shell and Domain Adapters

- Shell emits `oshun.*` normalized events.
- Domain adapters emit domain-native events and optional normalized mirror
  events for KPI-critical actions.
- BFF enforces envelope validation and rejects malformed events.

### Warehouse and BI

- Keep `event_name` canonical and immutable in curated tables.
- Preserve raw event payload in bronze/raw storage for forensics.
- Maintain version-aware transforms for `event_version` changes.

## Implementation Plan

### Phase 1: Contract Finalization

- Define canonical event-name regex and lint checks.
- Define envelope schema and required enums.
- Publish initial event dictionary with owners.

### Phase 2: Adapter Migration

- Add mapping layer for existing Veritas behavior events.
- Add canonical shell event emitter for mobile/web/PWA.
- Introduce dual-write where migrations are needed.

### Phase 3: Enforcement

- Add CI checks for disallowed event names.
- Add ingestion-side validation with reject/error metrics.
- Add dashboard for unknown/unmapped event names.

### Phase 4: Hardening

- Remove deprecated aliases after migration window.
- Run periodic taxonomy review and cardinality audits.
- Add playbooks for emergency event rollback/deprecation.

## Success Metrics

- 100% of OSHUN KPI dashboards use canonical `event_name` values.
- Unknown/unmapped event rate < 0.5% of daily event volume.
- Consent violation rate for KPI datasets = 0.
- Event schema validation pass rate >= 99.5%.
- Cross-platform metric parity (mobile vs web/PWA definitions) maintained.

## Consequences

### Positive Consequences

- ✅ Clear, scalable analytics naming model across shell and domains.
- ✅ Faster metric development with less downstream remapping.
- ✅ Better governance, auditability, and privacy control.

### Negative Consequences

- ❌ Upfront migration work in tracking adapters and dashboards.
- ❌ Ongoing taxonomy governance overhead.

### Risks and Mitigations

| Risk                                | Probability | Impact | Mitigation                                        |
| ----------------------------------- | ----------- | ------ | ------------------------------------------------- |
| Legacy tracker drift continues      | Medium      | High   | CI lint + ingestion validation + owner escalation |
| High-cardinality property explosion | Medium      | Medium | event review checklist + cardinality monitoring   |
| Dashboard breakage during rename    | Medium      | High   | dual-write period + mapping tables + cutover plan |
| Consent metadata missing in clients | Low         | High   | SDK-required fields + ingestion reject policy     |

## Monitoring and Observability

Track:

- event ingestion acceptance/rejection counts by reason
- unknown event-name frequency
- event-version distribution per `event_name`
- consented vs non-consented event ratios
- mapping-layer fallback usage during migration

## Related Decisions

- `docs/adr/ADR-0013-oshun-shell-architecture-and-domain-adapters.md`
- `docs/adr/ADR-0014-oshun-web-and-pwa-strategy.md`
- `docs/adr/ADR-0016-shared-identity-and-cross-domain-session-model.md`
- `docs/adr/ADR-0017-offline-caching-and-sync-strategy.md`
- `docs/adr/ADR-0010-eventing-strategy.md`

## References

- `docs/releases/v1/reports/metrics.md`
- `apps/veritas/web/src/lib/behavior-tracking/types.ts`
- `apps/veritas/web/src/lib/behavior-tracking/tracker.ts`
- `apps/veritas/mobile/src/services/notifications.ts`
- `libs/contracts/src/events/veritas.ts`
- `libs/shared/infrastructure/src/types.ts`
- `libs/shared/infrastructure/src/monitoring/monitoring-manager.ts`
