🚨 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
Cost & Conversion by Hour-of-Day BKK · 30d window
⚠ Decaying good→bad
↗ Recovering bad→good
Stability Matrix 7d × 30d transitions
🌐 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
⚙ 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 |
|---|
📦 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
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).
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
--full window. Chains pull-deposits-range → pull-players-range → pull-pa-stats-range → bulk-load-range → build-facts-range → build-classification → build-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
classification_scenarios table).
📊 Scenarios — threshold policies
| 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)
| bucket | formula | meaning | action |
|---|---|---|---|
| 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)
| tier | formula | meaning |
|---|---|---|
| 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 \ tier | whale ($大) | 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 | 边缘小额 | 中间地带 |
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
- Open the Brief tab. Look at Top Risk (single-campaign clear bans) and 🚫 Permanent Ban Candidates (cross-campaign repeat-offenders).
- 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.
- Single-campaign ban:
./scripts/plan-exclude.sh→ emitsmcp-propellerads.add_campaign_exclude_zonespayloads. Default scenario / 30d / no_register. - Cross-campaign ban (every camp the zone offended in):
./scripts/plan-exclude.sh --cross-campaign. - Paste the printed payload into chat → AI invokes the MCP call. Always dry-run; you confirm before execute.
- Open the Brief tab. Look at Top Opportunity — these are
good_ftd+ whale/mid tier zones. - Pull the JSON list:
./scripts/list-zones.sh --scenario default --bucket good_ftd --quality-tier whale --format json. - In chat, ask AI to invoke
mcp-propellerads.create_campaignwith these zone IDs astarget_zones+ a higher daily cap. - Tighten policy as winners stabilize: switch the dropdown to
strictorsuper_strictand re-pull — the bar moves up.
- Inside cockpit: Zones tab → search by zone_id.
- Full dossier from CLI:
./scripts/by-zone.sh <zone_id>— shows all (campaign × scenario) bucket assignments + spend / regs / deps. - Cross-scenario view:
./scripts/run.sh queries/exploration/zone-across-scenarios.sql --set campaign_id=<C> --set zone_id=<Z>.
- Open the Time-of-day tab — cost & conversion by hour for the active TZ.
- Brief tab also shows top 5 🔥 burning hours (spend, no deps) and ✨ golden hours (cheap deps).
- 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