Commit f353f51b by luoqi

fix(分配): 重排不再丢掉主管的按客服精调 —— 「重算丢东西」第三个

「李莉这周带教,这批最多给 5 条」只活在这一版的 agentOverrides 里,而 propose 的
基线读的是**上一次已确认批次** —— 点一下「换无专属客服的患者补上」他刚设的那条就没了,
李莉照常吃满,而且不报错。

实测(上海世纪公园 · 拔牙 3 年以上):
  不带精调重排 → 胡婷 16 条
  带 maxThisBatch=5 → 胡婷 5 条, overridden=true, agentOverrides 原样回传

三处补齐:RefillProposalRequestSchema 加 agentOverrides / 控制器透传 /
前端发**当前这一版**的(已含基线合并结果, 不能只发本轮新加的,否则沿用下来的会被当成撤销)。

契约测试加了一条**通例**断言:「他在这一版上做过的事」三样一样不能少
(narrowedBy.personaTags / narrowedBy.minSpendYuan / agentOverrides)——
这是同一形状的第三个 bug, 加第四个能触发重算的载体时先回来补一行。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 163b456e
......@@ -233,6 +233,8 @@ export class AssignmentController {
...(body.personaTags ? { personaTags: body.personaTags } : {}),
// 🔴 他自己加的那一刀 —— 漏了就是**悄悄退回整格**取人(见 schema 上那段)
...(body.minSpendYuan ? { minSpendYuan: body.minSpendYuan } : {}),
// 🔴 他在这一版上设的按客服精调 —— 漏了就是重排一下"李莉只给 5 条"悄悄失效
...(body.agentOverrides ? { agentOverrides: body.agentOverrides } : {}),
...(body.targetCount ? { targetCount: body.targetCount } : {}),
...(body.expiresInDays ? { expiresInDays: body.expiresInDays } : {}),
preferPlaceable: true,
......
......@@ -51,13 +51,36 @@ describe('重算类请求 —— 人群条件必须原样带回', () => {
expect(call).not.toMatch(/criteria\.(personaTags|minSpendYuan)/);
});
test('🔴 服务端 refill 把这两个条件透传下去(漏一个就是退回整格)', () => {
test('🔴 服务端 refill 把这几项全透传(漏一个就是悄悄换了一批人 / 丢了他的精调)', () => {
const handler = /async refill\([\s\S]*?\n \}/.exec(ASSIGN_CTRL)?.[0] ?? '';
for (const k of ['personaTags', 'minSpendYuan', 'potentialTreatment', 'temperature']) {
for (const k of [
'personaTags',
'minSpendYuan',
'potentialTreatment',
'temperature',
// 🔴 他在这一版上设的按客服精调 —— 第三个"重算丢东西",2026-08-15 补
'agentOverrides',
]) {
expect({ 参数: k, 透传: handler.includes(`body.${k}`) }).toEqual({ 参数: k, 透传: true });
}
});
/**
* 🔴 **「重算丢东西」到今天是第三个**,三个的形状完全一样:
* 工具/接口调对了、序列也对,只是**发的那一头少填一格**,而两边都不报错。
* ① 人群条件(治疗项 / 时间档)→ 按全池重算,圈出另一批人
* ② 主管自己那一刀(`narrowedBy`)→ 退回整格,实测差 1,882 人
* ③ 按客服精调(`agentOverrides`)→「李莉这批只给 5 条」悄悄失效
* ⇒ 这条断言是**通例**:⛔ 加第四个能触发重算的载体时,先回来补一行。
*/
test('🔴 「他在这一版上做过的事」三样一样不能少(重算丢东西的通例)', () => {
const call = /\.refill\(\{[\s\S]*?\n \}\)/.exec(CHAT_HOOK)?.[0] ?? '';
for (const k of ['narrowedBy?.personaTags', 'narrowedBy?.minSpendYuan', 's.agentOverrides']) {
expect({ 带回: k, : call.includes(k) }).toEqual({ 带回: k, : true });
}
// 人群那两轴走 criteria 快照(模型重算的依据),单独锁在上面那条
});
test('🔴 确认单快照带着「这批人是谁」(模型重算的唯一依据)', () => {
const shape = SheetSnapshotSchema.shape.criteria.shape;
expect(Object.keys(shape)).toEqual(
......
......@@ -584,6 +584,16 @@ export function useAssistantChat() {
*/
...(s.narrowedBy?.personaTags ? { personaTags: s.narrowedBy.personaTags } : {}),
...(s.narrowedBy?.minSpendYuan ? { minSpendYuan: s.narrowedBy.minSpendYuan } : {}),
/**
* 🔴 **他在这一版上设的按客服精调也要带回**(2026-08-15 补)。
* 服务端的基线读的是**上一次已确认批次**,所以不带 = 重排一下
* 「李莉这批只给 5 条」就没了,李莉照常吃满,⛔ 而且不报错。
* ⚠️ 发**当前这一版**的(`s.agentOverrides` 已经是基线与本轮合并后的结果),
* ⛔ 别只发"本轮新加的":那样上一次沿用下来的会被当成撤销。
*/
...(Object.keys(s.agentOverrides ?? {}).length
? { agentOverrides: s.agentOverrides }
: {}),
targetCount: s.batchSize,
expiresInDays: s.expiresInDays,
})
......
......@@ -936,6 +936,19 @@ export const RefillProposalRequestSchema = z.object({
* ⇒ 契约齐了不等于传到了 —— 改这里时**顺手看一眼调用方**(use-assistant-chat 的 refill)。
*/
minSpendYuan: z.number().positive().optional(),
/**
* 🔴 **他在这一版上做的按客服精调,也要原样带回**(2026-08-15 补 —— 此前整个漏了)。
*
* 「李莉这周带教,这批最多给 5 条」是主管**当场**设的,只活在这一版的 `agentOverrides` 里。
* 而 `propose` 的基线 `resolveBaseline` 读的是**上一次已确认批次** —— 重排一下,
* 他刚设的那条就没了,李莉照常吃满,**而且不报错**。
* ⚠️ 这是"重算丢东西"的第三个:前两个是丢人群条件、丢他自己那一刀
* (见本文件 `minSpendYuan` 与 `SheetSnapshot.criteria` 上的红字)。
* ⇒ **凡是能触发重算的载体,都要把"他在这一版上做过的事"带全**。
* ⚠️ 传空对象 `{}` 表示撤掉某人的精调(`mergeOverrides` 先合并后 sanitize),
* ⛔ 别把"没传"和"传空"当成一回事。
*/
agentOverrides: z.record(z.string(), AgentOverrideSchema).optional(),
/// 基数 N 与时效原样带回,否则重排会退回"沿用上一次",人数悄悄变了
targetCount: z.number().int().positive().optional(),
expiresInDays: z.number().int().positive().max(90).optional(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment