> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Contact Context Smoke

## 0. 这份 smoke 要回答什么

Call Input Smoke 检查单次 `calls` row 是否准确。Contact Context Smoke 检查的是下一层：

> `contacts-analyzer` 看到的 contact-level context 是否够准、够完整、够可解释。

如果 task 错了，可能不是 task decision policy 本身错，而是 contact context 已经缺关键事实：

- Contact snapshot 没带 `doNotContact` / `lifecycleStage` / `leadStatus`。
- Message 只有 `VoiceMail (no transcript)`，AI 根本没看到客户留言内容。
- Lead record 没带 booking 相关事实，AI 不知道这是不是已经预约。
- Open task 没带 `attemptCount` / `dueAt`，AI 不知道该 `record_progress` 还是 `close unable_to_reach`。
- Recently closed task 没进入 context，AI 可能重复创建同类 task。

这不是完整的 `Contact Accuracy Foundation`。它是 demo 前的 lightweight upstream sanity check：确认 Task Accuracy 的 contact input 没有明显歪掉。

## 1. 它在 pipeline 里的位置

```text
Neon facts
  contacts
  messages
  leads
  tasks
  task_progress_events
        |
        v
contacts-analyzer prompt context
  CONTACT / snapshot
  PREVIOUS SUMMARY
  LEAD RECORDS
  RECENT MESSAGES
  OPEN TASKS
  PLAYBOOK GUIDANCE
  RECENTLY CLOSED TASKS
        |
        v
ContactsAnalysisSchema
  contact profile fields
  taskDecisions[]
        |
        v
Policy Guard / writer / DB constraints
        |
        v
Task timeline + contact history + UI
```

这份 smoke 覆盖的是：

```text
contact/message/lead/task facts
   -> contacts-analyzer prompt context
   -> AI 是否能基于这些 facts 做正确 contact / task 判断
```

它不测试 raw audio transcription 质量，也不测试 Vapi。它直接假设 call/message/lead/task facts 已经落库，然后检查 context builder 是否把必要字段喂给 AI。

## 2. 当前 production context 已经喂了什么

当前 `contacts-analyzer` 的 user message 不是只喂一段 text。它会把多张表压成半结构化 context：

```text
CONTACT:
CURRENT TIME:
STORE TIMEZONE:
LOCAL TIME:
DO NOT CONTACT:
LIFECYCLE STAGE:
LIFECYCLE STATE:
LAST ACTIVITY:
CURRENT LEAD STATUS:

PREVIOUS SUMMARY:

LEAD RECORDS:
RECENT CALLS:
RECENT MESSAGES:
OPEN TASKS:
PLAYBOOK GUIDANCE:
RECENTLY CLOSED TASKS:
```

Contact Context Smoke 重点看 `RECENT CALLS` 以外的部分：current snapshot、messages、leads、open tasks、progress state、recently closed tasks 和 playbook guidance。

## 3. Smoke 的 review 原则

不要做所有全排列。这里按 contact decision point 覆盖：

- Source identity：这是 call、SMS、voicemail、lead record，还是 existing task？
- Recency：发生时间、当前时间、store timezone、last activity。
- Safety/context：`doNotContact`、terminal lifecycle、recently closed task、existing duplicate task。
- Process state：open task、`attemptCount`、`dueAt`、progress history。
- Lead facts：lead source、lead type、received time、booking facts。
- Message facts：SMS body、voicemail transcription、DNC keywords、callback timing。
- UI explainability：task timeline、customer summary、related open tasks 是否让 staff 能 30 秒看懂。

这个 smoke 不是看模型“会不会编一个合理故事”，而是看 production contact context 是否足够支撑正确判断。

## 4. 第一批 smoke scenario set

每个 scenario 至少检查三件事：

- `DB facts`：上游表里字段是否写对。
- `Prompt context`：`contacts-analyzer` user message 是否带到了这些字段。
- `Expected implication`：这些 facts 是否足够支持后续 contact / task decision。

### CT-01 Existing lead books intro

**业务状态**

Lead 接通电话并确认预约 intro / first class。

**必须看到的 contact context**

- Lead record 存在。
- `CONTACT` snapshot 能显示当前 lifecycle / lead status。
- Open task 如果是 `lead_outreach` / `lead_follow_up`，要出现在 `OPEN TASKS`。
- `OPEN TASKS` 里的 `taskRef`、`typeCategory`、`attemptCount`、`dueAt` 必须可见。

