Skip to content

feat(analytics): property-model foundation for cross-platform schema alignment (#2883)#2903

Open
piyalbasu wants to merge 13 commits into
masterfrom
feat/analytics-schema-foundation-spec
Open

feat(analytics): property-model foundation for cross-platform schema alignment (#2883)#2903
piyalbasu wants to merge 13 commits into
masterfrom
feat/analytics-schema-foundation-spec

Conversation

@piyalbasu

@piyalbasu piyalbasu commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

TL;DR

First slice of the cross-platform analytics refactor (#2883): brings the extension's Amplitude property model in line with the shared cross-platform schema, without renaming any events yet. Every event now carries a schema_version; a privacy-safe hashed account identifier replaces the truncated public key that events previously carried; device/app metadata is no longer hand-duplicated onto each event (the Amplitude SDK already supplies it); durable wallet traits (wallet count, has-hardware-wallet, has-imported-account) move to user-level properties; and a new app.opened event captures a one-time per-open snapshot. No new data is collected — behavioral autocapture stays off, and this net removes a truncated key from event payloads.

Event names are intentionally unchanged in this PR; the screen.viewed collapse and domain-event renames are follow-on slices. The canonical cross-platform schema this follows lives in the RFC (stellar/wallet-eng-monorepo#10).

Implementation details (for agents/reviewers)

All emission logic stays centralized in extension/src/helpers/metrics.ts — this PR changes its contents, not its shape (no typed-catalog rewrite; the RFC is a property/naming cleanup, and the spec guards drift).

What changed:

  • schema_version ("2") on every event — stamped centrally in buildCommonContext via the SCHEMA_VERSION constant. It marks the new property model even while event names remain legacy.
  • account_id_hash replaces the truncated publicKeygetAccountIdHash is a memoized, synchronous SHA-256 hex of the full G-address (via stellar-sdk's hash). This is a cross-platform contract — mobile must hash the same G-address string with SHA-256 to produce a matching value (committed test vector). The one other emit that carried a truncated key (freighterAccountFunded in storeBalanceMetricData) was also switched to account_id_hash.
  • Four-bucket property model (per RFC Part 2a): SDK-supplied fields (platform/platformVersion/appVersion) are no longer hand-sent — appVersion is passed once to amplitude.init and the SDK's context plugin supplies the rest; behavioral autocapture stays false. Volatile context (network, surface, account_type, account_funded, is_hardware_account, account_id_hash) stays event-level. Connectivity fields (connection_type/effective_type) drop to app.opened only.
  • Durable Identify traitssyncIdentifyTraits sends wallet_count (= allAccounts.length), has_hardware_wallet, has_imported_account via Amplitude Identify, dirty-checked so it only fires when a trait changes. The fingerprint is recorded only after the init/consent guard (so a pre-init call can't permanently suppress the first Identify).
  • app.opened — new event (emit) fired once per popup/fullpage/sidebar mount with the one-time snapshot. initAmplitude became async to resolve the surface first; hasInitialized is still set synchronously before the first await, so ordering is race-free and the App.tsx:48 fire-and-forget call is unaffected. getSurface()/resolveSurface() classify popup vs sidebar vs fullpage (async resolved once, cached, read synchronously on the hot path). The only new event name added to METRIC_NAMES is appOpened: "app.opened".

Verification: built via TDD, one commit per task with an independent spec-compliance + quality review per task and a whole-branch review at the end. yarn jest extension/src/helpers/metrics.test.ts → 20/20 (new suite); existing Account/SignTransaction/popup/metrics suites unaffected (they mock helpers/metrics); tsc --noEmit clean. A regression test asserts no event payload contains a raw/truncated public key.

Follow-ups / out of scope:

  • Slices B (screen.viewed collapse) and C (domain-event renames/consolidations/removals) — event renaming.
  • accountServices.ts has a private duplicate storeAccountMetricsData that doesn't call syncIdentifyTraits; non-functional gap (traits self-heal via useGetAppData), but the two copies should be deduped.
  • Cross-platform: mobile uses the same single-mnemonic HD model, so wallet_count = allAccounts.length matches (confirmed — mobile labels accounts "wallets" in UI but the count is identical). The one remaining cross-platform check is that mobile reproduces the account_id_hash SHA-256 vector when it implements.
  • The 2-line translation.json (en/pt) changes are auto-generated by the repo's husky i18next-scanner pre-commit hook filling a pre-existing gap on master; unrelated to this feature.

piyalbasu and others added 11 commits July 13, 2026 20:03
First slice of the cross-platform analytics schema-alignment epic:
global property-model cleanup (schema_version, account_id_hash, Identify
traits, drop SDK-duplicated fields, app.opened snapshot). Evolves the
existing emitMetric path in place; hard cutover, no dual-write. Event
renames deferred to follow-on slices B/C.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds deriveIdentifyTraits (pure wallet_count/has_hardware_wallet/
has_imported_account) and syncIdentifyTraits (dirty-checked Amplitude
Identify call), wired into storeAccountMetricsData.

Also fixes a privacy violation found in review: storeBalanceMetricData
emitted a truncated public key in the freighterAccountFunded event body.
Replaced with account_id_hash of the full public key, matching the
schema's "never emit a raw or truncated public key" constraint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
syncIdentifyTraits cached the dirty-check fingerprint before the
AMPLITUDE_KEY/hasInitialized guard, so a pre-init call would poison the
cache without ever sending an Identify, causing a later identical call
to silently short-circuit and never sync the user's traits. Move the
cache write to after the guard so it's only recorded once an Identify
actually fires.
…duplicate)

Final review found accountServices.ts uses a private duplicate
storeAccountMetricsData that doesn't call syncIdentifyTraits; the
effective sync sites are useGetAppData + the recover hook. Non-functional
gap (traits self-heal via useGetAppData). Note added + dedupe follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 14, 2026 17:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces the analytics property-model foundation without renaming existing events.

Changes:

  • Adds schema versioning, hashed account IDs, surface metadata, and app.opened.
  • Moves durable wallet traits to Amplitude Identify properties.
  • Adds analytics tests and design documentation.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
extension/src/helpers/metrics.ts Implements analytics context, identity traits, hashing, and initialization.
extension/src/helpers/metrics.test.ts Tests the new analytics behavior.
extension/src/popup/constants/metricsNames.ts Adds the app.opened event name.
extension/specs/ANALYTICS_SCHEMA_FOUNDATION.md Documents the schema foundation.
extension/src/popup/locales/en/translation.json Adds generated auto-lock labels.
extension/src/popup/locales/pt/translation.json Adds Portuguese locale entries.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread extension/src/helpers/metrics.ts Outdated
Comment thread extension/src/helpers/metrics.ts
Comment thread extension/src/helpers/metrics.ts Outdated
Comment thread extension/specs/ANALYTICS_SCHEMA_FOUNDATION.md Outdated
Comment thread extension/src/popup/locales/pt/translation.json Outdated
Comment thread extension/specs/ANALYTICS_SCHEMA_FOUNDATION.md Outdated
piyalbasu and others added 2 commits July 14, 2026 14:04
The canonical analytics schema lives in the cross-platform RFC
(stellar/wallet-eng-monorepo#10); this repo doc was a brainstorming
artifact (slice refs, PR numbers, migration-decision narrative) that
would rot as a second source of truth. Design context lives in the PR
description; cross-platform contracts go to the RFC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lock omission

- app.opened: defer emit until the data-sharing preference resolves to
  allowed (it defaults false and hydrates async, so emitting at init was
  suppressed and lost). Emit once, when consent becomes allowed.
- syncIdentifyTraits: gate on consent and don't cache the fingerprint
  while opted out, so traits re-sync after consent hydrates.
- buildCommonContext: omit account_type/account_funded/is_hardware_account
  pre-unlock (no active key), per the spec's omission rule.
- pt locale: real Portuguese for the auto-generated Auto-lock timer keys.
- Tests: 22/22; adds consent-hydration regressions for app.opened + Identify.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants