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

# Activity Timeline Schema

> **Live code verified：2026-07-15**
>
> **Source of truth：** `callytics-infrastructure/packages/common/src/db/schema/contact-timeline.ts`
> **表名：** `contact_timeline`；主键 `id` bigserial
>
> **Task domain boundary：** [Task V3 Product / Domain Contract](/product-design/v3/tasks-feature/task-domain-lifecycle.md) 是 selected Target；[Task V3 Rollout 状态快照](/product-design/v3/tasks-feature/task-v3-rollout-status.md) 记录 source、deployment、TEST、UAT 与 PROD readiness。下文的 V2 名称只描述 compatibility vocabulary，不定义当前 rollout 状态。

## 1. 当前职责

`contact_timeline` 是 Contact 维度的 append-only factual/audit ledger，集中记录 Lead、Message、Call pipeline、Contact 变化和 Task history。

对 Task 而言，当前正确边界是：

```text
tasks                    = 当前 Task snapshot
contact_timeline         = Activity + status/assignment/schedule history
task_suggestions         = 行动建议
```

当前没有 `task_progress_events` 表。新的真实工作记录写：

```text
event_type  = task.activity_recorded
entity_type = task
entity_id   = task_id
```

`task.progress_recorded` 仅保留给历史记录和兼容读取；不应再作为新写入的标准 event。

V3 Target 继续复用这份 ledger 记录 Activity、Next Action、status、Outcome correction 和 audit；不引入 rigid Step。目标语义见 [Task V3 Product / Domain Contract](/product-design/v3/tasks-feature/task-domain-lifecycle.md)；[Task V2+ audit](/product-design/v2/tasks-feature/task-v2-plus-engineering-audit.md) 只保留历史工程背景。

***

## 2. Live fields

### Contact 与 isolation

| Field          | SQL             | Type         | Null | 说明                                           |
| -------------- | --------------- | ------------ | ---- | -------------------------------------------- |
| `id`           | `id`            | bigserial PK | No   | 事件 ID                                        |
| `contactPhone` | `contact_phone` | text         | No   | Contact 关联键                                  |
| `franchiseId`  | `franchise_id`  | text         | No   | 审计/兼容字段                                      |
| `accountId`    | `account_id`    | text         | No   | 审计/兼容字段                                      |
| `storeId`      | `store_id`      | uuid         | Yes  | 当前 store isolation key；historical rows 仍可能为空 |
| `tenantId`     | `tenant_id`     | uuid         | Yes  | tenant 铺路                                    |

新写入路径必须有 `storeId`；历史 NULL 不能被当成安全的跨店合并依据。

### Event 与 entity reference

| Field           | SQL              | Type      | Null | 说明                                                      |
| --------------- | ---------------- | --------- | ---- | ------------------------------------------------------- |
| `eventType`     | `event_type`     | text enum | No   | `{entity}.{action}`                                     |
| `eventCategory` | `event_category` | text enum | No   | call/message/lead/task/contact/ai\_analysis/integration |
| `entityType`    | `entity_type`    | text      | Yes  | call/message/lead/task/contact 等                        |
| `entityId`      | `entity_id`      | text      | Yes  | 对应实体的 ID                                                |

### Payload

| Field      | SQL         | Type  | Null | 说明                               |
| ---------- | ----------- | ----- | ---- | -------------------------------- |
| `oldValue` | `old_value` | jsonb | Yes  | 变更前值                             |
| `newValue` | `new_value` | jsonb | Yes  | typed event payload / 变更后值       |
| `metadata` | `metadata`  | jsonb | Yes  | evidence refs、pipeline context 等 |

JSONB 不代表“随便写”。每个 canonical event 应有 versioned typed payload contract；API 不应要求客户端自行猜 JSON shape。

### Actor 与 business attribution

| Field               | SQL                   | Type      | Null | 说明                                                                          |
| ------------------- | --------------------- | --------- | ---- | --------------------------------------------------------------------------- |
| `actorType`         | `actor_type`          | text enum | No   | `system` / `call_analysis` / `contact_analysis` / `staff` / `lead_webhook`  |
| `actorName`         | `actor_name`          | text      | Yes  | 展示名称或系统标识                                                                   |
| `actorSubjectId`    | `actor_subject_id`    | text      | Yes  | 执行 write 的稳定主体；staff actor 时必填                                              |
| `staffId`           | `staff_id`            | uuid      | Yes  | 业务工作 credit 给谁，与 recorder 分开                                                |
| `actorSourceType`   | `actor_source_type`   | text      | No   | `human_ui` / `human_api` / `service` / `integration` / `import` / `unknown` |
| `actorSourceSystem` | `actor_source_system` | text      | Yes  | `studio_web`、`contacts_analyzer`、integration name 等自由标识                     |

`actorSubjectId` 与 `staffId` 不应合并：系统可以代写一条 Activity，但业务工作仍归属给实际执行员工。

### AI forensic

| Field             | SQL                 | Type    | Null |
| ----------------- | ------------------- | ------- | ---- |
| `aiRunId`         | `ai_run_id`         | text    | Yes  |
| `aiPromptVersion` | `ai_prompt_version` | text    | Yes  |
| `aiModelUsed`     | `ai_model_used`     | text    | Yes  |
| `aiConfidence`    | `ai_confidence`     | numeric | Yes  |
| `modifiedFields`  | `modified_fields`   | text\[] | Yes  |

这些字段解释“AI 为什么产生这条 proposal/mutation”；它们不能把 AI inference 自动升级为 externally verified fact。

### 幂等和时间

