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

# Task V3 Implementation Audit 与修复路线

> **状态说明（2026-07-23）**：本文是 2026-07-17 的 point-in-time technical snapshot，不是当前实施授权。当前路线和术语以 [Task V2 → Task V2+ → Task V3 工程审计与实施基线](/product-design/v2/tasks-feature/task-v2-plus-engineering-audit.md) 为准。
>
> 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 Proposal](/product-design/v3/tasks-feature/task-domain-lifecycle.md) 和 [Task V3 高层设计评估](/product-design/v3/tasks-feature/task-v3-design-evaluation.md) 都是待评估输入，不覆盖 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.ts`、`neon-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](/product-design/v3/tasks-feature/task-domain-lifecycle.md)，但它不能替代新的 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 尚未调用 `routeTaskEvidence`、`guardTaskProposal` 或 `InMemoryTaskStore`。
- production 写路径仍由现有 `task-orchestrator.ts` / `applyTaskAction` 负责。
- 当前 main 的 focused tests 为 Policy/Engine `113 pass`、harness `74 pass`；它们证明已覆盖 contract 的 mechanics，不证明下面的遗漏不存在。

可复核命令：

```bash
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.storeId` 且 `task.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：

```ts
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`。

**目标设计**：

```ts
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 允许 `subjectKey`，`deriveDedupeKey()` 只实现 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。
