# APAC PQL — one table, every moving part, with links

**Checked 2026-08-27.** The question this answers: *什麼功能、在哪裡、怎麼進去.* Nothing here is
design intent — every row was verified against the running system, the live database, or the source
repo on this date.

Companion docs: the narrative walk-through is
[`apac-operating-mechanism-2026-08-26.md`](apac-operating-mechanism-2026-08-26.md); the three-region
picture and the target design are [`../universal/infra/current-state.md`](../universal/infra/current-state.md)
and [`../universal/infra/target-state.md`](../universal/infra/target-state.md). Credentials and
per-identity access details are deliberately **not** in this repo — ask Growth.

---

## A. The chain, in order of execution

| # | Time (CST) | What happens | Where it runs | Owner | Link |
|---|---|---|---|---|---|
| 1 | 08:30 | Dispatcher: refresh HubSpot target list, select tenants + previous-day signups, sync Effective Sales Owner from NexusCRM (via BigQuery), fan out to SQS | AWS Lambda, stack `apac-tenant-monitoring`, acct `552185537300`, `us-west-2` | Yujie | [apac_lambda_tasks](https://github.com/pingcap-inc/pingcap-support/tree/main/apac_lambda_tasks) |
| 2 | +minutes | Worker: enrich (domain intelligence, clusters, Clinic QPS, billing/card), route Active vs Sales-owned, upsert | same stack | Yujie | [Worker README](https://github.com/pingcap-inc/pingcap-support/tree/main/apac_lambda_tasks/APAC-Tenants-Monitor-Fanout-Worker-Task) |
| 3 | +minutes | **Scoring engine #1**: `Activity40+Fit30+Urgency30`, rule overrides → P0/P1/P2 → writes score + daily snapshot → **PATCHes HubSpot Contact `apac_pql_priority_tier`** | same stack | Yujie | [Scoring task](https://github.com/pingcap-inc/pingcap-support/tree/main/apac_lambda_tasks/APAC-Tenants-Scoring-Task) · [rules HTML](https://github.com/pingcap-inc/pingcap-support/blob/main/apac_lambda_tasks/APAC-Tenant-Scoring-and-Priority.html) |
| 4 | 12:00 | Summary card: totals + up to 10 rows per section → Feishu | same stack (EventBridge rule; **verify it is ENABLED** — the Layer-2 runbook says this card arrives manually and irregularly) | Yujie | group `oc_a3d2252841c435991c57ea521aa21497` |
| 5 | 11:00 | **Scoring engine #2**: `plg_daily_digest.sql` re-scores every tenant from raw columns with its own dormancy/recency gates, emits one `digest_text` string | Feishu Anycross workflow `7661478774365522886`, MySQL node "TiDB APAC RO" | Yan | [plg_daily_digest.sql](https://github.com/yybaroq/plg-notification/blob/main/sql/plg_daily_digest.sql) |
| 6 | 11:00 | Digest posted as plain text | Feishu custom-bot webhook → group **"TiDB Bots"** `oc_e31cc48f07e15c78d8f544068284d69d` | Yan | [workflow doc](https://github.com/yybaroq/plg-notification/blob/main/workflow/anycross_workflow.md) |
| 7 | 11:15 Mon–Fri | Enrichment: parse digest → NexusCRM SOQL → Reo firmographic → rules R1–R24 → classify/route/dedupe/suppress | **Cowork scheduled task `plg-digest-crm-check`** | Yan | [prompt mirror](https://github.com/yybaroq/plg-notification/blob/main/automation/plg-digest-crm-check.md) — resynced 2026-08-27 (`49e2b96`) after 4 weeks stale |
| 8 | 11:15 | Interactive card posted + history row appended | Feishu group "TiDB Bots" + Bitable | Yan | [PLG Digest History](https://pingcap.jp.feishu.cn/base/PtJDb7NtYavifMsMRDAjLVd6pne?table=tblIZha3B45t8Myw) |
| 9 | 11:50 | Watchdog checks a card was posted | Cowork task `plg-card-watchdog` | Yan | — |
| 10 | ad hoc | **SDR works the lead → edits Lead Status in NexusCRM by hand** (the system of record) + edits Bitable 狀態/SDR Owner | [NexusCRM](https://nexuscrm-new-prod.pingcap.com) | SDR (Kenneth Lee for all actionable PLG rows) | — |
| 11 | ad hoc | Ops filters HubSpot on `apac_pql_priority_tier` → triggers outbound email workflows | HubSpot | Ops | [write-back contract](bot-to-hubspot-writeback/bot-to-hubspot-writeback.md) |

## B. Where each *rule* lives (the part that keeps surprising people)

| Rule | Engine #1 (Lambda) | Engine #2 (Yan's agent) |
|---|---|---|
| Market scope (CN in, JP out), plan filters | ✅ Python | inherits whatever is in the table |
| Fit enrichment (`domain_intelligence` + OpenAI) | ✅ Python + DB table | reads the column |
| `cluster_count ≥ 50 → P0` · `≥10 + known org → P1` | ✅ | ❌ **absent** |
| AI-related institution = 15 | ✅ | ❌ absent |
| HubSpot target list `19544` → force P0 | ✅ | only via a manually-posted card (R13) |
| Dormancy gate (QPS<0.001 & not 48h-new & no error → P2) | ❌ | ✅ since 2026-07-24 (P0 12→6) |
| POC / multi-cluster recency gates | ❌ | ✅ |
| 🆕 72h new-tenant channel + ICP filter (bypasses score) | ❌ | ✅ R16, prompt text |
| 🧟 dormancy marker, Day 3–6 compress, Day ≥7 rollup | ❌ | ✅ R17, prompt text |
| Exclusion list — full suppression before classification, audit line, manual release only | ❌ | ✅ R18 — a table inside the prompt |
| Net-new / win-back / SI / active-customer classification | ❌ | ✅ R7/R10 |
| Lead-company bridge for domain mismatch | ❌ | ✅ R5 |
| Routing: AI → named SDR, non-AI → territory SDR; **an existing Account routes to the Account Owner instead** | ❌ | ✅ R9 incl. its Step 0 account-owner-first clause |
| Read SDR follow-up back from CRM and suppress worked rows | ❌ | ✅ R22 (lead-status movement) + R22b (**touch test**: governing lead `LastModifiedDate` moved, bulk clusters ≥5/±60s excluded) |
| Comparison baseline = last enrichment day; streaks counted in enrichment days, never calendar days | ❌ | ✅ R19 |
| 🧟 dormancy evidence test (Reo activity >90d + no lead <90d + score static-term-explainable); QPS is never counter-evidence | ❌ | ✅ R20 |
| AE-owned rows: narrate once, then stay silent | ❌ | ✅ R21 |
| 📈 Fast-riser watch — score +≥15 in ≤7d or first P1 crossing, diffed off the score snapshots | ❌ | ✅ R23 (shipped 2026-08-27) |
| PLG reporting excludes AE-sourced deals | ❌ | ✅ R24 — a named list, not a stored field |

## C. Data landing points

| Store | Coordinates | Holds | Who writes |
|---|---|---|---|
| `apac_active_tenants` | TiDB `c5P1VZxiR27eA4p`, ap-southeast-1, db `regional_support` | 7,788 tenants: signals, score, priority, `track_step`(all 0) | Lambda |
| `apac_sales_owned_tenants` | same | 168 tenants, scored but excluded from all reporting | Lambda |
| `apac_tenant_score_snapshots` | same | 42,596 rows, 07-13→08-26, ~12.6% of tenants/day | Lambda |
| `apac_effective_sales_owner_snapshot` | same | CRM-derived owner/opp/lead decision, 971 tenants (12.5%) | Lambda (from BQ mirror) |
| `apac_hubspot_target_accounts` | same | 286 rows, synced 08-26 | Lambda |
| `apac_tenant_events` | same | **0 rows — nothing writes it**, so the Summary's import section is always empty | nobody |
| Bitable "PLG Digest History" | base `PtJDb7NtYavifMsMRDAjLVd6pne`, table `tblIZha3B45t8Myw` | one row per digest entry per day; SDR edits Owner/Status in place | Yan's agent + SDR |
| HubSpot Contact property | `apac_pql_priority_tier` (P0/P1/P2/Not Scored) | tier, for email workflow filtering | Lambda |
| **NexusCRM** | https://nexuscrm-new-prod.pingcap.com | **Lead Status = the follow-up system of record**, Account, Opportunity | SDR, by hand |

## D. How to get in (all verified 2026-08-26)

| Target | Command / URL | Identity | Notes |
|---|---|---|---|
| NexusCRM (read) | the read-only `nexuscrm-connect` OAuth app | runs as the signed-in user | no grant needed; SOQL `LIMIT 200`, token ~24h |
| APAC scoring DB | `source ~/.config/pingcap-plg/regional-db.env` → `mysql -h $APAC_TIDB_HOST -u $APAC_TIDB_USER …` | `c5P1VZxiR27eA4p.readonly_user` | SELECT only |
| Self-serve spend, tenant grain | BigQuery, via the Growth analyst account | Growth | reachable with existing grants; details held outside this repo |
| NexusCRM BQ mirror / daily billing source-cost dataset | — | — | ❌ not granted; [request filed](../universal/REQUEST_nexuscrm_read_access.md) |
| Lambda stack | AWS `552185537300`, `us-west-2`, stack `apac-tenant-monitoring` | engineering | Growth has no AWS access |
| Anycross workflow | https://anycross.feishu.cn — integration `7661478093248285654`, workflow `7661478774365522886` | Yan | canvas UI, see runbook |
| Cowork task | `plg-digest-crm-check`, cron `15 11 * * 1-5` | Yan | prompt is the source of truth, GitHub is a mirror |

## E. Repos

| Repo | What it is | Status |
|---|---|---|
| [pingcap-inc/pql-infrastructure](https://github.com/pingcap-inc/pql-infrastructure) (this one, renamed from `pql-modeling`) | intent, model contract, requests, audits | authoritative for *why* |
| [pingcap-inc/pingcap-support/apac_lambda_tasks](https://github.com/pingcap-inc/pingcap-support/tree/main/apac_lambda_tasks) | the running scoring pipeline | authoritative for *what runs* |
| [yybaroq/plg-notification](https://github.com/yybaroq/plg-notification) | Layer 2 digest + enrichment | **mirror, resynced 2026-08-27** (`49e2b96`, after 4 weeks stale). The live prompt is still the source of truth — the mirror can drift again. Do not port its code ([why](execution/apac-scoring-model-divergence-note.md)); do read its business rules |

## F. The five things that are nobody's job right now

1. Two scoring engines disagree, and no one owns reconciling them (B above).
2. Layer 2's rules live only in a prompt — no review, no diff, no test.
3. `apac_tenant_events` has no producer.
4. Follow-up state never returns from NexusCRM to the dataset.
5. HubSpot's send/reply outcome returns to nothing at all.
