🚀 Traffic Cockpit

loading...
Spend (30d)
Registers (30d)
Deposits (30d)
Mart rows

🚨 Top Risk clear ban candidates · 0 dep

🟢 Top Opportunity good_ftd whale · scale

💎 Review over_cpa with deposits — don't auto-ban; check tier

🚫 Permanent Ban Candidates same zone bad in N+ campaigns · check total dep before banning

🗂 Settled · Review Divergence my_bucket set, but system disagrees today — keep, revise, or clear

🔥 Burning Hours dayparting candidates

✨ Golden Hours protect / scale

Campaigns window=30d · click id → drill to Zones · click owner/status/channel → filter here

Edit names / owner / notes in dashboard/campaign-labels.json then 🔄 Refresh view. Status comes from campaign_config_snapshots (latest per campaign).

Zone Classification scenario=default · window=30d

Window:
Click bucket / my_bucket → filter · ✏️ set · 🔄 sync 1 · 🕐 history · click 🔄 toolbar to bulk-sync filtered

Cost & Conversion by Hour-of-Day BKK · 30d window

🌐 Mixed-GEO zones publisher inventory shared across countries · 30d window

These zones received PA spend from campaigns in 2+ countries. Banning a mixed zone in one GEO costs you traffic in the other — review per-GEO performance before deciding.

Pivot — click a preset, or drag dimensions yourself

Presets:

⚙ Pipeline steps · last 24h per-step process health · finer than the legacy header pill

Source: sync_runs. Each dot = one cron tick (24h history; rightmost = newest). Failed rows show error_msg in the last column.

Step Cadence Last status Age Last dur (s) 24h timeline Error Run
Snapshot taken:

📦 Ingest volume · last 24h rows per raw table filter imported_at · detects "sync stopped writing"

Counts of rows whose imported_at >= now - 24h. Orthogonal to lag below: this answers "did sync run", lag answers "is upstream stuck".

🔗 Funnel attribution health · click_id quality (24h registers) % of last-24h registers · low-and-clean = trustworthy CPA

% of last-24h registers whose click_id is NULL / placeholder {…} / unjoinable to lp_events_raw.query_params->clickid. High → upstream tracking leaking; CPA / bucket evaluation only sees a fraction of true funnel.

⏱ Upstream lag · minutes since freshest source-side timestamp computed off source ts (created_at / ts_utc) · NOT imported_at

Minutes between now and MAX(source_timestamp) per raw table. Detects upstream stalls that would be invisible if measured by imported_at.

🪣 Full backfill · manual escape hatch for new agent / fresh install / long offline window

Cron's hourly pulls use small windows: pull-pa-stats 2h · pull-deposits/players-from-zzzl 24h · bulk-load-from-zzzl incremental. Rows older than the window are unreachable until a backfill runs.

Offline tolerance (laptop closed, no other host running sync): ≤2h zero loss · 2h-24h PA stats start dropping → run Full PA stats · 24h-30d deposits/players/LP events drop too → run all four buttons · >30d deposits/players are permanently lost (script --full caps at 30d for zzzl ingest; pa-stats --full reaches 60d; bulk-load-from-zzzl --mode full has no time cap).

Outcome appears in the Pipeline-steps card above. Note: each *-full writes its own sync_runs row keyed by the whitelist name (separate from the cron-incremental row).
After backfill, run build-facts + build-cockpit from Pipeline-steps above to surface the new rows in the cockpit.

🪣 Repair date range · surgical refill half-open [since, until) UTC · ≤90d window

Refill specific UTC dates without re-pulling the full --full window. Chains pull-deposits-rangepull-players-rangepull-pa-stats-rangebulk-load-rangebuild-facts-rangebuild-classificationbuild-cockpit. Each step writes its own sync_runs row with the resolved {since, until} in params JSONB for audit. Stops on first failure; restart via the per-step ▶ buttons in Pipeline-steps if needed.

🆘 Cockpit Help

One-stop reference for the cockpit. Auto-generated from current data (scenarios reflect the live classification_scenarios table).

📊 Scenarios — threshold policies

Each scenario is a named bundle of thresholds. Same data, different rules → different bucket assignments. Use the header dropdown to switch the cockpit view between them. ⭐ marks the currently selected scenario.
scenario good CPA < bad CPA > bad_reg_min_spent bad_ftd_min_reg ≥ min_spent (pending gate) cross-camp ≥ description

🏷️ Buckets — primary classification (mutually exclusive, 7 values)

