Task V3 Product / Domain Contract

Product, Domain, AI Runtime, Backend Contract & UX Projection

MetadataValue
StatusSelected Target contract:产品与领域方向已选定;本文不宣告 deployment、UAT 或 PROD release
ScopeTask V3 稳定产品目标、领域 contract、Target Architecture 与安全边界
OwnerProduct + Engineering
Last contract/status-separation review2026-08-04
Contract coversTask、Display Type、Next Action、Activity、Business Progress、Outcome、Task Policy、AI authority、Workbench/Funnel projection
Dynamic rollout sourceTask V3 Rollout Status
Supersedes at rolloutCase + TaskTask → rigid Step → Activity、global closeResult、假想 provider/Milestone truth 等旧方案
Not a replacement forFigma visual spec、migration DDL、具体 API OpenAPI 文件、production code

0. 怎么读这份文档

这份文档只锁定 Task V3 的稳定产品目标、领域词汇和安全边界。PR 是否 merge、某环境是否 deployed/enabled、真实模型质量、员工 UAT 和 PROD release 都会变化,统一维护在 Task V3 Rollout Status。本文不再复制这些动态数字和待办清单。

contract selectedcode mergeddeployed to TESTlive-model evaluatedemployee UAT passedreleased to PROD 始终是不同的证据等级。任何读者都不得用前一项推导后一项。

Store 的出厂 automation tier 是 full_autocautiousstandard 保留为 Store override 与故障降级能力。full_auto 也不等于 AI 任意写库:模型只产生 typed proposal/command;DNC、Store isolation、dedupe、evidence、Policy、idempotency 和 atomic writer 仍由代码决定。

第一次接触 Retaintive 的产品经理、设计师或工程师,只读这一份就应该能回答:

  • 产品解决什么问题。系统真实能看到什么,又看不到什么。
  • TaskDisplay TypeNext ActionActivityBusiness ProgressOutcome 分别是什么。
  • 店员和 AI 分别可以做什么。
  • 数据库、API、AI loop 和 Workbench 分别负责什么,以及它们怎样配合。
  • Lead Funnel 为什么不是 Task status。
  • 新增一种业务 Task 时应该改哪里,避免再复制一套 hardcoded mapping。

文中会反复出现以下标签。它们表示一条事实或 proposal 目前处在哪个阶段:

  • Current source:最后一次核验时 source code/schema 已经存在的行为。它不自动表示某个 migration 已在 production 执行、某个 event source 已启用,或某家 Store 已经 cutover。
  • Enabled / deployed:必须由目标环境的 config、migration state 和 runtime evidence 单独证明。checked-in default 不能替代 production verification。
  • Proposed:本文中仍未定稿的 future extension 或产品细节,不能当成 current implementation。
  • Target:已经选定的系统终态;不表示相应代码已经 merge、部署、验证或向 PROD 开放。
  • Open Decision:还没有拍板。实现者不能把它当成已经接受的结论。

如果后文保留的历史 rollout phase、lead-only MVP 或 Store-by-Store canary 与当前状态冲突,当前状态Task V3 Rollout Status 为准,领域语义以本文为准。禁止偷偷改写历史数据;UI、prompt 和 backend 也不能各自发明另一套 vocabulary。

0.1 Contract 与 rollout status 的边界

问题Source of truth
Task / Next Action / Activity / Business Progress / Outcome 的含义,Policy、Guard、AI authority 和 Workbench projection 的不变规则本文
PR merge state、checked-in environment gates、Store authority、eval 结果、deployment、UAT、PROD release 与剩余待办Task V3 Rollout Status
某个具体环境实际运行了什么该环境的 config、migration state、runtime evidence 和 UAT 记录;任何文档都不能代替

2026-08-04 status-separation baseline(仅记录本文为什么移除旧 snapshot): #1908 的 approval backend 和 #2024 的 owner automation / proposal UI 已 merge;#1913 已移除 Contacts Analyzer 的 AI Task decision authority,但 applyTaskAction compatibility facade、deterministic lead backstop 和 legacy Timeline projection 仍存在;#1925 已开始 human-ratified/live eval,quality gate 未通过;#1982 使 MANAGED_TASK_KIND 中全部七类 objective 具备 Target/Shadow authority capability,#1993 把新 Store 的初始 authority 改为 target/target。checked-in main 中 TEST 的 Task V3 source gates 为 truepre/prodfalse。这些 source/merge 事实不证明员工 UAT 已通过,也不证明 PROD 已发布。分离日之后的变化只更新 rollout status 文档,不再复制到本节。

0.2 Unified Call AI chain contract

Unified Call path 复用同一次 Call analysis 产生的 task_semantics,而不是让两个 AI 重读同一份 transcript:

deterministic pre-triage
  → triage AI(未被 pre-triage 短路时)
  → classify AI:一次生成 Call 分类/总结 + Task-specific claims
  → Task Reasoner Turn 1
  → 如果缺少会改变决定的资料:server 执行 allowlisted read
  → Task Reasoner Turn 2(可选且必须是 final)
  → deterministic Guard + writer

因此,统一路径不再有独立的 “Task semantic extraction” AI call。对于本来就会做 Call analysis 的合格 Call,Task V3 的增量模型开销通常是一个 Reasoner turn,material context 不足时才是两个。triageclassify、可选 verify/coaching 属于现有 Call analysis 成本,仍须在 end-to-end 账单里展示,不能因为不是 Task 增量就隐藏。

Default two_turn contract 最多允许一个 read round、两个 approved read-only tool requests;同一 read request 在一个 run 中最多执行一次。首轮已经装入的 source text、bounded Task list 和 Policy catalog 会被 read ledger 标为已加载,重复请求只返回确定性的 rejection,不再次查库。第二轮之后必须输出 final proposal、requestStaffInputno_op。Adapter 也包含需显式 opt in 的 bounded native-tool mode;它的启用、资源预算和质量/成本证据必须单独审批,不能由本文推导为已在任何环境运行。

历史 Call artifact 或缺少统一 task_semantics 的来源暂时仍走 standalone semantic model fallback,以便兼容 rollout;它是明确的剩余质量风险,不是统一路径的目标形态。

PROD physical legacy columns 只有在 backfill 完成并证明零读者、零写者后才能 drop。暂时保留无人读写的旧列,不等于继续运行两套 active system。

存储边界也必须说清楚:Task V3 的 state、authority、source proof、receipts 和 audit 落在 Neon/PostgreSQL。 S3 继续保存录音、transcript 或 analysis 等大文件/不可变 artifact;部分现有 ingestion/control-plane 仍可能使用 DynamoDB。不能为了宣称“全部迁到 Neon”而删除仍有明确职责的 S3 或现有 infrastructure。


1. 两分钟理解整个系统

1.1 产品一句话

Retaintive 是一个 AI Revenue Workbench。它使用真实电话、短信、Email Lead 和员工掌握的线下信息来帮助门店工作:

发现值得处理的客户机会或问题,告诉员工下一步做什么,自动记录可观察的沟通,持续跟踪到有证据的结果,并让门店老板看到这些工作带来的业务价值。

产品闭环是:

发现机会
  → 建立 Task
  → 明确 Next Action
  → 员工采取行动
  → 记录 Activity
  → AI 或员工决定下一步
  → 必要时记录非终态 Business Progress
  → 得到有证据的 Outcome
  → 更新 Workbench、Funnel 与 Revenue reporting

1.2 最终只保留五个核心业务概念

概念用人话解释例子
Task一件需要持续处理,直到得到明确结果的客户机会、问题或目标推进王女士从 Lead 到明确销售结果;处理取消请求
Next Action这个 Task 当前已经采用的下一步计划(员工或获授权的 AI 都可以采用)现在打电话;周五下午回电
Activity实际发生过、可审计的一次工作或互动电话无人接;SMS 已发送;店员记录线下沟通
Business ProgressTask 处理中已经发生、但还不是最终 Outcome 的业务进展Intro 已预约;取消请求进入保留流程
OutcomeTask 最后怎么样、为什么结束、证据是什么Converted;客户明确拒绝;无法联系;员工确认

辅助概念只有四个,不再增加:

概念作用
Suggestion / AI Suggestion一个还没有被采用的建议。sourceType=ai 时,UI 把它叫作 AI Suggestion。它还不是 Next Action
Display Type员工现在在队列里看到的工作分类,例如 Lead Outreach。它不是另一个 Task,也不是另一个 lifecycle
Task Policy每一种 Managed Task 都要遵守的 contract。它规定如何创建和关闭、需要什么证据、SLA 怎么算、UI 怎么显示、reporting 怎么统计
Funnel Projection系统根据现有 evidence 算出的客户进展视图。它不是 Task lifecycle

1.3 MVP 明确不引入什么

MVP 不引入:

  • Case:Task 自己就代表完整的客户事项,不再多造一层意思相近的对象。
  • 固定 Step:销售和客服是人与人的沟通,不是标准化 SOP。系统只保留一个可以随情况变化的 Next Action
  • 第一等 Action 表:第一阶段把当前 Next Action 直接存在 Task current snapshot 中。每次历史变化写入 Timeline。
  • 复杂业务 Milestone / external outcome provider system:当前没有 booking、attendance、membership、billing、CRM/POS 的权威 integration,不能假装这些事实已经存在。RingCentral source-proof 仍然必须保存,因为它证明 Call/SMS/VoiceMail evidence identity;它不等于权威 membership/billing truth。
  • 永远运行的 autonomous agent:每次 event 到来时,AI 只运行一个有预算、有终点的 bounded loop。
  • AI 任意写数据库:AI 只能提出 typed domain command。Code/Policy Guard 决定是否执行。

Current Action 不是一个独立概念。文档、API 和 UI 统一叫:

Next Action / 下一步


2. 为什么这样设计

2.1 Task 已经足够表达“客户这件事”

以前 Case + Task 的解释是:

Case = 客户事项
Task = 员工具体工作

这样一来,产品和用户会同时面对两个都很像“任务”的对象。当前业务不需要这层重复结构。

在本模型中,同一件客户事项只用一个 Task 表达:

Task:推进王女士这个 Lead 到明确销售结果
  displayType:lead_outreach
  当前 Next Action:5 分钟内首次电话联系
  Activity 1:电话无人接
  displayType:lead_follow_up
  新 Next Action:发送 SMS,明天再次联系
  Activity 2:客户在 SMS 中确认周二 Intro 已预约
  displayType:booked_not_converted
  新 Next Action:确认到店计划,并根据客户需求讨论 Membership
  Activity 3:客户确认已经正式加入
  Outcome:converted

Task 从头到尾管理整件事:为什么开始、现在准备做什么、过程中发生了什么,以及最终得到什么 Outcome。Task 的 result 不需要再变成另一个 Case。它就是这个 Task 自己的 Outcome。

2.2 为什么不是 Task + 标准 Steps

一个 Task 可以发生很多次 Activity,但这些 Activity 不需要预先排成一套写死的 Steps。

  • 首次电话可能直接预约成功。
  • 无人接后可能发 SMS,也可能店员第二天线下遇到客户。
  • 客户可能主动回电,改变计划。
  • 一通沟通可能同时推进 lead_conversionreferral 两个 Tasks。

因此,无论沟通怎么变化,系统都只需要持续回答四个问题:

  1. 这件事为什么存在?
  2. 现在最合理的 Next Action 是什么?
  3. 实际发生了什么?
  4. 是否已有足够 evidence 关闭,Outcome 是什么?

2.3 一个 Contact 可以同时有多个 Tasks

Contact at Store A
  ├── Task: lead_conversion
  ├── Task: referral
  └── Task: payment_recovery

这三个目标互不依赖。它们可以分别分配,也可以分别关闭。

同一种 taskKind 能不能同时有多个 open Task,由对应 Task Policy 的 dedupeRule 决定。

查询 Contact、Task、Activity 或 Evidence 时,都必须用 store_id 隔离。不能只看 phone,然后把不同门店的数据合在一起。

2.4 Contact contract:identity anchor,不是 decision truth

在产品 UI 里,Contact 可以显示成“一个客户”。但在 MVP 的技术 contract 中,它只代表下面这个店内身份锚点:

Contact identity = store_id + normalized_phone

这个 identity 用来把某家 Store 内的 Calls、SMS、Email Leads、Tasks、Activities 和 Notes 串成 conversation history。

它不能证明现实中“这个号码永远只属于这一个人”。共享号码、换号、wrong number 和跨店到访都可能发生,所以必须允许员工纠正。系统不能只按 phone 做全局 merge。

关系与 cardinality:

Contact 1 ── * Tasks
Task    1 ── 0..1 current adopted Next Action
Task    1 ── * historical Next Action changes (Timeline)
Task    1 ── * Activities
Task    1 ── * Suggestions (可以多个,但不是多个 adopted Actions)

在 MVP 中,一个 open Task 同一时间最多只有一个已经采用的 Next Action

如果未来真的出现多项工作需要同时执行,而且每项都要分别分配、分别设置 deadline,再评估是否引入 first-class Action entity。在此之前,多个 Suggestions 仍然只是多个建议,不能当成多个 current Actions。

不同 Contact fields 的可信程度不同,不能一视同仁:

Contact dataRole in Task decision
storeId + normalizedPhone、source linkage用于关联和隔离数据;不能当作现实身份的绝对证明
DNC/restriction、authenticated staff correction/note有 provenance 的 hard guard 或员工提供的 context
Name 等 mixed-source profile field按每个 field 自己的 provenance 展示。员工确认优先,不能全部称为 AI-derived
AI summary、lead_status、lifecycle、purchase intent、action needed 等只作为 background/projection。不能仅凭这些字段 create、close 或 skip Task
原始 Call/SMS/Lead、Task Activity、staff_assertion当前做判断时最主要、而且可以回查的 evidence

Target reasoning 使用四类输入:fresh evidence、Task/Activity history、Policy,以及 Contact guards/background。

任何 derived Contact field 都不能反过来被当成 terminal Outcome truth。未来迁到稳定的 contactId 时,也必须保留 store isolation 和 identity-link audit,不能静默合并历史记录。


3. 系统真实拥有的数据

3.1 Current:可以看到

当前产品可以可靠使用以下输入:

  • RingCentral call metadata、录音/转写以及已生成的分析结果。
  • RingCentral SMS message 与 thread。
  • 通过 Email ingestion 进入系统的 Lead form 信息。
  • 员工在 Retaintive 中记录的 Activity、Note、Next Action 和 Outcome。
  • 系统已有的 Contact snapshot、Tasks、Suggestions 和 Timeline。

员工输入不是“自动化做不到时才用的 fallback”,而是一等数据源。店员知道很多系统看不到的线下 context,例如客户已经到店、客户当面做了承诺、员工在后台完成了操作,或者员工从其他系统看到了结果。

3.2 Current:看不到

当前不能假定系统知道以下事实:

  • booking system 的 authoritative state。
  • class attendance / intro attendance。
  • membership activation/cancellation。
  • billing/payment settlement。
  • 门店 CRM 或 POS 的完整客户历史。

因此,Contact Profile 只是根据现有 evidence 组装、再由 AI enrich 的 operational snapshot。它不是完整的 CRM truth。

姓名、DNC、Notes 等字段也可能来自 Lead、RingCentral 或员工,不能全部说成 AI 生成。更不能因为 AI 听起来像是“已经预约”,就把它写成“外部 booking system 已确认”。

3.3 Evidence 与 Verification vocabulary

Value含义典型来源Allowed system effect
system_observed系统直接看到了一个技术事实Email Lead 到达、RingCentral call/SMS metadata可以创建 Task 或记录 Activity;但单独使用时通常不能证明业务 Outcome
communication_explicit电话或 SMS 中有清楚、可以引用的客户或员工表述“I booked her for Tuesday at 6”可以按 Policy 记录非终态 booking progress,并重算 displayType。只有命中 terminal Outcome allowlist 时才能 auto-close
staff_confirmed有权限的员工明确确认了线下或后台事实员工确认 “Intro booked” 或 “Converted” 并写 evidence/note可以记录 progress 或关闭 Task。记录 progress 本身不会关闭 Task,而且这类确认优先于后续 AI inference
form_assertedLead form 或其他表单中填写的信息Lead 的 preferred time、历史 booked_date可以用于创建 Task、生成建议,或作为带 provenance 的 Funnel input;不能单独证明 terminal Outcome
ai_inferredAI 根据上下文推断出的结论,但原始沟通没有明确这样说“客户似乎有兴趣”不能自动关闭 Task;只能形成 Suggestion 或进入 human review
external_verified未来外部权威系统确认future CRM/booking/billing integration未来保留;MVP 不得生成
imported_legacy从旧系统迁移而来,来源强度信息不完整legacy Task close result不能自动升级成更强的 evidence

Reporting 的措辞必须与 evidence 强度相符:

  • AI-inferred:AI 推断。
  • communication-confirmed:沟通中明确表达。
  • staff-confirmed:员工确认。
  • externally verified:只有未来真的接入权威系统后才能使用。

外部客户端或 model 只能提交不带 scope 的 pointer。server 完成解析和鉴权后,才生成可以持久化的 ref。这样可以避免调用方自行填写并声称某个 storeId 可信:

type EvidencePointer = {
  type:
    | 'ringcentral_call'
    | 'ringcentral_message'
    | 'email_lead'
    | 'task_activity'
    | 'staff_assertion'
    | 'legacy_import'
    | 'external_record'; // future only
  id: string;
};

type ResolvedEvidenceRef = EvidencePointer & {
  storeId: string;       // server 注入/验证,AI 和客户端不能自报可信 store
  occurredAt?: string;
};

Task、Activity、Business Progress、Outcome snapshot 和 Timeline 保存 ResolvedEvidenceRef。public API 和 model tool input 接受 EvidencePointer,再由 server 检查它是否属于同一家店。

系统不能只保存一段自然语言,因为那样无法回查原始来源。

不同用途的 evidence refs 要分别命名为 creationEvidenceRefs / activityEvidenceRefs / progressEvidenceRefs / outcomeEvidenceRefs

sourceType/sourceEntityType/sourceEntityId 只保留为 legacy creation provenance shortcut,不能替代 typed evidence。verificationType 必须由 server evidence resolver 和 Policy Guard 根据已经解析的 evidence 决定。客户端和模型不能自己声明可信等级。

staff_assertion 是员工把线下信息正式写入系统时使用的 evidence。它不是把普通 note 包装成外部事实。

员工以 staff_confirmed 执行 recordBusinessProgresscloseTask 时,server 必须在同一个 transaction 内完成四件事:

  1. 要求员工填写一条简短、具体的 confirmation note。
  2. 生成 append-only staff_assertion Timeline event 和稳定 ID。
  3. 让 progress 或 Outcome 的 evidenceRefs 指向这个 event 的 ResolvedEvidenceRef
  4. 记录 authenticated actor。客户端不能提交或伪造 actor identity。

如果员工还说明了自己具体做过什么,例如“我在线下帮她完成预约”,同一次提交也可以生成一条 typed Activity。

如果员工只确认 progress 或 Outcome,没有声明自己做过实际工作,就不能因为他点了一次 Update/Close 而编造 Activity,也不能给他增加 staff revenue credit。

从 Current 迁到 Target 时,evidence adapter 必须按下面的规则显式转换:

Current provenance/sourceTarget handling
call如果能解析到同店 RingCentral Call row,就转成 ringcentral_call;否则转成 legacy_import
message如果能解析到同店 RingCentral message row,就转成 ringcentral_message;否则转成 legacy_import
lead确认来源是 Email ingestion lead row 时,转成 email_lead。phone/SMS lead signal 应引用对应的 Call/Message,不能冒充 Email Lead
manual新的 authenticated write 创建 staff_assertiontask_activity。历史 manual 数据如果没有 typed actor/evidence,只能转成 legacy_import
contact_analysis只代表 AI decision provenance,也就是某次 run/assessment;它不是 source fact。系统必须继续解析 underlying source refs,否则只能算 ai_inferred

Current verification 不能只按旧名字机械升级。

  • 如果 provider_confirmed 只说明 RingCentral source 存在,Target 最多只能映射为 system_observed
  • 只有 evidence excerpt 明确说出了结果,Policy 才能把它判断为 communication_explicit
  • 旧值 provider_confirmed 永远不能映射成 external_verified:它最多升到 system_observed;只有 evidence 原话明确表达了结果,才能经 Policy 判为 communication_explicit
  • staff_asserted 只有在 actor 和 note 都可审计时,才能映射成 staff_confirmed;其他情况保持 imported_legacy

3.4 Current store-scope boundary 与 historical-data discipline

本节之前记录的两个具体 query 漏洞已经在当前 source 中修复:

  • per-call queryCustomerHistory() 要求 server-resolved storeId,calls/leads 同时按 Store + phone 查询;缺 store_id 的 legacy rows fail closed,并单独计数供 repair。
  • Contact Profile 要求已授权的 storeId,外层 Contact 和 Timeline counts 都使用 exact Store + account + phone scope。

这并不把 Contact Profile 变成完整 CRM truth,也不允许把历史 AI fields 升级为 evidence。当前仍然遵守下面的边界:

  • Task Reasoner 的每个 read tool 都由 server 注入 storeId + contactPhone,逐表验证 scope、time window 和 field allowlist;模型不能自报 Store。
  • 历史 row 缺 store_id、缺 exact provider identity 或无法证明事件发生时 Store 时,保持 unresolved/imported legacy。禁止用“当前 phone 属于哪家 Store”回填历史 Store。
  • leads.booked_date 只保留为带 form_asserted provenance 的 Funnel/cohort input;它不自动写 completed booking progress,也不证明 membership Outcome。
  • customer_type / topic / summary / contacts.lead_status 等历史 AI fields 仍然只是 background。它们不能 create/close/skip Target Task,也不能证明 terminal Outcome。

因此,修复 query isolation 只解决“不能读取另一家店”的正确性问题,不会凭空补齐历史 CRM、booking、membership 或 billing truth。


4. End-to-end conceptual model

图中的业务对象之间有以下关系:

Contact  1 ── * Task
Task     1 ── * Activity
Task     1 ── 0..* business progress events (non-terminal, append-only)
Task     1 ── 0..1 active Next Action snapshot
Task     1 ── 0..* AI Suggestions
Task     1 ── 0..1 current terminal Outcome

Outcome correction、reopen 和所有历史值都保存在 append-only Timeline 中。Task row 只保存 current snapshot。


5. Lifecycle 与 UI projection

5.1 数据库只保存简单的 lifecycle

Selected Target lifecycle:

open → closed
closed → open  (reopen,必须追加 audit event)

