产品 2026-08-02 的六条裁决,本刀落三条(容量默认 50 / 低意愿门不做 / 不看 plan_executions 是下游口径,随 P3、P5 落)。 ## 到期自动回池(产品:也是给主管减负) 新 AssignmentExpiryScheduler,**默认开**(PAC_ASSIGNMENT_EXPIRY=off 可关)—— 注意与 PAC_PLAN_AUTO_RECYCLE(默认关)相反,spec 里有断言防搞混。 不做的后果不是"多几条过期单",是**容量口径整体失效**:客服在手只增不减, 几批之后全员触顶、再分就分不下去,而这在主管看来就是分配功能坏了。 四条纪律,全部有断言: ·⭐ ⭐ **照抄 recycle-scheduler 的 snoozedUntil 守卫** —— 客服约了 6/10 回访、 plan 已 snooze 到那天,到期也不能收。收了就是系统主动毁掉客服对患者的承诺, 而且单子还会被别人从池里捞走。已本地实测:一条过期但 snoozed 的单**没被动**。 ·⛔ **不写 release_reason** —— 那列只属于客服的处置。退回是"客服看了判断不该我做", 到期是"客服压根没动",混进一列则退回率的分子分母一起虚高,而"到期未动"这个数 本身才是主管要的信号(分多了?人不在?)。到期量单独从账本按 reason 数。 ·⛔ 不清批次归因三列(清了这批的分母就少一条)。 · 新 PlanEventReason.ASSIGNMENT_EXPIRED,不复用 timeout(那是认领超时,另一条路)。 判定时刻可注入(`runExpiry(at?)`),照 sync-incremental 刚立的规矩 —— 测试不靠真实时钟凑时间差。第一版没这么写,当场就复现了同一类 flake。 ## 五列不可逆快照(迁移 B) 对抗压测抓出来的:我原以为"拆四值枚举"解决了不可逆问题,**那是错的**。 真正补不回来的是快照 —— 有了它四个值全可推导,反过来不成立。 dedicated_cs_at_assign preferences.dedicatedCs 是 upsert 覆盖的当前值 dedicated_cs_last_visit_at⭐ 记**证据不是结论**:"在岗"是滚动窗口判定, 今天在岗的人半年后回查变离岗,存 boolean 就无法 按分配当时的口径重算(而回访表还在被 reparse 重摄) priority_score_at_assign 引擎在 reason 未变时**就地改分、不升版本、不留痕** source_confidence_at_assign 它是 score 里的 2× 乘子,不留就分不清"按分选人" 实际是不是在"按诊断来源选人" selection_mode rank/explore。探索配额是整套系统里**唯一的因果抓手** (入选本身与结果相关,纯观察数据解不开),不标记等于白留⚠ ️⚠ ️ 五列**已同步加进引擎的无条件继承集合**。漏了比不加更危险:引擎重出版本 由数据变化触发 → 与患者活跃度相关 → 与完成率相关,缺失是**系统性偏向**的, 半年后拿到一列 70% 填充率的快照,看着还能用,算出来的结论是错的。 ## 写路径改用 VALUES join,不再按桶 updateMany 快照列逐行不同,updateMany 的 data 全桶共用表达不了;逐行 update 是 500 次往返。 一条 `UPDATE ... FROM (VALUES ...)` 两个问题一起解决,且 RETURNING 直接给出 哪些行真落上了,不必再回查一次猜差额。9 变量/行 × 500 = 4,500,远低于上限 32,767。⚠ ️ 走原生 SQL 的两个代价手工补上:`@updatedAt` 不触发 → 显式 SET;where 自己写全。 ## 一致性硬闸(实测踩出来的) 端到端时发现:一条患者的专属客服是 576,却被标 `dedicated` 分给了 832 —— 标签与事实矛盾,而服务端**刚刚把当时的专属客服取到手**,这是可验证的。 不拦的话 T20 按 assign_strategy 分组时,一批实际铺平的单顶着 dedicated 标签混进 "专属完成率",**没有任何报错**。⚠ ️ 只拦这一个方向:spread_*/manual 依赖容量与主管意图,服务端不知道,仍以调用方为准 (错了也能靠 dedicated_cs_at_assign 事后重算 —— 这正是那列的价值)。 ## 验证 866 单测(新增 9 条到期断言)+ 本地真实数据端到端: 快照五列落库 selection_mode=rank/explore、pscore=94.8/91.8、conf=1.0、 dcs=832/576、dcs_last=2026-06-24✅ 标签不符 → 10001 拒绝,不落库 到期回收 过期无约定 → 回池(status=active、期限清空、**批次归因三列全在**、 release_reason 为空);账本 auto_release + assignment_expired⭐ snoozed 守卫 过期**但约了回访** → 未被收走,仍 assigned✅ 未过期的两条 未动 零 drift(diff 只剩两条与本次无关的既有项)。测试数据已清理。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>