Archived
活动声明了 gateWhen,发卡装配的手写清单没点它的名:落库载荷 undefined, 按钮禁用与服务端拒收一起无声失效(手写字段清单第四次丢值,与 variant 上次被丢的同一段)。抽 cardConditionGate 校验着转交(5 测);e2e w05 数据卡阶段补末端断言 expectCardGateWhen。活体验证:载荷带条件 → 闸关 → API 强行确认 400;e2e 案例 0 全绿。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -2026,6 +2026,22 @@
|
|||||||
**副产**:e2e 样例 3「原PN缺库」的前提已被污染(`LM2596S-3.3` 现在真在库里,多半是白天 ops 起单
|
**副产**:e2e 样例 3「原PN缺库」的前提已被污染(`LM2596S-3.3` 现在真在库里,多半是白天 ops 起单
|
||||||
落的主数据),那一例实际走的也是验收工单那条路 —— 名字与实测不符,清库存那件事记在这儿。
|
落的主数据),那一例实际走的也是验收工单那条路 —— 名字与实测不符,清库存那件事记在这儿。
|
||||||
|
|
||||||
|
- **「类型不一致还能点下一步」的病根:确认闸条件在发卡装配被丢 —— 手写字段清单第四次丢值**
|
||||||
|
(2026-09-01 用户:「两个芯片类型不一样,页面上方有红色提示,右边派发的卡片还可以点击下一步」):
|
||||||
|
活动白纸黑字声明了 `gateWhen`(2026-08-31 加的确认闸条件档),而**发卡装配那段手写清单**
|
||||||
|
(engines.ts 的 human 口 —— `variant` 上次被丢的同一段)逐项点名转交 `gateLedgers`/`variant`,
|
||||||
|
没点 `gateWhen`/`gateWhenSay` 的名:落库工单载荷里它是 undefined(稳定库任务 43 法证),
|
||||||
|
按钮禁用与服务端拒收读的都是它,**一起无声失效**;红带是视图画的所以照亮 ——
|
||||||
|
屏上两处说着相反的话。此前 fields / blocks / redoable 三次同款,这次的教训:
|
||||||
|
**中段是不可信卡骨架的缝,不能整包 spread,那就抽纯函数校验着转交并在 variant 的
|
||||||
|
测试位旁边钉死**(`cardConditionGate`,5 测:转交原样 / 不发明键 / 脏值不端 /
|
||||||
|
say 单独无意义 / when 可无 say)。**末端断言照 redoable 那课补上**:e2e w05 数据卡
|
||||||
|
阶段 `expectCardGateWhen` —— 卡一到手查 desk 载荷,丢了当场判失败。
|
||||||
|
活体验证(测试台真单):载荷带 `gateWhen` → detail 闸 `ready:false` 带活动那句话 →
|
||||||
|
API 强行确认 **400**;e2e w05 案例 0 全绿(released、被拒 0、模型 2 次)。
|
||||||
|
活动侧零改动(`GATE_OPEN_WHEN` 含类型不一致,mismatch 时发的必是带闸的数据卡;
|
||||||
|
⑧ 验收卡的件只读、碰不上这形)。门禁 exit 0(2595 测)。
|
||||||
|
|
||||||
- **「终止活动」接口被蒸馏吊 13–45 秒到报错:收尾不等模型**(2026-09-01 用户:「点击终止后
|
- **「终止活动」接口被蒸馏吊 13–45 秒到报错:收尾不等模型**(2026-09-01 用户:「点击终止后
|
||||||
接口迟迟不返回,还会报错,但实例确实终止了」):法证在库里 —— 四单被终止的任务,
|
接口迟迟不返回,还会报错,但实例确实终止了」):法证在库里 —— 四单被终止的任务,
|
||||||
`finished_at` 与蒸馏条目 `created_at` 差 13.2 / 36.3 / 43.4 / 44.6 秒:`finish` 把终态
|
`finished_at` 与蒸馏条目 `created_at` 差 13.2 / 36.3 / 43.4 / 44.6 秒:`finish` 把终态
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* **确认闸的条件档要在真发卡那条路上转交**(2026-09-01 用户实测:「两个芯片类型不一样,
|
||||||
|
* 页面上方有红色提示,右边派发的卡片还可以点击下一步」)。
|
||||||
|
*
|
||||||
|
* 病:发卡装配那段手写清单逐项点名转交 `gateLedgers`/`variant`,而 `gateWhen`/`gateWhenSay`
|
||||||
|
* 没被点名 —— 配方白纸黑字声明了「类型不一致不许确认」,落库的工单载荷里那两个字段是
|
||||||
|
* undefined(任务 43 法证)。红带是视图画的所以照亮;按钮禁用与服务端拒收读的都是
|
||||||
|
* 存下的 `order.gateWhen`,一起无声失效。**手写字段清单第四次丢值**(fields / blocks /
|
||||||
|
* redoable 之后),而且丢在 `variant` 上次被丢的同一段。
|
||||||
|
*
|
||||||
|
* 这个缝取的是模型也会路过的不可信卡骨架,逐项校验转交是设计 —— 所以治法不是整包 spread,
|
||||||
|
* 是把这两项的取用抽成纯函数、在 variant 的测试位旁边钉死。
|
||||||
|
*/
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { cardConditionGate } from '../wiring/engines.ts';
|
||||||
|
|
||||||
|
describe('cardConditionGate —— 条件档从卡骨架到工单载荷', () => {
|
||||||
|
it('声明了就转交,一个字不改', () => {
|
||||||
|
expect(cardConditionGate({ gateWhen: 'parts_profile_distinct_chip_type_id <= 1', gateWhenSay: '两颗料不是同一个芯片类型' })).toEqual({
|
||||||
|
gateWhen: 'parts_profile_distinct_chip_type_id <= 1',
|
||||||
|
gateWhenSay: '两颗料不是同一个芯片类型',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('没声明就什么都不带 —— 不发明键', () => {
|
||||||
|
expect(cardConditionGate({})).toEqual({});
|
||||||
|
expect(cardConditionGate({ gateLedgers: ['parts.profile'] })).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('脏值不端下去:非字符串 / 空白串一律丢', () => {
|
||||||
|
expect(cardConditionGate({ gateWhen: 42, gateWhenSay: '话' })).toEqual({});
|
||||||
|
expect(cardConditionGate({ gateWhen: ' ', gateWhenSay: '话' })).toEqual({});
|
||||||
|
expect(cardConditionGate({ gateWhen: null })).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('只有那句话没有条件 = 什么都不带(say 单独存在没有意义,评估端只认 when)', () => {
|
||||||
|
expect(cardConditionGate({ gateWhenSay: '两颗料不是同一个芯片类型' })).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('条件在、话没写:条件照转交,拒回用兜底话术', () => {
|
||||||
|
expect(cardConditionGate({ gateWhen: 'a > 1' })).toEqual({ gateWhen: 'a > 1' });
|
||||||
|
expect(cardConditionGate({ gateWhen: 'a > 1', gateWhenSay: '' })).toEqual({ gateWhen: 'a > 1' });
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -279,6 +279,24 @@ export function executionCardVariant(fromCard: unknown, fromStep: unknown): 'dat
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **确认闸的条件档从卡骨架到工单载荷**(2026-09-01 用户实测:「两个芯片类型不一样…
|
||||||
|
* 右边派发的卡片还可以点击下一步」)。配方声明了 `gateWhen`/`gateWhenSay`,发卡装配的
|
||||||
|
* 手写清单没点它们的名 —— 落库的工单载荷里两个字段是 undefined,按钮禁用与服务端拒收
|
||||||
|
* 读的都是它,一起无声失效(**手写字段清单第四次丢值**,且丢在 `variant` 上次被丢的同一段)。
|
||||||
|
* 这个缝取的是模型也会路过的不可信卡骨架,不能整包 spread —— 校验着转交:
|
||||||
|
* 条件必须是非空字符串;那句话只在有条件时才有意义(评估端只认 `when`)。
|
||||||
|
*/
|
||||||
|
export function cardConditionGate(card: Readonly<Record<string, unknown>>): { gateWhen?: string; gateWhenSay?: string } {
|
||||||
|
const when = card['gateWhen'];
|
||||||
|
if (typeof when !== 'string' || when.trim() === '') return {};
|
||||||
|
const say = card['gateWhenSay'];
|
||||||
|
return {
|
||||||
|
gateWhen: when,
|
||||||
|
...(typeof say === 'string' && say.trim() !== '' ? { gateWhenSay: say } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function decisionCardFace(
|
export function decisionCardFace(
|
||||||
input: { readonly variant?: 'review' | undefined; readonly checklist?: ReadonlyArray<string> | undefined },
|
input: { readonly variant?: 'review' | undefined; readonly checklist?: ReadonlyArray<string> | undefined },
|
||||||
step: { readonly cardVariant?: string | undefined; readonly acceptanceChecklist?: ReadonlyArray<string> | undefined } | undefined,
|
step: { readonly cardVariant?: string | undefined; readonly acceptanceChecklist?: ReadonlyArray<string> | undefined } | undefined,
|
||||||
@@ -1974,6 +1992,8 @@ export function wireEngines(deps: EngineWiringDeps): EngineWiring {
|
|||||||
...(blocks === undefined ? {} : { blocks: blocks as ExecutionWorkOrderDraft['blocks'] }),
|
...(blocks === undefined ? {} : { blocks: blocks as ExecutionWorkOrderDraft['blocks'] }),
|
||||||
...(gateLedgers.length === 0 ? {} : { gateLedgers }),
|
...(gateLedgers.length === 0 ? {} : { gateLedgers }),
|
||||||
...(variant === undefined ? {} : { variant }),
|
...(variant === undefined ? {} : { variant }),
|
||||||
|
/* 条件档(gateWhen/gateWhenSay)—— 校验着转交,见 cardConditionGate 的账 */
|
||||||
|
...cardConditionGate(input.card),
|
||||||
});
|
});
|
||||||
// 打扰率的计量对象:每发一张卡就是调了一次人 —— 不计就等于没有上限
|
// 打扰率的计量对象:每发一张卡就是调了一次人 —— 不计就等于没有上限
|
||||||
await taskStore.addConsumption(input.taskId, { humanCalls: 1 });
|
await taskStore.addConsumption(input.taskId, { humanCalls: 1 });
|
||||||
|
|||||||
@@ -615,6 +615,13 @@ const SUITES = {
|
|||||||
rowKey: 'internal_pn',
|
rowKey: 'internal_pn',
|
||||||
blankCol: 'brand_name',
|
blankCol: 'brand_name',
|
||||||
expectGateBlock: true,
|
expectGateBlock: true,
|
||||||
|
/*
|
||||||
|
* **确认闸的条件档必须跟到工单载荷上**(2026-09-01 用户实测:「两个芯片类型不一样…
|
||||||
|
* 卡片还可以点击下一步」)。配方声明了 `gateWhen`,发卡装配的手写清单没点它的名 ——
|
||||||
|
* 落库载荷里是 undefined,按钮禁用与服务端拒收一起无声失效(手写字段清单第四次丢值,
|
||||||
|
* 与 variant 上次被丢的是同一段)。这类丢值只有末端断言拦得住:卡一到手就查载荷。
|
||||||
|
*/
|
||||||
|
expectCardGateWhen: true,
|
||||||
edits: [],
|
edits: [],
|
||||||
extraTables: [
|
extraTables: [
|
||||||
/*
|
/*
|
||||||
@@ -1340,6 +1347,9 @@ async function runCase(suite, kase) {
|
|||||||
let fileCheck;
|
let fileCheck;
|
||||||
let gateLeaked = false;
|
let gateLeaked = false;
|
||||||
const gateChecked = new Set();
|
const gateChecked = new Set();
|
||||||
|
/** 确认闸的条件档(gateWhen)没跟到工单载荷上 —— 类型不一致将拦不住(末端断言,见 w05 数据卡阶段) */
|
||||||
|
let gateWhenMissing = false;
|
||||||
|
let gateWhenChecked = false;
|
||||||
let decisions = 0;
|
let decisions = 0;
|
||||||
missedRows.clear();
|
missedRows.clear();
|
||||||
asked.length = 0;
|
asked.length = 0;
|
||||||
@@ -1540,6 +1550,15 @@ async function runCase(suite, kase) {
|
|||||||
: ` ⚠️ 缺着必填还放过去了:提交闸失效`,
|
: ` ⚠️ 缺着必填还放过去了:提交闸失效`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (stage.expectCardGateWhen === true && !gateWhenChecked) {
|
||||||
|
gateWhenChecked = true;
|
||||||
|
const { body: det } = await api(`/api/desk/tasks/${taskId}`);
|
||||||
|
const wo = (det?.workOrders ?? []).find((o) => o.id === card.id);
|
||||||
|
if (wo?.gateWhen === undefined) {
|
||||||
|
gateWhenMissing = true;
|
||||||
|
console.log(' ⚠️ 确认闸的条件档(gateWhen)没跟到工单载荷上 —— 类型不一致将拦不住,判失败');
|
||||||
|
} else console.log(' 确认闸条件已随卡落库(gateWhen 在载荷上)');
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* **两阶段自定义填表**(w05「候选缺库·新建类型」专用)—— 在空手交卷的闸测过**之后**、
|
* **两阶段自定义填表**(w05「候选缺库·新建类型」专用)—— 在空手交卷的闸测过**之后**、
|
||||||
* 常规 `buildEdits` 之前跑一次:先铸 `parts.chip_type` 的自增 `type_id`,读回来才知道
|
* 常规 `buildEdits` 之前跑一次:先铸 `parts.chip_type` 的自增 `type_id`,读回来才知道
|
||||||
@@ -1675,6 +1694,10 @@ async function runCase(suite, kase) {
|
|||||||
console.log(' ⚠️ 数据工单确认即入库那道门没兑现 —— 判失败');
|
console.log(' ⚠️ 数据工单确认即入库那道门没兑现 —— 判失败');
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
|
if (gateWhenMissing) {
|
||||||
|
console.log(' ⚠️ 确认闸条件(gateWhen)被链上丢掉 —— 判失败');
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
// 「超限才叫人」是 A4 的要害:该叫的那一单没叫,等于底价门禁是摆设
|
// 「超限才叫人」是 A4 的要害:该叫的那一单没叫,等于底价门禁是摆设
|
||||||
if (missedRows.size > 0) {
|
if (missedRows.size > 0) {
|
||||||
console.log(` ⚠️ 案例点名要填的行,整例跑完也没在件上出现过(${missedRows.size} 处:${[...missedRows].slice(0, 3).join('、')}…)—— 判失败`);
|
console.log(` ⚠️ 案例点名要填的行,整例跑完也没在件上出现过(${missedRows.size} 处:${[...missedRows].slice(0, 3).join('、')}…)—— 判失败`);
|
||||||
|
|||||||
Reference in New Issue
Block a user