status 只回答一个问题:“这件客户事项还需不需要继续处理?”

它不表示员工是否已经尝试过,也不表示员工应该在什么时候处理。

关闭 Task 时,必须同时记录以下信息:

  • 一个 business resultCode,或者一个 administrative result。
  • 一个 closureReasonCode
  • verification 与 evidence。
  • 谁关闭了 Task、什么时候关闭、当时使用哪一版 Policy。

5.2 下面这些是计算出来的视图,不是 Task status

Projection计算含义是否写回 tasks.status
not_started当前 open period 还没有 qualifying ActivityNo
in_progress / attempted当前 open period 已经有 ActivityNo
needs_attention现在应该处理,而且没有安排未来的 Next ActionNo
schedulednextActionAt 在未来No
overduedeadlineAt 已过,但 Task 仍然 openNo;它只是 boolean/label
first_response_met第一次 qualifying Activity 发生在 SLA 要求的时间内No

no_answervoicemail_leftsent / delivered 描述某次 Activity 的 outcome。message_sent 描述某次 Activity 的 action。它们都不是 Task status,也不是 Task Outcome。

5.3 三种时间各自回答不同的问题

Field回答的问题Lead 例子
firstResponseDueAt最晚多久之内必须第一次采取 qualifying action?Lead 到达后 5 分钟
nextActionAt员工下一次应该什么时候处理?明天 10:00 再打
deadlineAt整件 Task 最晚什么时候必须解决或升级处理?7 天内完成 outreach policy

修改 Next Action 时,不能顺便移动 deadlineAt。记录了一次 Activity,也不等于 Task 已经 closed。

5.4 Reopen 在 MVP 中是什么意思

MVP 不增加 WorkCycle 表。不过,Task reopen 后,不能因为过去有 Activity 就立刻显示 attempted。具体规则如下:

  • Target 在 Task snapshot 中保存 currentOpenedAt
  • Task 初次创建时,currentOpenedAt 等于 createdAt。reopen 时,把它更新为 reopen 时间。
  • 计算 work state 时,只看 occurredAt >= currentOpenedAt 的 Activity。
  • reopen 时,从 current snapshot 清除旧 Outcome。但旧 Outcome 和 reopen reason 继续保留在 Timeline 中。
  • 如果以后同一个 Task 确实需要复杂的多-cycle 报表,再把 cycle 升级为 first-class entity。

5.5 Current implementation 已经支持什么

Current source 同时包含 Task V2 projection 与 Target projection,必须分开描述。

Target engine/API 已经支持:

  • tasks.status = open | closed,并以 currentOpenedAt 区分 reopen 后的新 open period。
  • nextActionText + nextActionAt 是 canonical adopted plan;deadlineAtfirstResponseDueAt 分别回答整件事期限和首次响应 SLA。dueAt 只保留 legacy compatibility。
  • recordActivityAndPlan、Next Action、deadline、priority、assignee、note、intro_booked progress、converted confirmation、correction 和 reopen typed commands。
  • atomic Task snapshot + append-only Timeline;Activity 数量不会隐式关闭 Task,terminal closure 必须经过显式 Outcome command。
  • Target work state 只读取 occurredAt >= currentOpenedAt 的 qualifying Activity;nextActionAt > now 投影为 Scheduled。

Task V2 queue/read path 仍保留现有 scheduling/work-state 语义。它可能把“门店本地时间明天 00:00 之前”的未来动作留在 Needs Attention,也可能按 Task 整个 lifetime 的 Activity 显示 attempted。

因此,部分 Target 语义已经存在于 executable code,但不能写成“所有 production 用户已经在用”,也不能由此推断应该 cutover。任何获批切换前必须运行 Task V2 / Task V3 dual projection,按 Store 对比 queue counts、SLA、Timeline、Funnel 和 Speed,再由 per-objective authority 执行可回滚 cutover。

MVP 不增加 waiting / blocked queue state。需要等待客户或内部信息时,员工或 AI 必须设置下一次 review 时间,所以 Task 仍会进入 Scheduled。这样可以避免 Task 无期限消失。

只有未来确认存在“无法安排日期的 hold”需求时,才增加 typed holdType / holdReason / holdUntil 和对应 commands。不能从 Next Action 文本中用 NLP 猜测 hold 状态。


6. Target fields 与 values

下面的字段名以 Target logical contract 为准。表里的 Current mapping 说明当前 source schema/runtime 的承载方式;Expand column present 表示 nullable compatibility column 已经在 migration/schema 中定义,但不表示 migration 已在 production 执行或 read authority 已切换。

6.1 Task fields

FieldType / valuesRequiredMeaningAuthorityCurrent mapping
taskIdUUIDYesTask identitySystemtasks.task_id
storeIdUUIDYes数据属于哪家店,也是隔离数据的边界Code onlytasks.store_id
contactPhonenormalized E.164 textYes当前 store 内客户的 MVP identity;它始终和 storeId 一起组成 scopeCode onlytasks.contact_phone
taskModemanaged / flexibleYes这个 Task 是否由 Policy 管理Staff/Policy at createExpand column present
taskKindManaged kind 或 customYes这个 Task 长期要解决哪类目标Staff or evidence-gated AIExpand column present;type_category 仍作 compatibility input
displayTypePolicy-defined text nullableLead yes员工当前在队列里看到的分类。可以 materialize,但必须能根据有效 evidence/Activity 重算Code/Policy only;员工通过记录事实纠正Expand column present;type_category 仍作 compatibility input
titletextYes员工可读标题Staff/Policy; AI 可建议Expand column present
objectivetext / policy labelYes这件事最终要达成什么Policy; flexible 由 staffTarget projection
taskReasontextYes为什么创建这个 Task,以及它现在为什么重要AI/staff, with evidencetask_reason
statusopen / closedYesTask 是否仍需处理,或者已经结束Domain command onlylive
priorityhigh / medium / lowYes当前队列优先级Policy/staff; AI 可建议live
assignedToStaffIdUUID nullableNo当前负责人Authorized staff/systemlive
sourceTypelead / contact_analysis / manual,以后可扩Yes创建来源Codelive
sourceEntityTypetext nullableNo主要 evidence 的类型Codelive
sourceEntityIdtext nullableNo主要 evidence 的 identityCodelive
sourceRunIdtext nullableNoAI/job/run provenanceCodelive
creationEvidenceRefstyped refsManaged yes支撑创建的完整同店 evidence。source fields 只是 legacy shortcutServer resolver/PolicyExpand column present
creationIdempotencyKeytextYes防止同一个 create request 重试时重复创建Codelive
dedupeKeytext nullableManaged normally yes当前 open business objective 的 identity。它与 request replay idempotency 是两件事Server/Policy onlyExpand column present
creationPolicyVersiontextManaged yes创建时使用的 effective Policy 版本CodeExpand column present
firstResponseDueAttimestamptz nullableNo首次响应 SLAPolicy/staff overrideExpand column present
firstResponseAttimestamptz nullableNoTask 整个 lifetime 内第一次 qualifying staff Activity 的时间;reopen 不重置Code-derived, materializedExpand column present + Timeline projection
deadlineAttimestamptz nullableNo整件 Task 的最终期限Policy/staff commandlive
currentOpenedAttimestamptzYes当前这一段 open period 从什么时候开始Domain transitionExpand column present
aggregateVersionintegerYesoptimistic concurrencyCodeExpand column present
createdAt / updatedAttimestamptzYes系统时间Codelive

Target 不把 taskKind 做成数据库 global enum。它应该是 text key,再由 versioned Task Policy Catalog 负责 application/domain validation。否则,每增加一种业务类型都要做一次 DB enum migration。

taskKinddisplayType 回答的是两个不同问题,不能混用:

  • taskKind 回答“这件事长期要解决什么”。它在同一个 Task 存续期间保持稳定,例如 lead_conversion
  • displayType 回答“员工现在应该把它当成哪类工作”。它可以随着有效 evidence 变化,例如 lead_outreach → lead_follow_up → booked_not_converted
  • displayType 不是 status,不是 Funnel stage,也不是另一个 Task。客户端不能直接提交任意值。员工在 UI 中记录 Call、SMS 或线下事实后,由 Policy Guard 重算。
  • 为了支持队列筛选,Target 可以把当前值 materialize 到 tasks.display_type。Timeline/evidence 仍然是审计和重算的来源。后台 reconciliation 必须能发现 drift。

creationIdempotencyKeydedupeKey 也解决不同问题:

  • creationIdempotencyKey 防止同一个 create request retry 时产生两条记录。
  • dedupeKey 防止不同 event 为同一个 open business objective 重复创建 Task。

Managed Task 的 dedupeKey 默认由 server 生成为 contact:{normalizedPhone}:kind:{taskKind}。如果 Task 还需要绑定一个具体业务对象,再加入 Policy-defined subjectKeylead_conversiondisplayType 发生变化时,key 不变。

Flexible Task 的 dedupeKey=null,所以同一个 Contact 可以同时拥有多个 open Flexible Tasks。不过,同一个 create request 仍然受 creationIdempotencyKey 保护。AI 和客户端都不能自行提交 dedupeKey

MVP 的物理 identity 继续使用 store_id + contact_phone。现有 ingestion normalization 会把 phone 写成 canonical E.164。

如果号码无法规范化,就不能拿 raw display string 做 dedupe。系统应生成 requestHumanReview/Suggestion,让员工纠正。MVP 不另外建设 identity-review subsystem。

未来迁到稳定的 contact_id 是一项独立 migration。在迁移真正完成前,不能在本设计中用抽象 contactRef 假装它已经存在。

6.2 Next Action fields

Next Action 只表示“已经采用的当前计划”。它不是所有可能建议的集合。

FieldType / valuesRequiredMeaningAuthority
nextActionTexttext nullableOpen Task normally yes下一步具体要做什么Staff;Policy 允许时 AI
nextActionAttimestamptz nullableNo什么时候做。按 V2 语义,NULL 表示 ready now/unscheduledStaff;Policy 允许时 AI
nextActionReasonCodecustomer_requested / activity_follow_up / staff_planned / new_inbound_evidence / reprioritized / manual / otherWhen changed为什么改变Command
nextActionSourcestaff / ai / policy / systemWhen set计划来源Code
nextActionSetByIdtext nullableNo具体 actorCode
nextActionSetAttimestamptzWhen set采用时间Code
sourceSuggestionIdUUID nullableNo如果这个计划来自 AI Suggestion,就指回原 proposalCode

MVP 不建 actions table。Task 只保存当前 snapshot。每次 replace、cancel 或 reschedule Next Action 时,都追加一条 task.next_action_changed Timeline event。

Current implementation: Target Expand schema 已有 nextActionText / nextActionSource / nextActionSetById / nextActionSetAt / sourceSuggestionId,Neon adapter 与 Staff commands 会原子更新 snapshot,并追加 task.next_action_changed。Task V2 rows 可能仍只有 nextActionAt 或 active task_suggestions.content.action;migration 不能把 Suggestion 猜成已经采用的 Next Action,未知时保持 null。

6.3 AI Suggestion fields

AI Suggestion 是先展示给员工看的建议,不是需要先批准才能看到的内容。员工可以直接参考,也可以什么都不做。

只有当员工把建议变成 Task 的实际 mutation 时,系统才需要记录它是否被采用。例如员工执行建议中的 changeNextActioncloseTask。thumbs up/down 只评价建议质量,不代表建议已经被采用或驳回。

FieldType / valuesMeaning
suggestionIdUUIDproposal identity
taskId / storeIdUUID归属与隔离
sourceTypeai / staffCurrent ai / human 的 Target 命名;说明建议来源
suggestedActionTexttext建议做什么
reasontext为什么建议
priorityhigh/medium/low建议优先级
evidenceRefstyped refs依据哪些 Call/SMS/Lead/Activity
statusactive / accepted / edited / dismissed / superseded / expiredproposal 的 lifecycle。active 表示当前可展示;expiredexpiresAt 到期后产生
proposedCommand + payloadtyped command nullablemutation 类提案必填(§11.2)。员工采纳时,以员工身份原样执行,不需要重新手工填写
generatedAgainstTaskVersioninteger nullablemutation 类提案必填;生成时的 aggregateVersion,用于展示层 stale 标记与执行层 Guard 拒绝
expiresAttimestamptz nullable建议的有效期。过期后自动退出 UI,不再渲染
promptVersion / model / runId / decisionPolicyVersiontext完整 provenance
createdAt / decidedAt / decidedBytimestamps/actor记录用户决定并用于审计

Current task_suggestions 会保留历史,不会破坏旧 content。新建议产生时,旧 row 会 UPDATE 为 superseded,同时插入新的 active row。表中已经有 promptVersion 和 runId。

Target 的 accepted / edited / dismissed 是供 audit 和 projection 使用的 proposal state。系统不能靠猜测产生这些状态,而要把员工的明确操作映射到 accept/edit/dismissSuggestion command,并写入 task.suggestion_decided event。UI 不平铺通用的 Accept/Edit/Dismiss 三个按钮(这是 §11.2 的硬规则,不是可选项);员工执行具体建议时,就等于 accept。这里的 dismissed 表示员工主动把建议移出当前工作区。员工只是看过后暂时不采用,可以直接忽略,不必先点 reject。

Current ai_feedback 不能直接冒充 adoption state,因为两者回答的问题不同:

  • helpful / not_relevant 是质量评价,也就是 thumbs up/down。它不表示 adopted 或 dismissed。
  • followed 是员工自己报告“已经照做”。只有同时存在关联 mutation command(changeNextAction / recordActivity / recordActivityAndPlan / recordBusinessProgress / closeTask)和 sourceSuggestionId,系统才能把它投影为 accepted/adopted。
  • corrected 保存员工给出的替代内容。只有这段内容通过 command 成为 Next Action 后,系统才能把它投影为 edited + accepted。

Target logical status 根据 suggestion row 和 decision event 一起计算。Current feedback 必须原样保留,migration 时不能把旧值一对一改名成新的 adoption status。

6.4 Activity fields 与 live values

Activity 记录已经发生过的事实,因此只能 append,不能覆盖旧记录。第一阶段继续把它写入 contact_timelinetask.activity_recorded,不同时维护第二套 Activity source of truth。

FieldType / valuesRequiredMeaning
activityIdstable UUIDYesActivity payload 的 identity。它不同于 contact_timeline.id 的 bigserial event-row ID
taskId / storeIdUUIDYesTask 归属和隔离
occurredAt / recordedAttimestamptzYes这件事实际发生的时间 / 系统写入的时间
channelphone / sms / email / in_person / otherYes通道
directioninbound / outbound nullableDepends方向
actioncall_completed / message_sent / conversation_logged / work_completedYes做了什么
outcomeconnected / no_answer / voicemail_left / sent / delivered / failed / wrong_number / reply_received / bounced / not_available / completed / no_response / otherYes这一次动作怎么样
conversationDispositionfollow_up_agreed / considering / not_interested / resolved / appointment_booked / other nullableNo这次沟通明确表达了什么
actorType / actorSubjectIdstaff / ai / system / import + stable identityYesTarget domain 中是谁执行或记录。contact_analysis/call_analysis/lead_webhook 属于 source/run provenance,persist 时再映射到 Current Timeline vocabulary
creditedStaffIdUUID nullableNo业务 credit 归给谁。它与 recorder 是两个不同角色
verificationTypecanonical evidence vocabularyYes事实如何确认
activityEvidenceRefstyped refsWhen available原通话、短信、人工说明
sourceInteractionKeytext nullableFor observed Call/SMS用于识别并去重同一个真实 interaction 的稳定 key
sourceSuggestionIdUUID nullableNo如果动作直接从 suggestion 发起,就携带这个引用。执行动作即表示采纳,是 adopted 投影(§6.3)的依据之一
notetext nullableNo必要上下文
idempotencyKeytextYes for command重放不重复记录

Legacy Timeline/API 仍可能出现 provider_confirmed / staff_asserted / imported_legacy。Target engine 已使用 §3.3 的 canonical vocabulary,并由 server evidence resolver + Policy Guard 生成 verification;migration 必须按 evidence strength 显式转换,不能机械改名。

Channel、action、outcome 不能任意组合。shared contract/Policy Guard 强制执行下面的组合表——不在表里的组合一律拒绝(与 Current TASK_ACTIVITY_CONTRACT 对齐):

ChannelActionAllowed outcomes
phonecall_completedconnected / no_answer / voicemail_left / wrong_number / failed / other
phoneconversation_loggedconnected / other
smsmessage_sentsent / delivered / failed / other
smsconversation_loggedreply_received / other
emailmessage_sentsent / delivered / bounced / failed / other
emailconversation_loggedreply_received / other
in_personconversation_loggedconnected / other
otherwork_completedcompleted / not_available / failed / no_response / other

例如,message_sent + no_answer 不是有效组合,backend 必须拒绝。UI options 和 AI tool schema 都要从同一份 contract 生成。

6.5 Business Progress fields

Business Progress 表示 Task 中已经有 evidence 支撑、但还不是终态的业务进展。它写成 append-only Timeline event。current value 取 latest non-invalidated projection,不新增 Milestone table。

FieldType / valuesRequiredMeaning
progressEventIdstable UUIDYes这次 progress fact 的 identity
taskId / storeIdUUIDYesTask 归属和隔离
progressCodeTask-specific;Lead MVP 为 intro_booked / intro_attendedYes已发生的中间业务进展
progressOccurredAttimestamptz nullableAttribution 时 Yes进展实际发生的时间。不能拿 recorded/closed time 代替
recordedAttimestamptzYes系统写入时间
verificationTypeevidence vocabularyYes这项进展是怎样确认的。由 server resolver 决定
progressEvidenceRefstyped refsYes支撑 progress 的同店 evidence
decisionPolicyVersiontextManaged yes哪版 Policy 接受/纠正本次 progress
reportingMetricCodetext nullableNo非终态 reporting projection;Lead booking 为 intro_booking
supersedesProgressEventIdUUID nullableCorrection only指明要纠正或作废哪次旧 progress。原 event 不删除
sourceSuggestionIdUUID nullableNo采纳并执行 suggestion 时携带。server 在同一个 transaction 中写 task.suggestion_decided,并把该 suggestion 投影为 accepted

6.6 Outcome fields

Outcome 是 Task 结束时的 terminal snapshot。它不是另一个 Case,也不等于其中某一次 Activity。

FieldType / valuesRequired on closeMeaning
resultCodeTask-specific codeYes业务上最终发生了什么
closureReasonCodeglobal stop reasonYes为什么现在不再继续处理
verificationTypeevidence vocabularyYes这个结果通过什么来源确认
outcomeEvidenceRefstyped refsYes;legacy import 可指向 legacy row支撑结果的证据;offline close 使用系统生成的 staff_assertion
outcomeOccurredAttimestamptz nullableAttribution 时 Yes结果实际发生的时间。未知时可以为空,但不能用 closedAt 代替
closedByTypestaff / ai / system / importYes谁执行了关闭操作
closedBySubjectIdtext nullableDependsgeneric actor identity
closedAttimestamptzYes系统写入关闭操作的时间
outcomePolicyVersiontextManaged yes关闭/纠正时哪版 effective Policy 接受该组合
revenueMappingCodetext nullableNoreporting projection key
notetext nullablePolicy dependent补充解释或员工掌握的 context
sourceSuggestionIdUUID nullableNo采纳 suggestion 并执行 close 时携带。server 在同一个 transaction 中写 task.suggestion_decided,并把该 suggestion 投影为 accepted

所有 Task 共用以下 closureReasonCode

goal_achieved
customer_declined
contact_exhausted
no_longer_needed
compliance_stop
invalid_identity
duplicate
superseded
other

positive / negative / neutral / unknown 只能根据 taskKind + resultCode + outcomePolicyVersion 派生,用于 UI/reporting。用户不能把这些值当成 canonical fact 直接选择。

contributionLevel=attributed 时,必须有 outcomeOccurredAt

  • communication_explicit 默认使用 qualifying Call/SMS 的 occurredAt
  • staff_confirmed 可以由员工填写真实发生时间。
  • 如果不知道真实时间,就保持 null。此时最高只能投影为 discovered/assisted

同样,任何 non-terminal progress attribution 都必须有 progressOccurredAt,不能拿 Task 最终的 closedAt 代替。

6.7 Derived projection fields

下面这些字段由 API projection 计算。客户端只能读取,不能写回:

  • workStatenot_started / in_progress / closed
  • queueStateneeds_attention / scheduled / closed
  • overdue:boolean。
  • firstResponseSlaStatenot_due / met / missed / not_applicable
  • outcomeValencepositive / negative / neutral / unknown
  • contributionLeveldiscovered / assisted / attributed。按 deterministic eligibility 计算。
  • businessProgress:Policy-defined、evidence-backed 的非终态业务进展;例如 intro_booked / intro_attended。它不是 Task Outcome。
  • displayType:Policy 驱动的当前 UI/queue classification;可以 materialize 方便筛选,但不得由客户端任意写。

lead_conversion.displayType 的 MVP 计算规则是 deterministic。系统按下面的 precedence 从上到下判断:

open + 有仍然有效的 completed booking/attendance evidence,且尚未 Converted
  → booked_not_converted

open + 当前 evidence chain 尚无 `initialContactCompletedEvidence`
  → lead_outreach

open + 已有 `initialContactCompletedEvidence`
  → lead_follow_up

initialContactCompletedEvidence 和 first-response SLA 的 outbound-only qualifying rule 不是同一个概念。

一次 outbound connected/no-answer/voicemail/sent 可以完成首次 outreach。如果 Task 本来就是由 meaningful inbound Call/SMS 创建,说明有效沟通已经发生,应直接进入 lead_follow_up,不能再要求员工做一次“首次联系”。

Completed booking/attendance evidence 可以来自 Call/SMS 中的明确表述,也可以来自员工记录的线下 staff_assertion

“Book me Tuesday at 6”只是一条 booking request,还不足以进入 booked_not_converted。如果 booking 后来被取消、被员工纠正或 evidence 失效,而且没有更晚的 valid attendance evidence,open Task 就回到 lead_follow_up

booked_not_converted 只表示“已经有有效的 Intro progress,但还没有 conversion evidence”。它不是 negative Outcome。converted 可以直接从三种 displayType 中的任意一种发生,不要求先有 Intro。

为了兼容现有用户语言,暂时保留 booked_not_converted 这个 code。但不能根据这个 code 反推客户究竟只是 booked,还是已经 attended。详情页和 Funnel 必须读取独立的 businessProgress 和 provenance。

