Task V3 Implementation Audit 与修复路线

状态说明(2026-07-23):本文是 2026-07-17 的 point-in-time technical snapshot,不是当前实施授权。当前路线和术语以 Task V2 → Task V2+ → Task V3 工程审计与实施基线 为准。

Source-of-truth order:live code/schema/tests → 当前批准的 engineering baseline → design proposal → PR/issue comments。Comments 只能提示调查方向,不能证明行为已经实现。

本文记录的是 point-in-time implementation audit。Task V3 Target Design ProposalTask V3 高层设计评估 都是待评估输入,不覆盖 Task V2 当前产品行为。

2026-07-21 状态更新:§1–§2 的"现状"描述是 2026-07-17 审计快照(3ea71d82)。callytics-infrastructure main 此后已前进 19 个 commit(至 d7daee1c,含 #1489/#1494/#1530/#1560/#1565)。已对 d7daee1c 逐条验证的变化:

  • Target production seams(SMS/Call/VoiceMail consumer)已存在于 source,且 lib/config/environments.ts 中全部 taskV3*Enabled checked-in defaults 为 false(21 处,0 处 true)——§1 "production runtime 尚未调用 engine" 仅对代码中的 legacy lane 仍成立。
  • B4 已修复:nextActionText 已进入 engine snapshot/store(in-memory-store.tsneon-store.ts)。
  • Store-scope 查询已修复:queryCustomerHistory(phone, franchiseId, storeId) 强制 server-resolved storeId;Contact Profile 查询强制 authorized storeId
  • Gate A 仍未通过:TASK_V3_GATE_A_DESIGN_REVIEW.md 记录 Human-ratified fixtures = 0——§3–§4 的 harness/eval 规则仍然有效,不受快照过期影响。

其余 blocker(B2/B3/B5–B9)的逐条现状未在本次更新中重新验证,引用前须对 live code 复核。V3 proposal 当时记录的 implementation snapshot 见 Task V3 Target Design Proposal §0.1,但它不能替代新的 live-code audit。

复核命令:git log --oneline 3ea71d82..d7daee1c | wc -l(19);git grep -c 'taskV3.*Enabled: false' d7daee1c -- lib/config/environments.ts(21)。

1. Current state snapshot

审计基线:callytics-infrastructure@3ea71d82b779c4575610e29ea10dbdccad5116a5,包含已合并的 PR #1493。

当前状态不是 production Task V3:

  • 已有 Policy Catalog、纯 TypeScript Router/Guard/InMemory store 和 offline harness。
  • registry 当前只注册 lead_conversion.v1
  • schema 已 expand 出 V3 columns/tables,其中 tasks.next_action_text 已存在。
  • production runtime 尚未调用 routeTaskEvidenceguardTaskProposalInMemoryTaskStore
  • production 写路径仍由现有 task-orchestrator.ts / applyTaskAction 负责。
  • 当前 main 的 focused tests 为 Policy/Engine 113 pass、harness 74 pass;它们证明已覆盖 contract 的 mechanics,不证明下面的遗漏不存在。

可复核命令:

cd packages/common
bun test src/domain/task-policy src/domain/task-engine

cd ../..
npm run test:task-v3-harness

2. Production wiring 前 blocker ledger

B1. Contact/store identity 必须来自 server context

现状:create proposal 可携带 model-provided contactPhone;Guard 没有 authoritative contact 参数。Router 也默认 caller 已经传入正确 store/contact 的 open tasks。

风险:同店合法 evidence 可以被绑定到错误 Contact;错误 caller scope 可能让 Router reuse/bind 另一 Contact 或另一门店 Task。

目标设计

  • Guard input 明确接收 authenticated/server-resolved storeId + contactPhone
  • create payload 中的 phone 仅作 display/audit,必须与 authoritative phone 完全一致;后续 model schema 删除该字段。
  • Router 对每个 candidate task 断言 task.storeId === evidence.storeIdtask.contactPhone === authoritativeContactPhone,不满足时返回 typed reject,不能静默过滤。
  • 每条 production command 在 SQL transaction 内再次校验 store scope。

测试:arbitrary-phone create、cross-contact reuse、cross-store unique-task binding 全部 fail closed 且留 audit。

B2. Evidence 必须绑定到具体 claim

现状:resolved evidence 只有泛化的 supports.completedFact 与 evidence-level verification。Guard 分别对所有 refs 做 OR,因此一条 ref 的 verification 和另一条无关 completed fact 可以拼成通过条件。

风险:booking evidence 可以被误用来证明 conversion;两条各自不充分的 evidence 可以组成 “split proof”。

目标设计:server resolver 产生 typed claims:

type TaskEvidenceClaim =
  | { kind: 'task_creation'; taskKind: string }
  | { kind: 'business_progress'; taskKind: string; progressCode: string }
  | { kind: 'task_outcome'; taskKind: string; resultCode: string };

Guard 必须从同一 ref 或显式允许的 evidence chain 找到与 command 完全匹配的 claim,并使用该 supporting ref 的 verification/fact time。没有 matching claim 时 reject 或要求 staff input;不能退回泛化 completedFact=true

需要进一步设计:production evidence resolver 怎样从现有 structured call/SMS/staff facts 生成 claim。Regex 只能用于 candidate discovery,不能成为 authorization-grade fact。

B3. DNC 必须在 create 与执行时 fail safe

现状:Guard 只验证 compliance_stop close,没有阻止 DNC Contact 的 auto-create。Policy executable subset 也没有 dncBehavior

目标设计

  • Policy 表达 close / block_outbound / keep_open_for_review
  • DNC active 时禁止生成或执行 outbound Next Action。
  • create/approve/execute 都重新检查最新 DNC;旧 proposal 不能绕过。
  • 对无法安全创建的 proposal 留 reject/audit,不静默丢失。
  • DNC 设置、清除 future outbound plan、处理 open Tasks 必须处于一个 transaction。

B4. Next Action 必须进入 aggregate

现状:model create payload 有 nextActionText,DB schema 也有 next_action_text,但 Engine create input/snapshot/event/store 没有保存它。

目标设计

  • create 时把 payload Next Action 或 Policy initial template 写入 snapshot/event。
  • 增加 changeNextAction orchestrator command 与 task.next_action_changed event。
  • Next Action text/time/reason 一起参与 aggregate version、idempotency 和 Suggestion adoption。
  • 读 projection 不再把第一条 active AI suggestion 冒充 adopted Next Action。

B5. Suggestion adoption 必须 atomic 且 fail closed

现状:无效、wrong-task 或非 active sourceSuggestionId 会被静默跳过,但 mutation 已成功。

目标设计:mutation 前验证 Suggestion exists、same task、active、版本/expiry 合法;任何失败都不改变 Task、Suggestion 或 events。成功时 mutation + task.suggestion_decided(accepted) + supersede remaining actives 同 transaction。

B6. Business fact time 必须来自 supporting evidence

现状:progress executor 使用 run time;generic earliest-completed helper 也不能判断最早的事实是否支持当前 progress/outcome。

目标设计:matching claim 携带或指向 authority-derived factOccurredAt;unknown 保持 null,不使用 run/closed time 填充。Invalid timestamp 在 resolver boundary fail/report,而不是静默改变 business history。

B7. request_human_review 必须拆成两个 typed concept

现状:Reasoner final output 只有 disposition + proposals;driver 不读取 disposition,空 proposals 最终被写成 no_op

目标设计

type ReasonerFinalOutput =
  | { kind: 'final'; disposition: 'proposals'; proposals: [TaskMutationProposal, ...TaskMutationProposal[]] }
  | { kind: 'final'; disposition: 'staff_input_required'; request: StaffInputRequest }
  | { kind: 'final'; disposition: 'no_op'; proposals: [] };
  • 已知具体 command 但没有 AI authority:proposal → Guard → needs_staff_confirmation
  • 无法形成具体 command,且员工能补关键事实:staff_input_required
  • 工程错误、model timeout、hallucinated/cross-store evidence:retry/reject/observability,不打扰员工。
  • 不新增 Task state 或 generic approve command。

B8. Dedupe subjectKey 必须显式支持或显式拒绝

现状:Policy type 允许 subjectKeyderiveDedupeKey() 只实现 contact phone/task kind。

目标设计:dedupe input 增加 server-derived subjectKey;Policy 要求但 resolver 未提供时 typed fail closed。当前 lead policy 不受影响;未来 referral 等 kind 不能靠 model 自报 subject。

B9. Production adapter 必须复用现有 mutation authority

不能另建第二套 production Orchestrator。V3 verdict 应翻译到或扩展现有 applyTaskAction transaction,并复用其 store boundary、DNC、idempotency、Contact/Timeline audit。新 engine 负责 proposal/authority contract,不获得平行数据库写权。

3. 为什么 #1489 不是可信 Gate

3.1 当前数字说明什么

当前 provisional report 的 aggregate create/close exact match 是 56.9%。分 slice 后:

  • expected close:2/22 = 9.1%
  • no expected close:27/29 = 93.1%

它说明 close journey 有严重问题,但不能被解释为最终 production accuracy,因为 evaluator 还存在结构性偏差。

3.2 Harness 必修项

  • staff_input_required 必须保留、单独计数,不能变成 no_op
  • Fast Lane 只有真实产生 mutation/event 后才能记 executed=true
  • applyErrors 必须使 case/Gate fail。
  • 所有额外 decision 都进入评分;不能只处罚 create/progress/close 子集而忽略错误 Activity/reuse。
  • MVP-supported corpus 与 future/unsupported task kinds 分开报告。
  • SMS 按 settled-window/high-water/lease 重放,不能每条 message 都假装一次独立 production run。
  • 两条 lane 使用可比 temporal contract;无法公平比较时只报告各自绝对质量,不宣称谁更优。
  • 除相对 Task V2 指标外,必须有绝对 create、close、DNC、scope、staff-burden thresholds。
  • Network/schema/model errors 进入固定 error policy;不能通过缩小 denominator 提高分数。

3.3 Eval integrity:禁止为了分数作弊

以下规则是 Gate 的硬约束:

  1. Regex 不得生成最终 ground truth 或 authorization-grade business claim。
  2. 不得从 expected[] 反推 evidence claims、Reasoner output 或 Guard trust。
  3. 不能为已知 case、手机号、fixture id 或固定短语 hardcode答案。
  4. corpus、labels、criteria、scoring 在 run 前冻结;看到成绩后不能删难题、改分母或降低阈值。
  5. 不用 majority vote 掩盖 first-run instability;可以另报 stability,但 first-run 仍保留。
  6. 不把 extra decision、staff input、apply failure 或 unsupported kind 隐藏为 no_op
  7. 不以“比 Task V2 高”代替绝对质量;例如 close 2/22 必须 fail。
  8. Prompt/Policy/Guard 迭代在 development corpus 上完成,最终用未参与调参的 holdout corpus 验证。
  9. 所有失败 case 保留并进入 regression set,不能在下一轮消失。
  10. 宁可分数真实下降,也不允许 evaluator 与 implementation 共同迎合已知答案。

当前 fixture-builder.ts 中的 completed-fact regex 只能保留为 corpus-review candidate 标记,不能再直接写入 Guard 会信任的最终 supports

4. Eval pyramid:不同测试回答不同问题

层级输入回答的问题优点局限
Contract/unit tests手工边界输入Policy/Guard/store 是否满足确定性 invariant快、可穷举安全边界不证明模型在真实故事上正确
Synthetic snapshot eval人工构造的一次全量快照Prompt/taxonomy 在已知场景上是否稳定回归可控、便于覆盖罕见边界时间过程被压平,容易被过拟合
Longitudinal replay一个 Contact 按时间发生的 Call/SMS/Lead/Task 序列create/reuse/progress/close journey 是否在正确时间发生更接近 lifecycle 和 production trigger标注、cadence、公平比较更难
Production shadow真实 trigger 和 production resolver,authority off实际流量、staff burden、drift、cost、correction rate真实性最高且不直接改状态需要稳定 wiring、观测窗口和人工复核

旧 Golden 的约 93% 属于 synthetic snapshot prompt regression;#1489 属于 longitudinal replay baseline。两者 corpus、trigger cadence、state、taxonomy 和 scoring 不同,不能用百分比直接相减。正确策略是保留四层,而不是选一个数字当“准确率”。

5. 实施 waves 与 exit criteria

Wave A:Domain safety contract

实现 B1–B8,并补 adversarial/property tests。Exit:任意 model payload 都无法绕过 authoritative contact/store、DNC、claim binding、Suggestion atomicity 或 fact time。

Wave B:Harness truthfulness

修正 disposition、Fast Lane execution、apply failure、full scoring、MVP/future split、SMS cadence 和 absolute thresholds。Exit:每个 decision、reject、staff input、apply error 都能从 report 追到 evidence/Policy/Guard/command。

Wave C:重新冻结并运行 Gate

人工 ratify labels;建立 development/holdout split;从 current head 完整 rerun。Exit:绝对 safety/quality thresholds 通过,且 results 不依赖 post-hoc corpus/threshold 变化。

Wave D:Production shadow adapter

基于现有 applyTaskAction 实现 DB/evidence adapter;先只写 run/proposal/audit,mutation authority 关闭。Exit:test/store canary 中 reconciliation 无跨店、无 silent drop、无非原子 adoption。

Wave E:Read projection 与 store rollout

接 typed API/Workbench,先 read projection,再按 store 开 mutation authority,最后 reporting。Notification、RingOut 产品化和 Scripts customization 仍不在本轮 scope。

6. 需要人工参与的事项

工程修复可以立即进行;下面几项不能由代码替用户决定:

  • ratify longitudinal fixture 的 expected decisions 与 evidence claims。
  • 选定 production Gate 的绝对 close/create/staff-burden thresholds。
  • 决定 email-only/no-phone Contact 的 canonical identity。
  • 确认 staff_input_required 的前端 placement,但这不改变其 domain contract。

这些事项未完成时,系统可以继续开发和 shadow,但不能宣称 Gate 已清或打开 production authority。