Archived
feat(w05): 重做的意见交回给 AI 重跑那一步 —— 不再只是把派生列重算一遍
用户:「唯一调用 ai 的这一步,使用重做后是否没有将重做原因带回去重新让 ai 重跑这一步?」 确实没有:S3 那一轮只跑三笔纯投影重算,人写的那句话除了留痕之外无人读 —— AI 判过的四态一个字不会变,除非他自己在件上逐格改。 平台:引擎把「人这次打回写了什么」注给配方(`case_redo_note`)。 · 下划线不是 case: 前缀 —— S-FEEL 的名字不许带冒号,而它最要紧的用法就是当 when 条件用; · **没打回也永远绑着空串**:未绑定的名字 `!= ""` 求值为真,不绑就等于那几笔每一轮都跑 (拿真 @sfeel 钉了这条,桩也照真语义写,否则钉不住); · 静态审认这个名字 —— 它当场拦下过一次未登记的引用,这道门做对了事。 W05@2.17.0:S3 多两笔,都钉在 `case_redo_note != ""` 上 —— 带意见重判(入参多带上一轮四态/影响/依据/UNKNOWN 去向 + 他那句话;判法与第一轮同一份 ASSESS_RULES,后接复核纪律:没被点到、也确实没判错的行原样带回,别趁重判把人改过的冲掉) + 判完落回明细(与第一轮同一笔投影,只多一个 when)。判法、出参形状、落明细抽成三个 共享常量,S2 与 S3 共用 —— 各写一份早晚分叉。 验收:e2e w05 5/5 released、被拒 0;案例 0(先点名驳回、再通过)模型调用恰 2 次, 其余四例仍是 1 次 —— 没被打回就不多花一次模型。auto-runner 3 测(含变异验证)、包内 +1。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -406,11 +406,41 @@ 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(['project', 'project', 'project', 'card', 'say']);
|
||||
expect(s3[0]?.kind === 'project' && s3[0].into).toBe('substitution.difference_item');
|
||||
expect(s3[1]?.kind === 'project' && s3[1].into).toBe('substitution.relation');
|
||||
expect(s3[2]?.kind === 'project' && s3[2].into).toBe('validation.record');
|
||||
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');
|
||||
});
|
||||
|
||||
/*
|
||||
* **人打回时,意见要交回给 AI 重判**(2026-09-01 用户定:「重做后是否没有将重做原因带回去
|
||||
* 重新让 ai 重跑这一步?」)—— 在此之前 S3 那一轮只把派生列重算一遍,人写的那句话除了留痕
|
||||
* 之外**无人读**:AI 判过的四态一个字不会变,除非人自己在件上逐格改。
|
||||
*
|
||||
* 只在被打回的那一轮跑:`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 != ""');
|
||||
/* 判法一处真源:复核指令以第一轮那份规矩打头,后面才接复核纪律 */
|
||||
const first = s2[0];
|
||||
const rules = first?.kind === 'analyze' ? first.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 : {});
|
||||
for (const key of ['last_status', 'last_impact', 'last_evidence', 'last_unknown_disposition', 'reviewer_note']) {
|
||||
expect(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);
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -420,7 +450,7 @@ describe('S3 / S4 配方', () => {
|
||||
* `s3[0]` 必须是**排在关系 / 验证记录投影之前**的差异明细重算笔。
|
||||
*/
|
||||
it('差异明细重算笔:只列主键与两个派生列,不碰 difference_status 自己、也不碰人能改的叙事列', () => {
|
||||
const recompute = s3[0];
|
||||
const recompute = s3[2];
|
||||
expect(recompute?.kind === 'project' && recompute.from).toEqual({ each: 'mine:substitution.difference_item' });
|
||||
/* `recompute` 经 `s3[0]` 的 tuple 定位精确 narrow 到这一笔自己的字面量类型,它压根没有
|
||||
`prune` 这个键(不是「值为 undefined」,是键不存在)—— `in` 判存在性,不做属性访问 */
|
||||
@@ -438,7 +468,7 @@ describe('S3 / S4 配方', () => {
|
||||
* 展开)——各写一份早晚分叉,而分叉的那一刻屏上会同时摆着两个互相矛盾的规则。
|
||||
*/
|
||||
it('S3 重算笔与 S2 差异明细创建笔共用同一份 judgement_class / risk_level when 链', () => {
|
||||
const s3Fields = s3[0]?.kind === 'project' ? s3[0].fields : undefined;
|
||||
const s3Fields = s3[2]?.kind === 'project' ? s3[2].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']);
|
||||
@@ -459,7 +489,7 @@ describe('S3 / S4 配方', () => {
|
||||
* 「人改四态 → 等级跟着变」这条链不能断),且与 S2 那份是**同一个 when 链引用**(不许分叉)。
|
||||
*/
|
||||
it('**S3 的关系笔只重算判定** —— 收口四格(理由/摘要/风险/验证要求)一列都不许出现,否则人改完一保存就被冲掉', () => {
|
||||
const relation = s3[1];
|
||||
const relation = s3[3];
|
||||
expect(relation?.kind).toBe('project');
|
||||
const fieldsOf = (act: (typeof s3)[number] | undefined): Readonly<Record<string, unknown>> =>
|
||||
act?.kind === 'project' ? act.fields : {};
|
||||
@@ -474,7 +504,7 @@ describe('S3 / S4 配方', () => {
|
||||
/* 主键带回:同 PK 覆盖,不是新开一行 */
|
||||
expect(fields['substitution_relation_id']).toBeDefined();
|
||||
/* 验证记录那一笔仍与 S2 同一份(它不涉及人可改的格子) */
|
||||
expect(s3[2]).toBe(s2[4]);
|
||||
expect(s3[4]).toBe(s2[4]);
|
||||
});
|
||||
|
||||
/*
|
||||
@@ -490,7 +520,7 @@ describe('S3 / S4 配方', () => {
|
||||
});
|
||||
|
||||
it('S3 的卡:审核面孔、单一选项、挂本步的件', () => {
|
||||
const card = s3[3];
|
||||
const card = s3[5];
|
||||
expect(card?.kind === 'card' && card.as).toBe('decide');
|
||||
expect(card?.kind === 'card' && card.variant).toBe('review');
|
||||
expect(card?.kind === 'card' ? card.options ?? [] : []).toHaveLength(1);
|
||||
|
||||
@@ -349,6 +349,136 @@ const GATE_OPEN_WHEN =
|
||||
'sub_eval_part_exists_status_found < 2 or sub_eval_part_missing > 0 or ' +
|
||||
'sub_eval_part_distinct_chip_type_id > 1 or parts_profile_missing > 0 or parts_parameter_missing > 0';
|
||||
|
||||
/**
|
||||
* **逐参数差异判定的规矩** —— S2 第一轮判、S3 被人打回后复核,**两处共用同一份**:
|
||||
* 各写一份早晚分叉,而分叉的那天,屏上会同时摆着两套判法判出来的结论。
|
||||
*/
|
||||
const ASSESS_RULES = [
|
||||
'你在做芯片替代的**逐参数差异判定**。输入是一组核心参数,每一条给了:参数名、这个参数为什么要紧(说明)、',
|
||||
'硬约束还是软约束、单位,以及原料与候选料各自的值和值的备注。',
|
||||
'',
|
||||
'对**每一条**给出四态之一:',
|
||||
'· match —— 候选满足原料在这个参数上的要求(相等,或候选的范围盖住原料的范围);',
|
||||
'· mismatch —— 候选达不到(范围更窄、方向相反、量级不足);',
|
||||
'· unknown —— 任一侧的值缺失或语义不明,判不了;',
|
||||
'· conditional —— 改外围、限定工况、或加一道验证之后可以成立。',
|
||||
'',
|
||||
'三条硬规矩:',
|
||||
'1. **值是多少不要复述** —— 系统手里已经有值了。影响说明要答的是「换上去会不会比现在差、差在哪儿」。',
|
||||
'2. **判不了就判 unknown,不要猜**;判了 unknown 就必须在 unknown_disposition 里写清楚:实测哪一项、或去哪儿补这个值。',
|
||||
'3. **硬约束不因为措辞变成 match** —— 硬约束不满足就是 mismatch,证据不足就是 unknown。',
|
||||
'',
|
||||
'范围值的读法:「下限~上限」是一个区间(分隔符可能是 ~ 或 - 或 至);只给一边的是半开区间;',
|
||||
'单位可能写在值里。判「盖不盖得住」按区间比,不要拿端点当定值。',
|
||||
'',
|
||||
'只输出 JSON,不要解释、不要 markdown 代码围栏。',
|
||||
].join('\n');
|
||||
|
||||
/** 判定出参的形状 —— 同上,两处共用一份 */
|
||||
const ASSESS_OUTPUT = {
|
||||
type: 'object',
|
||||
required: ['items'],
|
||||
properties: {
|
||||
items: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['param_name', 'difference_status', 'impact_statement', 'evidence_note'],
|
||||
properties: {
|
||||
param_name: { type: 'string' },
|
||||
difference_status: { type: 'string', enum: ['match', 'mismatch', 'unknown', 'conditional'] },
|
||||
impact_statement: { type: 'string' },
|
||||
evidence_note: { type: 'string' },
|
||||
unknown_disposition: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* **AI 判完之后落明细的那一笔** —— S2 第一轮、S3 被人打回后的复核轮,**共用同一份**。
|
||||
* 行由字典 `each` 出来,AI 的出参只是拿 `param_name` 去查的一张表:它漏一条,那一行的四态
|
||||
* 就是空 → `required` 立刻变成 `_missing > 0`,提交闸与放行门当场拦住(从前 `from each(AI 出参)`,
|
||||
* 它少给两条就少两行,**而每一行看着都合规**)。
|
||||
*/
|
||||
const DIFFERENCE_ITEM_FROM_RESULT = {
|
||||
kind: 'project' as const,
|
||||
into: 'substitution.difference_item',
|
||||
prune: true,
|
||||
from: { each: 'parts.type_param', where: { type_id: { in: 'typeId' } } },
|
||||
fields: {
|
||||
substitution_relation_id: { in: 'relationId' },
|
||||
difference_item_id: { cat: [{ in: 'relationId' }, '-', { col: 'param_name' }] },
|
||||
parameter_name: { col: 'param_name' },
|
||||
parameter_desc: { col: 'param_desc' },
|
||||
parameter_unit: { col: 'param_unit' },
|
||||
/* 约束来自字典这一行本身;值一律 lookup 台账 —— 基座读得到的事实不让模型转述 */
|
||||
constraint_type: { col: 'constraint_type' },
|
||||
original_value: { lookup: 'parts.parameter', by: { internal_pn: { in: 'originalPn' }, param_name: { col: 'param_name' } }, take: 'param_value', else: '' },
|
||||
candidate_value: { lookup: 'parts.parameter', by: { internal_pn: { in: 'candidatePn' }, param_name: { col: 'param_name' } }, take: 'param_value', else: '' },
|
||||
difference_status: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'difference_status', else: '' },
|
||||
impact_statement: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'impact_statement', else: '' },
|
||||
evidence_note: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'evidence_note', else: '' },
|
||||
unknown_disposition: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'unknown_disposition', else: '' },
|
||||
/* 合成列:先认四态,最后才按约束类型把 mismatch 分成硬 / 软 —— 与 S3 重算那一笔共用同一份 when 链,不许各写一份 */
|
||||
...JUDGEMENT_DERIVED_FIELDS,
|
||||
rule_version: RULE_VERSION,
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* **人把结论打回时,意见要交回给 AI 重判 —— 这是整个活动第二次(也是最后一次)出场**
|
||||
* (2026-09-01 用户定:「重做后是否没有将重做原因带回去重新让 ai 重跑这一步?」——
|
||||
* 当时确实没有:S3 那一轮只把派生列重算一遍,人写的那句话除了留痕之外无人读)。
|
||||
*
|
||||
* 只在被打回的那一轮跑:`case_redo_note` 是引擎注给配方的「人这次打回写了什么」
|
||||
* (没打回就是空串,永远绑得上 —— 见 `auto-runner.ts` 的 `AutoContext.redoNote`)。
|
||||
*
|
||||
* **入参比第一轮多两样**:①上一轮的结论(四态 / 影响 / 依据 / UNKNOWN 去向)——
|
||||
* 人可能已经就地改过几行,那也是「上一轮结论」的一部分;②他这次写的意见。
|
||||
* 判法本身与第一轮**同一份** `ASSESS_RULES`,后面只接一段复核纪律:
|
||||
* **没被点到、也确实没判错的行原样带回**,别趁重判把人改过的东西悄悄冲掉。
|
||||
*/
|
||||
const REVIEW_REDO_ANALYZE = {
|
||||
kind: 'analyze' as const,
|
||||
when: 'case_redo_note != ""',
|
||||
model: 'deepseek-v4-flash',
|
||||
into: 'items',
|
||||
instruction: [
|
||||
ASSESS_RULES,
|
||||
'',
|
||||
'【这一轮是复核,不是从头判】上一轮的结论已经摆在每一条的 last_* 里,审核人写下的意见在 reviewer_note。',
|
||||
'· 意见点到的那几条:按他说的复核,该改就改,并在 evidence_note 里写清这一轮改了什么、依据是什么;',
|
||||
'· **意见没点到、你也确实没判错的**:把 last_* 原样带回来 —— 人可能已经在件上亲手改过几行,重判不是把它们冲掉的机会;',
|
||||
'· 他说的若与硬规矩冲突(比如要把证据不足的硬约束判成 match),照硬规矩judge,并在 evidence_note 里说明为什么不能照办。',
|
||||
].join('\n'),
|
||||
input: {
|
||||
from: { each: 'parts.type_param', where: { type_id: { in: 'typeId' } } },
|
||||
fields: {
|
||||
param_name: { col: 'param_name' },
|
||||
param_desc: { col: 'param_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: '' },
|
||||
/* 上一轮的结论 —— 含人就地改过的那几行(读的是本单草稿的当下值) */
|
||||
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: '' },
|
||||
/* 人这次打回写的那句话 —— 每一行都带一份(入参只有行这一种形状),指令里说明它是整批的意见 */
|
||||
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;
|
||||
|
||||
export const ASSESSMENT_STEPS = [
|
||||
{
|
||||
item: 'S1_inputs' as const,
|
||||
@@ -648,26 +778,7 @@ export const ASSESSMENT_STEPS = [
|
||||
kind: 'analyze' as const,
|
||||
model: 'deepseek-v4-flash',
|
||||
into: 'items',
|
||||
instruction: [
|
||||
'你在做芯片替代的**逐参数差异判定**。输入是一组核心参数,每一条给了:参数名、这个参数为什么要紧(说明)、',
|
||||
'硬约束还是软约束、单位,以及原料与候选料各自的值和值的备注。',
|
||||
'',
|
||||
'对**每一条**给出四态之一:',
|
||||
'· match —— 候选满足原料在这个参数上的要求(相等,或候选的范围盖住原料的范围);',
|
||||
'· mismatch —— 候选达不到(范围更窄、方向相反、量级不足);',
|
||||
'· unknown —— 任一侧的值缺失或语义不明,判不了;',
|
||||
'· conditional —— 改外围、限定工况、或加一道验证之后可以成立。',
|
||||
'',
|
||||
'三条硬规矩:',
|
||||
'1. **值是多少不要复述** —— 系统手里已经有值了。影响说明要答的是「换上去会不会比现在差、差在哪儿」。',
|
||||
'2. **判不了就判 unknown,不要猜**;判了 unknown 就必须在 unknown_disposition 里写清楚:实测哪一项、或去哪儿补这个值。',
|
||||
'3. **硬约束不因为措辞变成 match** —— 硬约束不满足就是 mismatch,证据不足就是 unknown。',
|
||||
'',
|
||||
'范围值的读法:「下限~上限」是一个区间(分隔符可能是 ~ 或 - 或 至);只给一边的是半开区间;',
|
||||
'单位可能写在值里。判「盖不盖得住」按区间比,不要拿端点当定值。',
|
||||
'',
|
||||
'只输出 JSON,不要解释、不要 markdown 代码围栏。',
|
||||
].join('\n'),
|
||||
instruction: ASSESS_RULES,
|
||||
input: {
|
||||
from: { each: 'parts.type_param', where: { type_id: { in: 'typeId' } } },
|
||||
fields: {
|
||||
@@ -682,26 +793,7 @@ export const ASSESSMENT_STEPS = [
|
||||
candidate_note: { lookup: 'parts.parameter', by: { internal_pn: { in: 'candidatePn' }, param_name: { col: 'param_name' } }, take: 'param_note', else: '' },
|
||||
},
|
||||
},
|
||||
output: {
|
||||
type: 'object',
|
||||
required: ['items'],
|
||||
properties: {
|
||||
items: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['param_name', 'difference_status', 'impact_statement', 'evidence_note'],
|
||||
properties: {
|
||||
param_name: { type: 'string' },
|
||||
difference_status: { type: 'string', enum: ['match', 'mismatch', 'unknown', 'conditional'] },
|
||||
impact_statement: { type: 'string' },
|
||||
evidence_note: { type: 'string' },
|
||||
unknown_disposition: { type: 'string' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: ASSESS_OUTPUT,
|
||||
},
|
||||
/*
|
||||
* ②→④ **`RELATION_PROJECTION` 写两遍,中间夹着差异明细** —— 不是笔误
|
||||
@@ -730,30 +822,7 @@ export const ASSESSMENT_STEPS = [
|
||||
* 它漏一条,那一行的四态就是空 → `required` 立刻变成 `_missing > 0`,提交闸与放行门当场拦住。
|
||||
* 从前是 `from each(AI 出参)`:它少给两条就少两行,**而每一行看着都合规**。
|
||||
*/
|
||||
{
|
||||
kind: 'project' as const,
|
||||
into: 'substitution.difference_item',
|
||||
prune: true,
|
||||
from: { each: 'parts.type_param', where: { type_id: { in: 'typeId' } } },
|
||||
fields: {
|
||||
substitution_relation_id: { in: 'relationId' },
|
||||
difference_item_id: { cat: [{ in: 'relationId' }, '-', { col: 'param_name' }] },
|
||||
parameter_name: { col: 'param_name' },
|
||||
parameter_desc: { col: 'param_desc' },
|
||||
parameter_unit: { col: 'param_unit' },
|
||||
/* 约束来自字典这一行本身;值一律 lookup 台账 —— 基座读得到的事实不让模型转述 */
|
||||
constraint_type: { col: 'constraint_type' },
|
||||
original_value: { lookup: 'parts.parameter', by: { internal_pn: { in: 'originalPn' }, param_name: { col: 'param_name' } }, take: 'param_value', else: '' },
|
||||
candidate_value: { lookup: 'parts.parameter', by: { internal_pn: { in: 'candidatePn' }, param_name: { col: 'param_name' } }, take: 'param_value', else: '' },
|
||||
difference_status: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'difference_status', else: '' },
|
||||
impact_statement: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'impact_statement', else: '' },
|
||||
evidence_note: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'evidence_note', else: '' },
|
||||
unknown_disposition: { lookup: 'result:items', by: { param_name: { col: 'param_name' } }, take: 'unknown_disposition', else: '' },
|
||||
/* 合成列:先认四态,最后才按约束类型把 mismatch 分成硬 / 软 —— 与 S3 重算那一笔共用同一份 when 链,不许各写一份 */
|
||||
...JUDGEMENT_DERIVED_FIELDS,
|
||||
rule_version: RULE_VERSION,
|
||||
},
|
||||
},
|
||||
DIFFERENCE_ITEM_FROM_RESULT,
|
||||
RELATION_PROJECTION,
|
||||
VALIDATION_PROJECTION,
|
||||
{ kind: 'say' as const, text: '逐条差异结论已备好,推荐等级由规则按四态算出。' },
|
||||
@@ -798,6 +867,13 @@ export const ASSESSMENT_STEPS = [
|
||||
*/
|
||||
let: SHARED_LET,
|
||||
do: [
|
||||
/*
|
||||
* ①② **人打回时,把他的意见连同上一轮结论交回给 AI 重判**(2026-09-01 用户定)——
|
||||
* 只在被打回的那一轮跑(`case_redo_note != ""`),常路(第一次送审)这两笔跳过。
|
||||
* 判完落回明细,后面三笔重算照旧走:派生列跟着新四态变,等级、风险、验证记录跟着变。
|
||||
*/
|
||||
REVIEW_REDO_ANALYZE,
|
||||
DIFFERENCE_ITEM_FROM_REDO,
|
||||
/*
|
||||
* **人改了四态,先把这一行自己的判定分类 / 风险等级重算,关系与验证记录的投影才有
|
||||
* 真数据可读**(2026-08-30 修「结论纹丝不动」的要害缺陷,详见 `DIFFERENCE_ITEM_RECOMPUTE`
|
||||
|
||||
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
|
||||
activityId: ACTIVITY_ID,
|
||||
name: 'ACT-WORK-05 · 差异评估',
|
||||
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
|
||||
packageVersion: '2.16.0',
|
||||
packageVersion: '2.17.0',
|
||||
category: '需求分析与方案匹配',
|
||||
sdkVersion: '0.1.0-m5',
|
||||
checksum: 'dev-checksum-part-substitution-assessment',
|
||||
|
||||
Reference in New Issue
Block a user