如果未来用户研究证明这个 label 容易误解,只修改 Policy/UI label,不修改 Task identity。

Closed Task 不再参与 current queue 的 displayType 重算。历史 projection 保留 displayTypeAtClose,或者最后一个有效的 materialized value,用来解释关闭时的工作上下文。

Task reopen 后,再根据仍然有效的 current evidence 重算。不能因为 Outcome 被清除,就机械地回到 lead_outreach

这样,Workbench 仍然可以显示并筛选 “Lead Outreach”“Lead Follow-up”“Intro Booked · Conversion Pending”。但在 backend 中,这些工作始终属于同一个长期 lead_conversion Task。

关闭/reopen、Outcome 和 Funnel 都不能根据前端 label 反向推断。


7. Managed Task 与 Flexible Task

CapabilityManaged TaskFlexible Task
目的系统可以自动发现、跟踪和统计的核心 revenue/service opportunity员工临时创建或某家店特有的工作
taskModemanagedflexible
taskKindPolicy Catalog 中的 kindcustom
谁可创建AI、system 或员工;无论谁创建,都必须有 evidence/provenance员工创建;AI 可以建议创建
Next ActionAI 可以按 Policy 设置或建议;员工始终可以修改员工设置;AI 可以建议
ActivityRingCentral 可以自动关联;员工可以补充线下 Activity员工记录;也可以关联相关沟通
谁可关闭通过 evidence gate 的 AI,或员工员工;AI 默认只能 propose close
Revenue mappingPolicy 定义、带 verification strength默认不进入自动 revenue attribution
Outcome options根据 taskKind 动态生成通用 completed/cancelled + note

员工也可以创建 Managed Task,因为员工掌握的线下 context 是一等输入。引入 AI 自动化时,不能因此删掉人工入口。

Flexible Task 至少使用下面这组 terminal vocabulary:

  • resultCode = completed / cancelled / outcome_unknown
  • 继续使用 common closureReasonCode,例如 goal_achieved / no_longer_needed / duplicate / other
  • other / outcome_unknown 需要 note。
  • 默认 revenueMappingCode = null

第一版 Managed Task catalog:

lead_conversion
cancellation_request
payment_recovery
renewal
upgrade
win_back
referral

8. Task Policy Catalog

8.1 Policy 解决什么

先讲人话:每一种 Managed Task 都需要一本统一的“规则说明书”。这本说明书就是 Task Policy。它要同时告诉 AI、backend、UI 和 reporting:什么情况下能建 Task、接下来做什么、什么证据足以更新或关闭,以及最后怎样统计。

当前的问题不只是少了一张 typeCategory → closeResult 对照表。真正的问题是,同一条业务规则分散在 schema、prompt、UI、API 和 reporting 中:

taskKind
  → 什么 evidence 可以创建
  → 如何 dedupe
  → 初始 priority / SLA / Next Action
  → 哪些 Activity 有意义
  → 哪些 resultCode 合法
  → 哪些 closureReason 合法
  → 每个 Outcome 需要什么 evidence
  → AI 可以自动做什么
  → UI 展示哪些 options
  → Funnel / Revenue 如何投影

因此,这整条链必须由一个 versioned、code-owned Task Policy Catalog 统一定义。其他层不再各写一份规则,而是由 Catalog 生成或驱动:

  • AI prompt vocabulary 和 tool schema。
  • Backend validation 与 Policy Guard。
  • UI labels、help、create/close options。
  • Reporting valence、attribution wording。
  • Contract tests 和 golden eval fixtures。

8.2 Machine-readable policy shape

下面的 TypeScript type 是这本“规则说明书”的机器可读版本。读它时不必先理解所有 type;按业务问题看即可:

  • creation:什么证据允许创建 Task。
  • dedupe:怎样避免重复建同一件事。
  • initialPlan:Task 刚建立时,员工先做什么、多久内要响应。
  • displayprogressSignals:事实变化后,员工看到什么分类和业务进展。
  • outcomes:哪些最终结果合法,各自需要什么证据。
  • aiAuthority:AI 能做到哪一步;dncBehavior:客户 DNC 时这个 Task 怎么处置(close / block_outbound / keep_open_for_review,对员工和 AI 都生效)。
  • reportinguiMetadata:报表和界面怎样解释同一套规则。
type ManagedTaskPolicy = {
  taskKind: ManagedTaskKind;
  version: string;
  label: string;
  objective: string;

  creation: {
    allowedEvidenceTypes: EvidenceType[];
    requiresExplicitSignal: boolean;
    rules: CreationRule[];
  };

  dedupe: {
    keyParts: Array<'contactPhone' | 'taskKind' | 'subjectKey'>; // storeId is always DB scope
    reuseOpenTask: boolean;
    sourceRefIdempotency: boolean;
  };

  initialPlan: {
    nextActionTemplate: string;
    firstResponsePolicy?: {
      duration: DurationPolicy;
      clock: 'elapsed' | 'store_business_hours';
      qualifyingActivities: Array<{
        channel: ActivityChannel;
        direction?: 'outbound';
        actions: ActivityAction[];
        allowedOutcomes: ActivityOutcome[];
      }>;
    };
    defaultPriority: 'high' | 'medium' | 'low';
  };

  display: {
    defaultType: string;
    allowedTypes: string[];
    initialContactCompletedEvidence?: EvidenceRule[];
    deriveFromCurrentEvidence: DisplayTypeRule[];
  };

  progressSignals?: Record<ProgressCode, {
    allowedVerificationTypes: VerificationType[];
    requiresCompletedFact: boolean;
    invalidationRules: ProgressInvalidationRule[];
    displayType?: string;
    funnelStage?: string;
    reportingMetricCode?: string;
  }>;

  outcomes: Record<ResultCode, {
    allowedClosureReasons: ClosureReasonCode[];
    allowedVerificationTypes: VerificationType[];
    aiAutoCloseEvidence: VerificationType[];
    requiresCompletedFact: boolean;
    revenueMappingCode: string | null;
    reportingLabel: string;
  }>;

  contactExhaustionPolicy?: {
    attemptCount: number;
    elapsedWindow: DurationPolicy;
    qualifyingActivities: ActivityOutcome[];
  };

  aiAuthority: {
    canCreate: boolean;
    canAutoClose: boolean;
    canCloseFromInferenceOnly: false;
  };

  dncBehavior: 'close' | 'block_outbound' | 'keep_open_for_review';

  reporting: {
    attributionWindow?: DurationPolicy;
    qualifyingStaffActivities: ActivityRule[];
    revenueCreditKeyStrategy?: RevenueCreditKeyStrategy;
    estimateDefinitionVersion?: string;
  };

  uiMetadata: {
    workstream: 'lead_funnel' | 'member_care' | 'revenue_growth';
    icon: string;
    helpText: string;
    displayLabels: Record<string, string>;
  };
};

Policy registry 是这套规则的 source of truth。版本和 label 可以同步到数据库,方便查询;但可变的数据库配置不能在没有 review/test 的情况下改变历史语义。

落到代码里,一个 taskKind 对应一个声明式、版本化的 policy 文件,所有文件组成 TypeScript registry。增加第 N 种 Managed Task 时,只增加一个 policy 文件;不修改 prompt 主体、不动 schema、不动 Orchestrator。§8.6 会说明为什么。

Policy 会升级,但历史必须可复现——给定记录下来的版本号,要能重新加载那一版规则、重演当时的判定。因此每一次业务决定都要记住自己使用的是哪一版规则:

  • creationPolicyVersion 固定记录 Task 创建时的 effective Policy。
  • outcomePolicyVersion 记录 Close/Correction 当时实际执行 validation 的 effective Policy;长时间 open 的 Task 可以由 v1 创建、按 v2 关闭。
  • 每个 AI assessment、Suggestion 和 Timeline decision event 记录自己使用的 decisionPolicyVersion
  • store override 与 base Policy 合并后生成不可变 effective version/fingerprint;已经被历史记录引用的版本必须继续可加载。
  • Policy 升级不静默重算历史 Outcome 或 attribution;若要重算,必须发布新的 projection/definition version。

firstResponseAt 回答一个简单问题:员工第一次真正响应这个 Task 是什么时候。它由最早一条 qualifying Activity 确定性计算,并 materialize 到 Task-lifetime 字段,方便队列和报表读取;Timeline 仍是审计来源。

默认可以算作首次响应的员工动作包括:outbound connected/no-answer/voicemail Call,以及成功 sent/delivered 的 outbound SMS。以下内容不算 staff first response:failed Call、inbound message、Note、AI suggestion。

补录过去发生的 offline Activity 时,只有同时满足 authenticated staff、occurredAt >= createdAt 和 typed assertion,才可以计入首次响应。具体允许哪些 channel/outcome,以及 SLA 按 elapsed time 还是 business hours 计算,都由 Task Policy 定义。

8.3 所有 Managed Task 的共同硬规则

不管 taskKind 是什么,下面这些底线都一样:不能重复建单、不能拿推断当最终事实、不能用一次无人接就结束 Task,也不能让 AI 覆盖更新的人工决定。

  • Managed Task 默认由 server 生成 dedupeKey=contactPhone + taskKind;Policy 需要时加入 subjectKey。Flexible Task 不做 business-objective dedupe,依赖 creation idempotency 防 retry。
  • 相同 source evidence 必须通过 idempotency 避免重复创建。
  • 新 evidence 优先更新现有 open Task,不重复开同类 Task。
  • AI 创建的 Task 必须带 reason + evidenceRefs + creationPolicyVersion
  • ai_inferred 可以发现低风险机会,但不能单独证明 terminal Outcome。
  • 一次 no_answer 不能成为 contact_exhausted;必须满足该 Policy 的 exhaustion threshold 或员工确认。
  • AI auto-close 必须满足 Task-specific allowlist、同店 evidence、无更新人工冲突、可纠正/reopen,并写完整 provenance。
  • Staff confirmation 优先于后续 AI inference;AI 不得静默覆盖人工 Outcome、note、assignee 或 credit。

8.4 第一版 Managed Task policy matrix

下表把第一批 Managed Task 放在一起比较。阅读时要分清三种确定程度:

  • Task 的 objective、creation boundary 和 Outcome 语义是 Selected semantic boundaries;实现与 rollout 必须保持这些语义并用真实用户行为验证可用性。
  • 时间与 exhaustion 是 Proposed policy defaults
  • AI auto-close 是否在某家店启用,属于 Open rollout authority

在 shadow evaluation 完成前,表里的示例时间和 eligibility 只是待验证的默认建议,不代表 production feature flag 已经批准。

taskKindObjective 与 creation evidenceInitial Next Action / timingValid resultCodeAI authorityRevenue wording
lead_conversion推进一个 Lead 到明确销售结果。Email Lead、明确 Call/SMS prospect signal 或员工线下确认可以创建;Intro 是一种可能路径,不是强制目标新 Email Lead:立即联系,first-response SLA 建议 5m;后续按客户需求预约 Intro、回答问题或直接讨论 Membershipconverted / not_converted / outcome_unknown / not_applicable可 create;completed booking 只更新 progress/displayType,不关 Task;明确 membership 已完成或 staff confirmation 才可关 converted;明确最终拒绝可关 not_converted;一次 no-answer 不可关分开显示 “Intro booking progress attributed” 与 terminal “Membership conversion attributed”;都不是 CRM-verified revenue
cancellation_request处理明确取消请求并争取 retention。普通抱怨、负面情绪或 AI churn guess 不足以自动创建高优先级 callback,建议 same business day;不规定固定 save SOPcancel_saved / cancellation_completed / outcome_unknown / not_applicable明确取消信号可 create;客户明确决定保留可关 cancel_saved;“我要取消”只是 creation evidence;只有明确已处理完成或 staff confirmation 才关 cancellation_completedcancel_saved = estimated revenue protected;不得称 membership-system verified
payment_recovery处理明确 declined/past-due/payment update 问题。泛化价格问题不够安全联系客户并安排更新;建议 same business day;不得存完整 card datapayment_restored / payment_not_restored / outcome_unknown / not_applicable只在 payment issue 明确时 create;“我会更新卡”不够;明确已成功或 staff confirmation 才关 payment_restored“Payment recovery attributed”;金额未知时只报 count/opportunity
renewal对话明确 renewal/到期/续约决定,或员工知道真实 renewal window。不能靠 contact age 猜到期有可靠 renewal date 时在此前处理;无日期时进入 Needs Attentionrenewed / not_renewed / outcome_unknown / not_applicable明确 renewal signal 可 create;“愿意续”可能只是 commitment;明确续约已完成或 staff confirmation 才关 renewed“Renewal attributed / estimated retained revenue”
upgrade明确询问更高 tier、更多 classes、套餐变更,或员工线下发现机会。普通满意度不够回应需求并确认 option;强 inbound request 立即处理,否则建议 1 business dayupgraded / not_upgraded / outcome_unknown / not_applicable可按明确需求 create;“听起来不错”不等于生效;明确已处理/生效或 staff confirmation 才关 upgraded“Upgrade attributed / estimated expansion revenue”
win_back同时存在 former-member 背景和明确 return signal,或员工确认。仅长时间未联系不够确认 return goal 和 next move;建议 signal 后 1 business daywon_back / not_won_back / outcome_unknown / not_applicableformer + return signal 明确时可 create;“可能回来”不够;明确已重新加入或 staff confirmation 才关 won_back“Reactivation attributed / estimated reactivated revenue”
referral客户提到具体朋友/家人有兴趣、已有可用 contact info,或员工线下确认。仅满意/“以后会介绍”不够有信息时确认 consent 并处理;只有意向时请求可分享信息referral_captured / referral_not_captured / outcome_unknown / not_applicable明确 referral signal 可 create;实际可执行 referral 信息和 consent 或 staff confirmation 才关 referral_captured“Qualified referral captured”;本身不算 revenue

resultCode 说明“最后发生了什么”,closureReasonCode 说明“为什么现在停止处理”。两者不能随意组合;合法组合只能来自下面的 Outcome Decision Matrix。

8.5 Outcome Decision Matrix

这张表回答三个问题:这个 Task 可以用什么结果关闭、为什么可以关闭、需要什么证据。

allowedVerificationTypes 是“允许哪些 evidence 类型”的集合,不是把所有 evidence 硬排成一个线性分数。表中的 “AI eligible” 也不等于 AI 一定会自动关闭;它只表示语义上允许进入 auto-close guard。真正执行时,还必须同时满足该 Policy 版本的 feature flag、同店 evidence、无人工冲突和 rollout allowlist。

taskKindresultCodeAllowed closureReasonCodeAllowed verificationCompleted fact requiredAI eligiblerevenueMappingCode
lead_conversionconvertedgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYes;必须明确 Membership conversion 已发生explicit completed fact onlymembership_conversion
lead_conversionnot_convertedcustomer_declinedcommunication_explicit / staff_confirmed / imported_legacyNo;需明确 final decisionexplicit onlynull
cancellation_requestcancel_savedgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYes;必须明确撤回取消/决定保留explicit onlycancellation_save
cancellation_requestcancellation_completedgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYes;不能只凭取消请求explicit completed statement onlynull
payment_recoverypayment_restoredgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYesexplicit completed statement onlypayment_recovery
payment_recoverypayment_not_restoredcustomer_declinedcommunication_explicit / staff_confirmed / imported_legacyNo;需明确 final decisionexplicit onlynull
renewalrenewedgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYesexplicit completed statement onlyrenewal_retention
renewalnot_renewedcustomer_declinedcommunication_explicit / staff_confirmed / imported_legacyNo;需明确 final decisionexplicit onlynull
upgradeupgradedgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYesexplicit completed statement onlymembership_upgrade
upgradenot_upgradedcustomer_declinedcommunication_explicit / staff_confirmed / imported_legacyNo;需明确 final decisionexplicit onlynull
win_backwon_backgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYesexplicit completed statement onlymember_reactivation
win_backnot_won_backcustomer_declinedcommunication_explicit / staff_confirmed / imported_legacyNo;需明确 final decisionexplicit onlynull
referralreferral_capturedgoal_achievedcommunication_explicit / staff_confirmed / external_verifiedYes;需 usable referral + consentexplicit completed fact onlyqualified_referral
referralreferral_not_capturedcustomer_declinedcommunication_explicit / staff_confirmed / imported_legacyNo;需明确 final decisionexplicit onlynull

除了每种 Task 自己的业务结果,所有 Managed Task 还共享两个 administrative results。它们表示“结果未知”或“这件事已经不适用”,不是员工做失败了;reason mapping 仍然必须严格校验:

resultCodeAllowed closureReasonCodeAllowed verificationAI eligibilityReporting meaning
outcome_unknowncontact_exhausted / othersystem_observed / staff_confirmed / imported_legacy仅 deterministic exhaustion policy;other 必须人工 note目标结果未知,不是 negative business fact
not_applicableno_longer_needed / compliance_stop / invalid_identity / duplicate / supersededreason-specific system_observed / communication_explicit / staff_confirmed / imported_legacy仅 deterministic duplicate、DNC 或同等 allowlist不适用/停止,不算员工失败

Flexible Task 没有 Managed Task 的业务结果矩阵,因此只允许下面这组最小结果:

resultCodeAllowed closureReasonCodeRequired evidence
completedgoal_achievedauthenticated staff assertion;note 可选
cancelledno_longer_needed / duplicate / superseded / otherauthenticated staff assertion;other 必须 note
outcome_unknownotherauthenticated staff assertion + note

Lead Intro 是过程中的业务进展,不是 Task 的最终结果,所以不放进 Outcome Decision Matrix:

taskKindprogressCodeRequired evidencedisplayTypeFunnel projectionreportingMetricCode
lead_conversionintro_bookedcompleted booking statement with communication_explicit / staff_confirmed / external_verified;booking request 不够booked_not_convertedBooked + verification provenanceintro_booking
lead_conversionintro_attendedcompleted attendance statement with communication_explicit / staff_confirmed / external_verified;时间已过不够booked_not_convertedShowed/Trialed,仅在对应 Funnel definition 启用intro_attendance

如果 Booking evidence 后来被取消或纠正,而且没有 valid attendance evidence,系统要追加 correction event,再把 displayType 重算为 lead_follow_up。原来的 evidence 不能删除。

员工在 UI 里选择 “Intro booked/attended” 时,不是在关闭 Task。Backend 必须通过一个 typed command,原子地写入 staff_assertion、progress evidence 和新的 Next Action。

Lead display/progress decision table:

Current evidence / eventTask effectResulting displayType
新 Email Lead,尚无有效沟通create/reuse open lead_conversionlead_outreach
meaningful inbound Call/SMS 创建 Taskcreate/reuse;initial contact 已发生lead_follow_up
qualifying outbound attempt(包含 no-answer/voicemail/sent)record Activity;Task openlead_follow_up
“Book me Tuesday at 6”记录 request + Next Action;不是 completed bookinglead_follow_up
明确 booking completed evidenceprogressCode=intro_booked;Task openbooked_not_converted
明确 attendance completed evidenceprogressCode=intro_attended;Task openbooked_not_converted
booking cancelled/corrected/invalidatedcorrect progress;Task openlead_follow_up
任一状态明确 membership conversion completedterminal resultCode=convertedTask closed;保留 displayTypeAtClose

第一版 revenueMappingCode allowed values 只有:

membership_conversion
cancellation_save
payment_recovery
renewal_retention
membership_upgrade
member_reactivation
qualified_referral
null

intro_booking / intro_attendance 是 non-terminal reportingMetricCode,不是 Outcome 的 revenueMappingCode。这两类 code 只负责选择 calculation/wording policy,不表示金额已经被权威系统验证,也不把 verification 或 contribution 编码进 code 本身。

cancellation_completed 等没有正向 revenue mapping 的结果,仍然可以进入业务 Outcome/Funnel 报表。

8.6 为什么新增 Task 类型不应重写数据库、页面和 prompt

Policy Catalog 的价值,是把“新增一种业务规则”和“新增一种系统能力”分开。

例如,以后增加 freeze_return,目标是让 freeze 到期客户恢复服务。

如果它仍然满足现有通用能力:

  • 一个 Contact/store 下的 Task。
  • 一个当前 Next Action。
  • 多个 Activity。
  • 一个 evidence-backed Outcome。
  • 同样的 ownership、SLA、close、audit。

如果它仍然只需要上面这些通用能力,就只需:

  1. 在 Policy Catalog 增加 freeze_return.v1
  2. 定义 creation evidence、dedupe、outcomes、authority、reporting。
  3. 增加 policy/guard/eval tests。

UI 从 Policy 读 label、help、create/close options;AI 从同一 Policy 读 vocabulary;backend 从同一 Policy 验证;reporting 从同一 Policy 做 mapping。无需:

  • 新增数据库 enum/column/table。
  • 复制一个新的 Task 页面。
  • 在 prompt、Zod、UI、API、reporting 五处手写五份 mapping。

只有需求超出这些通用能力时,才需要 architecture change。例如:

  • 一个 Task 同时绑定多个 Contacts。
  • 多负责人并行且各自有独立 completion。
  • 真实 payment/refund transaction。
  • 外部系统写操作或 approval。
  • 需要多个同时 active、可独立分配的 Actions。

这区分了“新增一种业务类型”和“新增一种系统能力”。


9. Database design

9.1 Current:可直接复用的基础

先讲结论:Target 没有新建第二套 Task source of truth。当前 source 已在现有 Neon/PostgreSQL storage 上完成 nullable Expand schema、typed engine 与 receipts;剩余工作主要是 migration execution、historical reconciliation、read/authority cutover 和最终 Contract cleanup。

Live code 里,几块存储各自负责:

StoreCurrent responsibility
tasksTask current snapshot、ownership、canonical Next Action、SLA/deadline、current progress projection、Outcome 和 aggregate version
contact_timelineappend-only Activity、Business Progress、Outcome/correction、status/reopen、assignee、note、Next Action、deadline 与 staff assertion history
task_suggestionsAI/人工建议;content/history non-destructive,旧 row status 会 supersede
ai_task_decision_assessments / ai_task_reasoner_runsAI 写入前 assessment、bounded run provenance、proposal/no-op/failure audit
provider source-proof / receipts / cursors / outboxexact raw source envelope、source binding、idempotent custody、SMS window high-water 和 Call/VoiceMail semantic handoff
Target Task Engine + Neon adaptertyped command、Policy Guard、authority/version/idempotency、atomic snapshot + Timeline mutation

