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 是 selected Target;Task V3 Rollout 状态快照 记录 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 而言,当前正确边界是:

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

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

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 ContractTask V2+ audit 只保留历史工程背景。


2. Live fields

Contact 与 isolation

FieldSQLTypeNull说明
ididbigserial PKNo事件 ID
contactPhonecontact_phonetextNoContact 关联键
franchiseIdfranchise_idtextNo审计/兼容字段
accountIdaccount_idtextNo审计/兼容字段
storeIdstore_iduuidYes当前 store isolation key;historical rows 仍可能为空
tenantIdtenant_iduuidYestenant 铺路

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

Event 与 entity reference

FieldSQLTypeNull说明
eventTypeevent_typetext enumNo{entity}.{action}
eventCategoryevent_categorytext enumNocall/message/lead/task/contact/ai_analysis/integration
entityTypeentity_typetextYescall/message/lead/task/contact 等
entityIdentity_idtextYes对应实体的 ID

Payload

FieldSQLTypeNull说明
oldValueold_valuejsonbYes变更前值
newValuenew_valuejsonbYestyped event payload / 变更后值
metadatametadatajsonbYesevidence refs、pipeline context 等

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

Actor 与 business attribution

FieldSQLTypeNull说明
actorTypeactor_typetext enumNosystem / call_analysis / contact_analysis / staff / lead_webhook
actorNameactor_nametextYes展示名称或系统标识
actorSubjectIdactor_subject_idtextYes执行 write 的稳定主体;staff actor 时必填
staffIdstaff_iduuidYes业务工作 credit 给谁,与 recorder 分开
actorSourceTypeactor_source_typetextNohuman_ui / human_api / service / integration / import / unknown
actorSourceSystemactor_source_systemtextYesstudio_webcontacts_analyzer、integration name 等自由标识

actorSubjectIdstaffId 不应合并:系统可以代写一条 Activity,但业务工作仍归属给实际执行员工。

AI forensic

FieldSQLTypeNull
aiRunIdai_run_idtextYes
aiPromptVersionai_prompt_versiontextYes
aiModelUsedai_model_usedtextYes
aiConfidenceai_confidencenumericYes
modifiedFieldsmodified_fieldstext[]Yes

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

幂等和时间

FieldSQLTypeNull说明
idempotencyKeyidempotency_keytextYesnon-null 时 partial unique
occurredAtoccurred_attimestamptzNo事实真正发生时间
createdAtcreated_attimestamptzNoledger 写入时间

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


3. Live TIMELINE_EVENT_TYPE

当前 const 有 22 个 values:

领域Event types
Lead / Messagelead.createdmessage.created
Task basetask.createdtask.status_changedtask.updatedtask.note_updated
Task V2 compatibilitytask.due_at_changedtask.progress_recorded
Task canonical work modeltask.assignee_changedtask.activity_recordedtask.next_action_changedtask.deadline_changed
Contactcontact.lifecycle_changedcontact.lead_status_changedcontact.name_changedcontact.dnc_changedcontact.complaint_openedcontact.complaint_resolved
AI / Call pipelinecontact_analysis.completedcall.status_changedtranscribe.completedcall_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
channelphone / sms / email / in_person / other
directioninbound / outbound
actioncall_completed / message_sent / conversation_logged / work_completed
outcomeconnected / no_answer / voicemail_left / sent / delivered / failed / wrong_number / reply_received / bounced / not_available / completed / no_response / other
conversationDispositionfollow_up_agreed / considering / not_interested / resolved / appointment_booked / other
verificationLive 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

Activity 与其他层的边界:

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. 关键索引

QueryLive 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_activitiesactionstask_steps table。
  • 是否最终抽独立 activities table,应由 query、FK 和 scale 证据决定;不应同时维护两份 Activity source of truth。

8. Cross-references