{
  "id": "compare_affiliate_performance",
  "mcp_tool": "compare_affiliate_performance",
  "status": "approved",
  "source_system": "cake",
  "environment": ["staging", "prod"],
  "sharded": false,
  "connections": {
    "whale": "cake_whale_ro"
  },
  "objects": [
    "whale.lucos_ro_advertiser_stats_daily",
    "whale.lucos_ro_publisher_earnings_daily"
  ],
  "parameters": {
    "advertiser_id": { "type": "int", "required": false },
    "affiliate_id": { "type": "int", "required": false },
    "date_from": { "type": "date", "required": false },
    "date_to": { "type": "date", "required": false },
    "compare_from": { "type": "date", "required": false },
    "compare_to": { "type": "date", "required": false },
    "compare_mode": {
      "type": "enum",
      "required": false,
      "default": "clicks_drop",
      "values": [
        "clicks_drop",
        "clicks_surge",
        "earnings_drop",
        "earnings_surge",
        "conversion_rate_drop",
        "conversion_rate_surge"
      ]
    },
    "min_clicks": { "type": "int", "required": false },
    "sort_by": {
      "type": "enum",
      "required": false,
      "values": [
        "clicks",
        "earnings",
        "clicks_delta",
        "clicks_delta_pct",
        "earnings_delta",
        "earnings_delta_pct"
      ]
    },
    "sort_dir": {
      "type": "enum",
      "required": false,
      "values": ["desc", "asc"]
    },
    "limit": { "type": "int", "required": false, "default": 50 }
  },
  "constraints": {
    "max_window_days": 92,
    "default_window_days": 7,
    "max_rows": 50,
    "timeout_ms": 60000
  },
  "allow_fields": [
    "affiliate_id",
    "affiliate_name",
    "adv_id",
    "adv_name",
    "clicks",
    "compare_clicks",
    "clicks_delta",
    "clicks_delta_pct",
    "earnings",
    "compare_earnings",
    "earnings_delta",
    "earnings_delta_pct"
  ],
  "deny_fields": ["rev_share", "net_cpc", "cpc", "click_table_id"],
  "post_processing": [
    "Clicks from whale.lucos_ro_advertiser_stats_daily (show_oos=0), earnings from whale.lucos_ro_publisher_earnings_daily. Do not use Admin.lucos_ro_affiliate_cpc_summary_daily — that view re-aggregates click-grain aff_cpc_summary and times out unscoped.",
    "Drop modes seed the prior window ORDER BY SUM(clicks) DESC LIMIT 200, then IN-list those affiliate_ids in the current window. Surge modes seed current then look up prior. Seeding current-by-clicks would truncate publishers who collapsed toward zero.",
    "Do not GROUP BY adv_id — affiliate grain is one row per publisher across advertisers.",
    "compare_date_range omitted → equal-length window immediately before date_range. date_range omitted → last 7 days vs prior 7.",
    "clicks_drop: keep current_clicks < compare_clicks (net volume decline). Rank most-negative clicks_delta_pct first (sort_dir asc).",
    "clicks_surge: keep current_clicks > compare_clicks. Rank largest pct first (sort_dir desc).",
    "earnings_drop / earnings_surge: seed by publisher_earning; same predicates on earnings.",
    "conversion_rate_drop / conversion_rate_surge: no conversion grain; use clicks delta as proxy and note the limitation.",
    "clicks_delta = clicks - compare_clicks. clicks_delta_pct = (clicks - compare_clicks) / compare_clicks * 100 (null when compare_clicks = 0).",
    "earnings_delta = earnings - compare_earnings. earnings_delta_pct = similar.",
    "truncated=true when the seed hit 200 or more than limit rows matched after the predicate."
  ],
  "notes": "PREFERRED for 'which affiliate has more click volume drop'. Same clicks_source as get_advertiser_report. One row per publisher across advertisers. Do NOT use get_affiliate_report (single publisher, no comparison). Do NOT use get_advertiser_report (no period comparison). Do NOT fall back to adhoc_explore — that catalog cannot compare two windows."
}