换句话说,Current Target schema/runtime 已经能保存这些事实:

  • status = open / closed
  • legacy typeCategory compatibility,以及 stable taskKind + displayType Target contract。
  • sourceType = lead / contact_analysis / manual
  • priority = high / medium / low
  • canonical nextActionText + nextActionAtfirstResponseDueAt/firstResponseAtdeadlineAt、assignee、source provenance。
  • Managed objective dedupeKey、creation idempotency、aggregate version 和 existing-row writer ownership。
  • canonical Activity、Business Progress、Outcome、correction、reopen 与 Staff command Timeline events。

仍未完成的不是“再建一套 Task 表”,而是下面这些 rollout/data contract:

  • Production database migration state、runtime gates 和每个 Store 的 authority 必须外部核验。Checked-in source 已不是一个全局 legacy/disabled default:新 Store authority 会 seed 为 target/target,TEST Task V3 source gates 为 true,而 pre/prodfalse
  • open Task V2 Lead Tasks 与 ambiguous historical rows 需要 evidence-aware reconciliation,不能按当前 phone assignment 回填历史 Store。
  • Task V2 / Task V3 Workbench、Timeline、Funnel、Speed 和 reporting 需要 dual projection reconciliation。
  • human-ratified/live eval 已经开始,但 Gate A 质量闸门未通过;shadow、UAT 和各环境状态见 Task V3 Rollout Status
  • membership/billing amount 与 causal revenue attribution 尚无 authoritative integration。

以下 current source 是本节所有 implementation claim 的 source of truth:

  • callytics-infrastructure/packages/common/src/db/schema/tasks.ts
  • callytics-infrastructure/packages/common/src/db/schema/task-progress.ts
  • callytics-infrastructure/packages/common/src/db/schema/contact-timeline.ts
  • callytics-infrastructure/packages/common/src/db/schema/task-suggestions.ts
  • callytics-infrastructure/packages/common/src/db/schema/ai-task-decision-assessments.ts
  • callytics-infrastructure/packages/common/src/db/schema/ai-task-reasoner-runs.ts
  • callytics-infrastructure/packages/common/src/db/schema/task-objective-authority.ts
  • callytics-infrastructure/packages/common/src/db/schema/provider-interaction-source-proof.ts
  • callytics-infrastructure/packages/common/src/domain/task-engine/
  • callytics-infrastructure/packages/common/src/domain/task-policy/

9.2 Target MVP logical ERD

Target 继续把 tasks 当 current snapshot,把 contact_timeline 当历史账本。Suggestion、AI assessment、pre-task proposal 和 reasoner run 分别保存自己的 lifecycle;它们不会被塞进 Task status。

所以,第一阶段明确不创建下面这些新 source of truth:

  • cases
  • task_steps
  • actions
  • milestones
  • 第二份 task_activities source of truth。

9.3 tasks Expand schema

expand 的意思是:先在现有 tasks 表上增加 Target 字段,同时保留 legacy 字段做兼容;不是一次性替换线上读写。下列 columns 已在当前 source schema/migration 中定义为 nullable Expand fields;表中的 Present 不证明 production migration 已执行,也不表示 legacy columns 已可删除。

Target columnActionNotes
task_modePresent, nullable Expandmanaged / flexible
task_kindPresent, nullable ExpandPolicy key;先与 type_category dual-write
display_typePresent, nullable ExpandLead current UI classification;仅 projector/Policy Guard 写,可从 Timeline/evidence 重算;close 时冻结为历史 displayTypeAtClose
dedupe_keyPresent, nullable ExpandManaged open objective identity;Flexible null;server-derived
titlePresent, nullable Expand可先由 Policy label/projector 生成
creation_evidence_refsPresent, nullable Expand完整 typed creation evidence;legacy source fields 继续作 shortcut
creation_policy_versionPresent, nullable Expand创建时 pin;legacy rows 为 legacy-v1
next_action_textPresent, nullable Expandnext_action_at 组成 adopted plan
next_action_sourcePresent, nullable Expandstaff/ai/policy/system
next_action_set_by_idPresent, nullable Expandactor provenance
next_action_set_atPresent, nullable Expandplan adoption time
source_suggestion_idPresent, nullable Expand采纳 suggestion 时写
first_response_due_atPresent, nullable Expand与 deadline 分开
first_response_atPresent, nullable Expand首个 qualifying Activity
current_opened_atPresent, nullable Expandcreate/reopen period
result_codePresent, nullable ExpandTask-specific
closure_reason_codePresent, nullable Expandcommon reason
outcome_verification_typePresent, nullable Expandevidence strength
outcome_evidence_refsPresent, nullable Expandtyped refs array
outcome_occurred_atPresent, nullable Expand与 closedAt 分开
closed_by_typePresent, nullable Expandstaff/ai/system/import
closed_by_subject_idPresent, nullable Expandgeneric actor identity;Current closed_by_staff_id 仅覆盖 staff
outcome_policy_versionPresent, nullable ExpandClose/Correction 当时 validation 版本
revenue_mapping_codePresent, nullable Expand只保存 terminal Outcome mapping;booking 使用 progress projection
aggregate_versionPresent, nullable Expandoptimistic concurrency

第一阶段把 current Outcome 直接存在 tasks snapshot 中,原因很简单:

  • 每个 Task 同时只需要一个 current terminal Outcome。
  • reopen/correction history 已由 Timeline 保存。
  • 避免为尚未出现的查询需求增加 task_resolutions join 和双写复杂度。

只有未来真的出现“多个并存 resolution、法律级不可变 resolution entity、复杂 supersession query”,才把 Outcome 抽成独立 table。

task_suggestions 的 Expand fields 也已在 current source schema 中定义。纯文本建议仍可只存内容;如果一条 Suggestion 代表可执行的 mutation,就必须额外保存具体 command、生成时的 Task version 和有效期:

Target column / sourceActionNotes
evidence_refsPresent, nullable Expandresolved typed refs;legacy 无来源保持 null/legacy
proposed_command + payloadPresent, nullable Expandmutation 类提案的 typed command(§6.3/§11.2);Next Action 纯文本建议保持 null
generated_against_task_versionPresent, nullable Expand生成时的 aggregateVersion;执行层 Guard 拒绝 stale 提案,展示层据此计算 stale 标记
expires_atPresent, nullable Expand到期投影为 expired,退出 UI
ai_modelPresent, nullable Expand与 prompt/run provenance 同行
decision_policy_versionPresent, nullable Expand生成 Suggestion 时使用的 effective Policy
accept/edit/dismisstask.suggestion_decided Timeline/feedback-compatible event不覆盖原 suggestion content;记录 actor/time/decision
logical status / decidedAt / decidedByAPI projectionsuggestion row + latest valid decision event;不是把 ai_feedback 生硬改名

这里要把员工看到的 Suggestion 和 backend lifecycle 分开理解:

  • stale 不写成数据库 status。API 用 generated_against_task_version 与 Task 当前 aggregateVersion 比较后得出;展示层必须隐藏或明确标记(二选一),执行层 Guard 必须拒绝。
  • staff 对同一 Task 做 mutation、close 或 reopen 时,server 在同一 transaction 内把相关 active suggestion rows 标记为 superseded,并记录 reason。
  • reopen 不会复活上一个 open period 的旧建议。

这三条只规定后台怎样防止旧提案被误执行;员工界面怎样表达,见 §11.2 和 §13。

9.4 Timeline / Activity

历史继续写进 contact_timeline。Task row 只保留“现在是什么”,下面这些 event 保留“之前发生过什么”:

task.created
task.status_changed
task.activity_recorded
task.business_progress_recorded  (Target writer present;非终态,如 intro_booked)
task.business_progress_corrected (Target writer present;supersede/invalidates,不删历史)
task.next_action_changed
task.deadline_changed
task.assignee_changed
task.note_updated
task.outcome_corrected   (Target writer present)
task.suggestion_decided  (Target writer present;不得由 quality feedback 自动冒充)
task.staff_asserted      (Target writer present;线下 confirmation evidence)
task.reopened            (Target writer present;保留旧 Outcome history)

每条新写入都必须有 store_idoccurred_at 表示事实实际发生的时间,created_at 表示系统把它记下来的时间;两者不能混用。Activity credit 使用 staff_id,也不能把“谁录入”自动当成“谁完成了工作”。

一个 command 可能在同一个 transaction 里产生多条 Timeline events。为了既防止整个请求重复执行,又允许同一请求安全地写多条 event,幂等 identity 分成三层:

commandId             = 整个 request/receipt 的稳定 ID,payload hash 必须一致
eventIdempotencyKey   = commandId + eventType + ordinal
sourceInteractionKey  = 同一 RingCentral Call/SMS 的稳定事实 key

recordActivityAndPlan 就是一个例子:一次保存同时写 task.activity_recordedtask.next_action_changed。这两行不能竞争同一个唯一 key。Target 在 Timeline typed columns 或 metadata 中保存 commandId + commandPayloadHash;每一行非空的 idempotency_key 使用自己的 event-level key。

Target runtime 已经使用 canonical sourceInteractionKey:provider Activity 把它写进 Timeline metadata,并在 task_engine_receipts 保存 source receipt。当前没有单独的 contact_timeline.source_interaction_key column;不能因为不是独立 column 就说 contract 不存在,也不能让 reporting 从自然语言重新推断它。Legacy Timeline rows 如果只有 sourceRefs[] / providerRef,必须经 server-side adapter 解析;无法证明时保持 unknown。

Target 允许一通 Call/SMS 同时推进多个 Tasks,但底层 RingCentral interaction 仍然只是一条真实事实,不会被复制。Server 从已验证的 source row 确定性生成:

sourceInteractionKey = v1:{storeId}:{evidenceType}:{immutableSourcePrimaryKey}

这个 key 只能由 server 写入 Activity typed field/Timeline metadata,AI 和客户端不能自行指定。每个 task-specific Activity event 都引用同一个 sourceInteractionKey + ResolvedEvidenceRef

这样,Call volume 和 staff interaction credit 必须按 storeId + sourceInteractionKey 去重——同一次 interaction 推进两个 Tasks 也只计一次;不同 Task 的 progress/outcome contribution 仍可分别计算。

9.5 Index 与 integrity

下面这些 index 和 integrity rule 不是为了“把 schema 做复杂”,而是为了守住队列性能、同店隔离、去重、并发和 Outcome/Progress 的边界。Target 至少保留或增加:

  • open queue:store_id, status, next_action_at
  • overdue:store_id, status, deadline_at
  • ownership queue:store_id, assigned_to_staff_id, status, next_action_at
  • Task history:store_id, entity_type, entity_id, occurred_at desc
  • Activity aggregation:store_id, entity_id, occurred_at desc where event type is Task Activity。
  • dedupe:partial unique (store_id, dedupe_key) where status='open' AND dedupe_key IS NOT NULL。phone 是 canonical E.164;Managed key 由 Policy 生成,Flexible null。
  • optimistic write:所有 mutation 携带 expectedVersion,成功后 increment aggregateVersion
  • closed integrity:closed 必须有 Outcome/admin closure;open 的 current Outcome fields 必须为空。
  • lead progress integrity:intro_booked / intro_attended 不能写入 terminal result_code;open lead_conversion 的 materialized display_type 必须与 latest valid progress/contact evidence 一致。

store_id 必须来自 authenticated server context,或由 server 验证。AI 和客户端提交的任意 store_id 都不能直接信任。

非终态 Business Progress 写进 Timeline 时,最少需要下面这些 typed fields:

type TaskBusinessProgressEvent = {
  progressEventId: string;
  taskId: string;
  progressCode: 'intro_booked' | 'intro_attended';
  progressOccurredAt?: string; // attribution 需要真实时间;未知时不得拿 recordedAt 代替
  verificationType: VerificationType;
  evidenceRefs: ResolvedEvidenceRef[];
  decisionPolicyVersion: string;
  reportingMetricCode?: 'intro_booking' | 'intro_attendance';
  supersedesProgressEventId?: string;
};

这份 payload 写入 Timeline,不新增 Milestone table。当前有效的 progress 由 latest non-invalidated event 投影得出;display_type 只是为了查询方便而 materialize 的当前值。

9.6 Pre-task proposal、Reasoner run 与 SMS cursor

这里先区分两种对象:

  • Task 已经存在时,AI 可以针对这条 Task 产生 Suggestion。
  • Task 还不存在、且 AI 没有该类型的 auto-create 权限时,AI 只能提出“要不要建 Task”的 TaskProposal;有权限时按 §11.2 直接创建(Guard 照常把关)。

AI 发现潜在 Task 但没有 auto-create authority 时,不能写 task_suggestions,因为 task_suggestions.task_id 必填,而此时还没有 Task。Current Target schema/runtime 已把 ai_task_decision_assessments 的 pre-write audit 扩展成 persisted TaskProposal lifecycle。ReviewItem 只是 Workbench 对它的展示,不再创建一个同义 domain object:

proposalId / storeId / contactPhone
proposedTaskMode / proposedTaskKind / proposedTitle / proposedNextAction
reason / evidenceRefs / creationPolicyVersion / reasonerRunId
status = pending_review / approved / dismissed / executed / expired
proposalVersion / createdAt / decidedAt / decidedBy / executedTaskId

员工批准 proposal 时,系统仍要重新验证最新 DNC、dedupe、evidence、Policy version 和 Task state。旧 assessment 曾经建议 create,不代表可以绕过 current guard。

Implementation boundary: 原 infrastructure PR #1900 已关闭,合并后的 #1908 在 main 中提供下列 backend contract:

  • GET /v3/tasks/task-proposals:按 authenticated Store scope 读取 review queue;
  • POST /v3/tasks/task-proposals/:proposalId/approve:以 server-owned OWNER actor/time 重新运行 Guard,再进入 unified canonical writer;
  • POST /v3/tasks/task-proposals/:proposalId/dismiss:store-scoped、version-fenced、idempotent dismiss;
  • approve 使用一个 PostgreSQL transaction 锁定 pending proposal、执行 Task mutation、验证 exact command receipt,再写 status=executed + executedTaskId
  • deterministic command identity 使 retry、并发双击和 commit-unknown recovery 不会产生第二个 Task;
  • real PostgreSQL tests 已覆盖 atomic approve/replay、concurrent double-click 和 transaction-time objective authority 变化时两边一起 rollback。

当前 routes 保守要求 OWNER;这不是完整 RBAC 的最终结论。#2024 已 merge owner automation settings 与 /tasks/proposals approval queue UI,但 source/UI merge 仍不能证明目标环境已部署,也不能证明 owner/employee browser UAT 已通过。

Legacy text-only rows 仍可能只包含已有 Task 的 Next Action 建议;§9.3 的 typed mutation fields 与 Target persistence path 已存在,新的 progress/close mutation proposal 可以按 §11.2 进入同一 Suggestion lifecycle。

Bounded loop 和 2-minute SMS batching 还需要两份可恢复的 logical state:一份记录每次 Reasoner run,一份记录每个 SMS thread 已经处理到哪里。

StoreMinimum fields / constraint
ai_task_reasoner_runsrunId, storeId, contactPhone, triggerType, triggerSourceKey, reasonerVersion, decisionPolicyVersion, status, startedAt/completedAt, turn/tool/token/cost counters, finalDisposition, proposal/assessment refs, executedCommandId, errorCode;unique (storeId, triggerType, triggerSourceKey, reasonerVersion)
sms_task_reasoner_cursorsPK (storeId, threadId)contactPhone, lastProcessedCursor, pendingHighWaterCursor, quietUntil, maxWaitAt, leaseRunId, leaseExpiresAt, version, updatedAt

这两类 store、SMS Neon consumer 和 bounded model adapter 已在 current source 中实现,并有 fake-model/unit 与 PostgreSQL production-seam coverage。Call/VoiceMail 使用 provider source-proof、custody 和 outbox stores,完成 exact source re-read、fresh Guard 与 atomic audit/review/receipt commit。Checked-in main 中 TEST 的 SMS 与 provider source gates 为 truepre/prod 对应 gates 为 false。这只是 source config,不是 deployment/runtime evidence;fake-model tests 也只证明 orchestration、atomicity、failure/replay 和 Guard contract,不证明真实模型 semantic accuracy。当前环境状态见 Task V3 Rollout Status

SMS worker 先用 CAS/lease claim 固定这次要处理的 high-water。只有 run audit/proposal 安全落库后,cursor 才能向前推进。

运行期间到达的新消息留给下一个窗口。失败时不推进 cursor;retry 继续命中同一个 run key。DNC 等 immediate Fast Lane 也要记录 source idempotency,避免后面的 quiet-window run 把同一事实再执行一次。


10. API 与 domain commands

这一节只回答两个问题:页面怎样读取已经整理好的 Task 信息,以及用户/AI 怎样请求系统改变 Task。前者用 typed read projection,后者用明确的 domain command。前端和模型都不直接拼数据库状态。

10.1 Read projection

前端和 AI 需要的是“这张卡现在该显示什么”,而不是一堆要自己 join 的表和 Timeline JSON。Backend 因此提供下面这些 typed projections:

ReadReturns
listWorkbenchTasks卡片所需 Task、Contact、displayType/businessProgress、work/queue/timing、Next Action、latest Activity、suggestion count
getTaskDetailTask snapshot、current progress + provenance、Outcome、Policy label/options、ownership、reason
getTaskTimelinetyped Activity/progress/status/plan/assignment/outcome events
getTaskPolicy当前合法 result、closure reasons、evidence requirements、UI metadata
getEvidencestore-scoped excerpt、source type/id、occurredAt、verification
listTaskProposals尚未创建 Task 的 AI proposal/review items;不能混入 task_suggestions

10.2 Commands

Command 是系统认可的业务动作,例如“改下一步”“记录进展”“关闭 Task”。UI 可以使用更自然的按钮文案,但真正改变状态时,必须落到下面某个 command;不能让页面直接改字段。

CommandPurposeHumanAI
createManagedTask建立 Policy-controlled TaskYesPolicy-gated
createFlexibleTask员工自由 TaskYesNo,only suggest
changeNextAction采用/修改当前计划YesPolicy-gated
recordActivity记录实际工作Yes仅 observed communication/authorized automation
recordActivityAndPlan一次保存 Activity + Next ActionYesPolicy-gated
recordBusinessProgress记录非终态业务进展,例如 intro_booked,并重算 display/FunnelYesexplicit evidence + Policy-gated
correctBusinessProgress纠正/失效 progress,保留旧 evidenceYespropose only
changeDeadline / changePriority / updateNote修改独立字段,不借 Update Task 写平行状态YesPolicy-gated/propose;人工 note 不可覆盖
editFlexibleTaskTitle修改员工自由 Task 的标题YesNo
assign / unassign负责人管理Yes默认 propose/guarded
closeTask写 Outcome 并关闭Yesevidence-gated allowlist
reopenTask纠正/继续处理Yes默认不自动
correctOutcome修正结果但保留旧历史Yespropose only
accept/edit/dismissSuggestion人工处理建议YesNo
recordSuggestionFeedback只记录 helpful/not relevant 等质量评价;不等于 adoptionYesNo
approve/dismissTaskProposal处理尚未创建 Task 的 proposal;approve 后重新过 create guardYesNo
setContactDoNotContact写 Contact restriction 并按每种 Policy 执行 close/block/reviewYesexplicit evidence Fast Lane only
clearContactDoNotContact特权纠正;不自动 reopen 旧 TaskPrivilegedNever

表里有两个容易混淆的动作:

  • accept/edit/dismissSuggestion 处理“员工是否采用或隐藏这条建议”。
  • recordSuggestionFeedback 只评价建议是否 helpful/not relevant,不改变 Task,也不等于 adoption。

Public API request 不能自行声明一个可信的 actor 或任意 storeId。Actor、tenant 和 allowed stores 必须由 authenticated server context 注入。

下面的 JSON 是 server normalization 后交给 Orchestrator 的 internal mutation command,不是客户端可以照抄的 public body:

{
  "commandId": "stable-idempotency-key",
  "taskId": "uuid",
  "expectedVersion": 7,
  "actorFromAuthContext": {
    "type": "staff",
    "id": "staff-uuid"
  },
  "evidenceRefs": [
    {
      "type": "ringcentral_message",
      "id": "store-scoped-id",
      "storeId": "server-validated-store-uuid"
    }
  ]
}

Create 和 mutation 面对的并发问题不同:Create 要防止同一个目标被重复建立;mutation 要防止在旧 Task version 上覆盖新修改。因此它们的 concurrency/idempotency contract 也不同:

createManagedTask
  public: source/evidence + desired kind/plan
  internal: commandId + dedupeKey + expectedAbsent=true + actor/auth scope

changeNextAction / recordActivity / closeTask / reopenTask
  public: taskId + expectedVersion + command payload
  internal: commandId + taskId + expectedVersion + actor/auth scope

每次执行 command,Server 都必须按同一顺序把关:

  1. 从 auth context 解析允许的 stores。
  2. 验证 Task/Contact/Evidence 均属于同一 store_id
  3. 验证 expected version、status transition、Policy version 和 field-level authority。
  4. 用 idempotency key 防重。
  5. 在一个 transaction 内更新 snapshot、追加 Timeline;AI run 才写 AI audit,只有用户明确评价时才写 suggestion feedback。
  6. 返回 typed projection 与 deterministic error code。

校验失败时返回稳定、可判断的 error code,而不是一段需要猜测的自然语言。核心错误示例:

TASK_NOT_FOUND
STORE_SCOPE_MISMATCH
STALE_TASK_VERSION
INVALID_TASK_TRANSITION
OUTCOME_NOT_ALLOWED_FOR_TASK_KIND
INSUFFICIENT_OUTCOME_EVIDENCE
PROGRESS_NOT_ALLOWED_FOR_TASK_KIND
INSUFFICIENT_PROGRESS_EVIDENCE
HUMAN_AUTHORITY_CONFLICT
IDEMPOTENCY_PAYLOAD_MISMATCH

10.3 高频 atomic command

员工最常见的流程是:刚完成一次联系,马上记录结果,再安排下一步。UI 应让这两件事一次保存;backend 则在一个 transaction 中执行对应的 Activity 和 Next Action changes:

{
  "activity": {
    "channel": "phone",
    "direction": "outbound",
    "action": "call_completed",
    "outcome": "no_answer",
    "occurredAt": "2026-07-15T14:05:00Z"
  },
  "nextAction": {
    "text": "Send SMS, then call tomorrow morning",
    "at": "2026-07-16T14:00:00Z",
    "reasonCode": "activity_follow_up"
  }
}

对员工来说,这就是一次“保存结果和下一步”,而不是先 Log Activity、再 Update Task、再 Schedule 的三次后台操作。