bucketformulameaningaction
pending spent < min_spent_for_classify AND reg = 0 No signal yet — too small to judge. ⏳ Wait — let it accumulate
no_register spent > bad_reg_min_spent AND reg = 0 PA spent money but produced 0 registers — pure waste. 🔴 Ban (clear cut)
over_cpa reg > 0 AND CPA > bad_cpa_max Registers happened, but each cost more than threshold.
May still carry depositors — check tier.
💎 Review — see tier; whale/mid worth keeping
good_ftd CPA < good_cpa_max AND reg < bad_ftd_min_reg AND dep ≥ 1 Cheap registers + at least 1 deposit — early winner. 🟢 Scale — raise daily_cap
good_reg_bad_ftd CPA < good_cpa_max AND reg ≥ bad_ftd_min_reg AND dep = 0 Many cheap registers but 0 deposits — registrations are bait. ⚠ Ban — fake conversion source
good_register CPA < good_cpa_max (umbrella for the rest of cheap-CPA branch) Cheap registers, ftd undecided — potential winner. 🔵 Watch — let it reach signal volume
other Everything else (CPA in [good, bad] gray zone, or low-spend with non-zero reg) Doesn't fit a clear box. ⚪ No action — middle ground

💎 Tiers — depositor quality (mutually exclusive, 4 values)

tierformulameaning
whale Top third of avg_dep_per_user within the (scenario × window) slot This zone's depositors put down big money on average.
mid Middle third Normal-sized depositors.
low Bottom third Small depositors.
(blank / no-tier) dep_users = 0 No depositors at all — tier is not computable.

🧮 Combined — bucket × tier decision matrix

bucket \ tierwhale ($大)mid ($中)low ($小)(no dep)
good_ftd🔥 加大 cap🟢 加 cap🟡 保持—不存在—
good_register极少见,要看准 winner观察多数 — 让它跑
over_cpa保护 ⭐REVIEW倾向 ban直接 ban
no_register—不存在—全部 ban
good_reg_bad_ftd—不存在—全部 ban
other隐藏鲸鱼边缘 mid边缘小额中间地带
Reminder: same zone can show different bucket in different scenarios (e.g., over_cpa in super_strict but good_ftd in loose). That's the point of multi-scenario — you see the zone from multiple angles. Use the Pivot tab to cross-tab.

🛠️ Workflows — task-oriented recipes

Pick the task you want to do; follow the steps. These are the canonical paths for the most common cockpit jobs.
🚫 I want to ban bad zones
  1. Open the Brief tab. Look at Top Risk (single-campaign clear bans) and 🚫 Permanent Ban Candidates (cross-campaign repeat-offenders).
  2. For Permanent Ban rows: check the all-camp $ column first. If a zone has total deposits despite being bad in N+ camps, it's a whale-trap — review before banning.
  3. Single-campaign ban: ./scripts/plan-exclude.sh → emits mcp-propellerads.add_campaign_exclude_zones payloads. Default scenario / 30d / no_register.
  4. Cross-campaign ban (every camp the zone offended in): ./scripts/plan-exclude.sh --cross-campaign.
  5. Paste the printed payload into chat → AI invokes the MCP call. Always dry-run; you confirm before execute.
🟢 I want to scale winners
  1. Open the Brief tab. Look at Top Opportunity — these are good_ftd + whale/mid tier zones.
  2. Pull the JSON list: ./scripts/list-zones.sh --scenario default --bucket good_ftd --quality-tier whale --format json.
  3. In chat, ask AI to invoke mcp-propellerads.create_campaign with these zone IDs as target_zones + a higher daily cap.
  4. Tighten policy as winners stabilize: switch the dropdown to strict or super_strict and re-pull — the bar moves up.
🔍 I want to look up a zone
  1. Inside cockpit: Zones tab → search by zone_id.
  2. Full dossier from CLI: ./scripts/by-zone.sh <zone_id> — shows all (campaign × scenario) bucket assignments + spend / regs / deps.
  3. Cross-scenario view: ./scripts/run.sh queries/exploration/zone-across-scenarios.sql --set campaign_id=<C> --set zone_id=<Z>.
⏰ I want to see daypart timing patterns
  1. Open the Time-of-day tab — cost & conversion by hour for the active TZ.
  2. Brief tab also shows top 5 🔥 burning hours (spend, no deps) and ✨ golden hours (cheap deps).
  3. CLI: ./scripts/run.sh queries/exploration/golden-hours.sql.

📚 Cheat sheet — common commands

# Refresh local data + rebuild cockpit (everything, ~30s):
./scripts/sync-all.sh

# Pull only PA spend + rebuild (~10s, no zzzl side):
./scripts/pull-pa-stats.sh && ./scripts/build-facts.sh --hours 6 && ./scripts/build-cockpit.sh

# Pull a bucket's zone list as MCP-ready JSON:
./scripts/list-zones.sh --scenario default --bucket no_register --format json

# Generate ban plan (always dry-run; cross-campaign mode shown here):
./scripts/plan-exclude.sh --cross-campaign

# Drill into a single zone:
./scripts/by-zone.sh 8566333

# Switch theme tuning: edit ddl/classification_scenarios.sql then:
./scripts/setup.sh   # safe re-run; only re-seeds the 4 standard scenarios