| Field            | SQL               | Type        | Null | 说明                        |
| ---------------- | ----------------- | ----------- | ---- | ------------------------- |
| `idempotencyKey` | `idempotency_key` | text        | Yes  | non-null 时 partial unique |
| `occurredAt`     | `occurred_at`     | timestamptz | No   | 事实真正发生时间                  |
| `createdAt`      | `created_at`      | timestamptz | No   | ledger 写入时间               |

排序客户历史默认用 `occurredAt`，但必须保留 `createdAt` 以识别延迟写入、backfill 和 import。

***

## 3. Live `TIMELINE_EVENT_TYPE`

当前 const 有 22 个 values：

| 领域                        | Event types                                                                                                                                                    |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Lead / Message            | `lead.created`、`message.created`                                                                                                                               |
| Task base                 | `task.created`、`task.status_changed`、`task.updated`、`task.note_updated`                                                                                        |
| Task V2 compatibility     | `task.due_at_changed`、`task.progress_recorded`                                                                                                                 |
| Task canonical work model | `task.assignee_changed`、`task.activity_recorded`、`task.next_action_changed`、`task.deadline_changed`                                                            |
| Contact                   | `contact.lifecycle_changed`、`contact.lead_status_changed`、`contact.name_changed`、`contact.dnc_changed`、`contact.complaint_opened`、`contact.complaint_resolved` |
| AI / Call pipeline        | `contact_analysis.completed`、`call.status_changed`、`transcribe.completed`、`call_analysis.completed`                                                            |

关闭和 reopen 复用 `task.status_changed`，不另造 `task.closed` / `task.reopened`。

***

## 4. Task Activity contract

Activity payload vocabulary 由 `packages/common/src/db/schema/task-progress.ts` 定义：

| 维度                        | Values                                                                                                                                                                                   |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel`                 | `phone` / `sms` / `email` / `in_person` / `other`                                                                                                                                        |
| `direction`               | `inbound` / `outbound`                                                                                                                                                                   |
| `action`                  | `call_completed` / `message_sent` / `conversation_logged` / `work_completed`                                                                                                             |
| `outcome`                 | `connected` / `no_answer` / `voicemail_left` / `sent` / `delivered` / `failed` / `wrong_number` / `reply_received` / `bounced` / `not_available` / `completed` / `no_response` / `other` |
| `conversationDisposition` | `follow_up_agreed` / `considering` / `not_interested` / `resolved` / `appointment_booked` / `other`                                                                                      |
| `verification`            | Live enum：`provider_confirmed` / `staff_asserted` / `imported_legacy`                                                                                                                    |

`TASK_ACTIVITY_CONTRACT` 进一步限制 channel/action/outcome 的合法组合，例如 `message_sent + no_answer` 不是同一时刻的有效事实。

`provider_confirmed` 是 current schema 中的 future-facing scaffolding；当前产品没有可依赖的 booking/membership/billing provider writer。V3 Target evidence vocabulary 和 migration boundary 见 [Task V3 Product / Domain Contract §3.3](/product-design/v3/tasks-feature/task-domain-lifecycle.md#33-evidence-与-verification-vocabulary)。

Activity 与其他层的边界：

```text
no_answer / voicemail_left    → Activity outcome
follow_up_agreed              → conversation disposition + possible Next Action
booked / upgraded             → Task-specific Outcome + verification/evidence
DNC                           → Contact restriction
```

***

## 5. 关键完整性约束

- `actor_source_type` 必须属于 live 6-value vocabulary。
- `event_category` 必须属于 live 7-value vocabulary。
- `actor_type='staff'` 时 `actor_subject_id` 必填。
- `staff_id` 非空时 `store_id` 必填。
- `staff_id + store_id` 通过复合 FK 指向同一门店的 staff。
- non-null `idempotency_key` 必须唯一。

***

## 6. 关键索引

| Query                            | Live index strategy                                                                                    |
| -------------------------------- | ------------------------------------------------------------------------------------------------------ |
| 某 Contact 最近事件                   | `(store_id, contact_phone, occurred_at DESC)` partial                                                  |
| 某 Store 最近事件                     | `(store_id, occurred_at DESC)` partial                                                                 |
| 某 Contact 某 event type           | `(store_id, contact_phone, event_type, occurred_at DESC)` partial                                      |
| 某 entity history                 | `(store_id, entity_type, entity_id, occurred_at DESC)` partial                                         |
| staff business activity          | `(store_id, staff_id, occurred_at DESC, entity_id)` partial                                            |
| canonical Task Activity          | `(store_id, entity_id, occurred_at DESC, created_at DESC)` where `event_type='task.activity_recorded'` |
| Task create/close/reopen history | `(store_id, occurred_at, entity_id)` for `task.created/task.status_changed`                            |

这些索引支持当前 Task history 和 Activity query。Target 新增 event 时应根据真实 Workbench/reporting query 补索引，不预先引入 Step linkage。

***

## 7. Target boundary

- Task row 保存 current snapshot。
- `contact_timeline` 保存 Activity 与所有重要变化的 append-only history。
- 一次真实 interaction 只计一次 interaction/staff credit；它可以通过 evidence refs 支撑多个 Tasks 的判断。
- MVP 不新建 `task_activities`、`actions` 或 `task_steps` table。
- 是否最终抽独立 `activities` table，应由 query、FK 和 scale 证据决定；不应同时维护两份 Activity source of truth。

***

## 8. Cross-references

- [Task V3 Product / Domain Contract](/product-design/v3/tasks-feature/task-domain-lifecycle.md)
- [Task V3 Rollout 状态快照](/product-design/v3/tasks-feature/task-v3-rollout-status.md)
- [Historical Task V2+ audit](/product-design/v2/tasks-feature/task-v2-plus-engineering-audit.md)
- [Lead Funnel 与状态](/product-design/v2/lead-tracker-feature/lead-funnel-status.md)