员工确认线下 booking 时也是一样:一次提交 progress + plan。Public request 不允许自报 verificationType;server 根据 auth context 和同一 transaction 内生成的 staff_assertion 解析 verification:

{
  "progress": {
    "code": "intro_booked",
    "occurredAt": "2026-07-15T18:30:00Z",
    "staffConfirmationNote": "Customer booked in person for Tuesday 6 PM"
  },
  "nextAction": {
    "text": "Confirm attendance plan and discuss membership options",
    "at": "2026-07-16T14:00:00Z",
    "reasonCode": "staff_planned"
  }
}

11. AI authority

这一节必须分三层读:

  1. Authority:AI 有没有资格提出或执行这个动作。
  2. Suggestion lifecycle:暂时不能执行的 proposal 怎样安全保存、失效和审计。
  3. Employee interaction:员工看到什么文案和按钮。

三层使用同一套 domain command,但回答的问题不同。Backend 的 version/TTL 不能直接变成员工界面;员工点了什么,也不能绕过 backend Guard。

Current implementation 在 Store 维度下按 objective 拆开两个 rollout decisions:createLane 决定新 Task 由 legacy、Target 还是 paused lane 创建;semanticAutomationMode 决定 legacy/shadow/target/paused。Existing row 的 writer ownership 固定在 Task row 上,修改 authority 不能偷换旧 row 的 owner。每次 advance、rollback、kill switch 或 resume legacy 都使用 revision/CAS 并写 append-only audit。#1982 已使全部七类 canonical Managed objective 具备 Target/Shadow authority capability;#1993 之后,新 Store 会 seed 为 target/target,而真正缺少 authority row 时的兼容解析仍 fail safe 到 legacy/legacy。任何已存在 Store 的 persisted authority 与环境启用状态都必须从 rollout status 和 runtime evidence 核验,不能从 control-plane capability 反推。

11.1 核心原则

“这个 field AI 能不能改”这个问题太粗。系统每次都要结合谁在操作、做什么、依据什么规则和证据、Task 是否已经变化,以及员工是否刚做过决定:

actor
  + operation
  + task policy
  + evidence type
  + current task version
  + human lock/last edit
  = authority decision

11.2 Authority matrix

11.2.1 谁能做什么

下表先只回答 authority:AI 可以直接做、只能 propose,还是完全不能做;员工是否可以做;Guard 需要检查什么。

Object / operationAIStaffGuard
Create Managed Task可按 creation policy 自动创建可基于线上/线下 context 创建evidence、dedupe、store、idempotency
Create Flexible Task只能建议可以staff identity
Set Next Action可在 allowlist 内设置,其他只建议可以修改/清空stale version、human-wins
Record RingCentral Activity可把系统观察到的沟通结构化关联可补充/纠正source exists、idempotency
Record offline Activity不得伪造可以actor/staff attribution
Record business progress仅 explicit observed evidence + progress Policy allowlist;否则 propose可以确认线下 progressprogress code/evidence/version;不自动 close
Correct business progress只能 propose human review可以append-only supersession,重算 display/Funnel
Close Managed Task仅 Policy allowlist + sufficient evidence可以result/reason/evidence contract
Close Flexible Task只能 propose可以staff confirmation
Correct Outcome / reopen默认 propose human review可以append-only audit
DNC明确沟通可走 dedicated Fast Lane 设置;永远不能清除可以按权限设置;清除需 privileged不可被普通 Task mutation删除;Policy 决定 close/block/review
Wrong number可记录 observed Activity 并 propose identity review可以纠正 identity不可跨店自动 merge
Assignee / staff credit默认建议,不静默改变可以按权限改same-store FK、audit
Raw transcript/message永远不能修改也只能通过原系统更正流程immutable source evidence

本文只定义 domain capability,不定义门店的完整 RBAC。具体 front_desk / sales / manager / owner → capability 由 auth/RBAC spec 管理。

因此,表里的 “Staff” 不表示每一个员工账号都拥有全部权限。至少 DNC removal、Outcome correction、reopen、跨员工 credit 修改和 revenue-value override 应属于 privileged capability。

11.2.2 员工决定优先

Human-wins rules:

  • AI run 启动后发生的 staff edit,使旧 AI proposal stale。
  • Staff-confirmed progress/Outcome 或人工 lock 不能被后续 AI inference 覆盖。
  • AI auto-close 必须可见 evidence,员工可以 correct/reopen。
  • correction 产生新 event,不删除旧 AI decision。

这些规则的共同含义是:AI 可以根据新 evidence 继续判断,但不能静默覆盖更新的人工事实或决定。

11.2.3 Guard 怎样处理 AI proposal

Guard 对每个 AI proposal 只做三种裁决,而且每一种都写 audit:

裁决条件去向
auto_executepolicy、evidence、authority 全部满足直接执行
needs_staff_confirmation建议本身合理,但按当前规则 AI 不能直接执行Task 已存在:保存为这条 Task 的 Suggestion;员工点击具体动作后,系统以员工身份执行。Task 不存在:走 §9.6 的 TaskProposal
rejected类型 / 词表 / evidence / scope / version 校验失败留 reason + evidence 痕迹,绝不静默丢弃

这里不再另造第三种“AI 审批单”。如果 Task 已经存在,而 AI 只能建议、不能直接执行,系统就把建议保存到这条 Task 下。员工看到的是具体动作,不是一张抽象的审批单。如果 Task 还不存在,系统才使用 §9.6 的 TaskProposal。

11.2.4 Backend 怎样保存和淘汰 mutation Suggestion

这一段只讲 backend lifecycle,不规定 UI 长什么样。

要让 needs_staff_confirmation 真正可执行,Suggestion 不能只有一句自然语言。Legacy row 可能只有 suggestedActionText;Current Target schema/persistence 已为 mutation 类降级提案补齐下面四项,新的 writer 缺一不可:

  • proposedCommand + payload:Guard 已验证的 typed command(如 closeTask(resultCode, closureReasonCode, evidenceRefs));员工采纳时原样以员工身份执行,不重新手工填写。
  • generatedAgainstTaskVersion:记录生成时的 aggregateVersion。如果 Task 后来被人工修改,API 把提案投影为 stale,执行层 Guard 必须拒绝。
  • expiresAt:记录有效期。到期后投影为 expired 并退出 active UI。TaskProposal 已有 expired(§9.6);Suggestion 也需要,否则无人处理的建议会无限期停留。
  • 失效规则:staff 对同一 Task 做 mutation、close 或 reopen 时,相关 active suggestions 必须在同一 transaction 内进入 superseded,并记录 reason。Reopen 不得让上一个 open period 的旧建议复活。

11.2.5 员工看到什么

这一段只讲 interaction semantics。generatedAgainstTaskVersionexpiresAtsuperseded 等 backend 字段不直接展示给员工。

UI 按 effect 使用业务语言,不平铺通用 Accept/Edit/Dismiss 三按钮:

  • 低风险、可逆的 mutation 显示一个具体的一键动作,例如「设下一步:明早 10 点」或「Confirm booking」。
  • Close 等 terminal 变更显示「Review outcome」。它打开预填好的标准关单流程,让员工检查后再确认;不能一键关闭。
  • 能直接执行的建议显示具体动作按钮。员工点击动作本身,就代表采纳这条 Suggestion。

UI 不显示通用 Accept 按钮,但 domain 命令层仍保留 accept/edit/dismissSuggestion 语义。

“执行本身即采纳”适用于完整 mutation contract,不只适用于 Next Action 和 Activity。所有 mutation command(changeNextAction / recordActivity / recordActivityAndPlan / recordBusinessProgress / closeTask,以及 TaskProposal 的 approve)都接受可选 sourceSuggestionId

Server 必须在同一个 transaction 中完成三件事:执行 mutation、追加 task.suggestion_decided event、把对应 Suggestion 投影为 accepted。任何 mutation 类型都不允许出现“动作已经执行,但建议仍挂在 active”的状态。


12. 从 one-shot inference 到 bounded reasoning loop

12.1 Current

Current source 中只保留一个 AI semantic Task decider:Router / Reasoner → Guard → writer。#1913 已移除 Contacts Analyzer 的 Task prompt/output/writer authority;Contacts Analyzer 现在做 profile-only one-shot analysis,可以读 Task context 作为 background,但 output contract 明确禁止 taskDecisionsvisibleTaskAssessmentsnewObjectiveAssessments,不再生成 AI-driven Task mutation。

旧 Task V2 semantic path 保留在下面仅用于理解 retirement 对比,不是 current decider:

prefetch recent calls + messages + leads + Task snapshots + suggestions
  → build one prompt
  → one structured generateObject/invokeAI call
  → typed proposal
  → Orchestrator writes

从业务推理的角度看,历史 pipeline 只有一次 logical inference;retry 或 schema repair 只是重复同一次生成,不会让模型根据缺失信息主动再查一次,因此不等于 reasoning loop。当时的能力边界是:

  • 看的是近期 call summary/evidence snippet,不是完整 raw transcript。
  • 看 Task snapshot、active suggestion 和有限 closed history。
  • 没有读取 contact_timeline / Task Activity history 的 tool。
  • 不能在推理中按需反复读取 Call、SMS、Lead 或 Task Timeline。

AI decider 已退役不等于 legacy contract 全部删除。applyTaskAction 稳定 compatibility facade、Contacts Analyzer cron 中的 deterministic engaged-lead backstop、pure-legacy Task readers/blockers,以及 API/Web 的 legacy Timeline projection 仍在 source 中。它们用于兼容、安全不变量和历史展示,不是第二个 AI Task 大脑。

Target path 已实现本章的 bounded runtime,而不是只存在于设计中:

  • default two_turn adapter 最多两次 logical turns,read tools 只读且由 server 固定 scope;opt-in native-tool mode 也必须保持明确轮次/读取预算。Tests 可使用 fake model,production adapter 才调用真实 model。
  • SMS consumer 完成 claim → exact Neon source re-read → semantic evidence → model → fresh Task/DNC/authority Guard → atomic audit/review/auto-execution/cursor commit
  • Call/VoiceMail runtime 通过 source-proof + content-addressed artifact pointer + transactional outbox 绑定 exact provider event;#1908 中的新 Call analysis 会把同一次 classify 产生的 Task claims 连同 model/prompt/schema provenance 保存为 task_semantics,consumer 重新读取 exact artifact 并校验后直接复用,不再重读 transcript 做独立 semantic inference。
  • 没有统一 task_semantics 的历史 Call artifact 和其他兼容来源暂时回退到 standalone semantic resolver;这条 fallback 尚有 terminal Outcome 误判风险,不能当成目标主路径。
  • Email Lead create/reuse、canonical Call/SMS Activity 和 exact STOP/DNC 走 deterministic Fast Lane,不为了“用 AI”而调用模型。
  • Auto-execution 必须同时通过 Task Policy、resolved evidence、automation tier、per-objective authority 和 fresh Guard。full_auto 的 terminal/correction/reopen execution seam 已在 source 中,但 capability 存在不表示任何环境已启用或通过质量验收。

Checked-in main 中 TEST Task V3 source gates 为 truepre/prodfalse。这不是 deployment、UAT 或 PROD release evidence;人工标注/live eval 已开始,但 quality gate 未通过。当前状态和剩余退役面见 Task V3 Rollout Status

12.2 Target:它是一个有终点的 loop

Target 不把 Reasoner 变成一个长期运行的自主 agent。它只把一次 one-shot inference 扩展成一个有明确入口、有限读取次数和明确出口的 bounded loop:先判断现有 context 是否足够;只有缺少会影响决定的关键 evidence 时,才允许补读一次,然后必须给出 final proposal、转人工或明确 no-op。

图中的每一次 run 都必须先固定以下边界:

  • 明确 trigger。
  • 固定 store/contact scope。
  • 最大 model turns、read tool calls、tokens、timeout 和 cost budget。
  • 明确 final proposal,或者明确返回 requestHumanReview / no_op
  • 失败时绝不错误关闭 Task。

因此,模型不能自行决定什么时候再次运行。Trigger 必须由 deterministic 规则产生,尤其要防止 Orchestrator 刚写出的 Task event 又反过来唤醒 Reasoner,形成自触发循环:

TriggerDefault laneDedupe / debounceSelf-trigger rule
Email Lead ingestedFast Lane 建立确定性 lead_conversion;初始 displayType=lead_outreach,必要时 Reasoner 生成个性化建议按 lead source ID同一 lead/task create event 不重跑
Call analysis finalizedReasoner每个 storeId + callId + analysisVersion 一次本 run 产生的 Task events 只更新 projection
SMS thread/burst settledReasoner按 thread cursor;短 debounce window 由 config 定义新 external message 才推进 cursor;Task writes 不触发
Staff Activity recordedFast Lane 更新 projection;只有明确请求建议或新语义 evidence 才 ReasonercommandId/sourceInteractionKeyAI 建议/plan event 不反向触发 Activity analysis
Staff Outcome corrected/reopenedAudit/eval + 可选 human-requested Reasonercorrection event ID默认不允许 AI 立即重新关闭
Scheduled review timerFast Lane eligibility check;material uncertainty 才 ReasonertaskId + expectedVersion + reviewAt有更新 Activity/Outcome 后旧 timer no-op
task.* event from OrchestratorProjection onlycommandId + event ordinal永不直接作为新 Reasoner trigger

每次 run 都保存 triggerType + triggerSourceKey + reasonerVersion 唯一键。同一 payload 被 replay 时返回既有 decision,不再重复推理或 mutation。只有新的外部 Call/SMS/Lead,或人工明确提出的新请求,才可以开始新的 run。

SMS 也不能采用“一条消息调用一次 LLM”的模型。每条 message 仍然立即、deterministic 地持久化并更新 UI;Reasoner 调用则按 storeId + threadId 聚合成 conversation window:

  1. 保存 lastProcessedMessageCursor 与当前 high-water mark。
  2. 新消息重置 quiet-window timer;首版默认 2 分钟,具体值进 runtime config。
  3. quiet window 到期后,一次 run 只读取 cursor 之后的新 messages + compact Task context。
  4. 持续聊天首版设 5 分钟 max-wait,达到上限时对当前窗口运行,避免无限等待。
  5. run 成功/audit 落库后原子推进 cursor;失败按同一 trigger key retry,不重复 mutation。
  6. run 期间又到达的新消息留给下一个 window,不重读完整 thread。

普通 message logging、delivery status 和 UI Timeline 都不需要 LLM。只有 settled conversation window 才可能进入 Reasoner;DNC、威胁、安全/合规等少数信号直接走 immediate Fast Lane,不等待 2 分钟。这样,成本随“有意义的 conversation turns”增长,而不是随 SMS row 数量线性增长。

quiet/max-wait 的最终数值属于 rollout config,不能写死在数据库或 prompt。上线后必须根据真实 thread 分布以及延迟/成本指标校准。

每个 run 还要有初始资源预算。下面是 Recommended initial budget,属于 Target rollout default,不是永久常数

  • routine case:一次 structured inference。
  • 缺关键 context:最多一次 read-tool round、最多两个只读 tool calls。
  • 高风险或冲突:不继续烧 token,转 human review。
  • timeout/tool failure:保留现有 state,可创建 review suggestion,不执行 terminal mutation。

为了让这个上限由程序保证,Current Target adapter 使用两段式 structured generation,而不是依赖 vendor tool-calling loop:

  • 第 1 次调用的 output schema 允许两种顶层结果:final proposals,或 need_more(请求只能从已批准的 read tools 中选)。
  • 返回 need_more 时,runner 确定性取数(不调模型)拼进 context;第 2 次调用的 schema 只允许 final,必须收敛。
  • 「最多两次」指 logical reasoning turns,由 schema 结构保证,不是只写在 prompt 里的约定。schema validation repair、provider retry、transport retry 产生的 physical attempts 属于另一个维度(§12.1 已区分 Current 的两种计数)。run metrics 必须分别记录 logicalTurnCountphysicalAttemptCount / schemaRepairCount / retryCount。这样实现不依赖 provider,每一步都可 mock,也能分别记录 cost。

Target Reasoner 只负责 Task 决策,不负责 Contact profile enrichment。这里的“统一”发生在 transcript understanding 层:现有 Call classify 同一次输出 Call taxonomy/summary 与 evidence-backed Task claims;Reasoner 消费这些已确认事实,但不重新生成 customer profile,也不重新解释 raw transcript。这样省掉重复 semantic call,同时保持 profile/Call analysis 与 Task mutation authority 的边界。

Event Router 在 Reasoner 之前先完成 deterministic narrowing。一条新 evidence 到达后,纯代码先算出它属于哪个 Contact、可能影响哪些 open Tasks、需要检查哪些候选 Policies。每次 run 只预载候选 Policy,以及相关 open Task 的 Policy,从源头控制 context 大小。

如果通信与 Task 的关系没有歧义,例如员工外呼命中该 Contact 唯一的 open Task,Router 直接把它机械地记录成 Activity,不调用模型。

12.3 Read tools

Reasoner 发现关键 evidence 不足时,也不能自由查询系统。它只能调用以下 server-bound、typed、read-only tools:

readContactSnapshot
listRecentCalls
readCallEvidence
listRecentMessages
readMessageEvidence
listLeadRecords
listTasks(status, since, taskKind)
readTaskTimeline
readTaskPolicy

每个 read tool 都受同一组安全边界约束:

  • storeId 和 Contact identity 由 server run context 注入,不让模型任意指定。
  • tool 内部强制 store scope、row limit、time window 和 field allowlist。
  • 默认先返回 summary/index,只有需要时读 evidence excerpt。
  • 一个 run 内以 tool + ref 形成 read ledger key,同一请求最多执行一次。
  • 已在 Turn 1 装入的 exact source text、bounded Task list 与 Policy catalog 不重复读取;server 返回 typed rejection,并把 ledger 和可用的 E-ref/T-ref 清单交给 final turn。
  • 不提供 raw SQL、不提供任意 database write tool、不把 secrets 放入 model context。
  • 每个 tool call 记录 tool name、validated args、latency、rows/bytes、result hash 和 cost trace。
  • prompt 和 proposal 使用两类短引用:E{n} 表示本次 run 已装入的 evidence,T{n} 表示本次 run 已装入的 Task;server 保存它们与真实 ID 的映射。模型不抄写 UUID,也不能引用清单外的 E-ref/T-ref;未知引用在 model-output boundary 被拒绝或降级,不会进入 writer。

读取完成后,AI 只能提出下面这些 model-facing commands:

proposeTask
createAllowedTask
proposeNextAction
setAllowedNextAction
recordObservedActivity
proposeBusinessProgress
recordAllowedBusinessProgress
proposeClose
closeAllowedTask
requestHumanReview
noOp

这些名称只描述模型可以提出什么。最终 mutation 仍然必须由 Policy Guard + Task Orchestrator 执行,模型本身没有 write authority。

model-facing tool names 也不能发展成第二套 domain vocabulary。每个 proposal 必须固定映射到现有 domain command 或审计结果:

Model tool/proposalDomain command or result
proposeTask写 persisted TaskProposal/assessment;Task 尚不存在,不能写 task_suggestions
createAllowedTaskcreateManagedTask;Flexible 永不由 AI 调用
proposeNextAction写 Suggestion
setAllowedNextActionchangeNextAction
recordObservedActivityrecordActivity;source 必须由 server 验证
proposeBusinessProgress写 assessment/review proposal;不改 Task current progress
recordAllowedBusinessProgressrecordBusinessProgress;仅 explicit evidence + Progress Decision Matrix + rollout allowlist
proposeClose写 assessment/Suggestion;不关 Task
closeAllowedTaskcloseTask;只接受 Outcome Decision Matrix + rollout allowlist
requestHumanReview创建 review projection/Suggestion,不做 terminal mutation
noOp只写 run audit

因此,Task Reasoner 的 “skill” 只包装这些 read tools、proposal schemas 和 decision instructions。它不能绕过 Orchestrator,也不能获得 raw database、shell 或任意 network access。

12.4 Streaming、reasoning 与 harness 的关系

这里的 bounded reasoning 与输出是否 streaming 没有直接关系:

  • Streaming 只影响用户是否逐字看到模型输出,不决定 AI 能不能调用 tools 或循环推理。
  • 背景 Task Reasoner 不需要 streaming;未来的 interactive copilot 可以为了交互体感使用。
  • 实现上可以借鉴 Grok Build/coding harness 的 typed tools、permission boundary、trace、bounded loop 和 recovery 思路。
  • 不需要把 coding harness 整套搬进产品,也不能让模型获得 shell/SQL 能力。这里需要的是一个范围很窄、边界清楚的 Task runtime。

12.5 Cost control

Reasoner 的成本由 event volume、model turns、input context、tool result 和 output tokens 共同决定。应按下面的顺序控制,而不是先换更便宜的模型、再让所有 event 都调用它:

  1. Deterministic Fast Lane 不调用 LLM。
  2. 事件去重、debounce 同一 call/SMS burst。
  3. 先传 compact context,不把完整 customer history 每次全塞进去。
  4. 只有 material uncertainty 才调用 read tool。
  5. 高风险冲突转人工,不靠无限推理“猜准”。
  6. 按 Task kind 监控每次 run cost、auto-action rate、correction rate 和 incremental outcome value。

最终目标不是让每个 event 都运行一个更贵的大 agent,而是只在额外 reasoning 可能改变决策时支付这部分成本。


13. Workbench / UI projection contract

这一节只规定员工必须看懂什么、能够做什么,不规定具体像素或 visual layout。Domain 负责保存完整事实和审计信息;Workbench 只投影员工当前需要的部分。Figma 应引用这份 contract,不能反过来用某个 mock 定义 domain。

员工处理一张 Task 的日常路径可以按五步检查:

  1. 扫描队列,选出现在该处理的 Task。
  2. 看懂这件事为什么存在、下一步是什么、什么时候要做。
  3. 执行工作,例如 Call、Text 或线下处理。
  4. 记录结果,并在同一次操作里安排新的 Next Action。
  5. 只有发生业务进展或最终结果时,才确认 progress、close、correct 或 reopen。

UI 的信息顺序和按钮数量都应该服务这五步。Backend 有一个 field 或 lifecycle,并不代表前端必须为它单独画一个 section。

13.1 一张 Task card 必须回答

Task card 服务前两步:让员工不用打开 detail 就知道“这是谁、为什么现在要处理、下一步做什么、是否紧急”。例如:

Sarah Liddle · Lead

LEAD OUTREACH
Task goal: Reach a clear sales outcome
Why: New email lead requested information at 10:02

Next action: Call now
[Needs attention] [Not started] [High priority]
First-response SLA: 3 min remaining
Assignee: Unassigned

