Growth Studio metrics pipeline expert. Use when: adding channels, metrics, stages, or groups to the analytics module; modifying the progressive loading architecture; changing channel-to-group mappings; debugging missing channels in the dashboard. Triggers: 'nuevo canal', 'nueva metrica', 'agregar metrica', 'canal no aparece', 'growth studio metrics', 'stage service', 'channel registry', 'group mapping'.
78
97%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Two files together describe the analytics system. Both must stay in sync:
| File | Role |
|---|---|
backend/src/modules/analytics/domain/metric_catalog.py | Semantic catalog: what each metric means, its aggregation type, unit, providers that can emit it. Used at runtime. |
backend/src/modules/analytics/domain/extraction_contract.py | Extraction contract: which provider actually emits which metric, from which API endpoint, into which channel slug, when, and where it lands. Documentation + tests. |
docs/etl/extraction-contract.md | Auto-generated human-readable rendering of the contract. Read this FIRST when answering "where does X come from". |
Workflow rules for any change to the analytics module live in .claude/rules/etl-extraction-contract.md. Read it before you start.
Mandatory final step of any change that touches a provider, the pipeline, the scheduler, the workers, or the catalog:
make extraction-contract # regenerate the markdown
cd backend && .venv/bin/pytest tests/architecture/test_extraction_contract.py -x -q # verify no driftBoth must pass. The provider/pipeline change, the contract update, AND the regenerated Markdown go in the same commit.
Frontend (progressive loading)
Tier 0: Bowtie Summary → SummaryStageService (cache reader)
Tier 1: Stage Overview → StageOverviewService (cache reader)
Tier 2: Group Detail → GroupDetailService (cache reader)
Tier 3: Full Detail → {Stage}StageService (DB query + cache write)
Cache warming: overview miss → _warm_stage_cache() → stage serviceKey principle: Tiers 0-2 NEVER query the DB. Only Tier 3 stage services hit the DB.
| File | Role |
|---|---|
stage_services/constants.py | Single source of truth for ALL shared constants |
channel_registry.py | Channel definitions per stage + provider mapping |
stage_services/{stage}_stage.py | One per stage — computes metrics, writes cache |
stage_services/overview_stage.py | Thin cache reader for Tier 1 |
stage_services/group_detail.py | Thin cache reader for Tier 2 |
api/metrics.py | API routes — uses stage services directly |
metrics_service.py | Legacy (sankey, bowtie summary, timeseries ONLY) |
frontend/.../config/channel-display-registry.ts | Frontend channel display config |
frontend/.../config/dashboard-sections.ts | Deep-link section registry |
Example: adding "Pinterest Organic" to attraction stage.
channel_registry.py)# In STAGE_CHANNEL_MAP["attraction"], add:
{
"slug": "pinterest-organic",
"name": "Pinterest Organic",
"channel_type": "social", # existing type → no constants.py change needed
"source_label": "Pinterest",
"provider_name": "pinterest", # new provider → step 2 needed
"metric_names": ["impressions", "clicks", "saves"],
},channel_registry.py) — only if new provider# In PROVIDER_TO_CHANNEL_TYPES, add:
"pinterest": {"pinterest"},constants.py) — only if new channel_typeIf channel_type is new (not already in a GROUP_MAP), add it:
# In ATTRACTION_GROUP_MAP:
"social": "organic_social", # ← "social" already maps, so pinterest-organic auto-joins organic_socialIf the channel_type already exists in the GROUP_MAP, skip this step.
channel-display-registry.ts)'pinterest-organic': {
summaryMetrics: [
{ name: 'impressions', label: 'Impresiones' },
{ name: 'clicks', label: 'Clics' },
{ name: 'saves', label: 'Guardados' },
],
primaryMetric: { name: 'impressions', label: 'impresiones' },
},tests/modules/analytics/ that the channel appears in the correct groupconfig/__tests__/channel-display-registry.test.tsmetrics_service.py — never add stage metrics logic hereoverview_stage.py / group_detail.py — never modify for new channels_warm_stage_cache() — no changes for new channelsmetric_names list in STAGE_CHANNEL_MAP (channel_registry.py)aggregation_helpers.py_enrich_with_derived_metrics() in attraction_stage.pychannel-display-registry.ts summaryMetricsofficial_metrics tableExample: adding "ai_search" group to attraction.
constants.py)# In ATTRACTION_GROUP_MAP, add:
"ai_search": "ai_search",
# In STAGE_GROUPS["attraction"], add:
("ai_search", "Busqueda IA", "ai_search"),attraction_stage.py)# In groups dict:
groups: dict[str, list[ChannelMetricDTO]] = {
"organic_social": [],
"ga4_search": [],
"paid": [],
"outbound": [],
"ai_search": [], # ← new group
}
# In result DTO construction — update AttractionDetailDTO to include new fieldattraction_dto.py)class AttractionDetailDTO(BaseModel):
organic_social: TrafficGroupDTO
ga4_search: TrafficGroupDTO
paid: TrafficGroupDTO
outbound: TrafficGroupDTO
ai_search: TrafficGroupDTO | None = None # ← new optional groupAttractionCaptureDetail.tsxLazyChannelGroup for the new groupThis is rare (8 stages cover the full Bowtie). If needed:
stage_services/{name}_stage.py with get_metrics() methodapplication/dto/{name}_dto.pyconstants.py (GROUP_MAP if applicable, STAGE_GROUPS entry)api/metrics.py using the new stage service_warm_stage_cache() in api/metrics.pyFunnelStage enum in api/metrics.pystage_services/__init__.pyChecklist (in order):
STAGE_CHANNEL_MAP in channel_registry.pyPROVIDER_TO_CHANNEL_TYPES + tenant's channel_connections tableconstants.py for the channel_type → group mappingdocker exec visionarias_redis redis-cli DEL "metrics:{tenant_id}:{stage}:last_30_days"STAGE_GROUPS in constants.py includes the groupuseMemo filter for groupKeyconstants.pyget_*_metrics() to MetricsService (it's legacy — use stage services)overview_stage.py or group_detail.pyChannelRegistryMetricsService for new stage metrics featuresreferences/etl-extraction-contract.md — SSoT 2-files, 5-step workflow tras provider/pipeline change, best practicesreferences/analytics-metrics.md — stage services, group mappings, tiers progressive loading, agregar channelreferences/data-reliability.md — 4-layer verification protocol, trigger matrix, agregar providerd31f7bc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.