swap-analytics
Point product analytics somewhere other than BigQuery (a managed pipeline like Segment or PostHog, or a different warehouse). Use when the user decides to change where analytics events go. Not for deb
SKILL.md.claude/skills/swap-analytics/SKILL.md
GitHubSwap product analytics
- Interface:
Analytics(one method,record(event)) inpackages/nest-common/src/analytics.ts, and its Python twin, theAnalyticsprotocol inapps/ai/app/analytics.py(the ai service recordstake_reviewed, with its score, once a review finishes: only it knows when that is). - Today:
BigQueryAnalytics, built bycreateAnalytics(env)in the same file (null whenBIGQUERY_DATASETis unset: nothing is recorded). It streams one row per event into thedirector.eventstable with@google-cloud/bigquery(apps/ai: withgoogle-cloud-bigquery, the same row), authenticated the same way as every other Google Cloud call here (ADC). - Provided as
ANALYTICSinapps/api/src/modules/analytics/analytics.module.ts, wrapped byAnalyticsService(record(kind, fields)), which makes every write fire-and-forget and logs a failure instead of letting it fail the caller's action. Every call site importsAnalyticsServicefromapps/api/src/modules/analyticsand callsAnalyticsService.recordwith one ofAnalyticsKind(TypeScript checks the kind). In apps/ai,create_analytics(settings)builds it andRecordermakes each write fire-and-forget the same way;Director(app/director.py) owns one. - Env:
BIGQUERY_DATASET, andGOOGLE_CLOUD_PROJECT(shared with Director's live call), read by the api and the ai service, documented in.env.exampleanddocs/environment.md.
A managed pipeline or a different warehouse
- A class implementing
Analytics, in both services.recordtakes the sameAnalyticsEventshape (kind,userId,templateId,videoId,score,detail; snake_case in Python); map its fields to whatever the destination expects. - Choose it in
createAnalytics(andcreate_analyticsinapps/ai/app/analytics.py) from new variables next toBIGQUERY_DATASET, in the services' settings,.env.exampleanddocs/environment.md.AnalyticsService,Recorderand every call site stay the same. - If the new destination needs its own SDK, keep it inside the two
analyticsfiles: nothing outside the seam should import it.
Tests
packages/nest-common/src/analytics.test.ts runs BigQueryAnalytics against a stand-in
HTTP server speaking BigQuery's real streaming-insert protocol, with a BigQuery client
carrying its own pre-set access token (so it never reaches Google or the GCE metadata
server). apps/api/src/modules/analytics/analytics.service.test.ts covers the
fire-and-forget wrapper against a plain stand-in Analytics. In apps/ai,
tests/test_analytics.py does both (the stand-in insertAll server is in
tests/fakes.py), and tests/test_director.py checks a finished review's event. A new
implementation needs the same kind of protocol-speaking stand-in; add it rather than
mocking the new provider's SDK.
Finish
- The verify skill.
- Ask the
revieweragent to review the change, and thesecurity-revieweragent: a new destination sends product data somewhere new. - Update the seam's row in the README's "Scaling path" table if what's "Now" changed.