Latest activity: None
AI suggestion(话术类): Start by asking what brought her in

[Call] [Text] [Record activity] [Update next action] [Close]

员工不需要手动维护 Task status、work state、queue state、Funnel stage 四套平行状态。页面必须根据 Task、Timeline 和 Policy 自动计算这些 projection。

Lead Workbench 仍要让员工第一眼区分当前工作,不能只显示 backend 的长期 lead_conversion kind:

Lead
├── Lead outreach (12)
├── Lead follow-up (31)
└── Intro booked · conversion pending (8)

同一个 Task 的 card label 会随事实变化。下面三个 label 都是 displayType,不是三个 Tasks,也不会启动三套 lifecycle:

Sarah Liddle · New Lead
LEAD OUTREACH
Call now · First-response SLA 还剩 3 分钟

第一次 no-answer Activity 后:

Sarah Liddle · Lead
LEAD FOLLOW-UP
Text tomorrow 10:00 · 1 attempt

确认 Intro 已预约后:

Sarah Liddle · Lead
INTRO BOOKED · CONVERSION PENDING
Confirm attendance plan and discuss the right membership option

左侧 Lead / Member Care / Revenue Growth workstream、queue tabs 和 filters 可以保留。Lead Outreach / Lead Follow-up / Intro Booked · Conversion Pending 是可筛选的 derived/materialized displayType。它们解决员工扫描、优先级和 reporting 问题,但不会重新创建三个 backend lifecycles。

排序也要跟当前工作一致:lead_outreach 可以由 first-response SLA 排到更前;另外两类仍按 nextActionAt / deadlineAt / priority 排序。

13.2 Detail 必须回答

Detail pane 服务后三步:员工在这里执行工作、记录结果,并在必要时确认进展或关单。它必须回答:

  • 这是什么 Task,为什么现在存在。
  • 当前已经采用的 Next Action 是什么,什么时候处理。
  • AI Suggestion 是什么;如果需要员工动作,入口必须按 effect 使用业务语言,例如采用计划、确认事实或隐藏,不能平铺通用 Accept/Edit/Dismiss 三按钮。
  • Activity Timeline 中是谁、何时、通过什么 channel、发生了什么。
  • Customer summary 与 Contact Profile 依据哪些 evidence/provenance;必须明确它们是 evidence-assembled、AI-enriched operational snapshot,不能冒充 CRM truth。
  • 员工在哪里补充 Note 与线下 context。
  • 当前 assignee、first-response SLA 和 deadline。
  • 当前 taskKind Policy 允许哪些 Close options。
  • Outcome 的 verification、evidence、AI auto-close reason,以及怎样 correction/reopen。

13.2.1 Suggestion 的交互按 effect 决定

Suggestion 在展示前就已经存在,不需要员工先批准才能看。员工可以直接参考,也可以什么都不做。UI 先判断建议会产生什么 effect,再决定是否需要按钮:

Suggestion effect员工看到什么员工操作的含义Domain 结果
只提供 coaching、话术或判断参考,不执行系统 mutation直接展示建议内容;可以忽略、隐藏,也可以给 suggestion 级 thumbs up/down忽略不产生任何决定;隐藏表示员工主动移出工作面;thumbs 只评价质量无 Task state 变化;隐藏走 dismissSuggestion,质量反馈走 recordSuggestionFeedback
可以立即执行的真实动作或低风险、可逆 mutation一个具体按钮,例如「Review & send」或「设下一步:明早 10 点」点击动作本身就代表采纳,不再先点一次 Accept执行动作或 mutation,并用 sourceSuggestionId 把 Suggestion 投影为 accepted
需要员工补录系统看不到的线下事实业务语言按钮,例如「Record booking」或「Confirm booking」员工是在确认 evidence,不是在审批 AI写入 typed Business Progress/Activity;是否改变 Task 由 Policy 决定
Close 等 terminal 变更「Review outcome」,打开已预填的标准关单流程员工检查 result、reason 和 evidence 后确认确认后执行 closeTask;取消则不改变 Task
AI 已按 authority 自动执行已发生的结果、「set by AI」标识、evidence 和 correction/reopen 入口不需要事后 Accept;员工只在事实不对时纠正保留原 AI decision audit,纠正时追加新 event

Domain 命令仍保留 accept/edit/dismissSuggestion,但它们是 adoption lifecycle,不是必须逐个展示的通用按钮。recordSuggestionFeedback 回答的是建议是否 helpful/not relevant,也不等于 accepted、edited 或 dismissed。完整 backend lifecycle 见 §6.3 和 §11.2。

13.2.2 Detail 的信息层级

这份 contract 不固定 pane 必须画成几个 box,但默认工作面应优先放高频动作:

  1. 行动区:当前 Next Action、处理时间、关键 evidence,以及 Call/Text 和当前可执行的具体 Suggestion action。
  2. 记录区:一次完成 Activity + Next Action,并提供必要的 typed progress 入口。
  3. Task Timeline:默认留在主工作面,让员工一眼扫到之前做过什么。每条记录保持紧凑,并按时间连续显示;很久以前的记录可以用 Load older 继续加载,但不要把整个 Timeline 折叠起来或藏到次级 tab。(这是用户研究拍板的决定:门店反馈 Task Timeline 和 History 是高使用率功能——把信息整理好、方向给明确,员工自己会用,不需要替他们藏信息。)

Task reason、Customer summary、完整 evidence/provenance、Policy version、历史建议和教学说明仍然可访问,但不需要各自常驻成一个大 section。日常工作面不应重复展示同一个事实,也不应把 schema 中的每个概念直接变成 UI box。

13.3 对当前 UI 的审查结果

下面把 Current Workbench 中已经有效的部分,与需要接入新 domain contract 的部分分开:

DecisionCurrent elementTarget handling
Preservesplit Workbench、queue tabs、search/filter/sort、Contact grouping骨架适合高频处理,无需推倒
PreserveCall / Text、assignee、History/Timeline、Notes、evidence containers接到新的 command/projection contract
RefactorLead Outreach / Lead Follow-up / Booked Not Converted card typebackend 合并为 long-lived lead_conversion Task;前端保留三个 displayType,变化的是 current work context 与 Next Action
RefactorAction Guidance上半明确 adopted Next Action;AI guidance 单独作为 Suggestion
RefactorAttempted / Not attempted badge作为 derived work state,不是 lifecycle
Refactordeadline/timing分开 first-response SLA、Next Action time、Task deadline
RefactorUpdate task聚焦 assignee、priority、Next Action、deadline、note,并允许记录 typed progress(如 Intro booked/cancelled);不让用户直接改抽象平行状态
RefactorClose task dialog按 Policy 展示 result + reason + verification/evidence
RefactorFunnel/Profile labels展示 provenance,例如 AI-inferred / communication-confirmed / staff-confirmed
Remove全局 Positive/Negative/Neutral close pickervalence 由 Policy 派生
Remove固定编号 SOP / rigid StepsAI 给下一步建议,不强迫用户维护流程
Prevent任何 provider/CRM truth 暗示保留并强化 Current guidance:Profile 是 evidence-assembled/AI-enriched,Outcome 显示真实 verification
AddCreate task员工可创建 Managed 或 Flexible Task
AddRecord lead progress员工可确认/纠正 Intro booked 等线下事实;不必 Close Task
AddRecord activity + plan next action一次完成高频操作
AddSuggestion 决策入口(按 effect 命名)让 AI 帮忙但不混淆 proposal 与 state;能直接执行的建议渲染成具体动作按钮,执行即采纳
AddOutcome evidence/correction可看 AI 为什么关闭,也能人工纠正/reopen

Current Target detail 已接入 Record booking(Task 保持 open)、AI booking correction、Confirm converted 和 reopen,并通过 Target commands 写 snapshot + Timeline。Task V2 仍走现有 compatibility interaction;页面必须根据 Target contract availability 显式选择 lane,不能把 partial Target row 当成完整 Target truth。

Current 前端已经有 task-create.vue,但 active Workbench chain 没有引用它。Target Staff create API/domain command 已有基础;正式 UI entry 仍需接线和 cutover 验证,不能继续把组件存在误写成用户已经可以创建。

13.4 员工体验底线

无论最后采用哪一种 visual layout,以下底线都不能退让:

  • RingCentral 中已经发生的 Call/SMS 应尽量自动记录,不能要求员工重复录入。
  • 系统看不到的线下动作必须有快速人工入口。
  • 高频流程必须能在一次常用操作中完成 Activity + Next Action
  • AI 自动执行的事情必须可见、可解释、可纠正。
  • flexible work 不应要求员工先配置一个新的系统 type。
  • UI 应使用 “Lead outreach / Call now / Intro booked / Converted” 这样的业务语言,不能把 aggregateVersionverificationType 等内部字段直接扔给员工。
  • 同一个事实在主工作面只表达一次;provenance、Policy details 和教学说明应按需展开,不能挤占高频操作空间。

14. Funnel 与 Revenue projection

本节描述 Target 最终能力:怎样从证据计算 Funnel,以及怎样诚实说明业务贡献。首版不会一次实现完整 attribution 报表;MVP 首发范围见 §16.5。

14.1 Funnel 和 Task status 回答的是两件事

Funnel 回答“客户走到了哪一步”,Task lifecycle 回答“员工还要不要继续处理这件事”。不要把两者压进同一个 status。

Dimension它回答的问题
Task lifecycle这件工作还要不要继续处理?
Next Action / queue员工现在或以后要做什么?
Activity谁已经做过什么?
Outcome这件 Task 最终怎么样?
Funnel Projection现有 evidence 能证明这个 Lead 走到了哪里?

一条 evidence 可以同时影响 Task 和 Funnel。例如,一条明确的 booking SMS 可以更新 Funnel,也可以改变 Task 的 displayType 和 Next Action;但 Task 与 Funnel 必须各自计算,不能共用一套 status。

Current 的 contacts.lead_status 也不能继续充当“万能状态”。它目前是 AI 写入的 12-value profile snapshot:

new / attempted / connected / booked / showed / trialed / converted /
unreachable / lost_contact / neglected / bad_timing / not_interested

这个字段把三类不同信息混在了一起:客户进展、员工工作状态,以及停止处理的原因。Current prompt 已经把它放在 CONTACT BACKGROUND 中,并明确它不是决定 Task 是否创建或关闭的输入。Target 按下面的规则处理:

  • lead_status 不得用于创建、关闭或跳过 Task。
  • Target Funnel 只根据 typed evidence、Activities、Business Progress 和 Outcomes 计算。旧值最多显示为 AI-inferred profile summary
  • new / attempted / connected 要根据 intake 和 Activity evidence 重新计算。
  • booked / showed / trialed / converted 只有在各自所需的 verification evidence 存在时,才进入 Funnel。
  • unreachable / lost_contact / neglected / bad_timing / not_interested 要分别归到 work state、Outcome 或 reason,不能继续假装是 customer journey stage。
  • 等 Target Funnel 的 dual projection 稳定后,新功能应停止读取 lead_status。是否停写旧值要单独做 migration 决策;历史值绝不能一对一回填成 Task Outcome。

Current 的 contacts.action_neededcontacts.suggested_actions 也只保留兼容用途:前者由“同店是否有 open Task”确定性回写,后者目前已清空。它们不是第二套 Task 或 Suggestion source of truth。Target Workbench 只读取 Task 和 Task Suggestion projection;到了 Contract phase,再停止写入或移除这些 contact-level compatibility fields。

14.2 MVP 不建 Milestone table,也能算出可信 Funnel

MVP 可用的 Funnel 输入只有系统确实拥有的事实:

  • Email Lead received fact。
  • RingCentral Call/SMS observed facts 和 communication evidence。
  • Task Activities。
  • Task Business Progress。
  • Task Outcomes。
  • Staff confirmation。

用这些输入可以得到下面的 projection。它只是可能的客户路径,不要求每个 Lead 逐级经过:

Received
  → Outreach attempted
  → Connected
  → Booked
  → Converted

也允许:Received / Attempted / Connected → Converted

每一层的含义如下:

  • Received:Email Lead intake,或者 Call/SMS 中出现明确 lead signal 后建立的 lead_conversion evidence chain。Current Funnel 的两条 intake lane 都必须保留;cutover 后不能只剩 Email。
  • Attempted:已经发生 qualifying outbound Activity。
  • Connected:出现 connected Call 或 inbound reply。
  • Bookedlead_conversion 上存在仍然有效、并且有 completed booking evidence 支撑的 progress。Task 继续保持 open,同时投影 displayType=booked_not_converted
  • Convertedlead_conversion.resultCode=converted 的 terminal Outcome,并同时显示 verification。客户不需要先走 Intro/Booked,也可以直接 Converted。

系统如果不知道 attendance 或 membership 的真实状态,就不能因为预约时间已经过去而自动把 Booked 改成 Attended 或 Converted。员工确认或明确 communication 可以形成 attribution evidence,但仍不能被说成 external verification。

Current Funnel 还会把 Lead row 的 booked_date 当作 Booked evidence。Target 继续保留这批历史 cohort,但必须显示 form_asserted provenance。它可以让 Funnel 显示 “Booked (form-asserted)”,但不能自动写 completed booking progress,也不能自动把 displayType 改成 booked_not_converted。Task 仍然 open,Next Action 应是确认 booking。只有 communication_explicitstaff_confirmed 的 completed fact,才可以写 current booking progress。这样既不会丢掉历史 Funnel 数据,也不会把表单字段误当成权威 booking truth。

Current Funnel 的 stage vocabulary 还没有 Converted。这里的 Converted 是 Target projection。上线时必须给 dashboard definition 加 version,并对 Current 与 Target cohort 做 reconciliation,不能静默改口径。

14.3 报业务贡献时,要分开说明“谁促成”和“证据有多强”

Target 最终报表使用两个互相独立的维度。contributionLevel 说明 Retaintive/员工与结果之间的关系;verificationType 说明结果是怎样被确认的。不能用一个维度冒充另一个维度。

contributionLevel它回答的问题可以使用的措辞
discoveredRetaintive 发现了机会或结果,但在该 business result 之前没有 qualifying staff Activityopportunity/outcome discovered
assisted员工在结果前采用或操作过 Task/Suggestion,但没有形成满足 attribution 的完整 Activity chainAI-assisted opportunity/outcome
attributed结果满足下面的 deterministic eligibility,可以与员工使用 Workbench 后的行动关联attributed booking/conversion/retention
verificationType它回答的问题可以使用的措辞
communication_explicitCall/SMS 是否明确说出了结果communication-confirmed
staff_confirmed有权限的员工是否确认了线下或后台事实staff-confirmed
external_verified未来的权威 integration 是否确认了结果externally verified;MVP 不得生成
ai_inferred / imported_legacy结果是否只是 AI 推断或旧记录inferred / legacy;不得升级措辞

Terminal Outcome 只有同时满足以下六条,才可以标为 attributed

  1. Task 在 Outcome 发生前已经存在,而且带有同店 creation evidence。
  2. Outcome 之前存在该 Task Policy 定义的 qualifying staff Activity。只有 AI 发现结果,或员工只点了 Close,都不够。
  3. Activity 与 Outcome 位于同一个 storeId + contactPhone + taskId evidence chain。
  4. Activity 发生在 Policy 的 attributionWindow 内,并且早于 outcomeOccurredAt。使用的 window 和 Policy version 必须一起记录。
  5. Outcome 没有被 correction 或 reopen 推翻,并且 revenueMappingCode 不是 null。
  6. 如果只有 AI 观察到结果、员工没有采取行动,最多只能标为 discovered,不能标为 attributed

Non-terminal Intro progress 也遵守同一套 attribution 纪律,但使用自己的字段:progressCode=intro_booked / intro_attendedprogressOccurredAt 和对应的 reportingMetricCode。记录 progress 不需要关闭 Task,也不能把它写进 terminal resultCode。只有在 progress 之前存在同一 evidence chain 的 qualifying staff Activity,才可以说 “Booking/Attendance attributed”。如果 AI 只是从沟通中发现客户已经自行完成,措辞只能是 “discovered”。Progress mapping 和 terminal Outcome mapping 要分别记录 version 和 revenueCreditKey,防止同一个经济结果被重复计数。

员工在线下完成工作时,一个 UI submission 可以同时记录 Activity、staff_assertion 和 Outcome。如果员工只确认了结果,没有说明自己做过实际动作,那么 verification 可以是 staff_confirmed,但 contribution 不能自动升级成 attributed

去重要分两层做:

  • Call/SMS volume 和 staff interaction credit 按 storeId + sourceInteractionKey 只计算一次。即使同一次 interaction 推进了两个 Tasks,也不能算两次。
  • 如果两个 Tasks 映射到同一个经济结果,Policy 必须生成相同的 revenueCreditKey。Owner dashboard 对这笔金额只能算一次。不同业务 Outcome 可以分别展示,但不能重复声称同一笔美元价值。

每个带 revenue mapping 的 Policy 都必须明确定义 attributionWindow、qualifying Activities、revenueCreditKey 规则、estimate formula/version,以及 owner-facing wording。AI 不能临场发明这些规则。

Estimated influenced MRR 不能写成 accounting-recognized revenue。缺少可靠金额时,宁可只展示:

  • Bookings attributed。
  • Conversions staff-confirmed。
  • Cancellations saved。
  • Payment recoveries confirmed。
  • Estimated value,并清楚标记估算模型和 Policy version。

progressCode/resultCode、verification、reporting/revenue mapping 和 calculation definition 都必须 versioned。发生 correction 时,系统更新 current projection,同时保留原 decision 和 audit;不能静默改写过去的 dashboard。

MVP 首发报表不要求一次实现上述完整模型。首版只做 §16.5 定义的诚实计数;完整的 discovered / assisted / attributed projection 按 rollout 阶段逐步启用。


15. End-to-end scenarios

这些场景用具体操作说明 Target contract。当 Target 允许 AI 自动执行、但 MVP 只做 shadow 或人工确认时,场景会明确写出两者的差别。

15.1 新 Lead:第一次电话无人接,后来通过 SMS 完成预约

阶段系统应记录什么
InputEmail Lead 到达,verification 为 system_observed
Task creation创建 lead_conversiondisplayType=lead_outreach;reason 指向该 Lead;first-response SLA 为 5m
Next Action“Call now”
Activityoutbound phone / call_completed / no_answer
Task after ActivityTask 仍 open;workState=in_progressdisplayType=lead_follow_up
New Next Action“Send SMS now, call tomorrow if no reply”
Activity客户回复 “Book me Tuesday at 6”。这是 booking request,还不是 completed booking fact
Task after requestTask 仍 open;Next Action 改为 “Complete/confirm Tuesday booking”
Later Activity客户明确回复 “I booked myself for Tuesday at 6”,或员工发送 “You’re booked for Tuesday at 6”
AI decisionTarget:只有 completed statement 通过 Task Policy、resolved evidence、automation tier、per-objective authority 和 fresh Guard 时,才可写 progressCode=intro_booked,而且 booking progress 本身不会 close Task。缺少任何 authority/evidence 就转员工确认;本场景不宣告该 authority 已在任何环境启用
Task after bookingTask 仍 open;displayType=booked_not_converted;Next Action 是确认到店计划,并按客户需要推进 Membership
FunnelBooked,并标为 communication-confirmed
Revenuebooking attributed;不能写成 membership revenue
Later result客户明确已经正式加入,或员工确认 conversion 已完成
OutcomeresultCode=convertedclosureReason=goal_achieved,Task closed。客户不需要 Intro 时,也可以从前两种 displayType 直接到达这个 Outcome

15.2 客户在线下完成预约,但系统看不到

AI 不能因为后续没有消息就推断客户已经 booked。员工在 Workbench 用一次业务操作补录系统看不到的事实:

  1. 选择 Update / Record result,再选择 “Intro booked”。
  2. Backend 写入 progressCode=intro_booked
  3. Verification 写为 verificationType=staff_confirmed
  4. 员工填写一条简短 confirmation note;server 在同一个 transaction 中生成 staff_assertion EvidenceRef。
  5. 如果员工同时声明自己完成了预约工作,同一个表单还可以记录一条 Activity;如果只确认结果,就不能凭这次 click 编造 Activity 或 attribution。

写完以后,Task 仍然 open,displayType 变成 booked_not_converted,Funnel projection 也随之更新;员工同时设置下一步。这个人工入口不是自动化失败后的 exception,而是当前 data boundary 下必须存在的正常入口。

15.3 AI 把 booking 判断错了,员工怎样纠正

如果 AI 把一句含糊的对话误标成 booked,处理过程如下:

  1. 员工打开这条 progress 的 evidence。
  2. 选择 “Booking incorrect/cancelled”。
  3. 系统让当前 booking evidence 失效,并重新计算 displayType=lead_follow_up。Task 从始至终都保持 open。
  4. 原来的 AI progress 和新的 correction 都留在 Timeline。
  5. 这次 correction 进入 eval/monitoring;后续 AI 不能根据旧 proposal 把同一个错误再次写回。

15.4 一个 Contact 同时有 lead_conversionreferral

一次 connected Call 是一个真实 interaction,但它可以为两个 Task 提供证据。系统只记录一次底层 interaction,再让两个 Task 的 decision 分别引用它:

  • lead_conversion:如果客户完成预约,记录 progress,但 Task 继续 open;只有 converted 或其他 terminal Outcome 才关闭。
  • referral:如果客户只说以后可能介绍朋友,Task 继续 open,或只生成 Suggestion。

Timeline event 主要归属一个 Task;Current Target writer 在同一 communication 支撑多个 Tasks 时,让每个 Task event 引用同一个由 server 生成的 sourceInteractionKeyevidenceRefs。Call count 和 staff credit 按这个 key 去重,不能因为关联两个 Tasks 就算两次;各 Task 的 contribution 仍可分别计算。Legacy event 缺 key 时只能标 coverage unknown,不能当成 0 或用文字猜。

15.5 客户提出 Cancellation request

Customer says: "I want to cancel."
  → create cancellation_request
  → Next Action: manager callback
  → Activity: manager connected
  → Customer says: "I'll stay if we switch the plan."

最后一句可能只是 commitment,不一定表示后台变更已经完成。当前没有 external membership truth,因此 Target 按以下边界处理:

  • 如果客户明确表示“决定留下”,Policy 可以关闭为 cancel_saved 并标 communication-confirmed;也可以要求 staff confirmation。具体采用哪一种,由 rollout allowlist 决定。
  • “取消已经在后台处理完成”只有在出现明确 completed statement 或员工确认时,才能支持 cancellation_completed
  • 固定的 “call → form → provider confirmation” Steps 不属于 domain model。

