Archived
fix(w05): 重判的行由确定的一侧圈 —— 打回点名工作温度,不许去改静态电流
用户实测:「我打回让修改工作温度的结论,给我改了静态电流的结论」。库里的证据一字不差: 工作温度它没改(还给了理由:候选值 1℃ 远窄于 -40~+85℃,硬约束不满足 —— 这半截是对的), 而没人问过的静态电流被它从 soft_mismatch 翻成 match,evidence_note 里却写着「维持原判」。 根因不是提示词不够严,是**整表交出去、整表写回来**:没有任何东西限制它只动被点名的那一行, 而「我只改了那一条」这句话本身也出自它嘴里。规范 §4.6 纪律一(行由确定的那一侧决定, 不由 AI 决定)—— 昨天刚加的重判轮自己栽在这条上。 S3 复核轮两笔变四笔(全部只在 case_redo_note != "" 那一轮跑): ① 圈范围:按字面点名(contains,与基座给模型摆的「意见点名对上了哪几行」同一种认法)标 named / kept(明细表新增 redo_scope 列); ② 兜底:一条都没点到(意见说的是整批)→ 整表算被点名,读上一笔刚写下的 tally,不重算; ③ 重判只喂 named 的行 —— 没点名的行 AI 连见都见不到,自然改不着(也省 token); ④ 写回也只写这几行;模型漏答某一条时 else 兜回它上一轮的值。 同批修一条挡了两次路的老毛病(昨夜刚记 debt,今晚又撞停、还卡住了我自己的验证): 人答得比发卡那一拍收尾还快时,回传被判 stale、wake 又被那一拍的 park 覆盖,单子停在 awaiting-event 再没人叫醒。deliver 判了 stale 照旧不跑这一拍,但把停着的单子拨回 running。 验收:e2e w05 5/5 released、被拒 0;案例 0 新断言「打回只动了被点名的那几条,其余行一个字没变」 真跑通过(途中逼出脚本自己的坑:放行会清空草稿层,快照得留在放行之前); 模型调用 案例 0 = 2 次、其余 = 1 次。四处变异验证。W05@2.18.0 已装库。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -709,6 +709,16 @@ const SUITES = {
|
||||
* 这一趟往返没被揉坏——是内容正确性检查,不是归属检查。
|
||||
*/
|
||||
expectFinalValue: { address: 'substitution.relation', where: 'candidate_internal_pn', equals: 'XL2596S-5.0', column: 'recommendation_level', contains: 'conditional' },
|
||||
/*
|
||||
* **打回只许动被点名的那几条**(2026-09-01 用户实测:「我打回让修改工作温度的结论,
|
||||
* 给我改了静态电流的结论」)。这条意见点名的是「使能阈值」——
|
||||
* 交回之前先把明细存一份,收工时逐行比:名字没出现在意见里的行,四态与三段叙述一个字都不许变。
|
||||
*/
|
||||
expectRedoTouchesOnlyNamed: {
|
||||
address: 'substitution.difference_item',
|
||||
nameCol: 'parameter_name',
|
||||
cols: ['difference_status', 'impact_statement', 'evidence_note', 'unknown_disposition'],
|
||||
},
|
||||
/*
|
||||
* **跨案例隔离**(2026-08-30,协调者裁定①)—— 这一例为了让 `distinct=1` 成立,
|
||||
* 把**原 PN(LM2596S-5.0,案例 1/2 也在用它)**转挂到新建的 type_id=90;这一改
|
||||
@@ -1339,6 +1349,10 @@ async function runCase(suite, kase) {
|
||||
let dataCardSeen = false;
|
||||
/** w05「都在库」:匹配**验收工单**那一段的执行卡出现过 —— 数据齐了也要叫人一次 */
|
||||
let acceptCardSeen = false;
|
||||
/** 打回之前的明细快照(`expectRedoTouchesOnlyNamed`)—— 只存第一份 */
|
||||
let redoBefore;
|
||||
/** 放行前的最后一份明细快照 —— 放行会清空草稿层,收工时取不到 */
|
||||
let redoAfter;
|
||||
/** 那张卡的**面孔**不对(标题是验收工单,发出去却是执行工单)—— 2026-08-31 真撞过一次 */
|
||||
let acceptFaceWrong = false;
|
||||
/** w05「类型不一致·硬拦」:`sub_eval.case.gate_reason` 命中过 `kase.expectGateReason` 那个词 */
|
||||
@@ -1446,6 +1460,19 @@ async function runCase(suite, kase) {
|
||||
gateReasonMatched = caseRow !== undefined && kase.expectGateReason.test(String(caseRow['gate_reason'] ?? ''));
|
||||
}
|
||||
|
||||
/*
|
||||
* 打回之前把明细存一份(2026-09-01)—— 收工时拿它逐行比「没点名的行有没有被动过」。
|
||||
* 存在**答卡之前**:答完那一拍配方就重判了,再存就晚了。
|
||||
*/
|
||||
if (kase.expectRedoTouchesOnlyNamed !== undefined) {
|
||||
const rows = draftRows(body.drafts ?? [], kase.expectRedoTouchesOnlyNamed.address);
|
||||
/* 第一份 = 打回之前(S2 刚判完那一版);此后每轮覆盖,**留住放行前的最后一份** ——
|
||||
放行会把草稿并进正式表并清空,收工时再取就什么都没有了(实测踩到) */
|
||||
if (rows.length > 0) {
|
||||
if (redoBefore === undefined) redoBefore = rows.map((r) => ({ ...r }));
|
||||
redoAfter = rows.map((r) => ({ ...r }));
|
||||
}
|
||||
}
|
||||
// 决策卡挂在对话区,不在这一摞里 —— 每轮顺手看一眼有没有等着人点的
|
||||
decisions += await answerDecisions(taskId, kase, decided);
|
||||
await answerEntries(taskId, kase, entered);
|
||||
@@ -1678,6 +1705,30 @@ async function runCase(suite, kase) {
|
||||
console.log(' ⚠️ 这一例没有要补的数据,却派出了一张数据工单 —— 判失败');
|
||||
ok = false;
|
||||
}
|
||||
if (kase.expectRedoTouchesOnlyNamed !== undefined) {
|
||||
/* `address` 在轮询那一头取快照时用过,这儿只比对已经存下来的两份 */
|
||||
const { nameCol, cols } = kase.expectRedoTouchesOnlyNamed;
|
||||
const note = (kase.decideSeq ?? []).find((d) => String(d).startsWith('custom:')) ?? '';
|
||||
const after = redoAfter ?? [];
|
||||
if (redoBefore === undefined || redoBefore.length === 0 || after.length === 0) {
|
||||
console.log(' ⚠️ 打回前后的明细没取到,「只动被点名的行」这条没验成 —— 判失败');
|
||||
ok = false;
|
||||
} else {
|
||||
const moved = [];
|
||||
for (const row of after) {
|
||||
const name = String(row[nameCol] ?? '');
|
||||
// 被点名的那几行本来就该变
|
||||
if (name === '' || note.includes(name)) continue;
|
||||
const was = redoBefore.find((r) => String(r[nameCol] ?? '') === name);
|
||||
if (was === undefined) continue;
|
||||
for (const col of cols) if (String(was[col] ?? '') !== String(row[col] ?? '')) moved.push(`${name}.${col}`);
|
||||
}
|
||||
if (moved.length > 0) {
|
||||
console.log(` ⚠️ 打回只点了名的那几条,没点名的行却被改了:${moved.join('、')} —— 判失败`);
|
||||
ok = false;
|
||||
} else console.log(' 打回只动了被点名的那几条,其余行一个字没变');
|
||||
}
|
||||
}
|
||||
if (kase.expectAcceptCard && !acceptCardSeen) {
|
||||
console.log(' ⚠️ 数据齐了也该叫人一次(只读的验收工单),一张都没见到 —— 判失败(多半是 S1 又被拍首的 doneWhen 扫描收掉了)');
|
||||
ok = false;
|
||||
|
||||
Reference in New Issue
Block a user