W05 在途修订:差异评估结论审核件一个候选一张表 · 关键台账数据看板件(ledger-board)· S1 清单不再把人刚填的参数摆成缺口

活动包 `part-substitution-assessment`(case-model / manifest / report / target-style / badge / ledger-board 与两份包内测试)、
目标件「验收工单-01-差异评估结论审核」、`docs/ss02-act-05` 关键台账数据两份、端到端测试记录、e2e 脚本。
逐条口径见同批 CLAUDE.md 快照。2026-08-27 另一会话做的,一直留在工作树没入库;与同批另两条提交互相依赖。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
This commit is contained in:
xcs
2026-08-27 23:20:15 +08:00
co-authored by Claude Fable 5
parent 0923fbfe4d
commit 909b1fe4b7
13 changed files with 1943 additions and 208 deletions
@@ -94,7 +94,12 @@ export const CMMN_XML = `<?xml version="1.0" encoding="UTF-8"?>
却只建了第一颗,一行足以让门开着,直到 S2 写关系时才被引用检查拦下。
改成**逐颗对账**:\`parts_profile_rows >= created\` —— 标了几颗建好,就得有几行。
-->
<ifPart id="if_pr"><condition>sub_eval_pn_check_rows = count(candidateInternalPns) + 1 and sub_eval_pn_check_missing = 0 and sub_eval_pn_check_exists_status_absent = 0 and sub_eval_pn_check_exists_status_abandoned &lt; count(candidateInternalPns) and parts_profile_missing = 0 and parts_parameter_missing = 0 and parts_document_missing = 0 and parts_profile_rows &gt;= sub_eval_pn_check_exists_status_created and parts_document_rows &gt;= sub_eval_pn_check_exists_status_created and parts_parameter_rows = parts_parameter_distinct_param_name * sub_eval_pn_check_exists_status_created</condition></ifPart>
<!--
**不再等模型把 absent 改成 created**(2026-08-27 晚):那面旗是模型记的账,忘了记就永远开不了门
(「机检只读基座数」的哨兵形)。「一颗 absent 都不剩」的基座事实 = 要建的每一颗(absent + created)
主数据、参数、资料都落了且必填不缺;created 仍可写(说明书照旧让它记),但门不靠它。
-->
<ifPart id="if_pr"><condition>sub_eval_pn_check_rows = count(candidateInternalPns) + 1 and sub_eval_pn_check_missing = 0 and sub_eval_pn_check_exists_status_abandoned &lt; count(candidateInternalPns) and parts_profile_missing = 0 and parts_parameter_missing = 0 and parts_document_missing = 0 and parts_profile_rows &gt;= sub_eval_pn_check_exists_status_absent + sub_eval_pn_check_exists_status_created and parts_document_rows &gt;= sub_eval_pn_check_exists_status_absent + sub_eval_pn_check_exists_status_created and parts_parameter_rows = parts_parameter_distinct_param_name * (sub_eval_pn_check_exists_status_absent + sub_eval_pn_check_exists_status_created)</condition></ifPart>
</sentry>
<!-- ③ 评估输入已确认(旗 + 清单有行且必填齐)—— 标准化那一步的入场前提 -->
<sentry id="s_input_confirmed">
@@ -130,13 +135,13 @@ export const CMMN_XML = `<?xml version="1.0" encoding="UTF-8"?>
<!-- 启动期:铸案号、建需求 V1、锁规则版本、逐个料号查库(能算的不问人) -->
<humanTask id="S0_bind" name="建立评估上下文:绑定四项、新建需求、存在性核对"/>
<!-- 0.5a 等待数据(建主数据):P-08 数据工单;触达人,不给 doneWhen;都在库时 s_bound 不开,不进「当下可为」 -->
<!-- 0.5a 等待数据(建主数据):P-08 数据工单;人工激活,doneWhen 只在人答过卡之后求(入库事实);都在库时 s_bound 不开,不进「当下可为」 -->
<humanTask id="S0d_p08_master" name="数据工单:补建芯片主数据(P-08)">
<entryCriterion id="ec_s0d8" sentryRef="s_bound"/>
<itemControl><manualActivationRule id="m_s0d8"/></itemControl>
</humanTask>
<!-- 0.5b 等待数据(建资料):P-10 数据工单;触达人,不给 doneWhen;都在库时 s_p08_created 不开,不进「当下可为」 -->
<!-- 0.5b 等待数据(建资料):P-10 数据工单;人工激活,doneWhen 只在人答过卡之后求(入库事实);都在库时 s_p08_created 不开,不进「当下可为」 -->
<humanTask id="S0d_p10_document" name="数据工单:补建原厂技术资料(P-10)">
<entryCriterion id="ec_s0d10" sentryRef="s_p08_created"/>
<itemControl><manualActivationRule id="m_s0d10"/></itemControl>
@@ -256,7 +261,7 @@ key 是 {internal_pn, doc_seq:"01"});一行都没有的话人打开是空表、
"ask":"请把这些料号的 datasheet / 承诺函等资料记录补进原厂技术资料库(一份资料一行,可加行)。",
"steps":["资料类型、标题、版本、文件链接、资料状态为必填;证据定位写页码或章节,差异评估的证据引用会指向它",
"同一料号多份资料就多加几行,序号 01/02… 自己续",
"改完保存,回这张卡确认"],
"改完保存,回这张卡确认 —— 确认后这批资料记录即入库,活动接着往下办"],
"standard":"每颗料号至少一行 datasheet 记录,必填列全齐。",
"gateLedgers":["parts.document"],
"basis":[{"k":"触发原因","v":"按 internal_pn 查 P-10 查无记录"}]}}
@@ -275,15 +280,15 @@ key 是 {internal_pn, doc_seq:"01"});一行都没有的话人打开是空表、
{"kind":"human","itemId":"S1_input_confirm","workOrder":"execution","card":{
"gateLedgers":["${D}.input_gap"],
"title":"评估输入与资料补齐确认:<原 PN> → <N> 个候选",
"ask":"启动绑定的四项已锁定。请在工作区的件上补齐「待补 / 待确认」行的补充内容,默认值可改可确认。",
"ask":"启动绑定的四项已锁定。请在工作区的件上补齐「待补」行的补充内容,默认值可改可确认。",
"steps":["应用场景、BOM 位是启动时未收的:必须补(写在「补充内容」列)",
"原厂未披露的参数(如使能阈值)补不到就写「挂验证任务」或「留 UNKNOWN」—— 补不了也是合法答案,不拦流程",
"每颗料每个参数此刻的值摆在件上的「候选参数现值」表里(只读参考);原厂未披露的参数这里不用补 —— 差异评估会把它判成 UNKNOWN 并挂验证任务",
"替代类型 / 规则版本 / 负责人期限三行是系统默认,可改可不改",
"加一行 = 补充 AI 没列出的资料项;删一行 = 该项不适用;已绑定的四项不可改",
"改完保存,回这张卡提交"],
"standard":"每一条「待补」行的补充内容都有;补不了的写明理由。",
"basis":[{"k":"已绑定","v":"客户 <customerCode> · 需求 <request_id> V1 · 原 PN <原 PN> · 候选 <N> 颗"},
{"k":"缺口","v":"<N> 项待补 / 待确认"}]}}
{"k":"缺口","v":"<N> 项待补"}]}}
⑥ 验收工单 01 · 差异评估结论审核(S4_review;决策卡循环,不是终局验收)—— 发之前先在轨迹里找有没有已答过、还没处理的 \`[card-reply]\`:
· \`value\` 是 \`"approve"\` → 写 \`${D}.verdict.review_approved_total = 1\`,finish 这一项,别再发卡;
@@ -364,7 +369,13 @@ current_stage = "assessing"。application_scenario / bom_position **这一步不
reads: [`${D}.pn_check`, 'candidateInternalPns', 'originalInternalPn', 'datasheets', 'parts.profile', 'parts.parameter', 'parts.document'],
view: 'p08-master-create',
tools: [],
/* 触达人,不给 doneWhen;人工激活:发卡就算启动 */
/*
* 人工激活:发卡就算启动。doneWhen 只在**人答过卡之后**才由引擎求(2026-08-27 晚,harness 立此规矩):
* 要建的每一颗(absent + created)主数据与参数都落了、必填不缺 —— 值是人在件上填的,引擎数得出来,
* 不再靠模型记得 finish(剧本写了两处它照样忘,两步同时 Active、件停在 P-08)。名单里还有没建的料时它为假,
* 「再发一张 P-08」那条路照旧走得通。
*/
doneWhen: `parts_profile_rows > 0 and parts_profile_rows >= ${D}_pn_check_exists_status_absent + ${D}_pn_check_exists_status_created and parts_profile_missing = 0 and parts_parameter_missing = 0 and parts_parameter_rows = parts_parameter_distinct_param_name * parts_profile_rows`,
guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走 —— 都在库时入场前提本就不成立,这一项与下一项 S0d_p10_document 都不会进你的「当下可为」。**先分岔,再动作**:
【名单】= \`${D}.pn_check\` 里 **exists_status = "absent" 的每一行**,不管它是原 PN 还是候选 —— **一律建,不问人**
@@ -381,13 +392,13 @@ current_stage = "assessing"。application_scenario / bom_position **这一步不
没有 datasheet 就只写 internal_pn 这一列(其余留给人填,**别编**)。
2. 同一拍给每颗料写标准化参数集骨架(表 2,\`parts.parameter\`,一行一参数):
**参数名一律用中文,与库里既有料号的那几行同名**(输入电压范围 / 输出电压 / 输出电流 / 开关频率 /
静态电流 / 工作温度 / 使能阈值…)—— \`param_name\` 是主键的一半,写成 \`input_voltage_range\`
静态电流 / 工作温度…)—— \`param_name\` 是主键的一半,写成 \`input_voltage_range\`
与写成「输入电压范围」**不是同一行**:原 PN 那边是中文、候选这边是英文,比对时两套名字对不上,
逐条判成 UNKNOWN,结论就废了(2026-08-27 实测:27 条里 14 条 UNKNOWN)。
封装、工艺、生命周期这些**不是参数**,它们在表 1,别往表 2 里塞。
**建几颗料就用同一套名字**:门上有一条对账 —— 参数行数 = 不同参数名数 × 建过的料数,
只有每颗料的名字集合一模一样时才成立;混着写(这颗中文那颗英文、或同一颗写两套)这一步就过不去。有 \`datasheets[].text\` 就从里面读出典型参数
(如输入电压范围 / 输出电压 / 输出电流 / 开关频率 / 静态电流 / 工作温度 / 使能阈值等,按品类而定)各写一行,
(如输入电压范围 / 输出电压 / 输出电流 / 开关频率 / 静态电流 / 工作温度等,按品类而定)各写一行,
key {internal_pn, param_name},patch 带 param_value / param_unit / constraint_type(hard/soft)/ param_note(一句话说清这项为什么要卡,
如「须覆盖客户输入上限」);原厂没写的值写「未披露」(它会进入 UNKNOWN 并挂验证任务)。没有 datasheet 就只写
internal_pn + param_name 骨架(把你判断这颗料该评估的参数名列出来,**param_value 留空给人填,别编**)。
@@ -410,14 +421,13 @@ current_stage = "assessing"。application_scenario / bom_position **这一步不
而那颗料从此没人建,后面写替代关系时被引用检查逐颗拦下(2026-08-27 实测,一单连拒四次)。
· **一颗都不剩了** → 才走下面这两步。
这一拍的动作串**恰好两个,顺序不能反**:
① 先 \`finish S0d_p08_master\` —— **必须先把这一项收掉**。不收掉,引擎挑工作区画面时按
「当下还没干完的第一步」取 view,S0d_p08_master 只要还 Active,画面就一直停在
\`p08-master-create\`(P-08 那两张表);下一拍就算发出了 P-10 的卡,人的工作区也看不见
P-10 的资料表,一格都填不了,提交闸必然拦死。
② 再照说明书④发 P-10 数据工单 —— 下一项是 S0d_p10_document,同一批料号接着补资料记录。
这一拍的动作串:① \`finish S0d_p08_master\`(建好的料主数据与参数都已入库、必填不缺时引擎也会自己收这一项 ——
你 finish 一下更省一拍,忘了也不会卡住);② 照说明书④发 P-10 数据工单 —— 下一项是 S0d_p10_document,同一批料号接着补资料记录。
【这张卡一单只发一次】人交回来之后不许再发;答复里没有值(空 values)也算答过 —— 人在件上填的东西已经落草稿层。`,
【这张卡一单只发一次】人交回来之后不许再发;答复里没有值(空 values)也算答过 —— 人在件上填的东西已随确认**入了正式表**
(轨迹里有一条「数据工单确认入库」的痕点名哪几张表各几行;试跑单不落主数据时那条痕会如实写「只留在活动缓存」)。
这几颗料从此在库:**别再往 \`parts.profile\` / \`parts.parameter\` 写它们的键**(在库的键会被当场拒),也别去 drop 它们
(那是本单自己建好的行,撤不掉);值要改由人在件上改。`,
},
{
item: 'S0d_p10_document',
@@ -428,19 +438,21 @@ P-10 的资料表,一格都填不了,提交闸必然拦死。
reads: [`${D}.pn_check`, 'parts.profile', 'parts.document'],
view: 'p10-document-create',
tools: [],
/* 触达人,不给 doneWhen;人工激活:发卡就算启动 */
guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走(要走到这一步,S0d_p08_master 必然已经收完);都在库时入场前提本就不成立,
这一项与 S0d_p08_master 都不会进你的「当下可为」。**进到这一项时,S0d_p08_master 应当已经收掉了;若它还开着(Active),
先 \`finish S0d_p08_master\` 再发这张卡** —— 它没收掉,画面会一直停在 P-08 那两张表,人拿到 P-10 的卡也看不见资料表。
/* 人工激活:发卡就算启动。doneWhen 同 P-08 的道理:人答过卡之后,建过的每颗料都有一行资料记录且必填不缺 → 引擎自己收 */
doneWhen: `parts_document_rows > 0 and parts_document_rows >= parts_profile_rows and parts_document_missing = 0`,
guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走(要走到这一步,S0d_p08_master 已经收完 —— 引擎按入库事实自己收);都在库时入场前提本就不成立,
这一项与 S0d_p08_master 都不会进你的「当下可为」。
【发 P-10 数据工单】「建」的名单就是 \`${D}.pn_check\` 里仍是 exists_status = "absent" 的那几行(候选总数 ≥2 时被放弃的那些,
S0d_p08_master 那一步已经把它们改成 "abandoned",不在这份名单里)。同一拍照说明书④发那张 P-10 数据工单(title 里列全料号,只发一次),
发完这一拍结束(挂起)。
【P-10 那张确认回来了】把名单里每颗料的 \`${D}.pn_check\` 改成 exists_status = "created"(同 PK 覆盖写),然后 finish 这一项。
这时 S1_input_confirm 的入场前提(全部料号落定、本单建的主数据不缺必填)自己就成立了 —— 缺必填由提交闸兜着,卡回来时一定是齐的。
【P-10 那张确认回来了】把名单里每颗料的 \`${D}.pn_check\` 改成 exists_status = "created"(同 PK 覆盖写,记账用),然后 finish 这一项
(资料记录都入库、必填不缺时引擎也会自己收)。S1_input_confirm 的入场前提认的是**入库事实**(要建的每颗料主数据 / 参数 / 资料都落了且不缺必填),
不等这面旗 —— 缺必填由提交闸兜着,卡回来时一定是齐的。
【这张卡一单只发一次】人交回来之后不许再发;答复里没有值(空 values)也算答过 —— 人在件上填的东西已经落草稿层。`,
【这张卡一单只发一次】人交回来之后不许再发;答复里没有值(空 values)也算答过 —— 人在件上填的东西已随确认**入了正式表**
(轨迹里有一条「数据工单确认入库」的痕)。这几份资料记录从此在库:**别再往 \`parts.document\` 写它们的键**(会被当场拒),也别去 drop 它们。`,
},
{
item: 'S1_input_confirm',
@@ -453,14 +465,20 @@ S0d_p08_master 那一步已经把它们改成 "abandoned",不在这份名单里)
guide: `料号全部落定了,**该 PW 补评估输入了** —— 这是本活动唯一的执行工单,零输入。**先分岔**:【当前这一步】那行引擎事实说
「从没给它发过卡」→ 走【发卡】;说「人已经被问过了」→ 走【收口】。
【发卡】先把评估输入清单写下来(\`${D}.input_gap\`,一行一项,item_key 用英文短键,input_item 用人读名),一拍连做:
· 已绑定四项(status = "ok",detail 写绑定值,origin = "ai"):item_key = customer / request / original_pn / candidates
(候选多颗就 candidate_1、candidate_2… 一颗一行,detail 写料号与在库状态);
【发卡】先把评估输入清单写下来(\`${D}.input_gap\`,一行一项),一拍连做。
**每一行六列都要写,一列都不能少**:item_key(英文短键)/ input_item(人读名,就是下面每行「」里那几个字)/ source_ledger(来源台账)/
status / detail / origin = "ai"。input_item 是必填列 —— 漏了它,件上红带会点名「缺输入项」,人对着一张没有名字的清单什么也补不了,
卡就此交不回来(2026-08-27 实测:11 行全漏了 input_item,人把别的格填满也过不了闸)。
· 已绑定四项(status = "ok",detail 写绑定值):item_key = customer「客户」(source_ledger = "L-02")/ request「客户需求」(source_ledger = "P-01")/
original_pn「原 PN」(source_ledger = "P-08")/ 候选多颗就 candidate_1「候选 1」、candidate_2「候选 2」… 一颗一行(source_ledger = "P-08",detail 写料号与在库状态);
· 待补两项(status = "gap",detail 写「启动时未收」):application_scenario「应用场景」(source_ledger = "P-01")、bom_position「BOM 位」(source_ledger = "P-09");
· 默认可改三项(status = "default",detail 写默认值):substitution_type「替代类型」= "Pin-to-Pin"、rule_version「规则版本」= "${RULE_VERSION}"、owner「负责人 / 期限」= "按客户线默认 · 1 个工作日";
· 待确认(status = "gap"):逐颗候选看它名下 \`parts.parameter\` 的那几行,param_value 是「未披露」的各一行(item_key = unknown_<料号>_<序号>,
input_item 如「XL2596S-5.0 使能阈值」,source_ledger = "P-08",detail 写「原厂未披露(有而不全)」);
· 齐备项(status = "ok"):原 PN 资料版本(source_ledger = "P-10",detail 写 datasheet 版本)。
· 默认可改三项(status = "default",detail 写默认值,source_ledger = "GOV"):substitution_type「替代类型」= "Pin-to-Pin"、rule_version「规则版本」= "${RULE_VERSION}"、owner「负责人 / 期限」= "按客户线默认 · 1 个工作日";
· **候选参数一行都不写**:原厂未披露的参数不是这份清单的事 —— 差异评估那一步(S3)会把它判成 UNKNOWN 并挂验证任务;
件上另有一张只读的「候选参数现值」表(P-08 标准化参数集,一颗料一张,基座从台账现读),人看得见每颗料每个参数此刻的值。
这份清单里**不许出现任何参数行**(item_key 不许以 unknown_ 开头)—— 参数值有没有,是台账里的事实,不由你在这里替它宣布
(2026-08-27 实测三次:人在 P-08 填好的参数被这份清单当成缺口再问一遍);
· 齐备项(status = "ok"):original_doc_version「原 PN 资料版本」(source_ledger = "P-10",detail 写 datasheet 版本)。
发卡前自己核一遍:\`${D}_input_gap_missing\` 这个读数必须是 0(它只该等 supplement 那一列由人来填;不是 0 就是你漏写了必填列,回去补写再发)。
写完同一拍照说明书⑤发执行工单,发完这一拍结束(挂起)。
【收口】人交回之后只做三件事,然后 finish:
@@ -533,7 +551,7 @@ difference_status / compare_result / risk_level / impact_statement / unknown_dis
这一步的收步判据里就有这两个数,**不调够,写多少行都收不了步**。
① 逐条 \`substitution.difference_item\` 同 PK 覆盖写:difference_status / compare_result / risk_level 照第一拍出参**一字不改**;
impact_statement(MATCH 写「无」;其余说清对应用 / 验证 / 报价的影响,如「输入上限 32V < 客户 40V,不能上板」);
difference_status = unknown 的行**必须**写 unknown_disposition(「挂验证任务 VAL-<案号数字串>-01:实测使能阈值」或「向原厂补来源」);confidence(0~1)。
difference_status = unknown 的行**必须**写 unknown_disposition(「挂验证任务 VAL-<案号数字串>-01:实测<参数名>」或「向原厂补来源」);confidence(0~1)。
② 每个 UNKNOWN 写一行 \`validation.record\`:validation_id = "VAL-<案号数字串>-01/02…"、request_id、validation_internal_pn(候选)、
validation_type = "pin_to_pin"、validation_status = "not_started"、next_validation_action(要测什么、什么条件)、test_condition。
③ 逐条 \`substitution.relation\` 同 PK 覆盖写:recommendation_level / risk_level 照第二拍出参、recommendation_reason(工具 reason 扩写成人话,
@@ -548,6 +566,13 @@ difference_status / compare_result / risk_level / impact_statement / unknown_dis
item: 'S4_review',
/* 中间审核摆成验收工单的样子(机制是可循环的决策卡);由剧本声明,模型抄不抄都一样 */
cardVariant: 'review' as const,
/* 人工验收清单(2026-08-27 用户定):FAE/RI 拿着「差异评估结论审核」这份件要亲自核的步骤,固定 */
acceptanceChecklist: [
'逐个候选核对结论汇总:推荐程度、风险级别与它下方明细表的四态分布吻合(有硬约束 MISMATCH 或悬空 UNKNOWN 的候选没有给「推荐」)',
'逐条核对差异明细:每个候选的每个参数都有 MATCH / MISMATCH / UNKNOWN / CONDITIONAL 之一,没有漏项',
'核对 UNKNOWN 项的去向:每个 UNKNOWN 都挂了验证任务或写明补来源动作,没有悬空',
'核对证据引用:每项结论能追溯到 P-08 / P-10 / P-06 的记录或本单数据工单的创建记录;硬约束的 MATCH 都有证据支持',
],
intent: '差异评估结论审核:决策卡循环,通过或驳回 + AI 按意见改台账重发',
writes: ['substitution.relation', 'substitution.difference_item', 'validation.record', `${D}.verdict`],
reads: ['substitution.relation', 'substitution.difference_item', 'validation.record', `${D}.verdict`],
@@ -588,13 +613,23 @@ difference_status / compare_result / risk_level / impact_statement / unknown_dis
{
item: 'T_release',
intent: '拟落账内容已备好,提交 ACT-GATE-03 交 FAE/RI 放行;被打回就改台账再交',
/* 人工验收清单(2026-08-27 用户定):FAE/RI 拿着「评审放行与落账确认」这份件要亲自核的步骤,固定 */
acceptanceChecklist: [
'核对拟写 P-11 的每条替代关系:推荐等级与差异明细一致(有 UNKNOWN / CONDITIONAL 的候选没有给「推荐」),推荐理由与差异摘要说得通',
'核对分析状态:每条只写分析层(分析状态 = FAE 确认),客户批准状态保持「未提交」,不由本单产生',
'核对规则版本:每条结论都带本次锁定的 GOV 差异规则版本(R-2026.07)',
'核对拟回写 P-01 的五项:风险等级、验证要求、输出链接、下一步动作、当前阶段都填了,且与结论一致',
'核对验证任务:每个 UNKNOWN 对应的验证记录都在,要测什么、什么条件写清楚了',
],
/* 被打回重做时要改的就是这几张 —— 不声明就写不进去 */
writes: ['substitution.relation', 'substitution.difference_item', 'validation.record', 'demand.request', `${D}.verdict`],
reads: ['substitution.relation', 'substitution.difference_item', 'validation.record', 'demand.request', `${D}.verdict`],
view: 'landing-confirm',
tools: [],
/* 放行触达世界,必须人按一下 —— 引擎不自动收它 */
guide: `落账确认那一屏摆着拟写 P-11 主记录与拟回写 P-01。**先看轨迹里有没有「验收被打回重做」**:
guide: `落账确认那一屏摆着拟写 P-11 主记录与拟回写 P-01。**这一步不发执行工单、不发决策卡**:放行卡由 submit 发
(基座预检后自己发验收卡),发执行工单等于把同一件事问人两遍(2026-08-27 实测:先一张执行单、人答完才 submit 又一张验收单)。
**先看轨迹里有没有「验收被打回重做」**:
有 → 这一拍不是再提交一遍。照意见改**台账本身**(同 PK 覆盖写,不开新行):
· 他说推荐等级与明细不符 → 核对 \`substitution.relation.recommendation_level\` 与该关系明细的四态计数,不符的把 recommendation_reason 改实、把漏挂的验证要求补上;
· 他说 P-01 回写不全 → 补 \`demand.request\` 那五项;