15.6 Renewal / Upgrade / Payment:有意向不等于已经完成

Communication系统应该怎样处理
“I am interested in upgrading”创建或更新 Task,并设置 Next Action;不能关闭为 upgraded
“I agree to renew”可以记录 commitment;没有 completed fact 时 Task 仍 open
“I will update my card”设置 Next Action 和下一次 review 时间,进入 Scheduled;不能关闭为 payment_restored
“The upgrade was processed this morning”Target Policy 可以按 communication_explicit close,并标 attributed;不能说 externally verified
Staff sees completion elsewhere员工以 staff-confirmed close

其中 AI close 是否在某个环境执行,由 automation tier、per-objective authority、runtime gate 和 fresh Guard 共同决定;本场景只规定语义,不宣告 UAT 或 PROD 已开放。当前状态见 Task V3 Rollout Status

15.7 员工创建 Flexible Task

员工可以直接创建:

Title: Ask Sarah about accessibility preference
Mode: flexible
Next Action: Talk to Sarah on Friday

AI 可以总结相关 Call/SMS,也可以建议处理时间,但不能自动创建或关闭这个 Task。员工把它关闭为 completedcancelled,并可填写 note;默认不做 revenue mapping。

15.8 DNC、wrong number 和 duplicate

Current Target message runtime 对 exact STOP 先解析 server-owned Contact/Store party roles,再决定 inbound customer command;它不信任 raw msg.direction。Source-bound DNC write 同时受 idempotency 和 authority fence 保护。VoiceMail row 即使来自同一个 Unified Message Store,也不会被当成 SMS STOP 或员工 outreach。

EvidenceDomain 应怎样处理
明确 DNC写 Contact restriction;根据 Policy 关闭相关 outreach Tasks,closureReason=compliance_stop
wrong number记录 Activity outcome 并进入 identity review;Task 可用 invalid_identity 关闭;不能跨店自动 merge
duplicate open Task保留正确 Task;另一个用 closureReason=duplicate 关闭,不能伪装成 completed
新 evidence 表明旧 Task 已不再需要使用 closureReason=superseded/no_longer_needed,并保留 evidence chain
evidence 不足生成 Suggestion 或 requestHumanReview;不能做 terminal mutation

setContactDoNotContact 必须在一个 transaction 中完成所有必要动作:先验证 explicit evidence,再写 Contact restriction 和 contact.dnc_changed audit,清除所有不允许的 future outbound Next Action,最后根据每个 Task Policy 的 dncBehavior = close / block_outbound / keep_open_for_review 处理相关 open Tasks。

closeTask(not_applicable, compliance_stop) 只有两种情况下合法:Contact 已经处于 DNC,或者本次 command 在同一个 transaction 中同时写入 DNC。清除 DNC 必须使用 privileged command;AI 永远不能调用。清除后也不能自动 reopen 旧 Tasks。


16. Current → Target migration

本节把两件事分开:Target end state 说明最终要有什么;rollout phases 和 §16.5 说明第一版怎样安全到达那里。它不是 big-bang 重写计划。

先看整体改动范围:

Change classScope
Keeptasks current snapshot、open/closed、assignee、nextActionAt/deadlineAtcontact_timeline、suggestion/assessment stores、Task Orchestrator、Workbench 主骨架
Implemented in source; rollout pendingtaskMode/taskKind/displayType/dedupeKey/title/nextActionText/currentOpenedAt、typed progress/Outcome/evidence、split Policy versions、Policy Catalog、bounded read tools、SMS batching、provider semantic runtime、Target projections
Replaceglobal typeCategory + closeResult validation 改为 Task Policy;三个 legacy Lead categories 改为一个 lead_conversion Task 加 derived/materialized displayType;one-shot-only 改为 bounded optional read loop
Recalculatework state、queue semantics、Funnel 和 revenue contribution;先做 dual projection,不能静默改历史
Deprecatecontacts.lead_status 作为 stage truth、contact-level suggestions/action-needed truth、global valence mappings、旧 Task design docs
Do not add in MVPCase、rigid Step、standalone ActionMilestone/provider system、第二份 Activity table

这不是推倒重来。nullable Expand schema、独立 TypeScript domain、Policy Catalog、Guard、InMemory/Neon adapters 和 production seams 已经在 current source 中形成。现在的主线不再是“先设计字段再写 DDL”,而是保留 safe defaults,完成外部 gates、历史 reconciliation、shadow、dual projection 和 Store cutover。

“Domain 先离线验证、再做 schema Expand”是已经执行过的历史 sequencing decision,不是当前仍未开始的步骤。Schema/migration 可被代码和 PostgreSQL tests 验证,但 production migration state 仍必须在明确的 auth/account context 下单独检查。

16.1 Legacy typeCategory 怎样迁移

Current typeCategoryTargetMigration rule
lead_outreachlead_conversion + displayType=lead_outreach先对同 store/contact、同一 lead opportunity evidence chain 的 open sibling 做 reconciliation,再合并;不同历史 opportunity 不能机械合并
lead_follow_uplead_conversion + displayType=lead_follow_up变成同一个长期 Task 的 current work context、Next Action 和 history,不再 close 后 relay
booked_not_convertedlead_conversion + displayType=booked_not_convertedCurrent taxonomy 实际要求 Intro attended/completed;只有 supporting evidence 存在时才导入 businessProgress=intro_attended。只有 legacy label 时,不生成 progress 或 attribution
cancellation_riskcancellation_request 或 review只有明确取消请求才能映射;模糊 churn risk 不能自动升级
retentioncase-by-case它可能表示 cancellation、member issue、payment recovery 或普通 follow-up,不能一对一迁移
win_backwin_backcategory 一对一,但仍要分配 Policy version
upgradeupgradecategory 一对一
renewalrenewalcategory 一对一,但不能只看旧 label 就假定真实 renewal window
referralreferralcategory 一对一;未来的 subject key 另行扩展

Current code 中的这些语义互相矛盾,migration 必须处理:

  • Production prompt 把 lead_outreach 当成 long-lived objective,所以第一次 successful contact 后不会 close 再 relay。
  • UI Help 仍然写着:第一次 Call/Message 完成 Lead Outreach 后,转成 Lead Follow-up。
  • Current taxonomy 对 booked_not_converted 的实际定义是“已经 attended/completed Intro,但尚未入会”,与字面上的“已经 booking”不同。因此 migration 必须看 evidence,不能按 category 名字猜。

Target 以本文为准:一个 Lead 只有一个 stable lead_conversion Task。随着事实变化,系统更新的是 displayType、Next Action、Activities 和 non-terminal business progress。Intro 不是必经步骤,三种 displayType 都可以直接到达 converted

在 dual-write 期间,Target → Legacy type compatibility 必须集中由一个 adapter 完成:

Target taskKinddisplayTypeLegacy type_categoryFidelity / gate
lead_conversionlead_outreachlead_outreach只用于 UI compatibility;Task identity/dedupe 不变
lead_conversionlead_follow_uplead_follow_up只用于 UI compatibility;Task identity/dedupe 不变
lead_conversionbooked_not_convertedbooked_not_convertedTask 仍 open;close_result 必须为 null
cancellation_requestdefaultcancellation_risklossy;必须保留 Target kind/evidence
renewal / upgrade / win_back / referraldefault同名 legacy valuecategory 层面 lossless
payment_recoverydefaultunsupportedTarget read projection 切换前,不能启用 mutation authority
Flexible customn/aunsupported不能伪装成 retention

lead_conversion 改变 displayType 时,只更新 compatibility type_category,不能改变 taskId/taskKind/dedupeKey。如果 legacy schema 已经有 open sibling,adapter 必须在同一个 lock/transaction 中先做 reconciliation。

16.2 Legacy closeResult 怎样迁移

先把 Current 值放回正确的 Target 概念中:

Current valueTarget home
convertedlead_conversion.resultCode=converted
won_backwin_back.resultCode=won_back
issue_resolved对应 Managed/Flexible Task 的 result;无法判断 kind 时进入 legacy review
cancel_savedcancellation_request.resultCode=cancel_saved
renewedrenewal.resultCode=renewed
upgradedupgrade.resultCode=upgraded
referral_obtainedreferral.resultCode=referral_captured
wrong_numberActivity outcome 加 identity correction;Task 使用 closure reason invalid_identity
do_not_contactContact restriction 加 Task closure reason compliance_stop
otherTask-specific result,或 admin outcome_unknown;必须填写 note
not_interestedTask-specific negative result 加 customer_declined
already_memberqualification/identity fact;通常是 not_applicable / no_longer_needed,不是员工失败
booked导入为 businessProgress=intro_booked,不是 Target terminal Outcome。历史 closed row 保留 legacy closure;是否还需要另建 open conversion work,由 review policy 决定,不能静默 reopen
cancelledambiguous legacy:可能只是 “form sent”,也可能是 “cancellation completed”;必须看 evidence,不能自动当成已完成
unable_to_reachresultCode=outcome_unknownclosureReason=contact_exhausted;必须验证尝试次数和时间窗口都符合 Policy,不能写成确定的 not_* 业务结果

Current UI 用 global positive/negative/neutral 分组,backend 和 reporting 又各自维护另一份集合;referral_obtained 的 valence 已经出现 drift。Target 删除这些手写的平行 mapping,全部由 Task Policy 生成。

Dual-write 期间,Target → Legacy compatibility mapping 也只能由同一个 adapter 负责:

Target OutcomeLegacy close_resultFidelity / rollout rule
convertedconvertedlossless
cancel_savedcancel_savedlossless
cancellation_completedcancelledTarget 含义更窄;保留 Target evidence,legacy 只用于兼容显示
renewedrenewedlossless
upgradedupgradedlossless
won_backwon_backlossless
referral_capturedreferral_obtainedlossless
任一明确的 not_* + customer_declinednot_interestedlossy;Target fields 必须保留 Task kind
outcome_unknown + contact_exhaustedunable_to_reachstop-reason 层面 lossless
not_applicable + invalid_identityevidence 确实是 wrong number 时写 wrong_number,否则写 otherreason-specific
not_applicable + compliance_stopContact restriction 已经写入 DNC 时才写 do_not_contact,否则禁止 closecompliance invariant
not_applicable + no_longer_needed 且明确 already memberalready_memberreason-specific
payment_restored / payment_not_restored / referral_not_captured 等没有精确 legacy value 的结果otherlossy;只有已经上线 Target projection 的 store 才能启用 authority
Flexible completed / cancelled / outcome_unknownother / other / otherlossy;不能用 issue_resolved 制造 positive reporting;Target note/evidence 才是精确语义

Adapter 每次写入时都要记录 targetOutcome → legacyValue + fidelity(lossless/lossy) + outcomePolicyVersion。Shadow dashboard 必须监控 mismatch count。如果某个 Task kind 的 Target close 无法在 legacy UI 中安全表达,那么该 store 在切换 Target read projection 前,不能开启这个 kind 的 AI close authority。否则会出现 Target closed、Legacy open 的 split-brain。

progressCode=intro_booked / intro_attended 使用独立的 progress compatibility:写 type_category=booked_not_converted,同时保持 status=openclose_result=null。绝不能借用旧的 close_result=booked 表达非终态 progress,否则 legacy writer 会再次把 booking 变成 terminal closure。

Legacy close_type = auto_closed / manual_closed / create_closed 只说明旧系统走了哪条关闭路径,不能直接当作 actor。迁移时按下面的顺序判断:

  1. 先读取该 Task 最新一条 closing task.status_changed Timeline event,查看 actor_type / actor_subject_id / staff_id / closeInitiator
  2. 有可信 staff actor 时,写 closedByType=staff;contact-analysis/AI writer 写 ai;deterministic service 写 system
  3. create_closed 只表示“创建时就关闭”,不等于 AI;auto_closed 也可能来自 system rule,不能机械映射成 ai
  4. 找不到可核验的 closing event 时,写 closedByType=importverificationType=imported_legacyclosedBySubjectId=null

Expand 阶段可以先把新 Target columns 设为 nullable;进入 Contract 前,必须按下面的规则完成可审计 backfill:

FieldLegacy backfill rule
taskMode默认 managedsource_type=manual 不自动等于 Flexible,必须按实际语义 review
taskKind按 §16.1 mapping。无法判断的 closed/history row 使用 migration-only legacy_unclassified;它不能被新建、不能接受 AI mutation,也不能做 revenue attribution
displayType三种 legacy Lead category 分别保留 lead_outreach / lead_follow_up / booked_not_converted,并标 compatibility provenance;closed row 保存 displayTypeAtClose
businessProgressLegacy booked_not_converted 只有 supporting evidence 存在时才导入 intro_attended。只有 label 时,保留 compatibility display 和 imported_legacy provenance,不生成 attribution;legacy closed/booked 不自动 reopen
dedupeKeyopen-row reconciliation 完成后由 Target Policy 生成;Flexible 为 null
creationEvidenceRefs能解析到同店 source 时才转换;否则指向 legacy_import,不能根据 AI summary 编造
title使用 mapped Policy label;unclassified 显示 “Legacy task”
taskReason保留现值。null 时写明确占位 “Legacy reason unavailable”,并标 legacy provenance;不能让 AI 补写一段假的历史
priority保留现值。null 时使用 migration policy default,并记录 legacy_backfill;不能伪装成人工选择
nextActionText不能把 active Suggestion 猜成 adopted plan;不确定时保持 null,等待员工或新 command 采用
creationPolicyVersionlegacy-v1
outcomePolicyVersionLegacy closed row 写 legacy-v1
currentOpenedAt优先使用最后一次 closed → open Timeline 的 occurredAt;没有时使用 createdAt
aggregateVersion初始为 0;Target command 第一次成功后递增
firstResponseAt能从 qualifying task.activity_recorded 确定时,取最早的 occurredAt;无法确定时保持 null,不能从 legacy note 猜
resultCode / closureReasonCode只有 exact mapping 才写精确结果;ambiguous row 使用 outcome_unknown / other + imported_legacy note
outcomeEvidenceRefs指向可解析 source;无法解析时指向 legacy Task row
outcomeOccurredAt只有真实 evidence time 才填写;不能用 closedAt 代替
revenueMappingCodeambiguous/imported legacy 默认 null
SMS cursorrollout 时从已经捕获的 high-water bootstrap,并写 audit;不能从零重跑整个 thread
Reasoner run不伪造历史 loop;旧 assessments 继续作为 legacy one-shot audit
close actor fields按上面的 Timeline-first rule 回填

只有当 backfill coverage、unclassified count、null count,以及 Current/Target projection mismatch 都达到 rollout threshold 后,才能增加 Target NOT NULL/CHECK,并进入 Contract phase。

16.3 按阶段 rollout,不让员工突然换一套系统

一项能力是否可以向下一 evidence level 推进,必须按 gate 判定,不能用“代码很多”或“PR 已 merge”代替:

PhaseStable evidence gate
Phase 0 — Contract/eval人工 ratified corpus、冻结后的 fresh real-model comparison、预先固定的 quality/cost/safety 标准
Phase 1 — Expand在明确环境验证 migration、schema drift、legacy compatibility 和 rollback preflight
Phase 2 — Shadow真实 event source 以 shadow authority 运行,并有 quality/cost/error/correction evidence;不改用户 state
Phase 3 — Dual projection按 Store/cohort 对齐 Task/Timeline/Funnel/Speed/UI,每个 unknown/partial/mismatch 都可解释
Phase 4 — Authority cutoverlegacy open Task reconciliation、read/semantic/reporting staged authority、kill switch 和 rollback drill 都有证据
Phase 5 — Contractactive runtime 稳定且 rollback window 结束后,才退役 legacy Task decision/write path;历史 Timeline 继续保留

这些 Phase 可以在工程上并行准备,但不能跳过 evidence gate。尤其是 unit/PostgreSQL tests 不能替代 real-model quality gate,checked-in gate 也不能替代目标环境 runtime evidence。各 Phase 的当前状态只在 Task V3 Rollout Status 更新。

Phase 0 — Contract and eval

这一阶段只验证 contract,不改 production state:

  • 冻结本文 vocabulary 和 Policy schema。
  • 用真实历史 Call/SMS/Lead/Task 构建 store-scoped eval fixtures。
  • 对每个 Managed kind 验证 create、close、Next Action 和 evidence。
  • 选择第一批 AI authority allowlist。Booking 只能 auto-progress,不能 auto-close。Terminal close 可以先对明确的 lead_conversion/converted 做 shadow;production 可以更保守,先一律要求 staff confirmation。

Phase 0 的可执行形态是一个离线双跑 harness

  • 把同一批真实历史 evidence 按时间顺序回放,同时驱动两条 lane。旧 lane 运行现有 Contacts Analyzer 的 prompt/schema,真实调用模型,但 mock writer;新 lane 运行 Router → Reasoner → Guard → Orchestrator,并使用 in-memory adapter。所有副作用都 mock,模型调用保持真实,因此成本来自实测而不是估算。
  • Harness 提供两种模式:live-model 真实调用模型,用来测质量和成本;replay 回放已经录制的模型输出,用零模型成本迭代 Guard/Engine 逻辑。
  • 成功标准必须在运行前写好,不能看完结果再改标准。例如:新 lane 在期望 case 上的 create/close 正确率不低于旧 lane;Guard 对无法解析的 hallucinated evidence 引用拦截率为 100%;中位数为 1 次 logical turn,physical attempts 单独报告;每个 mutation 都能回溯到 evidence、Policy version 和 Guard decision。
  • 对比报告至少包含:按 §16.1 mapping 对齐后的 decision diff、正确率、每个 case 的 token/实测成本、Guard 干预清单,以及 read model 是否能重建 §13 所需的全部 UI projection。
  • 每个 case 的成本报告必须同时给出两个口径:Task Reasoner 的 incremental Task-only cost,以及包含 source understanding(Call triage/classify 或 standalone semantic fallback)、retry 和 repair physical attempts 的 attributable end-to-end cost。只报增量会把总成本隐藏起来,不能通过 Phase 0 成本闸门。

Phase 1 — Expand

这一阶段只增加兼容能力,不切换用户读路径:

  • 增加 Target nullable columns、Policy Catalog 和新 validation。
  • 保持 legacy API 和 UI 正常工作。
  • 新 command dual-write type_category + task_kind/display_type,并 dual-write close_result + Outcome fields。Progress 使用单独的 dual projection,绝不能写 close_result=booked
  • Timeline 只追加 versioned events,不删除旧记录。

Phase 2 — Shadow

这一阶段让新 Reasoner 观察真实流量,但不改变用户看到的 state:

  • 新 Task Reasoner 只写 assessment/proposal。
  • 对比 Task V2 与 Task V3 的 create、update 和 close 决策。
  • 重点监控 false auto-close、missing Task、duplicate、跨店污染和 staff correction。
  • Shadow 是 Phase 0 harness 的在线延伸:把 replay driver 换成真实 event consumer,把 in-memory adapter 换成真实存储,但仍然只写 assessment/proposal;复用 lambda/shared/utils/ai 模块已有的 ai_usage 结构化记账,在真实流量上复测成本。

Phase 3 — Dual projection

这一阶段同时计算两套读模型,继续对账:

  • Backend 同时生成 legacy 和 Target Workbench/Funnel/Revenue projection。
  • 按 store/cohort 对比 reconciliation counts、queue、SLA、booked、conversion 和 retention。
  • Figma/前端基于 Target API 实现 feature-flagged UX。

Phase 4 — Authority cutover and reconciliation

任何 existing Store/objective 的 authority 变更都必须按可回滚顺序执行:

  • 先切 read projection,再切 AI authority,最后切 reporting。
  • Staff manual create/update/close 始终保留。
  • 必须能 rollback 到 legacy read;已经写入的新 Timeline/Outcome 数据不能丢。

Phase 5 — Contract

满足以下全部条件后,才能移除 legacy type_category / close_result / due_at 写路径:

  • 所有 active stores 都已完成 cutover。
  • 历史 ambiguous rows 都有明确 handling。
  • Prompt、UI、backend 和 reporting 都只从 Policy Catalog 读取 vocabulary。
  • Rollback window 已结束。
  • Dashboard definition version 已公开。

16.4 Migration 绝不能做的事

Current historical-row Store backfill scripts 已改成 audit-only:--apply 在创建 database client 之前就 fail closed;current phone assignment 只能作为 audit signal,不能执行 historical UPDATE。这条安全栏必须保留,不能为了提高 migration coverage 重新开放写路径。

  • 不能把每个 legacy row 原地 rename 后,就宣称语义已经正确。
  • 不能把 lead_outreach + lead_follow_up 机械地变成两个 Target Tasks。
  • 不能从 due_atnextActionAt、first-response SLA 或 deadline。
  • 不能把 legacy closed 自动解释为 goal achieved。
  • 不能从 AI summary 反向编造不存在的 Activity 或 evidence。
  • 不跨 store_id merge——按 phone 不行,加任何其他证据也不行;跨店归并只能人工处理(§15.8)。
  • 不能静默重算历史 Funnel/Revenue。
  • 不能 big-bang 替换 production Workbench。

16.5 Original MVP acceptance slice(不再是当前 rollout scope)

本节保留最初用于验收 UX 的最短、可回溯闭环,不再定义当前的 objective allowlist、environment enablement 或 PROD launch scope:

AI 从 Call/SMS/Lead 发现事项并创建 Task
  → 员工看到清楚的 Next Action 和关键 evidence
  → 员工直接 Call/Text
  → 系统自动记录可观察沟通
  → 员工用一次表单记录线下 Activity + Next Action
  → 员工确认 Outcome
  → Owner 看到“机会 → 行动 → 结果”的 evidence chain

