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:
@@ -406,12 +406,11 @@ describe('S3 / S4 配方', () => {
|
||||
const s4 = stepOf('T_release')?.auto?.do ?? [];
|
||||
const s2 = stepOf('S2_assess')?.auto?.do ?? [];
|
||||
|
||||
it('S3 七笔:复核重判 → 落明细 → 明细重算 → 关系 → 验证记录 → 卡 → 交代', () => {
|
||||
expect(s3.map((a) => a.kind)).toEqual(['analyze', 'project', 'project', 'project', 'project', 'card', 'say']);
|
||||
expect(s3[1]?.kind === 'project' && s3[1].into).toBe('substitution.difference_item');
|
||||
expect(s3[2]?.kind === 'project' && s3[2].into).toBe('substitution.difference_item');
|
||||
expect(s3[3]?.kind === 'project' && s3[3].into).toBe('substitution.relation');
|
||||
expect(s3[4]?.kind === 'project' && s3[4].into).toBe('validation.record');
|
||||
it('S3 九笔:圈范围 → 兜底扩范围 → 复核重判 → 落明细 → 明细重算 → 关系 → 验证记录 → 卡 → 交代', () => {
|
||||
expect(s3.map((a) => a.kind)).toEqual(['project', 'project', 'analyze', 'project', 'project', 'project', 'project', 'card', 'say']);
|
||||
for (const at of [0, 1, 3, 4]) expect(s3[at]?.kind === 'project' && s3[at].into).toBe('substitution.difference_item');
|
||||
expect(s3[5]?.kind === 'project' && s3[5].into).toBe('substitution.relation');
|
||||
expect(s3[6]?.kind === 'project' && s3[6].into).toBe('validation.record');
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -422,25 +421,56 @@ describe('S3 / S4 配方', () => {
|
||||
* 只在被打回的那一轮跑:`case_redo_note` 是引擎注给配方的那句话(没打回就是空串)——
|
||||
* 常路(第一次送审)这两笔跳过,整个活动仍然只调一次模型。
|
||||
*/
|
||||
it('复核轮:两笔都钉在「人打回了才跑」上,判法与第一轮同一份规矩,入参多带上一轮结论与他的意见', () => {
|
||||
const redo = s3[0];
|
||||
const land = s3[1];
|
||||
expect(redo?.kind === 'analyze' && redo.when).toBe('case_redo_note != ""');
|
||||
expect(land?.kind === 'project' && land.when).toBe('case_redo_note != ""');
|
||||
it('复核轮四笔都钉在「人打回了才跑」上 —— 常路一笔都不跑,整单仍只调一次模型', () => {
|
||||
/* `when` 不是每一种笔都有的键 —— 按「有没有这个键」取,不做属性访问(与本文件既有的 `'prune' in …` 同一写法) */
|
||||
const whenOf = (at: number): unknown => {
|
||||
const act = s3[at];
|
||||
return act !== undefined && 'when' in act ? act.when : undefined;
|
||||
};
|
||||
for (const at of [0, 1, 2, 3]) expect(String(whenOf(at) ?? ''), `第 ${at} 笔`).toContain('case_redo_note != ""');
|
||||
/* 后面那几笔(重算 / 关系 / 验证)照旧每一轮都跑 —— 人就地改了四态也要跟着重算 */
|
||||
for (const at of [4, 5, 6]) expect(whenOf(at), `第 ${at} 笔`).toBeUndefined();
|
||||
});
|
||||
|
||||
/*
|
||||
* **谁可以变,由确定的一侧圈**(2026-09-01 用户实测:「我打回让修改工作温度的结论,给我改了
|
||||
* 静态电流的结论」)—— 第一版把整表交给 AI 重判、整表写回,没有任何东西限制它只动被点名的
|
||||
* 那一行;它改了没人问过的那条,`evidence_note` 里还写着「维持原判」。
|
||||
* 现在:①按字面点名圈出 `named`;③入参**只有** `named` 的行;④写回也只写这几行。
|
||||
*/
|
||||
it('圈范围:点名的标 named,其余 kept;一条都没点到时整表算被点名', () => {
|
||||
const mark = s3[0];
|
||||
const widen = s3[1];
|
||||
const markFields: Readonly<Record<string, unknown>> = mark?.kind === 'project' ? mark.fields : {};
|
||||
expect(markFields['redo_scope']).toEqual({ when: 'contains(case_redo_note, parameter_name)', then: 'named', else: 'kept' });
|
||||
/* 兜底那一笔读的是上一笔刚写下的 tally 读数 —— 不重新算一遍谁被点到 */
|
||||
expect(widen?.when).toBe('case_redo_note != "" and substitution_difference_item_redo_scope_named = 0');
|
||||
expect(widen?.kind === 'project' ? widen.fields['redo_scope'] : undefined).toBe('named');
|
||||
});
|
||||
|
||||
it('重判与写回都只认 named 的行 —— 没点名的行 AI 连见都见不到,自然改不着', () => {
|
||||
const redo = s3[2];
|
||||
const land = s3[3];
|
||||
const named = { each: 'mine:substitution.difference_item', where: { redo_scope: 'named' } };
|
||||
expect(redo?.kind === 'analyze' ? redo.input.from : undefined).toEqual(named);
|
||||
expect(land?.kind === 'project' ? land.from : undefined).toEqual(named);
|
||||
/* 判法一处真源:复核指令以第一轮那份规矩打头,后面才接复核纪律 */
|
||||
const first = s2[0];
|
||||
const rules = first?.kind === 'analyze' ? first.instruction : '';
|
||||
const rules = s2[0]?.kind === 'analyze' ? s2[0].instruction : '';
|
||||
expect(redo?.kind === 'analyze' && redo.instruction.startsWith(rules)).toBe(true);
|
||||
expect(redo?.kind === 'analyze' && redo.instruction).toContain('原样带回');
|
||||
/* 入参:上一轮四态 + 他写的意见 —— 少了任何一样,重判要么冲掉人改过的行,要么读不到意见 */
|
||||
const fields = Object.keys(redo?.kind === 'analyze' ? redo.input.fields : {});
|
||||
/* 入参:上一轮结论 + 他写的意见 —— 少了任何一样,重判要么无据可依,要么读不到意见 */
|
||||
const fields: Readonly<Record<string, unknown>> = redo?.kind === 'analyze' ? redo.input.fields : {};
|
||||
for (const key of ['last_status', 'last_impact', 'last_evidence', 'last_unknown_disposition', 'reviewer_note']) {
|
||||
expect(fields).toContain(key);
|
||||
expect(Object.keys(fields)).toContain(key);
|
||||
}
|
||||
expect(redo?.kind === 'analyze' && redo.input.fields['reviewer_note']).toEqual({ in: 'case_redo_note' });
|
||||
/* 落明细那一笔与第一轮同一份投影(只多一个 when)—— 各写一份早晚分叉 */
|
||||
const s2Land = s2[2];
|
||||
expect(land?.kind === 'project' && land.fields).toBe(s2Land?.kind === 'project' ? s2Land.fields : undefined);
|
||||
expect(fields['reviewer_note']).toEqual({ in: 'case_redo_note' });
|
||||
/* 模型漏答某一条:兜回这一行上一轮的值 —— 漏答不该变成「把它清空」 */
|
||||
const landFields: Readonly<Record<string, unknown>> = land?.kind === 'project' ? land.fields : {};
|
||||
for (const col of ['difference_status', 'impact_statement', 'evidence_note', 'unknown_disposition']) {
|
||||
expect(landFields[col]).toMatchObject({ lookup: 'result:items', else: { col } });
|
||||
}
|
||||
/* 派生列与两处创建笔共用同一份 when 链 */
|
||||
const s2Fields: Readonly<Record<string, unknown>> = s2[2]?.kind === 'project' ? s2[2].fields : {};
|
||||
expect(landFields['judgement_class']).toBe(s2Fields['judgement_class']);
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -450,7 +480,7 @@ describe('S3 / S4 配方', () => {
|
||||
* `s3[0]` 必须是**排在关系 / 验证记录投影之前**的差异明细重算笔。
|
||||
*/
|
||||
it('差异明细重算笔:只列主键与两个派生列,不碰 difference_status 自己、也不碰人能改的叙事列', () => {
|
||||
const recompute = s3[2];
|
||||
const recompute = s3[4];
|
||||
expect(recompute?.kind === 'project' && recompute.from).toEqual({ each: 'mine:substitution.difference_item' });
|
||||
/* `recompute` 经 `s3[0]` 的 tuple 定位精确 narrow 到这一笔自己的字面量类型,它压根没有
|
||||
`prune` 这个键(不是「值为 undefined」,是键不存在)—— `in` 判存在性,不做属性访问 */
|
||||
@@ -468,7 +498,7 @@ describe('S3 / S4 配方', () => {
|
||||
* 展开)——各写一份早晚分叉,而分叉的那一刻屏上会同时摆着两个互相矛盾的规则。
|
||||
*/
|
||||
it('S3 重算笔与 S2 差异明细创建笔共用同一份 judgement_class / risk_level when 链', () => {
|
||||
const s3Fields = s3[2]?.kind === 'project' ? s3[2].fields : undefined;
|
||||
const s3Fields = s3[4]?.kind === 'project' ? s3[4].fields : undefined;
|
||||
const s2Fields = s2[2]?.kind === 'project' ? s2[2].fields : undefined;
|
||||
expect(s2Fields?.['judgement_class']).toBeDefined();
|
||||
expect(s3Fields?.['judgement_class']).toBe(s2Fields?.['judgement_class']);
|
||||
@@ -489,7 +519,7 @@ describe('S3 / S4 配方', () => {
|
||||
* 「人改四态 → 等级跟着变」这条链不能断),且与 S2 那份是**同一个 when 链引用**(不许分叉)。
|
||||
*/
|
||||
it('**S3 的关系笔只重算判定** —— 收口四格(理由/摘要/风险/验证要求)一列都不许出现,否则人改完一保存就被冲掉', () => {
|
||||
const relation = s3[3];
|
||||
const relation = s3[5];
|
||||
expect(relation?.kind).toBe('project');
|
||||
const fieldsOf = (act: (typeof s3)[number] | undefined): Readonly<Record<string, unknown>> =>
|
||||
act?.kind === 'project' ? act.fields : {};
|
||||
@@ -504,7 +534,7 @@ describe('S3 / S4 配方', () => {
|
||||
/* 主键带回:同 PK 覆盖,不是新开一行 */
|
||||
expect(fields['substitution_relation_id']).toBeDefined();
|
||||
/* 验证记录那一笔仍与 S2 同一份(它不涉及人可改的格子) */
|
||||
expect(s3[4]).toBe(s2[4]);
|
||||
expect(s3[6]).toBe(s2[4]);
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -520,7 +550,7 @@ describe('S3 / S4 配方', () => {
|
||||
});
|
||||
|
||||
it('S3 的卡:审核面孔、单一选项、挂本步的件', () => {
|
||||
const card = s3[5];
|
||||
const card = s3[7];
|
||||
expect(card?.kind === 'card' && card.as).toBe('decide');
|
||||
expect(card?.kind === 'card' && card.variant).toBe('review');
|
||||
expect(card?.kind === 'card' ? card.options ?? [] : []).toHaveLength(1);
|
||||
|
||||
@@ -428,17 +428,56 @@ const DIFFERENCE_ITEM_FROM_RESULT = {
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* **人把结论打回时,意见要交回给 AI 重判 —— 这是整个活动第二次(也是最后一次)出场**
|
||||
* (2026-09-01 用户定:「重做后是否没有将重做原因带回去重新让 ai 重跑这一步?」——
|
||||
* 当时确实没有:S3 那一轮只把派生列重算一遍,人写的那句话除了留痕之外无人读)。
|
||||
* ── 复核轮(人把结论打回那一轮)的四笔 ──────────────────────────────────
|
||||
*
|
||||
* 只在被打回的那一轮跑:`case_redo_note` 是引擎注给配方的「人这次打回写了什么」
|
||||
* (没打回就是空串,永远绑得上 —— 见 `auto-runner.ts` 的 `AutoContext.redoNote`)。
|
||||
* **背景**:2026-09-01 第一版做成了「把意见连同整表交回给 AI 重判」,当天就被用户实测打脸:
|
||||
* 「我打回让修改**工作温度**的结论,给我改了**静态电流**的结论」。库里的证据一字不差 ——
|
||||
* 工作温度它没改(还给了理由:候选值 1℃ 远窄于 -40~+85℃,硬约束不满足),
|
||||
* 而没人问过的静态电流被它从 soft_mismatch 翻成了 match,`evidence_note` 里却写着「**维持原判**」。
|
||||
*
|
||||
* **入参比第一轮多两样**:①上一轮的结论(四态 / 影响 / 依据 / UNKNOWN 去向)——
|
||||
* 人可能已经就地改过几行,那也是「上一轮结论」的一部分;②他这次写的意见。
|
||||
* 判法本身与第一轮**同一份** `ASSESS_RULES`,后面只接一段复核纪律:
|
||||
* **没被点到、也确实没判错的行原样带回**,别趁重判把人改过的东西悄悄冲掉。
|
||||
* **根因不是提示词不够严,是行由 AI 决定**:整表交出去、整表写回来,没有任何一样东西限制它
|
||||
* 只动被点名的那一行;「我只改了那一条」这句话本身也出自它嘴里,而它说的不作数。
|
||||
* 这正是规范 §4.6 纪律一 —— **行由确定的那一侧决定,不由 AI 决定**。
|
||||
*
|
||||
* **治法**:先由确定的一侧圈出「这一轮谁可以变」(①②),再把**只有这几行**交给 AI(③),
|
||||
* 写回也只写这几行(④)。没被点名的行 AI 连见都见不到,自然也就改不着。
|
||||
*/
|
||||
|
||||
/** ① 圈范围:人打回的那句话里点了名的行标 `named`,其余 `kept` —— 与基座给模型摆的「意见点名对上了哪几行」同一种认法(字面包含) */
|
||||
const REDO_SCOPE_MARK = {
|
||||
kind: 'project' as const,
|
||||
when: 'case_redo_note != ""',
|
||||
into: 'substitution.difference_item',
|
||||
from: { each: 'mine:substitution.difference_item' },
|
||||
fields: {
|
||||
substitution_relation_id: { col: 'substitution_relation_id' },
|
||||
difference_item_id: { col: 'difference_item_id' },
|
||||
redo_scope: { when: 'contains(case_redo_note, parameter_name)', then: 'named', else: 'kept' },
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* ② 一条都没点到 → 整表都算被点名(「结论整体判得太松」这类意见说的是整批,不是某一条)。
|
||||
* 读的是上一笔刚写下的 tally 读数(每一笔之前刷读数)—— 不重新算一遍谁被点到,同一件事只许有一处判定。
|
||||
*/
|
||||
const REDO_SCOPE_WIDEN = {
|
||||
kind: 'project' as const,
|
||||
when: 'case_redo_note != "" and substitution_difference_item_redo_scope_named = 0',
|
||||
into: 'substitution.difference_item',
|
||||
from: { each: 'mine:substitution.difference_item' },
|
||||
fields: {
|
||||
substitution_relation_id: { col: 'substitution_relation_id' },
|
||||
difference_item_id: { col: 'difference_item_id' },
|
||||
redo_scope: 'named',
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* ③ **带着意见重判 —— 只判被点名的那几条**(整个活动第二次、也是最后一次模型出场)。
|
||||
*
|
||||
* 入参直接来自明细表自己的行(参数名/说明/约束/单位/两侧的值/上一轮结论都在上面),
|
||||
* 外加人写的那句话;**没被点名的行压根不在这一批里**,所以「别去动它」不需要靠提示词求它。
|
||||
* 判法与第一轮**同一份** `ASSESS_RULES`,后面只接一段复核纪律。
|
||||
*/
|
||||
const REVIEW_REDO_ANALYZE = {
|
||||
kind: 'analyze' as const,
|
||||
@@ -448,36 +487,57 @@ const REVIEW_REDO_ANALYZE = {
|
||||
instruction: [
|
||||
ASSESS_RULES,
|
||||
'',
|
||||
'【这一轮是复核,不是从头判】上一轮的结论已经摆在每一条的 last_* 里,审核人写下的意见在 reviewer_note。',
|
||||
'· 意见点到的那几条:按他说的复核,该改就改,并在 evidence_note 里写清这一轮改了什么、依据是什么;',
|
||||
'· **意见没点到、你也确实没判错的**:把 last_* 原样带回来 —— 人可能已经在件上亲手改过几行,重判不是把它们冲掉的机会;',
|
||||
'· 他说的若与硬规矩冲突(比如要把证据不足的硬约束判成 match),照硬规矩judge,并在 evidence_note 里说明为什么不能照办。',
|
||||
'【这一轮是复核】下面**只列了审核人这次点到的那几条**(他写的话在 reviewer_note,每条都带着上一轮的结论 last_*)。',
|
||||
'· 按他说的复核这几条:该改就改,并在 evidence_note 里写清这一轮改了什么、依据是什么;',
|
||||
'· 复核下来确实是上一轮判对了,就把 last_* 原样带回,evidence_note 说明为什么维持;',
|
||||
'· 他要的若与硬规矩冲突(比如要把证据不足的硬约束判成 match),照硬规矩判,并在 evidence_note 里说明为什么不能照办;',
|
||||
'· **列出来的每一条都要回**,一条不多、一条不少。',
|
||||
].join('\n'),
|
||||
input: {
|
||||
from: { each: 'parts.type_param', where: { type_id: { in: 'typeId' } } },
|
||||
from: { each: 'mine:substitution.difference_item', where: { redo_scope: 'named' } },
|
||||
fields: {
|
||||
param_name: { col: 'param_name' },
|
||||
param_desc: { col: 'param_desc' },
|
||||
param_name: { col: 'parameter_name' },
|
||||
param_desc: { col: 'parameter_desc' },
|
||||
constraint_type: { col: 'constraint_type' },
|
||||
unit: { col: 'param_unit' },
|
||||
original_value: { lookup: 'parts.parameter', by: { internal_pn: { in: 'originalPn' }, param_name: { col: 'param_name' } }, take: 'param_value', else: '' },
|
||||
original_note: { lookup: 'parts.parameter', by: { internal_pn: { in: 'originalPn' }, param_name: { col: 'param_name' } }, take: 'param_note', else: '' },
|
||||
candidate_value: { lookup: 'parts.parameter', by: { internal_pn: { in: 'candidatePn' }, param_name: { col: 'param_name' } }, take: 'param_value', else: '' },
|
||||
candidate_note: { lookup: 'parts.parameter', by: { internal_pn: { in: 'candidatePn' }, param_name: { col: 'param_name' } }, take: 'param_note', else: '' },
|
||||
unit: { col: 'parameter_unit' },
|
||||
original_value: { col: 'original_value' },
|
||||
candidate_value: { col: 'candidate_value' },
|
||||
/* 值的备注不在明细表上,回台账现取 —— 与第一轮同一份事实来源 */
|
||||
original_note: { lookup: 'parts.parameter', by: { internal_pn: { in: 'originalPn' }, param_name: { col: 'parameter_name' } }, take: 'param_note', else: '' },
|
||||
candidate_note: { lookup: 'parts.parameter', by: { internal_pn: { in: 'candidatePn' }, param_name: { col: 'parameter_name' } }, take: 'param_note', else: '' },
|
||||
/* 上一轮的结论 —— 含人就地改过的那几行(读的是本单草稿的当下值) */
|
||||
last_status: { lookup: 'mine:substitution.difference_item', by: { parameter_name: { col: 'param_name' } }, take: 'difference_status', else: '' },
|
||||
last_impact: { lookup: 'mine:substitution.difference_item', by: { parameter_name: { col: 'param_name' } }, take: 'impact_statement', else: '' },
|
||||
last_evidence: { lookup: 'mine:substitution.difference_item', by: { parameter_name: { col: 'param_name' } }, take: 'evidence_note', else: '' },
|
||||
last_unknown_disposition: { lookup: 'mine:substitution.difference_item', by: { parameter_name: { col: 'param_name' } }, take: 'unknown_disposition', else: '' },
|
||||
/* 人这次打回写的那句话 —— 每一行都带一份(入参只有行这一种形状),指令里说明它是整批的意见 */
|
||||
last_status: { col: 'difference_status' },
|
||||
last_impact: { col: 'impact_statement' },
|
||||
last_evidence: { col: 'evidence_note' },
|
||||
last_unknown_disposition: { col: 'unknown_disposition' },
|
||||
/* 人这次打回写的那句话 —— 每一行都带一份(入参只有行这一种形状) */
|
||||
reviewer_note: { in: 'case_redo_note' },
|
||||
},
|
||||
},
|
||||
output: ASSESS_OUTPUT,
|
||||
} as const;
|
||||
|
||||
/** 复核轮判完之后落回明细 —— 与第一轮同一笔投影,同样只在被打回的那一轮跑 */
|
||||
const DIFFERENCE_ITEM_FROM_REDO = { ...DIFFERENCE_ITEM_FROM_RESULT, when: 'case_redo_note != ""' } as const;
|
||||
/**
|
||||
* ④ 判完写回 —— **同样只写被点名的那几行**(`from` 里压根没有别的行)。
|
||||
* 模型漏答某一条时 `else` 兜回这一行上一轮的值:漏答不该变成「把它清空」。
|
||||
*/
|
||||
const DIFFERENCE_ITEM_FROM_REDO = {
|
||||
kind: 'project' as const,
|
||||
when: 'case_redo_note != ""',
|
||||
into: 'substitution.difference_item',
|
||||
from: { each: 'mine:substitution.difference_item', where: { redo_scope: 'named' } },
|
||||
fields: {
|
||||
substitution_relation_id: { col: 'substitution_relation_id' },
|
||||
difference_item_id: { col: 'difference_item_id' },
|
||||
constraint_type: { col: 'constraint_type' },
|
||||
difference_status: { lookup: 'result:items', by: { param_name: { col: 'parameter_name' } }, take: 'difference_status', else: { col: 'difference_status' } },
|
||||
impact_statement: { lookup: 'result:items', by: { param_name: { col: 'parameter_name' } }, take: 'impact_statement', else: { col: 'impact_statement' } },
|
||||
evidence_note: { lookup: 'result:items', by: { param_name: { col: 'parameter_name' } }, take: 'evidence_note', else: { col: 'evidence_note' } },
|
||||
unknown_disposition: { lookup: 'result:items', by: { param_name: { col: 'parameter_name' } }, take: 'unknown_disposition', else: { col: 'unknown_disposition' } },
|
||||
/* 派生列与两处创建笔共用同一份 when 链 —— 四态一变,判定分类与风险跟着变 */
|
||||
...JUDGEMENT_DERIVED_FIELDS,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const ASSESSMENT_STEPS = [
|
||||
{
|
||||
@@ -868,10 +928,13 @@ export const ASSESSMENT_STEPS = [
|
||||
let: SHARED_LET,
|
||||
do: [
|
||||
/*
|
||||
* ①② **人打回时,把他的意见连同上一轮结论交回给 AI 重判**(2026-09-01 用户定)——
|
||||
* 只在被打回的那一轮跑(`case_redo_note != ""`),常路(第一次送审)这两笔跳过。
|
||||
* ①-④ **人打回那一轮:先圈出他点名的是哪几条,只把这几条交回给 AI 重判**
|
||||
* (2026-09-01;第一版整表重判,当天被实测打脸 —— 见 `REDO_SCOPE_MARK` 上面那段头注)。
|
||||
* 常路(第一次送审)这四笔全跳过(`case_redo_note` 是空串),整单仍然只调一次模型。
|
||||
* 判完落回明细,后面三笔重算照旧走:派生列跟着新四态变,等级、风险、验证记录跟着变。
|
||||
*/
|
||||
REDO_SCOPE_MARK,
|
||||
REDO_SCOPE_WIDEN,
|
||||
REVIEW_REDO_ANALYZE,
|
||||
DIFFERENCE_ITEM_FROM_REDO,
|
||||
/*
|
||||
|
||||
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
|
||||
activityId: ACTIVITY_ID,
|
||||
name: 'ACT-WORK-05 · 差异评估',
|
||||
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
|
||||
packageVersion: '2.17.0',
|
||||
packageVersion: '2.18.0',
|
||||
category: '需求分析与方案匹配',
|
||||
sdkVersion: '0.1.0-m5',
|
||||
checksum: 'dev-checksum-part-substitution-assessment',
|
||||
@@ -529,6 +529,14 @@ export const manifest: ActivityManifest = {
|
||||
col({ name: 'unknown_disposition', type: 'text', label: 'UNKNOWN 去向(验证任务号 / 补来源动作)', requiredWhen: { column: 'difference_status', value: 'unknown' } }),
|
||||
col({ name: 'risk_level', type: 'text', label: '风险等级', required: true, tally: ['low', 'medium', 'high'] }),
|
||||
col({ name: 'rule_version', type: 'text', label: '差异规则版本', required: true }),
|
||||
/*
|
||||
* **这一轮复核点到了哪几条**(2026-09-01 用户实测:「我打回让修改工作温度的结论,
|
||||
* 给我改了静态电流的结论」)—— 人打回时写的那句话里点了名的行标 `named`,其余 `kept`。
|
||||
* 谁可以变由这一列说了算(**行由确定的那一侧决定,不由 AI 决定** —— 规范 §4.6 纪律一):
|
||||
* 只有 `named` 的行才进重判的入参、也只有它们会被写回;`kept` 的行 AI 连见都见不到。
|
||||
* 意见一条都没点到时另有一笔把整表标 `named`(那种意见说的是整批,不是某一条)。
|
||||
*/
|
||||
col({ name: 'redo_scope', type: 'text', label: '本轮复核范围', tally: ['named', 'kept'] }),
|
||||
],
|
||||
},
|
||||
/* P-06 验证记录:一单一行,关联的是本单案号(P-01 不在本活动里,顶掉 request_id) */
|
||||
|
||||
Reference in New Issue
Block a user