**Expected implication**

已有 lead task 应该 close as `booked`。不要因为已经 booked 再创建 intake、waiver、arrival logistics task。

### CT-02 Trial completed but not converted

**业务状态**

Lead 已经完成 trial / first class，但还没购买 membership。

**必须看到的 contact context**

- 有可靠 attendance / post-trial 证据，不只是 generic SMS。
- Contact stage 仍是 `lead`。
- `leadStatus` 可以是 `showed` / `trialed`。
- `PREVIOUS SUMMARY` 或 recent facts 说明 trial 体验和未转化原因。

**Expected implication**

后续 task 才能走 `booked_not_converted`。如果只是 booked upcoming class，不应该提前创建。

### CT-03 Outbound no-answer on existing open task

**业务状态**

员工对一个 open task 打电话，没人接。

**必须看到的 contact context**

- `OPEN TASKS` 里有对应 open task。
- `attemptCount` 和 `dueAt` 出现在 `OPEN TASKS`。
- 最新 call facts 能证明 outbound no-answer。
- 没有客户新表达的 objection / DNC / close outcome。

**Expected implication**

通常是 `record_progress`，不是 create new task，也不是随便 close。若 `attemptCount` 达到 policy threshold，再按 Task Accuracy policy 判断是否 close `unable_to_reach`。

### CT-04 Voicemail callback request

**业务状态**

客户留 voicemail，说希望明天回电或指定时间 follow up。

**必须看到的 contact context**

- `messages.type = VoiceMail`
- `voicemailTranscription` 有内容
- Prompt 显示 `VoiceMail (transcribed)`
- 文本里能看出 callback timing
- Store timezone / local time 存在，方便生成 UTC `nextDueAt`

**Expected implication**

如果有 existing task，应该 `record_progress` with `callback_requested` / `follow_up_scheduled`。如果没有 existing task，而且是清晰 unresolved objective，才考虑 create task。

### CT-05 SMS STOP / DNC

**业务状态**

客户回复 STOP / unsubscribe / do not contact。

**必须看到的 contact context**

- `RECENT MESSAGES` 显示 SMS body。
- Contact snapshot 的 `doNotContact` 如果已是 true，必须传给 AI。
- Writer / policy 仍要 sticky enforce，不能只靠模型。

**Expected implication**

AI 不应该创建普通 outreach task。DNC 是 safety gate，应该阻断 proactive call/SMS/email。

### CT-06 Wrong number / non-customer business inquiry

**业务状态**

来电或消息是 wrong number、vendor、corporate sponsorship、partner、marketing pitch，和健身 customer lifecycle 无关。

**必须看到的 contact context**

- Current snapshot 能允许 `lifecycleStage = unknown`
- Recent call/message summary 保存有用事实，但不要伪造成 lead/member
- 如果已有 open fitness task，要能在 `OPEN TASKS` 看到它，方便 close invalid existing task

**Expected implication**

`lifecycleStage` 应该是 `unknown`，普通 fitness task 应该 no-op 或 close invalid existing task。

### CT-07 Unresolved complaint

**业务状态**

会员投诉 billing、staff、facility、policy 或 app issue，且没有明确解决。

**必须看到的 contact context**

- call/message summary 里有 complaint evidence
- `followUpNeeded = yes` when unresolved
- Contact profile 要能设置或保留 `hasOpenComplaint = true`
- 如果已有 retention/cancellation task，prompt 里必须能看到 existing task context

**Expected implication**

这类通常进入 `retention` 或 `cancellation_risk`，取决于是否有 churn/cancel risk。不能只当成普通 history summary。

### CT-08 Referral / bring-a-friend opportunity with existing task

**业务状态**

现有会员主动说要带朋友、问 guest pass、推荐家人，或留下 referral opportunity；同一个 contact 可能还有 renewal / cancellation / retention task。

**必须看到的 contact context**

- Recent call/message 明确提 referral / guest / friend
- Contact identity 明确是 member 或 known customer
- Existing open tasks 全部进入 context，不要只显示一个 objective
- UI 能显示 same contact 的 related open tasks，避免 staff 误以为只生成一个 objective

**Expected implication**

这可以产生 `referral` task。若同一个 contact 还有另一个 open task，Task UI 要能让 staff 看见 related open tasks。

### CT-09 Churned member asks to rejoin

**业务状态**

前会员主动来电或发消息，说想重新加入或回来上课。

**必须看到的 contact context**

- Contact snapshot 已是 `churned`，或 recent call facts 显示 `former_member`
- inbound customer-initiated re-engagement evidence
- 如果 interaction 当场完成 rejoin，outcome 要能显示 success；否则 follow-up remains open

**Expected implication**

后续 task 应该偏向 `win_back`。这不是 cold outbound win-back；是 customer-initiated reactivation。

## 5. 当前已发现的 contact context 风险

### 5.1 Lead facts 可能不够

当前 prompt 里的 `LEAD RECORDS` 主要展示：

```text
receivedAt + firstName + lastName + leadType
```

但 `leads` schema 还有 booking/source/raw email 相关字段。对于“lead 是否已经 booked / trialed / needs follow-up”的判断，v1 smoke 应该验证现有字段是否够用。

如果不够，下一步不是马上把 `rawBody` 全塞进 prompt，而是先加 targeted context：

- booking date/time
- lead source / email subject
- parsed class time if available
- ingestion source when helpful

### 5.2 Store isolation 的 prompt 输入要继续验证

Open tasks 当前按 `phone + storeId` 取，这是对的，因为 task mutation 必须 store-scoped。

Recent calls / messages / leads 当前更像 phone + franchise scoped context。demo 前 smoke 要验证同一个 phone 在多 store 下不会把另一个 store 的 facts 喂给 AI。若发现风险，应该优先把 recent calls/messages/leads context 也收敛到 store-scoped 或 source-store-aware。

### 5.3 Playbook guidance 只在 open task 存在时注入

当前 `PLAYBOOK GUIDANCE` 是按 open task categories 注入的。这个对 progress / close / update 很有用。

但 create case 没有 existing open task 时，AI 主要靠 task taxonomy 和 call/contact facts 创建新 task。后续如果要让 create 的 suggestedActions 更稳定，可以考虑给 create candidate category 注入轻量 playbook guidance，但不要在 demo 前强行扩 scope。

### 5.4 Contact Accuracy Foundation v1 的长期形态

Contact Accuracy 不是让 AI 读一堆历史后自由发挥“这个人是什么状态”。更稳的形态是：

```text
Neon facts
  contacts / calls / messages / leads / tasks / progress / closed tasks
  ↓
Context Builder
  - store-scoped contact snapshot
  - recent calls/messages with source store evidence
  - lead records with booking/source facts
  - open tasks with taskRef / category / attemptCount / dueAt
  - task_progress_events
  - recently closed tasks
  - playbook guidance when applicable
  ↓
LLM structured output
  - lifecycleStage / lifecycleState evidence
  - intent / complaint / DNC signals
  - taskDecisions[]
  ↓
Deterministic writer / Policy Guard
  - sticky DNC
  - duplicate task guard
  - store_id isolation
  - allowed mutation policy
  ↓
persisted contact state + tasks + timeline
```

这和 AI Agent Foundation 的关系是：

- `Facts`：多张表的历史事实，不是一段自由文本。
- `Context Builder`：决定 AI 到底看见什么；如果这里漏字段，下游 task 再聪明也会错。
- `Structured Output Schema`：让 contact state 和 `taskDecisions[]` 可验证、可 replay。
- `Policy Guard`：DNC、duplicate、store mismatch 不能只靠模型自觉。
- `Eval / Replay`：Contact Accuracy smoke 先验证 input 是否喂对，完整 foundation 再验证 lifecycle / intent / safety state 是否稳定。

## 6. Demo 前怎么用这份 smoke

每个 demo candidate 至少检查这一条链：

```text
DB row facts
  -> contacts-analyzer userMessage
  -> AI output
  -> writer mutation
  -> Task UI / timeline
```

建议最小操作：

1. 从 8-12 个 demo candidate 里选 3 个真实端到端 case。
2. 对每个 case 保存一份 prompt context 或 OpenRouter log。
3. 在 DB 里确认 contact/message/lead/task 的关键字段。
4. 在 UI 里确认 task card、timeline、customer summary、related open tasks 能解释发生了什么。
5. 如果错了，先归因：contact context 错、prompt contract 错、task decision 错、writer/policy 错，还是 UI 隐藏了正确结果。

一句话：

> Demo 要讲的是 Task 准 + 写库安全 + UI 可解释；Contact Context Smoke 确保“Task 准”不是建立在看错 contact context 的基础上。