该 acceptance slice 的约束如下:

  • 历史 lead-only pilot 已被后续决定取代;MANAGED_TASK_KIND 中全部七类 objective 现在都具备 Target/Shadow authority capability。它们在具体 Store/环境是否实际启用,只从 rollout status 和 runtime evidence 判断。
  • Auto-execution 只有在 Task Policy、evidence、automation tier、per-objective semanticAutomationMode=target、runtime gate 和 fresh Guard 全部授权时才执行。full_auto terminal seam 在 source 中存在,但 checked-in pre/prod Task V3 source gates 仍为 false;本文不声称 PROD auto-close 已发布。
  • Suggestion 分三条 lane 处理,另有一类根本不是 Suggestion 而是补录事实;页面上不出现通用 Accept 按钮
    • 有明确 evidence、风险低、且可以撤销的变更:系统 auto_execute,显示 “set by AI”,并提供一键撤销。
    • 可以马上执行的建议:显示成具体动作,例如“设为下一步”或 “Review & send”。员工执行这个动作,就等于采纳;command 携带 sourceSuggestionId
    • 纯 coaching 或话术:直接展示,允许隐藏,并提供 suggestion 级质量反馈 helpful/not_relevant;不要求员工先 Accept。
    • 线下事实和 terminal 变更:使用业务语言确认,例如 “Record booking” 或 “Review close”。这是让员工补录系统看不到的 evidence,不是在审批 AI。
  • 同一屏最多显示一条现在就能做的建议。执行时间晚于当前 Next Action 的建议不渲染按钮(最多显示为一行备忘):MVP 只有一个已经采用的 Next Action(§2.4),装不下第二个未来计划。当前动作完成后,下一次 run 再根据最新事实重新提出。
  • Workbench detail 首版只保留三块:行动区(Next Action、关键 evidence、Call/Text)、记录区(Record activity + plan 原子操作)、默认可见的 Task Timeline。Timeline 用紧凑记录让员工快速回看已经做过什么;较早历史按需继续加载。Provenance、Policy version 和教学文案不进入日常工作面(§13.4)。
  • Owner 报表首版只做诚实计数:发现的机会数、员工 Call/SMS 数、带 provenance 的 intro_booked/Outcome 数,以及能沿 evidence → Task → Activity 回溯的结果数。Policy 可以映射 transparent potential value,但必须区分 AI-observed、communication-explicit 和 staff-confirmed;在 membership/billing integration 前,不得称为 recognized dollar revenue。完整 discovered/assisted/attributed 模型按 §14.3 分阶段上线。

判断一项能力是否进入 MVP,只看两个问题:它是否直接服务上面的最短闭环;或者它是否属于不能延期的安全正确性底线。Store isolation、DNC、idempotency、append-only audit 和 human-wins 属于底线,即使不直接出现在 UI,也必须首版具备。其他能力不进首版。

一句话原则:AI 在后台可以复杂;员工面前,方向唯一明确——第一眼永远是“现在做什么”;信息完整但整理好——Task Timeline 和 History 常驻可见。人没有那么笨,不要替员工藏信息,把信息整理好、给出方向,他们自己会做。


17. Validation、testing 与 observability

这里列的是 Target contract 的完整验证面。Current source 已有 contract/unit/fake-model/PostgreSQL production-seam coverage;这只能证明代码边界和 transaction semantics。Human-ratified/live eval 已经开始,但 Gate A 质量闸门未通过;shadow、dual projection reconciliation、UAT 和 release state 仍必须按 Task V3 Rollout Status 与目标环境 evidence 核验。

17.1 Contract tests:证明词汇和 Policy 没有漂移

  • 每个 taskKind 都必须有合法的 result/closure/evidence matrix。
  • Prompt vocabulary、Zod schema、backend Policy Guard、UI options 和 reporting mapping 必须从同一个 registry 生成,或者有一致性测试证明它们完全一致。
  • 每个 Managed Task Policy 都必须有 create、no-create、auto-close、human-review 和 correction fixtures。
  • 遇到 unknown taskKind 或 unknown Policy version 时必须 fail closed,不能退化成任意 global result。
  • lead_conversion 测试必须覆盖三种 display transition、meaningful inbound 直接进入 follow-up、booking request 不等于 completed booking、booking 完成不关闭 Task、booking correction 回到 follow-up,以及三种 displayType 都能直接 converted。
  • Outcome Guard 必须拒绝 lead_conversion.resultCode=booked。Progress reporting 和 Outcome reporting 要分别 versioned,并证明不会 double count。

17.2 Domain/API tests:证明写入安全且可回放

  • 所有路径都要测试 store_id isolation,包括同一个手机号出现在多家 store。
  • 测试 dedupe,以及同一个 idempotency key 携带不同 payload 时的 mismatch。
  • 测试 optimistic concurrency、stale AI proposal 和 human-wins。
  • 测试 Activity 与 Next Action 在同一个 transaction 中成功或失败。
  • 测试 business progress、staff_assertion 和 Next Action 的 atomicity;员工没有声明实际工作时,不能生成 Activity credit。
  • Reopen 后的 work state 只读取 current open period。
  • 测试 closed integrity 和 Outcome correction audit。
  • 测试 DNC hard stop、wrong number、duplicate 和 superseded。
  • Recorder 与 credited staff 必须分别记录,不能重复或错归。

17.3 AI eval:把不同错误分开量

Harness 是执行 eval 的工程化 runtime,不是答案本身。Fixture 是有 trigger/evidence/as-of state/expected decision 的 test case;只有业务团队人工批准的 exact fixture 才能进入 Gate A denominator。#1925 已把首批人工签署的历史 journey/checkpoint 和 live-model 评估纳入 source,所以“formal eval 尚未开始”已不正确;但当时的 semantic diagnostic 远未达到 release 闸门,不能 replay 成 Gate A PASS。最新 denominator、score 和未通过项只在 rollout status 文档更新。AI 不能自行 ratify 自己的 expected answer,也不能通过删难题、改 denominator、case-ID/regex hardcode 或偷看未来 evidence 提高分数。

至少单独衡量以下指标,不能合成一个“AI accuracy”:

  • Task discovery precision/recall。
  • taskKind 是否正确。
  • Next Action 是否有用,以及 suggestion 最终是 accepted、edited 还是 dismissed。这里记录的是 domain decision;UI 仍使用具体动作,不显示通用 Accept/Edit/Dismiss 三按钮。
  • Outcome accuracy。
  • Evidence citation accuracy。
  • False auto-close rate。
  • Staff correction/reopen rate。
  • Unnecessary tool-call rate。
  • Cross-store leakage 必须为 0。

无论某环境的 terminal authority 当前是 shadow、staff review 还是 full_auto,eval 都要单独计算 false auto-close rate;该指标不能反向证明 PROD auto-close 已开启。

17.4 Runtime metrics:每次 run 都能解释成本和结果

每个 run 都要记录以下字段:

triggerType
storeId (internal only)
taskKind / displayType / progressCode
creationPolicyVersion / decisionPolicyVersion / outcomePolicyVersion
model / promptVersion / schemaVersion
logicalTurnCount / physicalAttemptCount / schemaRepairCount / retryCount / toolCallCount
inputTokens / outputTokens / estimatedCost(按 physical attempt 逐次归账后汇总)
costStatus / knownCost / missingCostAttemptCount
latency / timeout
proposal
guardDecision + rejectionReason
executedCommand
staffProgressCorrection / staffOutcomeCorrection / reopen

成本报告必须同时分开:

  • existing Call analysis cost:triage/classify 与可选 verify/coaching
  • incremental Task cost:Reasoner logical turns、read tools、retry/repair;
  • attributable end-to-end cost:从触发到 Guard 决定的全部 physical attempts;
  • incomplete cost:provider 没有返回 usage/cost 的 attempt 数量与已知最低成本,不能把缺失值当成 $0

Historical smoke snapshot(2026-07-31,#1908 source): 下表只保留当时用于暴露 contract/成本问题的有上限 OpenRouter smoke,不是当前 score,也不属于 human-ratified Gate A:

Run结果Physical attemptsToken / cost evidence结论边界
Unified Call 10-case synthetic regression10/1011input 57,392;output 24,981;known cost $0.007619776;1 attempt 缺 cost七类 objective、三个 lead boundary 和 unrelated no-op 的主路径 smoke 通过;一次 schema repair,无 retry storm
Lead boundary focused rerun3/33input 15,630;output 6,288;cost $0.002133012,completecompleted signup → terminal Outcome;intro booked → Business Progress;pricing question → lead Task creation
Formal Provider createpass3input 10,533;output 2,301;cost $0.0015889988,completesemantic 1 attempt + Reasoner 2 attempts;exact evidence、checkpoint、create/close 与 decision contract 均通过
Legacy fallback full-auto closefail safe5input 15,191;output 3,167;known cost $0.0012393124;2 attempts 缺 coststandalone resolver 把 completed signup 降级成 creation;Guard 返回 needs_staff_confirmation,没有执行错误 close

这些数字只能证明 bounded smoke 的当前表现。它们不能替代 20–30 个经过人工 ratify 的历史 customer journey、TEST runtime/shadow、员工 UAT 或 formal V3 accuracy gate。下一轮真实评估必须优先覆盖 terminal Outcome 与 legacy fallback,而不是继续增加无上限 synthetic sampling。

业务层还要同时观察:

  • Opportunity discovered。
  • Time to first Activity。
  • Task Outcome rate。
  • Evidence-backed progress rate,例如 Intro booked,以及后续 conversion rate。
  • Staff adoption。
  • AI suggestion acceptance/edit rate;这是 domain adoption projection,不要求 UI 显示通用 Accept/Edit。
  • Outcome verification strength。
  • Assisted/attributed value。

“AI 做了更多 mutation”不是成功。成功意味着错误更少、员工负担更低,并且有 evidence 支撑的业务 Outcome 更多。


18. Open Decisions

下面这些问题还没有拍板。实现者不能把 Current recommendation 当成 approved contract;必须用真实数据、shadow eval 和 business owner 的决定来关闭问题。

尚未决定的问题当前建议它会阻塞什么
每种 Task 的 exact SLA放在 Policy/config 中,不写死在 DB。先验证 lead_conversion 的 first-response 5m;不能把 outreach SLA 当成整个 Task deadlinequeue/alerts
contactExhaustionPolicy 的次数和窗口按 taskKind/store policy 配置;一次 no-answer 绝不能结束 Taskauto-close exhaustion
第一批 AI auto-close allowlist先对少量 explicit outcomes 启用(shadow 验证通过后),再根据 false-positive 扩大production authority
Cancellation 中“决定留下”是否足以 closecommunication-confirmed 可以用于 attribution;是否仍需 staff confirmation,由 rollout policy 决定cancellation policy
Outcome 长期是否拆成独立 tableMVP 使用 Task snapshot 加 Timeline;只有出现复杂 supersession 才拆future schema
Flexible Task 将来是否允许 AI create/close默认继续由员工拥有;等安全性和价值证据足够后再决定flexible automation
Revenue estimate 使用什么口径先展示 counts 和透明的 estimate;不能冒充 recognized revenueowner dashboard
Funnel current stage 冲突时怎样选先明确使用 latest-supported 还是 furthest-ever,并决定 correction 是否回退、多个历史 Tasks 怎样选择;不能让 SQL 作者临场决定Funnel API/UI
Funnel historical time 用哪个时间业务 cohort 默认使用 occurredAt,audit latency 使用 recordedAt;正式 dashboard definition 仍待确认historical reporting
Funnel 是否重算历史必须发布新的 definition version,并显式 release;保留 old/new cohort reconciliationhistorical reporting
contact_id 怎样迁移MVP 继续使用 store_id + normalized contact_phone;稳定 Contact ID 另行设计和 backfillidentity/API evolution
何时建立 first-class Action entity只有业务真的需要多个 simultaneous assignable Actions 时才升级future workflow
何时允许 external_verified真正接入并验证 authoritative source 后integration roadmap

19. Selected Target decision log

下面这些是 selected Target contract 已采用的设计选择。它们解释结构理由,但不能用来推导某项代码已部署、某个环境已启用或 rollout gate 已通过。

Selected design decision原因
不使用 Case它与 Task 表达同一件事,只会增加用户和工程认知成本
不使用 rigid Step销售和客服不是固定程序;Next Action 会随着沟通变化
统一使用 Next ActionCurrent Action 只是“当前采用”的描述,不应再成为一个 entity
Activity append-only系统必须知道谁实际做过什么,summary 不能覆盖历史事实
Task owns OutcomeTask result 不需要额外的 Case;Activity outcome 与 Task Outcome 必须分开
Funnel 是独立 projection客户进展跨越多个时间点,不能用 Task current status 代替
MVP 不建 Milestone/provider systemCurrent 没有 authoritative booking、membership 或 billing integration
Target 允许 AI evidence-gated close Managed Task保留自动化能力,但必须同时通过 Policy、evidence、automation tier、runtime gate、per-objective authority、fresh Guard、audit 和 human override;具体环境状态只从 rollout evidence 判断
一个 stable lead_conversion 加三个 displayType长期销售目标保持不变,同时让员工一眼区分 Outreach、Follow-up 和 Booked pending conversion
intro_booked 是 business progress,不是 OutcomeBooking 后仍要继续推进或确认 conversion;中间 Funnel 进展不能冒充 Task 已完成
dedupeKey 与 creation idempotency 分开Managed objective 需要业务去重,同时允许同一个 Contact 创建多个 open Flexible Tasks
Flexible Task 由员工创建和关闭给线下工作和未知业务保留出口,也避免 AI 发明不可管理的类型
Policy Catalog 是扩展点同一个 contract 驱动 prompt、backend、UI 和 reporting,消除 mapping drift
使用 bounded loop,不使用 perpetual agent只有 material uncertainty 时才多读 context,控制 cost、latency 和 blast radius
用两段式 structured generation 实现 bounded loopmax-2 由 output schema 保证,不依赖 prompt 约定;实现与 provider 无关、可 mock、成本可逐步记录
Domain 先在独立 package 中离线双跑,再做 schema Expand这是已执行的 implementation sequencing;当前 source 已有 domain + Expand schema,rollout 仍需独立 production migration verification
统一 Call understanding,Reasoner 只做 Task decisionclassify 在一次 transcript understanding 中同时产出 Call analysis 与 Task claims,省掉重复 semantic call;Reasoner 消费已确认事实并保持独立 mutation authority,profile enrichment 不塞进 Reasoner
Guard 只有三种 decision,权限不足统一降级为 Suggestion/TaskProposal不新增第三种审批队列;所有拒绝都必须留痕,不能静默丢弃

20. Implementation source map

下表中的路径用来核验 Current source 实现,不是 Target 已经部署或启用的证明。

ConcernCurrent source
Task schema/enums/indexescallytics-infrastructure/packages/common/src/db/schema/tasks.ts
Per-objective authority / rollback auditcallytics-infrastructure/packages/common/src/db/schema/task-objective-authority.tscallytics-infrastructure/packages/common/src/domain/task-authority-control.ts
Current Task taxonomy wordingcallytics-infrastructure/packages/common/src/taxonomy/task/definitions.ts
Activity vocabularycallytics-infrastructure/packages/common/src/db/schema/task-progress.ts
Timeline ledgercallytics-infrastructure/packages/common/src/db/schema/contact-timeline.ts
Suggestion storecallytics-infrastructure/packages/common/src/db/schema/task-suggestions.ts
AI assessment auditcallytics-infrastructure/packages/common/src/db/schema/ai-task-decision-assessments.ts
Unified Call understanding contractcallytics-infrastructure/lambda/ai-analysis-processor/src/core/stages/classify.tscallytics-infrastructure/lambda/ai-analysis-processor/src/core/stages/pipeline.tscallytics-infrastructure/lambda/shared/task-reasoner/unified-call-understanding-contract.ts
Bounded Reasoner runs / SMS cursorcallytics-infrastructure/packages/common/src/db/schema/ai-task-reasoner-runs.tscallytics-infrastructure/packages/common/src/db/schema/sms-task-reasoner-cursors.tscallytics-infrastructure/packages/common/src/db/schema/sms-task-reasoner-receipts.ts
Provider source proof / outboxcallytics-infrastructure/packages/common/src/db/schema/provider-interaction-source-proof.tscallytics-infrastructure/packages/common/src/db/schema/provider-reasoner-outbox.ts
TaskProposal approvalcallytics-infrastructure/apps/api/src/routes/tasks/task-proposals.tscallytics-infrastructure/packages/common/src/domain/task-engine/task-proposal-decision-store.ts
Domain commands / Policy / Guard / writerscallytics-infrastructure/packages/common/src/domain/task-engine/callytics-infrastructure/packages/common/src/domain/task-policy/
Neon Target adaptercallytics-infrastructure/packages/common/src/domain/task-engine/neon-store.ts
Shared bounded model adaptercallytics-infrastructure/lambda/shared/task-reasoner/model-adapter.ts
Bounded OpenRouter regressioncallytics-infrastructure/scripts/task-v3-harness/unified-call-live-regression.cli.ts
SMS production seamcallytics-infrastructure/lambda/message-processor/src/sms-reasoner-consumer.tscallytics-infrastructure/lambda/message-processor/src/infrastructure/task-v3-sms-reasoner-consumer.ts
Call/VoiceMail production seamcallytics-infrastructure/lambda/shared/task-v3-provider-reasoner-consumer.tscallytics-infrastructure/lambda/shared/task-v3-provider-neon-consumer-store.tscallytics-infrastructure/lambda/shared/task-v3-provider-reasoner-lambda.ts
Call mechanical runtimecallytics-infrastructure/lambda/transcribe-processor/src/infrastructure/task-v3-call-runtime.ts
Message mechanical/STOP runtimecallytics-infrastructure/lambda/message-processor/src/infrastructure/task-v3-message-runtime.tscallytics-infrastructure/lambda/message-processor/src/infrastructure/task-v3-message-dnc-runtime.ts
Contacts Analyzer profile-only AI(无 Task decision authority)callytics-infrastructure/lambda/contacts-analyzer/src/infrastructure/ai-client.tscallytics-infrastructure/lambda/contacts-analyzer/src/core/prompts/output-contract.ts
AI context buildercallytics-infrastructure/lambda/contacts-analyzer/src/core/prompt-builder.ts
Legacy compatibility / deterministic backstop / Timeline projectioncallytics-infrastructure/packages/common/src/domain/task-orchestrator/index.tscallytics-infrastructure/lambda/contacts-analyzer/src/core/lead-task-invariant.tscallytics-infrastructure/apps/api/src/routes/tasks/target-projection-contract.tscallytics-infrastructure/apps/web/src/pages/tasks/composables/task-timeline-view.ts
RingCentral inputcallytics-infrastructure/apps/api/src/services/ringcentral/
Email Lead inputmarket-lead-tracking/src/
Target Staff command APIcallytics-infrastructure/apps/api/src/routes/tasks/commands.tscallytics-infrastructure/apps/api/src/routes/tasks/target-staff-outcome.ts
Workbench / Task UIcallytics-infrastructure/apps/web/src/pages/tasks/
Checked-in rollout defaultscallytics-infrastructure/lib/config/environments.tscallytics-infrastructure/lambda/shared/task-v3-provider-reasoner-lambda.ts
Historical Gate A design review(2026-07 snapshot)callytics-infrastructure/scripts/task-v3-harness/TASK_V3_GATE_A_DESIGN_REVIEW.md
Current human-ratification corpuscallytics-infrastructure/scripts/task-v3-harness/ratification/LEDGER.json;当前 score、gate 与 PR state 见 Task V3 Rollout Status

产品语义仍以本文为准;machine-readable Policy、schema、Zod/API contracts 和 tests 是 executable source of truth。后续任何 PR 都必须同步更新二者。Source code 实现完成后,仍要用目标环境的 migration/config/runtime evidence 证明 deploy、shadow、cutover 和 business effect,不能让 merge status 代替 production truth。


21. Glossary

English term用人话解释
Task一件要持续处理到有结果的客户机会、问题或目标
Managed Task由 versioned Policy 管理、可以自动发现并进入统计的核心 Task
Flexible Task员工自由创建、默认不做自动 attribution 的 Task
Next Action这个 Task 当前已经采用的下一步计划
AI SuggestionAI 提出的、尚未成为当前计划的 proposal
Activity已经真实发生、可以审计的一次工作或互动
Business ProgressTask 过程中有 evidence 支撑、但还不是最终结果的进展,例如 intro_booked
Display Type员工当前在 queue 中看到的 Task 分类;它由 Policy/evidence 计算,不是 lifecycle,也不是另一个 Task
OutcomeTask 最终发生了什么、为什么停止,以及证据是什么
Task Policy某一种 Task 的完整可执行 contract
Evidence支撑 Task 创建、更新或关闭的来源事实
VerificationEvidence 是怎样被确认的,以及能使用多强的措辞
Queue StateAPI 计算出的 Needs attention、Scheduled 或 Closed view
Funnel Projection根据现有 evidence 计算出的 Lead/customer progress
Revenue Attribution对 Retaintive 工作与业务结果之间关系的可解释估算
Policy Guard用确定性代码检查权限、状态、evidence、store isolation 和 mapping
Bounded Reasoning Loop一次有明确 trigger、tool/cost budget 和终止条件的 AI run
Task ProposalAI 发现潜在 Task、但尚未创建时保存的 pre-task proposal,供员工审核

22. Contract review checklist

在继续实现、修改 contract 或评审 Figma 前,逐项回答下面的问题。任何一项为 No,都表示文档或实现已经偏离 selected Target 的边界:

  • 全文和产品是否一致使用 Task / Next Action / Activity / Business Progress / Outcome 这五个核心业务概念?
  • 是否明确说明 Current 没有 authoritative CRM/provider truth?
  • 是否允许员工创建 Managed/Flexible Task、补录线下 Activity、关闭 Task,并纠正错误?
  • 是否把 Suggestion 与已经采用的 Next Action 分开?
  • 所有 suggestion decision 是否都使用业务语言,并且只显示现在能做的动作?页面是否避免平铺通用 Accept/Edit/Dismiss?
  • 是否把 Activity outcome、Task result、closure reason 和 Funnel stage 分开?
  • 是否保持 taskKind=lead_conversion 稳定,只让三种 Lead displayType 随事实变化,并且 booking 不关闭 Task?
  • UI、prompt、backend 和 reporting vocabulary 是否都由 Task Policy 生成?
  • 所有读写是否都强制 store_id
  • AI 是否只能使用 read tools 和 typed commands,而不能 raw DB write?
  • AI auto-execution 是否同时通过 Task Policy、resolved evidence、automation tier、runtime gate、per-objective authority、fresh Guard、atomic audit 和 correction/reopen?
  • Queue、SLA 和 deadline 是否分别计算,而不是互相冒充?
  • 是否避免 fixed Steps、global closeResult 和 silent history rewrite?
  • 是否能通过 shadow 和 dual projection rollout,而不打断现有客户?
  • 是否把 source-ready、enabled、deployed、shadow-verified、cutover、uplift-proven 分开报告,而没有用 PR/CI 状态冒充 production truth?

Contract 文档验收不是“看起来完整”,而是一个第一次接触 Retaintive 的人读完后,能够:

准确解释产品价值,画出 Target data model,评估合法 API 与 AI authority,设计 Workbench,并且不会重新引入 Case、固定 Step、假想 provider truth 或 global closeResult。