Task V3 Product / Domain Contract
Product, Domain, AI Runtime, Backend Contract & UX Projection
0. 怎么读这份文档
这份文档只锁定 Task V3 的稳定产品目标、领域词汇和安全边界。PR 是否 merge、某环境是否 deployed/enabled、真实模型质量、员工 UAT 和 PROD release 都会变化,统一维护在 Task V3 Rollout Status。本文不再复制这些动态数字和待办清单。
contract selected、code merged、deployed to TEST、live-model evaluated、employee UAT passed 和 released to PROD 始终是不同的证据等级。任何读者都不得用前一项推导后一项。
Store 的出厂 automation tier 是 full_auto。cautious 和 standard 保留为 Store override 与故障降级能力。full_auto 也不等于 AI 任意写库:模型只产生 typed proposal/command;DNC、Store isolation、dedupe、evidence、Policy、idempotency 和 atomic writer 仍由代码决定。
第一次接触 Retaintive 的产品经理、设计师或工程师,只读这一份就应该能回答:
- 产品解决什么问题。系统真实能看到什么,又看不到什么。
Task、Display Type、Next Action、Activity、Business Progress、Outcome分别是什么。- 店员和 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 的边界
2026-08-04 status-separation baseline(仅记录本文为什么移除旧 snapshot): #1908 的 approval backend 和 #2024 的 owner automation / proposal UI 已 merge;#1913 已移除 Contacts Analyzer 的 AI Task decision authority,但
applyTaskActioncompatibility 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-inmain中 TEST 的 Task V3 source gates 为true,pre/prod为false。这些 source/merge 事实不证明员工 UAT 已通过,也不证明 PROD 已发布。分离日之后的变化只更新 rollout status 文档,不再复制到本节。
0.2 Unified Call AI chain contract
Unified Call path 复用同一次 Call analysis 产生的 task_semantics,而不是让两个 AI 重读同一份 transcript:
因此,统一路径不再有独立的 “Task semantic extraction” AI call。对于本来就会做 Call analysis 的合格 Call,Task V3 的增量模型开销通常是一个 Reasoner turn,material context 不足时才是两个。triage、classify、可选 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、requestStaffInput 或 no_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 和员工掌握的线下信息来帮助门店工作:
发现值得处理的客户机会或问题,告诉员工下一步做什么,自动记录可观察的沟通,持续跟踪到有证据的结果,并让门店老板看到这些工作带来的业务价值。
产品闭环是:
1.2 最终只保留五个核心业务概念
辅助概念只有四个,不再增加:
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 的解释是:
这样一来,产品和用户会同时面对两个都很像“任务”的对象。当前业务不需要这层重复结构。
在本模型中,同一件客户事项只用一个 Task 表达:
Task 从头到尾管理整件事:为什么开始、现在准备做什么、过程中发生了什么,以及最终得到什么 Outcome。Task 的 result 不需要再变成另一个 Case。它就是这个 Task 自己的 Outcome。
2.2 为什么不是 Task + 标准 Steps
一个 Task 可以发生很多次 Activity,但这些 Activity 不需要预先排成一套写死的 Steps。
- 首次电话可能直接预约成功。
- 无人接后可能发 SMS,也可能店员第二天线下遇到客户。
- 客户可能主动回电,改变计划。
- 一通沟通可能同时推进
lead_conversion和referral两个 Tasks。
因此,无论沟通怎么变化,系统都只需要持续回答四个问题:
- 这件事为什么存在?
- 现在最合理的 Next Action 是什么?
- 实际发生了什么?
- 是否已有足够 evidence 关闭,Outcome 是什么?
2.3 一个 Contact 可以同时有多个 Tasks
这三个目标互不依赖。它们可以分别分配,也可以分别关闭。
同一种 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 中,它只代表下面这个店内身份锚点:
这个 identity 用来把某家 Store 内的 Calls、SMS、Email Leads、Tasks、Activities 和 Notes 串成 conversation history。
它不能证明现实中“这个号码永远只属于这一个人”。共享号码、换号、wrong number 和跨店到访都可能发生,所以必须允许员工纠正。系统不能只按 phone 做全局 merge。
关系与 cardinality:
在 MVP 中,一个 open Task 同一时间最多只有一个已经采用的 Next Action。
如果未来真的出现多项工作需要同时执行,而且每项都要分别分配、分别设置 deadline,再评估是否引入 first-class Action entity。在此之前,多个 Suggestions 仍然只是多个建议,不能当成多个 current Actions。
不同 Contact fields 的可信程度不同,不能一视同仁:
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
Reporting 的措辞必须与 evidence 强度相符:
AI-inferred:AI 推断。communication-confirmed:沟通中明确表达。staff-confirmed:员工确认。externally verified:只有未来真的接入权威系统后才能使用。
外部客户端或 model 只能提交不带 scope 的 pointer。server 完成解析和鉴权后,才生成可以持久化的 ref。这样可以避免调用方自行填写并声称某个 storeId 可信:
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 执行 recordBusinessProgress 或 closeTask 时,server 必须在同一个 transaction 内完成四件事:
- 要求员工填写一条简短、具体的 confirmation note。
- 生成 append-only
staff_assertionTimeline event 和稳定 ID。 - 让 progress 或 Outcome 的
evidenceRefs指向这个 event 的ResolvedEvidenceRef。 - 记录 authenticated actor。客户端不能提交或伪造 actor identity。
如果员工还说明了自己具体做过什么,例如“我在线下帮她完成预约”,同一次提交也可以生成一条 typed Activity。
如果员工只确认 progress 或 Outcome,没有声明自己做过实际工作,就不能因为他点了一次 Update/Close 而编造 Activity,也不能给他增加 staff revenue credit。
从 Current 迁到 Target 时,evidence adapter 必须按下面的规则显式转换:
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-resolvedstoreId,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_assertedprovenance 的 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
图中的业务对象之间有以下关系:
Outcome correction、reopen 和所有历史值都保存在 append-only Timeline 中。Task row 只保存 current snapshot。
5. Lifecycle 与 UI projection
5.1 数据库只保存简单的 lifecycle
Selected Target lifecycle:
status 只回答一个问题:“这件客户事项还需不需要继续处理?”
它不表示员工是否已经尝试过,也不表示员工应该在什么时候处理。
关闭 Task 时,必须同时记录以下信息:
- 一个 business
resultCode,或者一个 administrative result。 - 一个
closureReasonCode。 - verification 与 evidence。
- 谁关闭了 Task、什么时候关闭、当时使用哪一版 Policy。
5.2 下面这些是计算出来的视图,不是 Task status
no_answer、voicemail_left、sent / delivered 描述某次 Activity 的 outcome。message_sent 描述某次 Activity 的 action。它们都不是 Task status,也不是 Task Outcome。
5.3 三种时间各自回答不同的问题
修改 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;deadlineAt和firstResponseDueAt分别回答整件事期限和首次响应 SLA。dueAt只保留 legacy compatibility。recordActivityAndPlan、Next Action、deadline、priority、assignee、note、intro_bookedprogress、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
Target 不把 taskKind 做成数据库 global enum。它应该是 text key,再由 versioned Task Policy Catalog 负责 application/domain validation。否则,每增加一种业务类型都要做一次 DB enum migration。
taskKind 和 displayType 回答的是两个不同问题,不能混用:
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。
creationIdempotencyKey 和 dedupeKey 也解决不同问题:
creationIdempotencyKey防止同一个 create request retry 时产生两条记录。dedupeKey防止不同 event 为同一个 open business objective 重复创建 Task。
Managed Task 的 dedupeKey 默认由 server 生成为 contact:{normalizedPhone}:kind:{taskKind}。如果 Task 还需要绑定一个具体业务对象,再加入 Policy-defined subjectKey。lead_conversion 的 displayType 发生变化时,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 只表示“已经采用的当前计划”。它不是所有可能建议的集合。
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 时,系统才需要记录它是否被采用。例如员工执行建议中的 changeNextAction 或 closeTask。thumbs up/down 只评价建议质量,不代表建议已经被采用或驳回。
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_timeline 的 task.activity_recorded,不同时维护第二套 Activity source of truth。
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 对齐):
例如,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。
6.6 Outcome fields
Outcome 是 Task 结束时的 terminal snapshot。它不是另一个 Case,也不等于其中某一次 Activity。
所有 Task 共用以下 closureReasonCode:
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 计算。客户端只能读取,不能写回:
workState:not_started / in_progress / closed。queueState:needs_attention / scheduled / closed。overdue:boolean。firstResponseSlaState:not_due / met / missed / not_applicable。outcomeValence:positive / negative / neutral / unknown。contributionLevel:discovered / 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 从上到下判断:
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
员工也可以创建 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:
8. Task Policy Catalog
8.1 Policy 解决什么
先讲人话:每一种 Managed Task 都需要一本统一的“规则说明书”。这本说明书就是 Task Policy。它要同时告诉 AI、backend、UI 和 reporting:什么情况下能建 Task、接下来做什么、什么证据足以更新或关闭,以及最后怎样统计。
当前的问题不只是少了一张 typeCategory → closeResult 对照表。真正的问题是,同一条业务规则分散在 schema、prompt、UI、API 和 reporting 中:
因此,这整条链必须由一个 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 刚建立时,员工先做什么、多久内要响应。display和progressSignals:事实变化后,员工看到什么分类和业务进展。outcomes:哪些最终结果合法,各自需要什么证据。aiAuthority:AI 能做到哪一步;dncBehavior:客户 DNC 时这个 Task 怎么处置(close / block_outbound / keep_open_for_review,对员工和 AI 都生效)。reporting和uiMetadata:报表和界面怎样解释同一套规则。
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 已经批准。
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。
除了每种 Task 自己的业务结果,所有 Managed Task 还共享两个 administrative results。它们表示“结果未知”或“这件事已经不适用”,不是员工做失败了;reason mapping 仍然必须严格校验:
Flexible Task 没有 Managed Task 的业务结果矩阵,因此只允许下面这组最小结果:
Lead Intro 是过程中的业务进展,不是 Task 的最终结果,所以不放进 Outcome Decision Matrix:
如果 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:
第一版 revenueMappingCode allowed values 只有:
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。
如果它仍然只需要上面这些通用能力,就只需:
- 在 Policy Catalog 增加
freeze_return.v1。 - 定义 creation evidence、dedupe、outcomes、authority、reporting。
- 增加 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 里,几块存储各自负责:
换句话说,Current Target schema/runtime 已经能保存这些事实:
status = open / closed。- legacy
typeCategorycompatibility,以及 stabletaskKind + displayTypeTarget contract。 sourceType = lead / contact_analysis / manual。priority = high / medium / low。- canonical
nextActionText + nextActionAt、firstResponseDueAt/firstResponseAt、deadlineAt、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/disableddefault:新 Store authority 会 seed 为target/target,TEST Task V3 source gates 为true,而pre/prod为false。 - 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.tscallytics-infrastructure/packages/common/src/db/schema/task-progress.tscallytics-infrastructure/packages/common/src/db/schema/contact-timeline.tscallytics-infrastructure/packages/common/src/db/schema/task-suggestions.tscallytics-infrastructure/packages/common/src/db/schema/ai-task-decision-assessments.tscallytics-infrastructure/packages/common/src/db/schema/ai-task-reasoner-runs.tscallytics-infrastructure/packages/common/src/db/schema/task-objective-authority.tscallytics-infrastructure/packages/common/src/db/schema/provider-interaction-source-proof.tscallytics-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_activitiessource of truth。
9.3 tasks Expand schema
expand 的意思是:先在现有 tasks 表上增加 Target 字段,同时保留 legacy 字段做兼容;不是一次性替换线上读写。下列 columns 已在当前 source schema/migration 中定义为 nullable Expand fields;表中的 Present 不证明 production migration 已执行,也不表示 legacy columns 已可删除。
第一阶段把 current Outcome 直接存在 tasks snapshot 中,原因很简单:
- 每个 Task 同时只需要一个 current terminal Outcome。
- reopen/correction history 已由 Timeline 保存。
- 避免为尚未出现的查询需求增加
task_resolutionsjoin 和双写复杂度。
只有未来真的出现“多个并存 resolution、法律级不可变 resolution entity、复杂 supersession query”,才把 Outcome 抽成独立 table。
task_suggestions 的 Expand fields 也已在 current source schema 中定义。纯文本建议仍可只存内容;如果一条 Suggestion 代表可执行的 mutation,就必须额外保存具体 command、生成时的 Task version 和有效期:
这里要把员工看到的 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 保留“之前发生过什么”:
每条新写入都必须有 store_id。occurred_at 表示事实实际发生的时间,created_at 表示系统把它记下来的时间;两者不能混用。Activity credit 使用 staff_id,也不能把“谁录入”自动当成“谁完成了工作”。
一个 command 可能在同一个 transaction 里产生多条 Timeline events。为了既防止整个请求重复执行,又允许同一请求安全地写多条 event,幂等 identity 分成三层:
recordActivityAndPlan 就是一个例子:一次保存同时写 task.activity_recorded 与 task.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 确定性生成:
这个 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 descwhere event type is Task Activity。 - dedupe:partial unique
(store_id, dedupe_key)wherestatus='open' AND dedupe_key IS NOT NULL。phone 是 canonical E.164;Managed key 由 Policy 生成,Flexible null。 - optimistic write:所有 mutation 携带
expectedVersion,成功后 incrementaggregateVersion。 - closed integrity:closed 必须有 Outcome/admin closure;open 的 current Outcome fields 必须为空。
- lead progress integrity:
intro_booked / intro_attended不能写入 terminalresult_code;openlead_conversion的 materializeddisplay_type必须与 latest valid progress/contact evidence 一致。
store_id 必须来自 authenticated server context,或由 server 验证。AI 和客户端提交的任意 store_id 都不能直接信任。
非终态 Business Progress 写进 Timeline 时,最少需要下面这些 typed fields:
这份 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:
员工批准 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 已经处理到哪里。
这两类 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 为 true,pre/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:
10.2 Commands
Command 是系统认可的业务动作,例如“改下一步”“记录进展”“关闭 Task”。UI 可以使用更自然的按钮文案,但真正改变状态时,必须落到下面某个 command;不能让页面直接改字段。
表里有两个容易混淆的动作:
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:
Create 和 mutation 面对的并发问题不同:Create 要防止同一个目标被重复建立;mutation 要防止在旧 Task version 上覆盖新修改。因此它们的 concurrency/idempotency contract 也不同:
每次执行 command,Server 都必须按同一顺序把关:
- 从 auth context 解析允许的 stores。
- 验证 Task/Contact/Evidence 均属于同一
store_id。 - 验证 expected version、status transition、Policy version 和 field-level authority。
- 用 idempotency key 防重。
- 在一个 transaction 内更新 snapshot、追加 Timeline;AI run 才写 AI audit,只有用户明确评价时才写 suggestion feedback。
- 返回 typed projection 与 deterministic error code。
校验失败时返回稳定、可判断的 error code,而不是一段需要猜测的自然语言。核心错误示例:
10.3 高频 atomic command
员工最常见的流程是:刚完成一次联系,马上记录结果,再安排下一步。UI 应让这两件事一次保存;backend 则在一个 transaction 中执行对应的 Activity 和 Next Action changes:
对员工来说,这就是一次“保存结果和下一步”,而不是先 Log Activity、再 Update Task、再 Schedule 的三次后台操作。
员工确认线下 booking 时也是一样:一次提交 progress + plan。Public request 不允许自报 verificationType;server 根据 auth context 和同一 transaction 内生成的 staff_assertion 解析 verification:
11. AI authority
这一节必须分三层读:
- Authority:AI 有没有资格提出或执行这个动作。
- Suggestion lifecycle:暂时不能执行的 proposal 怎样安全保存、失效和审计。
- 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 是否已经变化,以及员工是否刚做过决定:
11.2 Authority matrix
11.2.1 谁能做什么
下表先只回答 authority:AI 可以直接做、只能 propose,还是完全不能做;员工是否可以做;Guard 需要检查什么。
本文只定义 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:
这里不再另造第三种“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。generatedAgainstTaskVersion、expiresAt、superseded 等 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 明确禁止 taskDecisions、visibleTaskAssessments 和 newObjectiveAssessments,不再生成 AI-driven Task mutation。
旧 Task V2 semantic path 保留在下面仅用于理解 retirement 对比,不是 current decider:
从业务推理的角度看,历史 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_turnadapter 最多两次 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 为 true,pre/prod 为 false。这不是 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,形成自触发循环:
每次 run 都保存 triggerType + triggerSourceKey + reasonerVersion 唯一键。同一 payload 被 replay 时返回既有 decision,不再重复推理或 mutation。只有新的外部 Call/SMS/Lead,或人工明确提出的新请求,才可以开始新的 run。
SMS 也不能采用“一条消息调用一次 LLM”的模型。每条 message 仍然立即、deterministic 地持久化并更新 UI;Reasoner 调用则按 storeId + threadId 聚合成 conversation window:
- 保存
lastProcessedMessageCursor与当前 high-water mark。 - 新消息重置 quiet-window timer;首版默认 2 分钟,具体值进 runtime config。
- quiet window 到期后,一次 run 只读取 cursor 之后的新 messages + compact Task context。
- 持续聊天首版设 5 分钟 max-wait,达到上限时对当前窗口运行,避免无限等待。
- run 成功/audit 落库后原子推进 cursor;失败按同一 trigger key retry,不重复 mutation。
- 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 必须分别记录
logicalTurnCount与physicalAttemptCount / 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:
每个 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:
这些名称只描述模型可以提出什么。最终 mutation 仍然必须由 Policy Guard + Task Orchestrator 执行,模型本身没有 write authority。
model-facing tool names 也不能发展成第二套 domain vocabulary。每个 proposal 必须固定映射到现有 domain command 或审计结果:
因此,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 都调用它:
- Deterministic Fast Lane 不调用 LLM。
- 事件去重、debounce 同一 call/SMS burst。
- 先传 compact context,不把完整 customer history 每次全塞进去。
- 只有 material uncertainty 才调用 read tool。
- 高风险冲突转人工,不靠无限推理“猜准”。
- 按 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 的日常路径可以按五步检查:
- 扫描队列,选出现在该处理的 Task。
- 看懂这件事为什么存在、下一步是什么、什么时候要做。
- 执行工作,例如 Call、Text 或线下处理。
- 记录结果,并在同一次操作里安排新的 Next Action。
- 只有发生业务进展或最终结果时,才确认 progress、close、correct 或 reopen。
UI 的信息顺序和按钮数量都应该服务这五步。Backend 有一个 field 或 lifecycle,并不代表前端必须为它单独画一个 section。
13.1 一张 Task card 必须回答
Task card 服务前两步:让员工不用打开 detail 就知道“这是谁、为什么现在要处理、下一步做什么、是否紧急”。例如:
员工不需要手动维护 Task status、work state、queue state、Funnel stage 四套平行状态。页面必须根据 Task、Timeline 和 Policy 自动计算这些 projection。
Lead Workbench 仍要让员工第一眼区分当前工作,不能只显示 backend 的长期 lead_conversion kind:
同一个 Task 的 card label 会随事实变化。下面三个 label 都是 displayType,不是三个 Tasks,也不会启动三套 lifecycle:
左侧 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。
- 当前
taskKindPolicy 允许哪些 Close options。 - Outcome 的 verification、evidence、AI auto-close reason,以及怎样 correction/reopen。
13.2.1 Suggestion 的交互按 effect 决定
Suggestion 在展示前就已经存在,不需要员工先批准才能看。员工可以直接参考,也可以什么都不做。UI 先判断建议会产生什么 effect,再决定是否需要按钮:
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,但默认工作面应优先放高频动作:
- 行动区:当前 Next Action、处理时间、关键 evidence,以及 Call/Text 和当前可执行的具体 Suggestion action。
- 记录区:一次完成
Activity + Next Action,并提供必要的 typed progress 入口。 - 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 的部分分开:
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” 这样的业务语言,不能把
aggregateVersion、verificationType等内部字段直接扔给员工。 - 同一个事实在主工作面只表达一次;provenance、Policy details 和教学说明应按需展开,不能挤占高频操作空间。
14. Funnel 与 Revenue projection
本节描述 Target 最终能力:怎样从证据计算 Funnel,以及怎样诚实说明业务贡献。首版不会一次实现完整 attribution 报表;MVP 首发范围见 §16.5。
14.1 Funnel 和 Task status 回答的是两件事
Funnel 回答“客户走到了哪一步”,Task lifecycle 回答“员工还要不要继续处理这件事”。不要把两者压进同一个 status。
一条 evidence 可以同时影响 Task 和 Funnel。例如,一条明确的 booking SMS 可以更新 Funnel,也可以改变 Task 的 displayType 和 Next Action;但 Task 与 Funnel 必须各自计算,不能共用一套 status。
Current 的 contacts.lead_status 也不能继续充当“万能状态”。它目前是 AI 写入的 12-value profile snapshot:
这个字段把三类不同信息混在了一起:客户进展、员工工作状态,以及停止处理的原因。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_needed 和 contacts.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:Email Lead intake,或者 Call/SMS 中出现明确 lead signal 后建立的
lead_conversionevidence chain。Current Funnel 的两条 intake lane 都必须保留;cutover 后不能只剩 Email。 - Attempted:已经发生 qualifying outbound Activity。
- Connected:出现 connected Call 或 inbound reply。
- Booked:
lead_conversion上存在仍然有效、并且有 completed booking evidence 支撑的 progress。Task 继续保持 open,同时投影displayType=booked_not_converted。 - Converted:
lead_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_explicit 或 staff_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 说明结果是怎样被确认的。不能用一个维度冒充另一个维度。
Terminal Outcome 只有同时满足以下六条,才可以标为 attributed:
- Task 在 Outcome 发生前已经存在,而且带有同店 creation evidence。
- Outcome 之前存在该 Task Policy 定义的 qualifying staff Activity。只有 AI 发现结果,或员工只点了 Close,都不够。
- Activity 与 Outcome 位于同一个
storeId + contactPhone + taskIdevidence chain。 - Activity 发生在 Policy 的
attributionWindow内,并且早于outcomeOccurredAt。使用的 window 和 Policy version 必须一起记录。 - Outcome 没有被 correction 或 reopen 推翻,并且
revenueMappingCode不是 null。 - 如果只有 AI 观察到结果、员工没有采取行动,最多只能标为
discovered,不能标为attributed。
Non-terminal Intro progress 也遵守同一套 attribution 纪律,但使用自己的字段:progressCode=intro_booked / intro_attended、progressOccurredAt 和对应的 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 完成预约
15.2 客户在线下完成预约,但系统看不到
AI 不能因为后续没有消息就推断客户已经 booked。员工在 Workbench 用一次业务操作补录系统看不到的事实:
- 选择 Update / Record result,再选择 “Intro booked”。
- Backend 写入
progressCode=intro_booked。 - Verification 写为
verificationType=staff_confirmed。 - 员工填写一条简短 confirmation note;server 在同一个 transaction 中生成
staff_assertionEvidenceRef。 - 如果员工同时声明自己完成了预约工作,同一个表单还可以记录一条 Activity;如果只确认结果,就不能凭这次 click 编造 Activity 或 attribution。
写完以后,Task 仍然 open,displayType 变成 booked_not_converted,Funnel projection 也随之更新;员工同时设置下一步。这个人工入口不是自动化失败后的 exception,而是当前 data boundary 下必须存在的正常入口。
15.3 AI 把 booking 判断错了,员工怎样纠正
如果 AI 把一句含糊的对话误标成 booked,处理过程如下:
- 员工打开这条 progress 的 evidence。
- 选择 “Booking incorrect/cancelled”。
- 系统让当前 booking evidence 失效,并重新计算
displayType=lead_follow_up。Task 从始至终都保持 open。 - 原来的 AI progress 和新的 correction 都留在 Timeline。
- 这次 correction 进入 eval/monitoring;后续 AI 不能根据旧 proposal 把同一个错误再次写回。
15.4 一个 Contact 同时有 lead_conversion 和 referral
一次 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 生成的 sourceInteractionKey 和 evidenceRefs。Call count 和 staff credit 按这个 key 去重,不能因为关联两个 Tasks 就算两次;各 Task 的 contribution 仍可分别计算。Legacy event 缺 key 时只能标 coverage unknown,不能当成 0 或用文字猜。
15.5 客户提出 Cancellation request
最后一句可能只是 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:有意向不等于已经完成
其中 AI close 是否在某个环境执行,由 automation tier、per-objective authority、runtime gate 和 fresh Guard 共同决定;本场景只规定语义,不宣告 UAT 或 PROD 已开放。当前状态见 Task V3 Rollout Status。
15.7 员工创建 Flexible Task
员工可以直接创建:
AI 可以总结相关 Call/SMS,也可以建议处理时间,但不能自动创建或关闭这个 Task。员工把它关闭为 completed 或 cancelled,并可填写 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。
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 重写计划。
先看整体改动范围:
这不是推倒重来。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 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 完成:
lead_conversion 改变 displayType 时,只更新 compatibility type_category,不能改变 taskId/taskKind/dedupeKey。如果 legacy schema 已经有 open sibling,adapter 必须在同一个 lock/transaction 中先做 reconciliation。
16.2 Legacy closeResult 怎样迁移
先把 Current 值放回正确的 Target 概念中:
Current UI 用 global positive/negative/neutral 分组,backend 和 reporting 又各自维护另一份集合;referral_obtained 的 valence 已经出现 drift。Target 删除这些手写的平行 mapping,全部由 Task Policy 生成。
Dual-write 期间,Target → Legacy compatibility mapping 也只能由同一个 adapter 负责:
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=open 和 close_result=null。绝不能借用旧的 close_result=booked 表达非终态 progress,否则 legacy writer 会再次把 booking 变成 terminal closure。
Legacy close_type = auto_closed / manual_closed / create_closed 只说明旧系统走了哪条关闭路径,不能直接当作 actor。迁移时按下面的顺序判断:
- 先读取该 Task 最新一条 closing
task.status_changedTimeline event,查看actor_type / actor_subject_id / staff_id / closeInitiator。 - 有可信 staff actor 时,写
closedByType=staff;contact-analysis/AI writer 写ai;deterministic service 写system。 create_closed只表示“创建时就关闭”,不等于 AI;auto_closed也可能来自 system rule,不能机械映射成ai。- 找不到可核验的 closing event 时,写
closedByType=import、verificationType=imported_legacy和closedBySubjectId=null。
Expand 阶段可以先把新 Target columns 设为 nullable;进入 Contract 前,必须按下面的规则完成可审计 backfill:
只有当 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”代替:
这些 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-writeclose_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_at猜nextActionAt、first-response SLA 或 deadline。 - 不能把 legacy closed 自动解释为 goal achieved。
- 不能从 AI summary 反向编造不存在的 Activity 或 evidence。
- 不跨
store_idmerge——按 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:
该 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_autoterminal seam 在 source 中存在,但 checked-inpre/prodTask 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。
- 有明确 evidence、风险低、且可以撤销的变更:系统
- 同一屏最多显示一条现在就能做的建议。执行时间晚于当前 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_idisolation,包括同一个手机号出现在多家 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 都要记录以下字段:
成本报告必须同时分开:
- 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:
这些数字只能证明 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 的决定来关闭问题。
19. Selected Target decision log
下面这些是 selected Target contract 已采用的设计选择。它们解释结构理由,但不能用来推导某项代码已部署、某个环境已启用或 rollout gate 已通过。
20. Implementation source map
下表中的路径用来核验 Current source 实现,不是 Target 已经部署或启用的证明。
产品语义仍以本文为准;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
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稳定,只让三种 LeaddisplayType随事实变化,并且 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。