diff --git a/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-一键启动入参说明.md b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-一键启动入参说明.md new file mode 100644 index 0000000..3d09761 --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-一键启动入参说明.md @@ -0,0 +1,46 @@ +# ACT-WORK-05 差异评估 — 一键启动入参说明(运营端) + +> 本文档与《SS02-SS03-SS04_Demo台账字段细化与统一方案》字段口径一致(统一字段名、编号归并、主记录/子表结构均按该方案 4.2 节与第五节执行)。 + +## 一、背景 + +ACT-WORK-05 差异评估平时由上游触发:ACT-WORK-03 检索调用,或 ACT-GATE-02 路径判定后进入。 + +**本文档是运营端一键启动功能的入参规格**:表单在独立运营端完成,参数在启动时填好传入,系统据此直接启动活动(人工扮演上游,把进入条件变成表单)。 + +**极简原则(2026-08-26 定)**:一键启动只收 **4 个参数** —— 客户、客户需求、原 PN、替代 PN(数组)。**启动不等资料**:应用场景、BOM 位、替代类型、datasheet、需求版本调整等一切其余信息,都在活动启动后、执行过程中由工作台的数据工单逐项补齐(数据工单一单一类可多条,确认即入库,活动从断点续跑)。 + +**渠道无感知原则**:活动与工单对触发渠道无感知——上游触发(ACT-WORK-03 / ACT-GATE-02)传入的字段集与本规格完全一致(同样 4 项)。运营端按本规格传参,活动按进入条件消费,不记录渠道;仅 `source_type=手动启动` 在运营端留痕,供 ACT-GATE-03 评审放行时追溯。 + +## 二、入参总表 + +一键启动的全部参数,就这四行: + +| 中文字段 | 统一字段名 | 必填性 | 输入方式 | 默认值 | 说明 | +|---|---|---|---|---|---| +| 客户 | customer_id | 必填 | L-02 选择器 | — | 新建 P-01 需求时挂客户 ID;决定通知对象与默认负责人线。 | +| 客户需求 | request_description | 必填 | 文本 | — | 保留原始需求语义;据此新建 P-01 需求主记录(版本 V1)。 | +| 原内部料号 | original_internal_pn | 必填 | P-08 选择器 | — | 差异评估基准,引用 P-08.internal_pn。 | +| 候选内部料号 | candidate_internal_pn[] | 必填(**数组,≥1**) | P-08 选择器(**多选**) | — | 被评估物料,**一次可传多个**,逐个评估、逐个做存在性检查;P-08 无记录不拦启动——启动后按存在性检查逐个派数据工单创建(候选 ≥2 也可选择放弃,记录放弃原因)。 | + +**系统带出 / 默认值**(不在表单上出现,启动时自动落):需求版本 V1、替代类型 Pin-to-Pin、规则版本当前发布版 R-2026.07(as_of 锁定,随结论落账)、期限 1 个工作日、负责人(PW)与审核(FAE)按客户线默认、`source_type=手动启动`。以上任何一项要改,都在启动后的工单里改,不回表单。 + +## 三、点击启动后系统行为 + +1. 生成任务号(如 TASK-BT02-20260818-007); +2. 新建 P-01 需求主记录(V1):客户 ID + 需求描述落账,绑定原 PN 与候选 PN 数组; +3. **存在性检查**(按 internal_pn 查 P-08 等台账,候选数组逐个查):原 PN 无数据 → 硬阻塞,派数据工单,活动停「等待数据」;候选 PN 无数据 → 候选 ≥2 可放弃【记录放弃原因】或派数据工单创建,候选 =1 必须创建;数据工单**确认即入库**(确认人=责任人留痕)后活动自动唤醒。**启动时的存在性检查只是惰性触发的第一次**——活动执行中每次读台账仍可能派发新数据工单(一单一类可多条,D02、D03…,全部确认后从断点续跑); +4. 生成输入快照 input_snapshot_ref(活动自持中间产物,支持版本回溯); +5. 进入步骤 1 · 建立评估上下文,派执行工单 01——启动传入的 4 项只读核对,**未齐资料全部在工单里补**:应用场景、BOM 位、替代类型确认、需求版本调整等缺口字段,PW 在工作台逐项填写,不回流表单。 + +## 四、极简参数集 + +手工必给的只有 4 个:**客户、客户需求、原 PN、候选 PN(数组)** —— 这就是一键启动的全部入参,没有第二张表。 + +设计意图:**启动的门槛越低,活动跑得越早**;资料不齐不该拦启动,缺口在执行中由数据工单追着人补,而不是在表单里追着人填。数据工单里 datasheet 选填(传了 AI 预填,没传全手填),同样不拦流程。 + +## 五、与现有工单的衔接 + +执行工单 01 的「评估输入清单」(`in.evaluation_inputs`)中,启动传入的 4 项回显为「已绑定 / 已锁定」的灰底只读行:客户、客户需求(及其新建的 P-01 需求与版本 V1)、原 PN、候选 PN(数组逐行);其余行(应用场景、BOM 位、替代类型、规则版本确认、负责人/期限调整等)为 PW 补缺口的可编辑行。**启动参数如有误,须退回启动人在运营端修订后重新启动,工单内不可改**。工单不展示、也不区分触发渠道——上游触发传入同一字段集时,呈现与本表单回显完全相同。 + +PN 不在库时派发的**数据工单**(`in.create_p08_fields` / `in.create_p08_params`):datasheet 在**数据工单里**上传(选填)——传了则 AI 预填字段与参数集(示例 8 项字段 + 7 行参数),没传则人全填;人在对话区【数据卡】确认即正式入库,确认人=数据责任人并留痕,入库后活动自动唤醒进步骤 1。 diff --git a/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-全流程分支图.html b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-全流程分支图.html new file mode 100644 index 0000000..a84e283 --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-全流程分支图.html @@ -0,0 +1,370 @@ + + + + + +ACT-WORK-05 差异评估 · 全流程与分支一览 + + + +
+ + +
+
SS02 · 需求分析与方案匹配 | BT-02 跨品牌替代选型场景
+

ACT-WORK-05 差异评估 · 全流程与分支一览

+
SS02 需求分析与方案匹配 | 负责人 FAE/RI | 门禁 ACT-GATE-03 评审放行 | 工单:1 数据(条件触发)+ 1 执行 + 2 验收
+
+ + +
+
业务场景(演示示例)
+

客户工业电源项目在用 LM2596S-5.0(TI,12V→5V、工业温域、40V 输入上限),原厂发布 PCN 该料转 NRND,客户要求验证替代料。你(FAE)从料库筛出 3 种候选:A = XL2596S-5.0(XLSEMI)、B = LM2596S-ADJ(TI 同族可调版)、C = 国产某 DCDC。

+

触发双通道:①上游触发(ACT-WORK-03 检索调用 / ACT-GATE-02 路径判定)②运营端一键启动(可快速创建需求单:request_id 系统生成、requirement_version 默认 V1)。两通道传入同一字段集——活动内部不区分渠道,工单无差别呈现。

+
+ + +
+

本活动只回答一个问题——技术上哪种料能替(输出:每个候选的推荐等级与证据)。

+

它不回答:买不买得到、数量够不够、多少钱——那些是下游活动的事(SS03 询价、ACT-WORK-24 报价),本图末尾灰显仅作衔接示意。

+
+ + +
+

进入条件 · 启动校验(agent 创建活动实例前必查)

+ +
+ +

主 链 · 启动期 + 5 步工作链条(右侧挂分支)

+
+ + +
+
+
0

启动期 · 参数绑定与存在性检查

运营端 / 上游 · 系统数据工单 01(条件触发)
+

参数绑定(任务 / 版本 / 双 PN / BOM 位 / 规则版本)→ 生成输入快照 input_snapshot_ref;存在性检查发现「完全没有」→ 派数据工单:活动停「等待数据」→ 确认入库 → 唤醒进步骤 1。

+
读写台账:P-01(W 写入/回链)· P-08(R 校验)· P-10(R/W 校验)
+
+
+
分支 · 启动前置 / 数据补齐
+
0a新客户无档案先建 L-02 客户档案(数据工单),再返回启动
+
0b候选无 P-08 主数据同分支 1d:派数据工单创建,确认入库后继续
+
0c未传 datasheet不再拦截:datasheet 已为选填,仅意味着数据工单全手填
+
+
+ +
+ + +
+
+
1

建立评估上下文

PW / AI执行工单 01
+

评估输入清单(一张表):已绑定行灰底只读核对(启动仅绑定 4 项:客户 / 需求 / 原 PN / 候选数组)+ 缺口行可编辑补齐(应用场景、BOM 位等待补项 + 默认·可改项,补充内容必填*,可加行/删行)。工单内不可改绑定值。

+
读台账:P-01 · P-09 · EXT-02(快照:input_snapshot_ref)
+
+
+
分支 · 绑定、缺口与数据创建
+
1d任何读数点查无记录 → 派数据工单一单一类可多条(字段即表头、每行一条,可批量创建);模板统一(实例仅两处可变:目标台账决定表头 schema、预填内容决定行集);多实例 D01/D02…。原 PN=硬阻塞·必须创建;候选 ≥2 可放弃或创建(人判断);候选 =1 必须创建。确认即正式入库(无草稿无审核、确认人=责任人留痕)→ 断点续跑。只建主数据/资料,事实/证据类(P-06)不可创建、挂验证任务
+
1a绑定参数有误退回触发方修订后重新触发(工单内不可改;手动→退回运营端启动人,上游→退回上游活动)
+
1b补缺口四情形(有而不全):参数未披露→挂验证任务 | 资料版本过旧→补最新版 | 应用约束不清→问客户并更新需求单版本(V1→V1.1)| 外部来源补充
+
1c缺口补不了留 UNKNOWN 进入评估(合法路径,不阻断)
+
+
+ +
+ + +
+
+
2

读取并标准化资料

AI不派工单
+

统一参数名 / 单位 / 版本 / 证据引用,产出标准化参数对照表。每次读数做存在性检查:主数据 / 资料类查无 → 派数据工单;事实 / 证据类(P-06)查无 → 挂验证任务。缺失项标记 UNKNOWN。

+
读台账:P-08 · P-10 · P-06 · GOV(规则版本 rule_version 锁定)
+
+
+
分支 · 标准化冲突
+
2a单位冲突阻断,回步骤 1 补证
+
2b证据版本冲突人工复核哪份资料为准
+
+
+ +
+ + +
+
+
3

逐项差异评估

AI不派工单
+

按硬约束(封装 / 工艺 / 电气上限等)与软约束逐项判定,输出 MATCH / MISMATCH / UNKNOWN / CONDITIONAL,附影响、风险、证据与置信度,形成 P-11 差异明细子表草稿。

+
读:步骤 2 对照表 + GOV 规则(取参数同样做存在性检查)| 产出:P-11 差异明细子表草稿(审核前不落账)
+
+
+
分支 · 硬约束失败
+
3a硬约束 MISMATCH该候选直接「不推荐」——如候选 C 输入上限 32V < 客户 40V
+
+
+ +
+ + +
+
+
4

FAE/RI 审核与补证

FAE / RI验收工单 01
+

审核 P-11 差异明细子表草稿(含约束类型列):确认参数解释、风险等级、证据可信度与验证要求;四态逐项无漏项,UNKNOWN 必须有去向。

+
条件写:P-06(审核意见与验证要求 verification_requirement)
+
+
+
分支 · 审核驳回
+
4aUNKNOWN 无去向退回步骤 1~2 补证 / 挂验证任务
+
4b证据链不全退回补齐证据回链(证据来源含数据工单创建记录:人工录入 · 工单号 · 确认人)
+
+
+ +
+ + +
+
+
5

落账与提交 ACT-GATE-03

FAE / RI门禁验收工单 02
+

确认拟落账内容后提交门禁:P-11 替代关系主记录落账——推荐等级 recommendation_level 四态(推荐 / 条件推荐 / 备选 / 不推荐)、analysis_status=FAE确认;customer_approval_status 由独立审批流维护,本活动不碰。并回写 P-01。

+
写台账:P-11(W)· P-01(W 回写)| 每个候选各一条 P-11 记录
+
+
+
分支 · 门禁结果
+
5aGATE-03 放行P-11 落账生效,衔接下游
+
5bGATE-03 驳回REWORK:回步骤 4 重审,或回步骤 1 重补输入
+
+
+ +
+ + +

完 结 · 本示例三候选评估结果

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
候选推荐等级关键依据下一步
A XL2596S-5.0
XLSEMI
条件推荐温度范围更优(-40~+125℃ vs +85℃);使能阈值 UNKNOWN,挂 VAL-2026-0188 实测验证闭环后转正
B LM2596S-ADJ
TI 同族可调版
备选反馈网络不同(可调版 vs 固定 5V),需改 BOM 外围客户接受改设计则启用
C 国产某 DCDC不推荐硬约束失败:输入上限 32V < 客户 40V(分支 3a)归档,不再评估
+
+

注:多候选并行评估、独立落账(各一条 P-11 记录),不存在「选一个再评下一个」。

+
+
6a至少一个推荐交下游衔接(见灰显区)
+
6b全是条件推荐等验证闭环:通过→升级推荐;不过→降级不推荐
+
6c全不推荐重新检索替代料 / 继续用原 PN / 与客户协商放宽约束
+
6d客户不批准走独立审批流(customer_approval_status),不在本活动
+
+
+ + +

下 游 · 范围外,仅作衔接示意

+
+ 范围外 · 仅作衔接示意 + +
+ + +

图 例 · 全部分支对照表(18 条)

+
+ + + + + + + + + + + + + + + + + + + + + + +
分支触发条件去向
0a启动期:新客户无档案先建 L-02 客户档案(数据工单),再返回启动
0b启动期:候选无 P-08 主数据同分支 1d:派数据工单登记创建
0c启动期:未传 datasheet不再拦截——datasheet 已是选填,仅意味着数据工单由「AI 预填」变为「人全填」
1a步骤 1:绑定参数有误退回触发方修订后重新触发(工单内不可改)
1b步骤 1:输入存在缺口(有而不全)补缺口四情形:候选 PDF 未入库→补链接+版本;参数原厂未披露→挂验证任务(P-06);原 PN 资料版本过旧→补最新版;应用约束不清→回头问客户并更新需求单版本
1c步骤 1:缺口补不了留 UNKNOWN 进入评估(合法路径,不阻断)
1d任何读数点:查无记录(完全没有)惰性触发派数据工单(一单一类可多条,模板统一,可多实例 D01/D02/…):原 PN=硬阻塞·必须创建;候选 ≥2 可放弃【记录放弃原因】或创建(人判断);候选 =1 必须创建。确认即入库(责任人留痕)→ 活动从断点续跑。事实/证据类查无(如 P-06)不可创建,挂验证任务
2a步骤 2:单位冲突阻断,回步骤 1 补证
2b步骤 2:证据版本冲突人工复核哪份资料为准
3a步骤 3:硬约束 MISMATCH该候选直接「不推荐」(如候选 C 输入上限 32V < 40V)
4a步骤 4:UNKNOWN 无去向退回步骤 1~2 补证 / 挂验证任务
4b步骤 4:证据链不全退回补齐证据回链(证据来源含数据工单创建记录:人工录入 · 工单号 · 确认人)
5a步骤 5:ACT-GATE-03 放行P-11 落账(analysis_status=FAE确认),衔接下游
5b步骤 5:ACT-GATE-03 驳回REWORK:回步骤 4 重审,或回步骤 1 重补输入
6a完结:至少一个推荐交下游衔接(ACT-WORK-08 / SS03 / ACT-WORK-24)
6b完结:全是条件推荐等验证闭环——通过→升级推荐;不过→降级不推荐
6c完结:全不推荐重新检索替代料 / 继续用原 PN / 与客户协商放宽约束
6d完结:客户不批准走独立审批流(customer_approval_status),不在本活动
+
+ + +
+ 口径与同目录工单及三份文档(活动流程与工单说明 / 一键启动入参说明 / 数据工单 note)一致;示例数据为演示占位。 + ACT-WORK-05 · 差异评估 · 全流程分支图 +
+ +
+ + diff --git a/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-执行工单-01-评估输入与资料补齐确认.html b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-执行工单-01-评估输入与资料补齐确认.html new file mode 100644 index 0000000..d527d56 --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-执行工单-01-评估输入与资料补齐确认.html @@ -0,0 +1,296 @@ + + + + + +执行工单 1 · 评估输入与资料补齐确认 · SS02 ACT-WORK-05 差异评估 + + + +
+
+ +
+
执行
+
+
SS02 · ACT-WORK-05 差异评估 · 执行工单 1/1
+

评估输入与资料补齐确认

+
收件人:PW · 王工 · 关联任务:TASK-BT02-20260818-007
+
+
+ 执行工单 + 步骤 1/5 · 建立评估上下文 + 最迟交回:1 个工作日 +
+
+ +
+ +

【做什么】—— 启动 4 项已绑定,其余信息在工单里补齐

+
+

AI 已完成:系统已按启动传入的 4 项完成绑定——客户 customer_id、客户需求 request_description(新建 P-01 · 版本 V1)、原 PN、候选 PN(数组,逐个评估)——并生成输入快照(input_snapshot_ref)。本轮评估对象:LM2596S-5.0(TI)→ 3 个候选(XL2596S-5.0 / LM2596S-ADJ / 国产 DCDC)Pin-to-Pin 跨品牌替代(BT-02 场景)。

+

需要你补齐:启动时未收的信息(应用场景、BOM 位——不回表单,直接在下表补);系统已带出默认值(替代类型 Pin-to-Pin、规则版本 R-2026.07、负责人/期限),可确认可改。本单缺口只覆盖「有而不全」;「完全没有」(PN 不在库)由系统派数据工单创建整条记录,不在本单。datasheet 在数据工单里上传(选填),启动与本单都不收。点「编辑」在下表「补充内容」列填入;加一行 = 补充 AI 未列出的资料项,删一行 = 该输入项不适用。未补齐的缺口项将按 UNKNOWN 进入差异评估。

+
+ +

【交回什么】—— 评估输入清单(已绑定项只读核对,缺口项补齐,*为必填)

+ +
+ 3 项资料存在缺口或待确认(应用场景 / BOM 位 / 使能阈值),另有 3 项默认值已带出可改;「已绑定」行启动时冻结,不可改 + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#输入项来源状态绑定值 / 缺口说明补充内容
1客户 customer_idL-02已绑定CUS-2025-0007—
2客户需求 request_descriptionP-01已绑定REQ-20260826-001 · V1(启动时新建:工业电源 12V→5V,替代 LM2596S-5.0)—
3原内部料号 original_internal_pnP-08已绑定LM2596S-5.0(TI)—
4候选内部料号 ① candidate_internal_pn[]P-08已绑定XL2596S-5.0(XLSEMI)—
5候选内部料号 ② candidate_internal_pn[]P-08已绑定LM2596S-ADJ(TI 同族可调版)—
6候选内部料号 ③ candidate_internal_pn[]P-08已绑定国产 DCDC(32V 输入上限 · 占位型号)—
7应用场景 application_scenarioP-01待补*启动时未收,需向客户确认12V→5V、工业温域、40V 输入上限(示例已补)
8BOM 位 bom_positionP-09待补*启动时未收U3 电源位(示例已补)
9替代类型 substitution_type—默认·可改默认 Pin-to-Pin—
10规则版本 rule_versionGOV默认·可改默认当前发布版 R-2026.07(as_of 锁定,随结论落账)—
11负责人 / 期限 owner · deadline—默认·可改PW · 王工 / 审核 FAE · 李工 · 1 个工作日(按客户线默认)—
12候选 PN 使能阈值P-08待确认·需补原厂未披露(有而不全)关联验证任务 VAL-2026-0188
13原 PN 资料版本P-10齐备Rev H(2023-09)已核验—
14外部来源清单EXT-02齐备——
+
+ +
+ + 新加行的「补充内容」为必填;「绑定值 / 缺口说明」栏写清这项资料是什么、AI 为什么拿不到 +
+ +
审批 / 提交:本工单只承载内容;「提交」请在对话区的【执行卡】中进行。提交后去向:补齐后进入步骤 2 · AI 读取与标准化,仍缺失的项将按 UNKNOWN 进入差异评估。注意:启动参数如有误,需退回触发方修订后重新触发,本单不可改。
+ +
+ + + +
+
+ + + + diff --git a/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-数据工单-01-芯片主数据创建.html b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-数据工单-01-芯片主数据创建.html new file mode 100644 index 0000000..98a0fde --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-数据工单-01-芯片主数据创建.html @@ -0,0 +1,341 @@ + + + + + +数据工单 1 · 芯片主数据创建(P-08)· SS02 ACT-WORK-05 差异评估 + + + + +
+
+ +
+
数据
+ +
+
SS02 · ACT-WORK-05 差异评估 · 数据工单 1/1
+

芯片主数据创建(P-08)

+
收件人:PW · 王工 · 关联任务:TASK-BT02-20260818-007
+
+
+ 数据工单 + 等待数据 · 步骤 0.5/5 + 最迟交回:1 个工作日 +
+
+ +
+ +

【做什么】—— 批量创建 P-08 主数据记录

+
+

模板定位:本单是「数据工单」统一模板的实例——一单一类(可多条):目标台账=P-08、本批创建=XL2596S-5.0(1 条,可加行增建);字段即表头、每行一条新记录。同一活动每次读数缺数据都可能再派发新实例(D02、D03…),模板与规则完全相同。

+

触发原因:系统按 internal_pn 查 P-08 无「XL2596S-5.0」记录——是「完全没有」,不是字段缺失。本单为系统判断派发的数据创建工单(与执行工单的「有而不全」严格区分,互不嵌套)。

+

AI 预填:本单已上传 datasheet(选填),AI 已预填 1 条记录 · 8 项字段 + 7 行参数(标「预填」),请核对并补齐空缺;若本单未上传 datasheet,则全部手填,同为合法路径。

+

纪律:确认即正式入库(无草稿、无审核),确认人 = 数据责任人并留痕;只建主数据 / 资料,事实与证据记录(如验证记录)须实测产生;在对话区【数据卡】确认后,活动自动从「等待数据」唤醒进入步骤 1,本单不复用。

+
+ +

【创建什么 · 表 1】—— 主数据记录(每行一条,*为必填列)

+ +
+ 每行一条新记录,点「编辑」核对预填、补齐空缺;「+ 加一行」= 再建一条;必填列(*)全齐才允许确认 + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#内部料号 internal_pn全链路统一基准,不可自由文本品牌 brand_name与制造商、供应商分开制造商 manufacturer_name与品牌不一致时分开记录规格描述 specification_description按原厂资料口径封装 package影响硬约束比对工艺 process_technology硬约束比较项生命周期 lifecycle_statusActive/NRND/EOL/停产/风险EOL/PCN eol_pcn_status条件必填:通知/影响/生效时间Datasheet 引用+版本成对条件:有引用必填版本适配场景 application_scenarios判断实际适用性
1XL2596S-5.0预填XLSEMI预填XLSEMI预填降压型 DCDC 稳压器,3A,固定 5.0V,150kHz预填TO-263-5预填BCD预填Active预填—xlsemi.com/datasheet/XL2596S-5.0.pdf · Rev C预填工业电源;消费电源
+
+ +
+ + 新行的必填列(*)全部填写后才允许确认;删一行 = 该记录不建 +
+ +

【创建什么 · 表 2】—— 标准化参数集 normalized_parameter_set(每行一个参数,按所属料号归属,*必填)

+ +
+ AI 已预填 7 行参数(全部归属 XL2596S-5.0),点「编辑」核对修正;「+ 加一行」补充未列出的参数 + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#所属料号 internal_pn归属表 1 哪条记录参数名值单位约束(硬/软)说明
1XL2596S-5.0预填输入电压范围4.5~40V硬约束必须覆盖客户输入(本例上限 40V)
2XL2596S-5.0预填输出电压5.0(固定)V硬约束固定版 vs 可调版是硬差异点
3XL2596S-5.0预填输出电流3A硬约束满载能力,须 ≥ 设计电流
4XL2596S-5.0预填开关频率150kHz软约束影响外围电感 / 电容选型
5XL2596S-5.0预填静态电流4.2mA软约束影响待机功耗
6XL2596S-5.0预填工作温度-40~+125℃硬约束工业温域必须覆盖
7XL2596S-5.0预填使能阈值未披露V硬约束未披露项将进入 UNKNOWN 并挂验证任务
+
+ +
+ + 所属料号 / 参数名 / 值 / 单位 / 约束类型齐全;「未披露」请直接写未披露,将进入 UNKNOWN 并挂验证任务;「说明」列为系统注释,无需填写 +
+ +
提交 / 确认:本工单只承载内容;确认请在对话区【数据卡】中进行。
+ +
+ + + +
+
+ + + + diff --git a/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-活动流程与工单说明.md b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-活动流程与工单说明.md new file mode 100644 index 0000000..35c4052 --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-活动流程与工单说明.md @@ -0,0 +1,297 @@ +# ACT-WORK-05 差异评估 — 活动流程与工单说明 + +> 素材来源:`activity/SS02-需求分析与方案匹配/SS02_ACT-WORK-05_差异评估_E04-I01架构版_V1.2.html`(v1.2 Demo 架构版) +> 工单目录:`SS02-ACT-WORK-05-html/`(1 数据(条件触发)+ 1 执行 + 2 验收,共 4 份) +> 本套工单已按《SS02-SS03-SS04_Demo台账字段细化与统一方案》(2026-08)完成字段级对齐。 + +--- + +## 一、活动定位 + +- **活动**:ACT-WORK-05 差异评估,P0,所属 SS02 需求分析与方案匹配子系统 / BT-02 跨品牌替代选型场景,负责人 FAE/RI。 +- **是什么**:将「候选 PN 是否可替代」拆解为参数、封装、工艺、生命周期、应用边界和验证条件的逐项比较,形成逐项可复核的差异结论(MATCH / MISMATCH / UNKNOWN / CONDITIONAL),并保留证据、风险和下一步动作。输出 AI 分析草稿和 FAE 审核结论,**不自动批准替代关系**。 +- **产出物**:差异评估结论 + 推荐等级 recommendation_level(推荐 / 条件推荐 / 备选 / 不推荐),审核通过后落账 **P-11 替代关系主记录**(含 P-11 差异明细子表,一条参数差异一条记录 difference_item_id),并回写 **P-01**(风险等级、验证要求、输出链接、下一步动作、当前阶段)。 +- **触发**:双通道——①上游触发:ACT-WORK-03 检索调用 / ACT-GATE-02 路径判定后;②一键手动启动:运营端按钮表单直接启动(绕过 ACT-GATE-02 路径判定,故启动理由必填、source_type=手动启动 留痕,详见《ACT-WORK-05-一键启动入参说明.md》)。**触发渠道仅是活动外部关注点,活动内部与工单不承载触发渠道信息——两种通道传入同一字段集,工单无差别呈现。** +- **门禁**:ACT-GATE-03 评审放行(本活动终点)。 +- **下游**:ACT-WORK-08 证据归集消费本活动结论。 +- **冷启动能力**:依赖库为空(PN 按 internal_pn 查无记录)时,系统派**数据工单**创建主数据,确认即入库后活动自动从「等待数据」唤醒继续——活动不因空库终止。 +- **台账读写**:P-01(R+W)、P-06(R/C,条件写)、P-08(R;无记录时经数据工单 W 创建)、P-09(R)、P-10(R;无记录时经数据工单 W 创建)、P-11(R+W;其中客户批准状态 customer_approval_status **只读边界**,由审批流程维护,本活动不产生)、GOV 规则(R,带版本锁定 rule_version)。 +- **进入条件(极简口径)**:启动入参仅 **4 项**——客户 customer_id、客户需求 request_description(新建 P-01·V1)、原 PN、候选 PN(数组 ≥1);应用场景、BOM 位、替代类型、规则版本、负责人/期限等**启动后在工单内补齐,不回表单**;datasheet 在数据工单里上传(选填),只影响 AI 预填程度。 +- **退出条件**:每个候选 PN 都有差异状态、证据/缺口、风险和验证建议,并完成 FAE/RI 审核。 + +## 二、整体流程(5 步工作链条) + +**步骤 1 · 建立评估上下文(PW/AI)** +- AI 做什么:绑定评估对象,生成输入快照。 +- AI 怎么做:读 → 经读数工具读 P-01(启动新建的 demand 主记录:客户、需求描述、版本 V1)、P-09(BOM 位 bom_position)、EXT-02(外部来源清单);加工 → 绑定启动 4 项(客户/需求/原 PN/候选数组),核对资料齐备度,标出缺口项(应用场景、BOM 位等待补);输出 → 输入快照 input_snapshot_ref(活动自持中间产物)。 +- 人做什么:**PW 介入**——补启动时未收的信息(应用场景、BOM 位),确认或调整系统默认值(替代类型、规则版本、负责人/期限),补 AI 拿不到的资料(实测记录等)(执行工单 01)。 +- 触发纪律:触发传入参数即活动进入条件,系统据此生成 input_snapshot_ref 并完成绑定后派执行工单 01,PW 仅补缺口,绑定参数不可改(双通道与渠道无感知原则详见第七节)。 + +**步骤 2 · 读取并标准化资料(AI)** +- AI 做什么:把多来源资料整理成可比对的标准参数表。 +- AI 怎么做:读 → P-08(原 PN 物料基准)、P-10(候选原厂 Datasheet、补证、承诺函,资料类型由 document_type 区分)、P-06(历史验证事实)、GOV 规则(评估口径);**每次读台账均做存在性检查:查无记录→主数据/资料类派数据工单(一单一类可多条),事实/证据类(如 P-06)挂验证任务**;加工 → 统一参数名称、单位、版本和证据引用,缺失项标记 UNKNOWN;输出 → 标准化参数对照表(活动自持)。 +- 人做什么:无需介入(纯 AI 加工态)。 + +**步骤 3 · 逐项差异评估(AI)** +- AI 做什么:逐项给出差异结论。 +- AI 怎么做:读 → 步骤 2 的标准化对照表 + GOV 规则版本 rule_version(取参数时同样做存在性检查:查无记录→派数据工单或挂验证任务);加工 → 按硬约束(封装/工艺等)与软约束(constraint_type)逐项判定,输出 MATCH / MISMATCH / UNKNOWN / CONDITIONAL(difference_status),并说明影响、风险等级、证据引用与置信度;输出 → P-11 差异明细子表草稿(一条参数差异一条记录 difference_item_id,**审核前不落账**),不改变客户批准关系。 +- 人做什么:无需介入(AI 计算态),结论交步骤 4 审核。 + +**步骤 4 · FAE/RI 审核与补证(FAE/RI)** +- AI 做什么:把步骤 3 草稿整理成可审核视图(四态 pill + 证据链)。 +- AI 怎么做:读 → P-11 差异明细子表草稿;加工 → 按验收判据组织展示(漏项检查、UNKNOWN 去向、证据回链、硬约束检查);输出 → 审核工单视图。 +- 人做什么:**FAE/RI 介入**——确认参数解释、风险等级、证据可信度和验证要求,必要时退回补证;审核意见与补充条件条件写 P-06(验收工单 01)。 + +**步骤 5 · 提交评审放行(FAE/RI + Gate)** +- AI 做什么:汇总拟落账内容供确认。 +- AI 怎么做:读 → 审后结论;加工 → 生成拟写 P-11 替代关系主记录字段值(替代类型/原内部料号/候选内部料号/推荐等级/推荐理由/差异摘要/替代分析报告/分析状态/风险等级/验证要求/证据可信度/差异规则版本)与拟回写 P-01 字段值(风险等级/验证要求/输出链接/下一步动作/当前阶段);输出 → 落账确认视图。 +- 人做什么:**FAE/RI 介入**——确认拟落账内容无误后,在对话区卡提交 ACT-GATE-03(验收工单 02)。放行后更新 P-01 状态,结论供 ACT-WORK-08 证据归集消费;客户批准状态 customer_approval_status 仍由审批流程维护,本活动不写。 + +## 三、工单划分逻辑 + +本活动为**纯数据/内容生产型**(同 I01),无物理执行环节(没有拜访、递送、线下执行),因此**没有执行回执类工单**。本活动使用**三类工单**,触发条件与分工严格区分: + +| 类型 | 触发条件 | 判断方 | 人做什么 | 入库方式 | +|---|---|---|---|---| +| **数据工单** | **完全没有**:按 internal_pn 查台账(P-08 等主数据/资料类)无此记录 | 系统(硬查询) | 从零创建整条记录 | **确认即正式入库**(无草稿态、无审核环节),确认人=数据责任人并留痕 | +| **执行工单** | **有而不全**:PN 在库、个别字段/资料缺失 | AI(字段级判断) | 补缺字段、补资料链接 | 对话区【执行卡】提交,缺口可留 UNKNOWN | +| **验收工单** | AI 已产出结论,需人拍板 | AI 产出 + 人判断 | 通过 / 驳回 | 对话区【验收卡】通过后落账 | + +- 数据工单与执行工单**严格区分、互不嵌套**:一个是「整条记录不存在」(系统判断),一个是「记录存在但字段不全」(AI 判断)。 +- 数据工单**铁律一**:只能建主数据/资料类记录(P-08 / P-10 / S-03 / L-02),不能建事实/证据记录(P-06 验证记录须实测产生,只能挂任务)。 + +对生产流程逐步判定: + +- **步骤 0.5(存在性检查,条件触发)**:启动校验发现 PN 不在库 → 派 **数据工单 01**(芯片主数据创建,本活动唯一数据单),确认入库前活动停在「等待数据」状态,确认后自动唤醒。 +- **步骤 1(建立评估上下文)**:AI 能读台账建快照,但「参数原厂未披露、资料版本过旧」这类 **AI 拿不到的可信来源资料只有人能补** → 派 **执行工单 01**(评估输入与资料补齐确认,本活动唯一执行单)。 +- **步骤 2(读取并标准化)、步骤 3(逐项差异评估)**:均为**纯 AI 加工态**——读台账、标准化、按规则计算,不需要人补内容、也没有产出要人拍板的中间结论 → **不派工单**,其结论并入步骤 4 的验收单呈现。 +- **步骤 4(FAE/RI 审核)**:AI 已产出差异结论(四态 + 风险 + 证据),需要人判断「结论可信不可信、UNKNOWN 有没有去向」→ 派 **验收工单 01**(差异评估结论审核)。 +- **步骤 5(提交 ACT-GATE-03)**:AI 拟好了落账内容,需要人确认「无误后入库 / 放行」→ 派 **验收工单 02**(评审放行与落账确认)。 + +结论:**1 数据(条件触发)+ 1 执行 + 2 验收**。本活动无独立复盘流程(结论复用由下游 ACT-WORK-08 与后续替代/方案流程承担),无需拆时间轴。三条硬约束全程成立:工单不承载审批/确认动作(动作在对话区卡);要素完整(期限/判据/必填/去向);交回内容可校验(执行单必填*、验收单判据清单、数据单必填全齐才允许确认)。 + +## 四、每个工单详细说明 + +### 工单 0:数据工单 01 · 芯片主数据创建(WO-ACT-WORK-05-D01,条件触发) +- **对应步骤**:步骤 0.5 · 等待数据(惰性触发点之一;启动校验期的存在性检查只是第一次)。 +- **三条定稿规则**: + - **一单一类(可多条)**:每张数据工单只针对**一种台账**;**字段即表头、每行一条新记录,可一次创建多条**(如 P-08 一次建多个料号);主记录与结构子表(如参数集)分表承载,子表行以主键(所属料号)归属记录;其它台账的缺失另行派发新实例。 + - **惰性触发(不限于启动)**:活动执行到任何一步,**每次读台账**发现「完全没有」(按主键查无记录)即派一张;同一活动可派发多张,按派发顺序编号 WO-<活动>-D01/D02/…;活动进入「等待数据」状态,全部确认入库后从断点续跑。启动校验只是惰性触发的第一次,不是唯一一次。并发去重不考虑(一活动一负责人)。 + - **模板统一**:数据工单只有**一个统一模板**(类型级),职责只有「数据创建」;实例化时按目标台账的字段 schema(《统一方案》字段表)渲染表头 + AI 预填/人全填。实例之间只有两处不同:**目标台账(决定表头 schema)、预填内容(决定行集)**(本单:目标台账=P-08 `meta.data.target_ledger`、本批记录=XL2596S-5.0 `meta.data.target_key`)。 +- **触发**:系统按主键查台账**无此记录**(完全没有,系统硬查询)——原 PN 无数据=硬阻塞必须创建;候选 PN 无数据:候选数 ≥2 时人可二选一(放弃该候选【记录放弃原因】或创建),候选数 =1 时不能放弃、必须创建。事实/证据类查无(如 P-06 无验证记录)**不可创建**,只能挂验证任务。 +- **创建什么**:①表 1 主数据记录(**字段即表头、每行一条新记录、可加行批量创建**:internal_pn★ / brand_name★ / manufacturer_name / specification_description★ / package★ / process_technology / lifecycle_status★ / eol_pcn_status(条件)/ datasheet_ref+datasheet_version(成对条件)/ application_scenarios,字段说明放表头第二行小字,tbody `in.create_p08_fields`);②表 2 标准化参数集 normalized_parameter_set★(**每行一个参数、以「所属料号 internal_pn」列归属表 1 记录**:所属料号★/参数名/值/单位/约束类型/说明只读注释列,可加行,tbody `in.create_p08_params`,示例预填 7 行)。启动时传了 datasheet → AI 预填(计数槽 `sd.prefill_count`,示例 1 条记录 · 8 项字段 + 7 行参数);没传 → 人全填,同为合法路径。 +- **为什么两张表(粒度不同,一张表摆不平)**:表 1 的粒度是「一个料一行」——每个字段对每条记录只有一个值,字段集合由台账 schema 定死(主记录);表 2 的粒度是「一个参数一行」——每个料挂一串行数不固定的参数,每行内部还有 参数名/值/单位/硬软约束 四件套(结构子表)。两者行数没有固定比例,合成一张表只有两条死路:①参数塞进料号行的某格——一坨文本,机器没法逐项消费(差异评估要逐项比对)、人没法校验必填,违反《统一方案》「重复明细用子表承载,不在单一文本字段堆叠」;②参数也变成列——不同料的参数清单完全不同(DCDC 和 MCU 没有共同列),列数不固定,表头无法按 schema 统一渲染,通用模板破产。所以必须两张表,靠表 2 的「所属料号」列做连接键(映射主表+子表的外键关系):建 3 个料 → 表 1 三行、表 2 按料号分 3 组;入库时系统按该键把参数集挂到各自主记录下。 +- **确认即入库与责任人**:人在对话区【数据卡】点确认 → **直接入正式库(record_status=有效),无草稿态、无审核环节**;确认人=该数据责任人,系统留痕(确认人 / 数据工单号 / 所属活动);必填字段全齐才允许确认(表单级校验,不算审核)。入库后活动自动唤醒进步骤 1;入库后发现错误走主数据治理修正,工单不复用。 +- **铁律一**:只能建主数据/资料类记录(P-08 / P-10 / S-03 / L-02),不能建事实/证据记录(P-06 验证记录须实测产生,只能挂任务)。 +- **为什么是独立类型**:条件触发(只在「完全没有」时派发,非常驻);产出是**共享资产**(进正式库、跨活动复用,不是本活动的中间产物);模板直接来自台账字段定义(P-08 字段表),不是活动自定义表单;与执行工单「有而不全」严格区分、互不嵌套。 + +### 工单 1:执行工单 01 · 评估输入与资料补齐确认(WO-ACT-WORK-05-01) +- **对应步骤**:步骤 1/5 · 建立评估上下文。 +- **做什么**:确认触发传入参数已绑定(只读回显),补齐「有而不全」的缺口资料。 +- **AI 产出**:一张「评估输入清单」(`in.evaluation_inputs`)——按状态列区分:**已绑定**(灰底只读行,启动时冻结,仅 4 项:客户 customer_id、客户需求 request_description(新建 P-01 · V1)、原内部料号 original_internal_pn、候选内部料号 candidate_internal_pn[] 数组逐行)、**待补**(应用场景 application_scenario、BOM 位 bom_position——启动时未收)、**默认·可改**(替代类型 substitution_type 默认 Pin-to-Pin、规则版本 rule_version 默认 R-2026.07、负责人/期限按客户线默认)、**待确认·需补**(候选 PN 使能阈值原厂未披露,P-08)、**齐备**(原 PN 资料版本 P-10 Rev H 已核验、外部来源清单 EXT-02);编辑闸读数 `sd.input_gap_count` 显示缺口项数。 +- **人填什么**:缺口行「补充内容」列(必填*)——资料链接 / 任务编号;可加行补充 AI 未列出的资料项(新行自动标「新增·待补」),可删行声明不适用;已绑定行在编辑态下仍只读、不可删。未补齐项按 UNKNOWN 进入评估。启动参数如有误,退回触发方修订后重新触发,本单不可改。 +- **为什么是执行**:本单只覆盖「**有而不全**」——PN 已在库、个别字段缺失(AI 判断);「**完全没有**」(PN 不在库)由系统派数据工单创建,不在本单,两类互不嵌套。可信来源资料的获取在 AI 识别规则之外(原厂未公开、实测未做),只有 PW 能补。 + +### 工单 2:验收工单 01 · 差异评估结论审核(WO-ACT-WORK-05-02) +- **对应步骤**:步骤 3~4/5 · 逐项差异评估 + FAE/RI 审核(步骤 2~3 为纯 AI 加工,不单独派单,结论并入本单)。 +- **AI 产出**:P-11 差异明细子表草稿(7 行示例:输出电压/输出电流/开关频率/封装 MATCH,工作温度 MISMATCH 高风险,使能阈值 UNKNOWN 挂 VAL-2026-0188,生命周期 CONDITIONAL 凭 P-10 供货承诺函),每项带约束类型 constraint_type(硬/软约束)、四态 pill difference_status、风险等级、证据引用 evidence_ref、AI 置信度。 +- **人审什么**(4 条判据):关键参数差异均有明确状态(无漏项);UNKNOWN 项均有去向(挂验证任务或补来源);证据链完整(可追溯 P-08/P-10/P-06 或 EXT-02);硬约束项未被自动判 MATCH。 +- **为什么是验收**:结论是 AI 按规则算出的,FAE/RI 只拍板通过/驳回,不改内容;审核意见与补充条件写 P-06。 + +### 工单 3:验收工单 02 · 评审放行与落账确认(WO-ACT-WORK-05-03) +- **对应步骤**:步骤 5/5 · 提交 ACT-GATE-03。 +- **AI 产出**:两张只读表——拟写 P-11 替代关系主记录(替代类型 substitution_type / 原内部料号 original_internal_pn / 候选内部料号 candidate_internal_pn / 推荐等级 recommendation_level「条件推荐」pill / 推荐理由 / 差异摘要 / 替代分析报告 analysis_report_ref RPT-2026-0188 / 分析状态 analysis_status「FAE确认」/ 客户批准状态 customer_approval_status「未提交」/ 风险等级 / 验证要求 / 证据可信度 / 差异规则版本 rule_version);拟回写 P-01(风险等级 / 验证要求 VAL-2026-0188 / 输出链接 / 下一步动作 / 当前阶段)。 +- **人审什么**(4 条判据):推荐等级与差异明细一致(存在 UNKNOWN/CONDITIONAL 不得给「推荐」);分析状态与客户批准状态分离(本单只写分析层 analysis_status=FAE确认,customer_approval_status 保持未提交,不由差异评估产生);GOV 规则版本已锁定(R-2026.07);回写 P-01 字段完整。 +- **为什么是验收**:落账内容已由 AI 拟好,人只确认「无误后入库 + 放行」;客户批准不在本单范围(customer_approval_status 只读边界,由审批流程维护)。 + +## 五、文件对应关系与闭环流程图 + +| 文件 | 类型 | 对应步骤 | 一句话 | +|---|---|---|---| +| `ACT-WORK-05-数据工单-01-芯片主数据创建.html` | 数据 1/1(条件触发) | 步骤 0.5 | PN 不在库时系统派发,人创建整条 P-08 记录,确认即入库(责任人留痕)后活动唤醒 | +| `ACT-WORK-05-执行工单-01-评估输入与资料补齐确认.html` | 执行 1/1 | 步骤 1 | 评估输入清单:已绑定项只读核对 + 「有而不全」缺口项 PW 补齐,缺口项按 UNKNOWN 进入评估 | +| `ACT-WORK-05-验收工单-01-差异评估结论审核.html` | 验收 1/2 | 步骤 3~4 | FAE/RI 审核 AI 逐项差异结论(四态 + 证据链),意见写 P-06 | +| `ACT-WORK-05-验收工单-02-评审放行与落账确认.html` | 验收 2/2 | 步骤 5 | 确认拟写 P-11 / 回写 P-01 内容,提交 ACT-GATE-03 | +| `ACT-WORK-05-一键启动入参说明.md` | 说明文档 | 触发 | 运营端一键启动的入参总表、系统行为与工单衔接说明 | +| `ACT-WORK-05-全流程分支图.html` | 可视化 | 全流程 | 面向业务负责人的流程与全部分支一览(自包含零依赖) | + +``` +触发(双通道:ACT-WORK-03 检索调用 / ACT-GATE-02 路径判定 | 一键手动启动,绕过 GATE-02,启动理由留痕) + │ + ▼ +步骤0.5 存在性检查(系统,条件触发)── PN 不在库?(全链路惰性检查的第一次;之后每次读台账仍可能再派 D02/D03…) + │──►【数据工单 01】芯片主数据创建 ── 一单一类可多条(字段即表头、每行一条),对话区【数据卡】确认即入库 + │ 确认前活动停「等待数据」;全部确认入库后从断点续跑 + ▼ +步骤1 建立评估上下文(PW/AI) + │──►【执行工单 01】评估输入与资料补齐确认 ── PW 补资料,对话区【执行卡】提交 + ▼ +步骤2 读取并标准化资料(AI,不派单)── 缺失项标 UNKNOWN + ▼ +步骤3 逐项差异评估(AI,不派单)── P-11 差异明细子表草稿(不落账) + ▼ +步骤4 FAE/RI 审核与补证 + │──►【验收工单 01】差异评估结论审核 ── 对话区【验收卡】通过/驳回 + │ 通过:意见与验证要求条件写 P-06 + │ 驳回:退回补证 / 验证(回步骤 1 或挂 VAL 任务) + ▼ +步骤5 提交评审放行 + │──►【验收工单 02】评审放行与落账确认 ── 对话区【验收卡】通过/驳回 + │ 通过:P-11 替代关系主记录落账 + P-01 回写 + 提交 ACT-GATE-03 + ▼ +ACT-GATE-03 评审放行(门禁,本活动终点) + ▼ +下游 ACT-WORK-08 证据归集消费 +(P-11.customer_approval_status 客户批准:全程只读边界,由审批流程另行维护) +``` + +## 六、动态数据标注 + +命名空间速查:`meta.*` 任务元数据 | `card.*` 工单身份 | `in.*` 执行/数据工单输入行集 | `out.*` 验收单输出行集 | `sd.*` 状态读数。 + +### 工单 0(数据 01)动态位置 + +| 位置 | 动态数据 | 绑定地址 | 类型 | +|---|---|---|---| +| 卡头 sub | 收件人 | `meta.task.owner` | data-slot | +| 卡头 sub | 关联任务 | `meta.task.id` | data-slot | +| 卡头 side | 当前步骤(等待数据 · 步骤 0.5/5) | `meta.phase` | data-slot | +| 卡头 side | 最迟交回 | `card.deadline` | data-slot | +| note 读数 | AI 预填项数 | `sd.prefill_count` | data-slot | +| 表 1 tbody | 主数据记录行集(每行一条记录,可加行批量创建) | `in.create_p08_fields` | data-rows | +| 表 1 表头 th / 单元格 td | 内部料号★/品牌★/制造商/规格描述★/封装★/工艺/生命周期★/EOL-PCN(条件)/Datasheet 引用+版本(成对)/适配场景 | internal_pn / brand_name / manufacturer_name / specification_description / package / process_technology / lifecycle_status / eol_pcn_status / datasheet_ref / application_scenarios | data-h / data-field | 列标签 / 列绑定 | +| 表 2 tbody | 标准化参数集行集(每行一个参数,按所属料号归属,可加行) | `in.create_p08_params` | data-rows | +| 表 2 表头 th / 单元格 td | 所属料号★ / 参数名 / 值 / 单位 / 约束类型 / 说明(只读注释 ro) | internal_pn / param / value / unit / constraint / desc | data-h / data-field | 列标签 / 列绑定 | +| 页脚 | 工单编号 | `card.id` | data-slot | + +### 工单 1(执行 01)动态位置 + +| 位置 | 动态数据 | 绑定地址 | 类型 | +|---|---|---|---| +| 卡头 sub | 收件人 | `meta.task.owner` | data-slot | +| 卡头 sub | 关联任务 | `meta.task.id` | data-slot | +| 卡头 side | 当前步骤 | `meta.phase` | data-slot | +| 卡头 side | 最迟交回 | `card.deadline` | data-slot | +| 编辑闸读数 | 缺口项数 | `sd.input_gap_count` | data-slot | +| 表格 tbody | 评估输入清单行集(已绑定行只读 + 缺口行可编辑) | `in.evaluation_inputs` | data-rows | +| 表头 th | 输入项 / 来源 / 状态 / 绑定值·缺口说明 / 补充内容(req*) | data-h | 列标签 | +| 单元格 td | input_item / source_ledger / status / detail / supplement | data-field | 列绑定 | +| 页脚 | 工单编号 | `card.id` | data-slot | + +### 工单 2(验收 01)动态位置 + +| 位置 | 动态数据 | 绑定地址 | 类型 | +|---|---|---|---| +| 卡头 sub | 收件人 | `meta.task.owner` | data-slot | +| 卡头 sub | 关联任务 | `meta.task.id` | data-slot | +| 卡头 side | 当前步骤 | `meta.phase` | data-slot | +| 卡头 side | 最迟交回 | `card.deadline` | data-slot | +| 表格 tbody | 差异明细行集(P-11 差异明细子表草稿,difference_item_id) | `out.difference_items` | data-rows | +| 表头 th | 参数项 parameter_name / 原PN值 original_value / 候选PN值 candidate_value / 单位 parameter_unit / 约束类型 constraint_type / 结论 difference_status / 风险等级 / 证据引用 evidence_ref / AI置信度 | data-h | 列标签 | +| 单元格 td | parameter_name / original_value / candidate_value / parameter_unit / constraint_type / difference_status / risk_level / evidence_ref / confidence | data-field | 列绑定 | +| 页脚 | 工单编号 | `card.id` | data-slot | + +### 工单 3(验收 02)动态位置 + +| 位置 | 动态数据 | 绑定地址 | 类型 | +|---|---|---|---| +| 卡头 sub | 收件人 | `meta.task.owner` | data-slot | +| 卡头 sub | 关联任务 | `meta.task.id` | data-slot | +| 卡头 side | 当前步骤 | `meta.phase` | data-slot | +| 卡头 side | 最迟交回 | `card.deadline` | data-slot | +| 表 1 tbody | 拟写 P-11 替代关系主记录行集 | `out.ledger_p11` | data-rows | +| 表 2 tbody | 拟回写 P-01 行集 | `out.writeback_p01` | data-rows | +| 表头 th | 字段 / 拟写入值 | data-h | 列标签 | +| 单元格 td | field / value | data-field | 列绑定 | +| 页脚 | 工单编号 | `card.id` | data-slot | + +--- + +## 七、启动与触发(实现规格) + +1. **双通道与渠道无感知原则**:触发方式 = ①上游触发(ACT-WORK-03 检索调用 / ACT-GATE-02 路径判定)②运营端一键手动启动。两通道传入**同一字段集**;活动内部与工单不承载、不区分触发渠道。source_type / 启动理由属运营端留痕字段,不传入活动。 +2. **进入条件(启动校验,agent 必须在创建活动实例前校验,极简口径)**:启动入参仅 **4 项**——客户 customer_id、客户需求 request_description(无 P-01 记录则新建·版本 V1)、原 PN、候选 PN(数组 ≥1)。**应用场景、BOM 位、替代类型、规则版本、负责人/期限不是启动入参**——替代类型默认 Pin-to-Pin、规则版本默认当前发布版、负责人/期限按客户线默认,均在执行工单 01 列为「默认·可改」行由 PW 确认或调整;应用场景、BOM 位为「待补」行由 PW 补录。**datasheet 在数据工单里上传(选填)**——传了则 AI 在数据工单里预填,没传则人全填,不影响能否启动。 +3. **存在性检查 = 全链路惰性触发(系统硬查询)**:启动校验期的存在性检查只是**第一次**;活动执行中**每次读台账**(步骤 2 读 P-08/P-10/P-06、步骤 3 取参数等)发现「完全没有」均再派新数据工单(一单一类可多条,按派发顺序 D01/D02/…)。活动可并行等待多张数据工单,全部确认入库后从断点续跑。启动期的两档语义: + - 原 PN 无数据 → **硬阻塞**:必须派数据工单创建,确认入库前活动停在「等待数据」状态; + - 候选 PN 无数据 → 候选数 **≥2** 时人可二选一(放弃该候选【记录放弃原因】或派数据工单创建);候选数 **=1** 时不能放弃、必须创建; + - 数据工单确认入库后,活动自动唤醒进入步骤 1。 + - datasheet 的有无不参与拦截,只决定数据工单是「AI 预填」还是「人全填」。 +4. **任务锚点二选一(重点:request_id 与 requirement_version 是系统产出,不是入参)**: + - **关联已有需求**:选 request_id,版本自动带出; + - **快速创建**(客户口述需求未建档场景):入参 = customer_id(老客户下拉选;新客户先建 L-02,属分支 0a)+ 需求描述(口述整理成文字);系统行为 = **自动创建 P-01 记录、生成 request_id、requirement_version 默认 V1**,并把原 PN 与候选 PN 数组作为独立启动参数挂载绑定。 +5. **口述需求的版本纪律**:版本属于需求记录而非客户口述;客户后续补充 / 澄清 → 更新 P-01 记录,requirement_version 递增(V1→V1.1,旧版保留);每次评估锁 as_of 版本(input_snapshot_ref 记录当时按哪版评),全程可回溯。 +6. **启动后系统行为序列**:校验进入条件(4 项) →(快速创建时建 P-01 · V1)→ **存在性检查**(按 internal_pn 查台账,候选数组逐个查;缺则派数据工单,活动停「等待数据」,确认入库后唤醒)→ 绑定客户 / 需求 / 原 PN / 候选数组 + 落系统默认值(替代类型、规则版本、负责人/期限)→ 生成 input_snapshot_ref → 进入步骤 1 派执行工单 01。 +7. **冻结与退回纪律**:触发时绑定的参数在工单内只读不可改;绑错的唯一处理 = 退回触发方修订后重新触发(手动 → 退回运营端启动人;上游 → 退回上游活动)。 +8. 参数字段集详见同目录《ACT-WORK-05-一键启动入参说明.md》(运营端规格)。 + +## 八、完整流程分支总表(含示例) + +1. **示例场景**(口径注意:本活动只评技术可替代性,示例不涉及库存数量 / 采购情节):客户工业电源项目在用 LM2596S-5.0(TI,12V→5V),原厂 PCN 转 NRND,客户要求验证替代;候选 A=XL2596S-5.0、B=LM2596S-ADJ、C=国产某 DCDC;一键快速创建需求单 REQ-20260826-001(V1);应用场景(12V→5V、工业温域、40V 输入上限)启动后在执行工单 01 补录。 +2. **分支总表**(18 条全列): + +| 分支 | 触发条件 | 处理与去向 | +|---|---|---| +| 0a | 启动期:新客户无档案 | 先建 L-02 客户档案(数据工单),再返回启动 | +| 0b | 启动期:候选无 P-08 主数据 | 同分支 1d:派数据工单登记创建 | +| 0c | 启动期:未传 datasheet | **不再拦截**——datasheet 已是选填,仅意味着数据工单由「AI 预填」变为「人全填」 | +| 1a | 步骤 1:绑定参数有误 | 退回触发方修订后重新触发(工单内不可改) | +| 1b | 步骤 1:输入存在缺口(有而不全) | 补缺口四情形:候选 PDF 未入库→补链接+版本;参数原厂未披露→挂验证任务(P-06);原 PN 资料版本过旧→补最新版;应用约束不清→回头问客户并更新需求单版本 | +| 1c | 步骤 1:缺口补不了 | 留 UNKNOWN 进入评估(合法路径,不阻断) | +| 1d | 任何读数点:查无记录(完全没有) | 惰性触发派数据工单(一单一类可多条,模板统一,可多实例 D01/D02/…):原 PN=硬阻塞·必须创建;候选 ≥2 可放弃【记录放弃原因】或创建(人判断);候选 =1 必须创建。确认即入库(责任人留痕)→ 活动从断点续跑。事实/证据类查无(如 P-06)不可创建,挂验证任务 | +| 2a | 步骤 2:单位冲突 | 阻断,回步骤 1 补证 | +| 2b | 步骤 2:证据版本冲突 | 人工复核哪份资料为准 | +| 3a | 步骤 3:硬约束 MISMATCH | 该候选直接「不推荐」(示例:候选 C 输入上限 32V < 客户 40V) | +| 4a | 步骤 4:UNKNOWN 无去向 | 退回步骤 1~2 补证 / 挂验证任务 | +| 4b | 步骤 4:证据链不全 | 退回补齐证据回链 | +| 5a | 步骤 5:ACT-GATE-03 放行 | P-11 落账(analysis_status=FAE确认),衔接下游 | +| 5b | 步骤 5:ACT-GATE-03 驳回 | REWORK:回步骤 4 重审,或回步骤 1 重补输入 | +| 6a | 完结:至少一个推荐 | 交下游衔接(ACT-WORK-08 / SS03 / ACT-WORK-24) | +| 6b | 完结:全是条件推荐 | 等验证闭环——通过→升级推荐;不过→降级不推荐 | +| 6c | 完结:全不推荐 | 重新检索替代料 / 继续用原 PN / 与客户协商放宽约束 | +| 6d | 完结:客户不批准 | 走独立审批流(customer_approval_status),不在本活动 | + +3. **结果矩阵(示例)**: + +| 候选 | 推荐等级 | 关键依据 | 下一步 | +|---|---|---|---| +| A · XL2596S-5.0(XLSEMI) | 条件推荐 | 温度范围更优(-40~+125℃ vs +85℃);使能阈值 UNKNOWN,挂 VAL-2026-0188 实测 | 验证闭环后转正 | +| B · LM2596S-ADJ(TI 同族可调版) | 备选 | 反馈网络不同(可调版 vs 固定 5V),需改 BOM 外围 | 客户接受改设计则启用 | +| C · 国产某 DCDC | 不推荐 | 硬约束失败:输入上限 32V < 客户 40V(分支 3a) | 归档,不再评估 | + +4. **多候选并行原则**:N 个候选 = N 条独立 P-11 记录,并行评估、独立落账,不存在「选一个再评下一个」。 +5. 可视化版本见同目录《ACT-WORK-05-全流程分支图.html》。 + +## 九、后端实现纪律(agent 必读,逐条不可违反) + +1. request_id / requirement_version / input_snapshot_ref 一律系统生成,agent 不得要求调用方传入。 +2. 活动实例创建前必须做进入条件校验与存在性检查;原 PN 无数据必须硬阻塞并派数据工单,返回缺什么;候选 PN 无数据按规则处理(≥2 可放弃【记录放弃原因】或创建,=1 必须创建)。 +3. 已绑定参数任何接口不允许修改;修改请求一律返回「退回触发方重新触发」。 +4. UNKNOWN 是一等公民:缺参数不得伪造、不得留空字符串冒充,必须显式标 UNKNOWN 并记录去向(挂验证任务 / 补来源)。 +5. 硬约束 MISMATCH 不得被任何人工操作改判 MATCH;人只能补证据后重评。 +6. analysis_status 与 customer_approval_status 严格分离:本活动只能推进 analysis_status(AI草稿→FAE确认),customer_approval_status 一律不碰(由独立审批流维护)。 +7. 规则版本 as_of 锁定:评估开始即记录 rule_version;评估中途 GOV 规则更新不得影响进行中的评估。 +8. 本活动输出只回答「技术可替代性」:不得在活动内做数量、价格、交期、采购判断(那是 SS03 / ACT-WORK-24 的事)。 +9. 执行工单缺口的处理闭环:补不了的缺口必须能以 UNKNOWN 合法放行,不得阻塞流程等人填(人可以不填)。 +10. 多候选并行:任何「单候选」假设(字段只存一个 candidate)都是错的,必须支持 N 候选独立评估与落账。 +11. 数据工单**确认即入库**:无草稿态、无审核环节,确认直接写正式库(record_status=有效);确认人=数据责任人,系统必须留痕(确认人 / 数据工单号 / 所属活动)。 +12. 数据工单必填字段全齐才允许确认(表单级校验,不算审核);缺必填不得放行确认。 +13. 事实/证据类记录禁止工单创建:P-06 验证记录等只能实测产生、挂任务闭环;数据工单只能建主数据/资料类记录(P-08 / P-10 / S-03 / L-02)。 +14. datasheet 选填:不得作为启动拦截条件;其有无只决定数据工单 AI 预填程度。无 datasheet 创建的数据,差异明细证据来源必须标「人工录入 · 数据工单 WO-xxx · 确认人 xxx」。 +15. 并发去重不考虑:一个活动只绑一个负责人,不实现数据工单的去重 / 合并逻辑。 +16. **一单一类(可多条)**:每张数据工单只针对一种台账,禁止混合台账;一张单可批量创建多条记录(字段即表头、每行一条);不同台账 / 不同读数点的缺失 = 多张单(D01/D02/…,按派发顺序编号)。 +17. **惰性触发不限于启动**:每次读台账都必须做存在性检查,查无记录即派单;不得只在启动时检查一次。活动可同时等待多张数据工单,全部确认后从断点续跑。 +18. **模板统一**:数据工单只有一个统一模板(类型级,职责只有数据创建);实例化按目标台账字段 schema(《统一方案》字段表)渲染表头,实例间仅两处可变——目标台账(决定表头 schema)、预填内容(决定行集)。 + +--- + +## 附:v1.2 待确认 4 项 — 落地状态 + +素材第六节列出 4 项待确认问题。按《SS02-SS03-SS04_Demo台账字段细化与统一方案》(2026-08),本套工单的落地状态如下: + +| # | 待确认问题 | 落地状态 | +|---|---|---| +| 1 | 差异明细正式归属是什么?(P0) | **已落地**:差异明细 = P-11 差异明细子表(difference_item_id,一条参数一条记录);验收工单 01 的 `out.difference_items` 行集即其草稿视图,含 parameter_name / original_value / candidate_value / parameter_unit / constraint_type / difference_status / evidence_ref 等字段。 | +| 2 | P-11 何时允许写入?(P0) | **已落地**:以 analysis_status 枚举区分写入时机(AI草稿→FAE确认),客户批准独立为 customer_approval_status 字段,由审批流程维护,不由差异评估产生;验收工单 02 只写分析层(analysis_status=FAE确认),customer_approval_status 保持「未提交」。 | +| 3 | 硬约束规则由哪个 GOV 对象维护?(P0) | rule_version 字段已落在差异明细子表(记录评估时采用的硬/软约束规则版本,本单锁 R-2026.07);维护方归属仍待 GOV 确认(P0 遗留)。 | +| 4 | 是否按 BOM 位独立评估?(P1) | bom_position 字段已落在 P-01 与 P-11 主记录(执行单评估输入清单以「BOM 位 bom_position」待补可编辑行承载,启动后补录,示例 U3 电源位);多 BOM 位边界不混用,不做按位独立评估。 | +| 5 | 冷启动与数据工单 | **已落地**:新增第三类工单「数据工单」——PN 不在库(完全没有,系统硬查询)时派发,人创建整条主数据记录,**确认即入库**(无草稿态、无审核环节),确认人=数据责任人并留痕;与执行工单「有而不全」严格区分、互不嵌套。 | diff --git a/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-验收工单-01-差异评估结论审核.html b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-验收工单-01-差异评估结论审核.html new file mode 100644 index 0000000..3093802 --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-验收工单-01-差异评估结论审核.html @@ -0,0 +1,212 @@ + + + + + +验收工单 1 · 差异评估结论审核 · SS02 ACT-WORK-05 差异评估 + + + +
+
+ +
+
验收
+
+
SS02 · ACT-WORK-05 差异评估 · 验收工单 1/2
+

差异评估结论审核

+
收件人:FAE · 李工 · 关联任务:TASK-BT02-20260818-007
+
+
+ 验收工单 + 步骤 3~4/5 · 逐项差异评估 + FAE/RI 审核 + 最迟交回:1 个工作日 +
+
+ +
+ +

【AI 结论说明】—— 结论是怎么算出来的

+
+

AI 已完成的计算:按 GOV 评估规则(锁定版本 R-2026.07,rule_version),对 Original=LM2596S-5.0(TI) / Candidate=XL2596S-5.0(XLSEMI) 逐项比对,产出四态结论——MATCH / MISMATCH / UNKNOWN / CONDITIONAL,并给出风险等级、证据引用与置信度。差异明细 = P-11 差异明细子表(一条参数差异一条记录,difference_item_id);当前为 AI 草稿,FAE 审核通过前不落账。评估参数可能含本次数据工单创建的主数据(确认人背书;未附 datasheet 的参数项会显式标注「人工录入」)。

+
+ +

【验收判据】—— 你据此判断「通过 / 驳回」

+
+
判断这份差异评估结论是否可信,请逐项核对
+
    +
  • 关键参数差异均有明确状态:逐项结论无漏项,每个参数都有四态之一的判定;
  • +
  • UNKNOWN 项均有去向:每个 UNKNOWN 都挂了验证任务(如 VAL-2026-0188)或补来源动作,不允许悬空;
  • +
  • 证据链完整:每项结论可追溯至 P-08 / P-10 / P-06 或 EXT-02 的具体记录,或追溯至数据工单创建记录(标注:人工录入 · 数据工单号 · 确认人);
  • +
  • 硬约束项未被自动判 MATCH:封装 / 工艺等硬约束未经证据支持不得判 MATCH。
  • +
+
+ +

【AI 产出的差异明细】—— 只读展示(P-11 差异明细子表草稿 · 一条参数一条记录 difference_item_id)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#参数项 parameter_name原 PN 值 original_value候选 PN 值 candidate_value单位 parameter_unit约束类型 constraint_type结论 difference_status风险等级证据引用 evidence_refAI 置信度
1输出电压5.05.0V硬约束MATCH低P-08 · P-100.98
2输出电流33A硬约束MATCH低P-08 · P-100.97
3开关频率150150kHz硬约束MATCH低P-08 · P-100.95
4封装TO-263-5TO-263-5—硬约束MATCH低P-08 · P-100.99
5工作温度-40~+85-40~+125℃硬约束MISMATCH高P-100.93
6使能阈值1.4未披露V硬约束UNKNOWN中挂 VAL-2026-0188—
7生命周期ActiveActive—软约束CONDITIONAL中P-10(候选供货承诺函)0.90
+
+ +
审批:本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。审核意见与补充条件写入 P-06(验证要求 verification_requirement);通过后去向:进入步骤 5 · 评审放行与落账确认。
+ +
+ + + +
+
+ + diff --git a/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-验收工单-02-评审放行与落账确认.html b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-验收工单-02-评审放行与落账确认.html new file mode 100644 index 0000000..65e8795 --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/ACT-WORK-05-验收工单-02-评审放行与落账确认.html @@ -0,0 +1,226 @@ + + + + + +验收工单 2 · 评审放行与落账确认 · SS02 ACT-WORK-05 差异评估 + + + +
+
+ +
+
验收
+
+
SS02 · ACT-WORK-05 差异评估 · 验收工单 2/2
+

评审放行与落账确认

+
收件人:FAE · 李工 · 关联任务:TASK-BT02-20260818-007
+
+
+ 验收工单 + 步骤 5/5 · 提交 ACT-GATE-03 + 最迟交回:1 个工作日 +
+
+ +
+ +

【AI 结论说明】—— 本单确认什么

+
+

本活动以 ACT-GATE-03 评审放行为终点。本单确认拟落账内容:评估结论层写 P-11 替代关系主记录(替代类型 substitution_type / 原内部料号 original_internal_pn / 候选内部料号 candidate_internal_pn / 推荐等级 recommendation_level / 推荐理由 / 差异摘要 / 替代分析报告 / 分析状态 analysis_status / 风险等级 / 验证要求 / 证据可信度 / 差异规则版本),需求状态回写 P-01(风险等级 / 验证要求 / 输出链接 / 下一步动作 / 当前阶段)。客户批准不属于本活动——客户批准状态 customer_approval_status 由审批流程维护,本单只写分析层(analysis_status=FAE确认),customer_approval_status 保持「未提交」,不由差异评估产生。

+
+ +

【验收判据】—— 你据此判断「通过 / 驳回」

+
+
放行与落账前,请逐项核对
+
    +
  • 推荐等级与差异明细一致:差异明细中存在 UNKNOWN / CONDITIONAL 时,推荐等级 recommendation_level 不得给「推荐」;
  • +
  • 分析状态与客户批准状态分离:本单只写分析层(analysis_status=FAE确认),customer_approval_status 保持「未提交」,不由差异评估产生;
  • +
  • GOV 规则版本已锁定:本次评估按 R-2026.07 执行,rule_version 随结论一并落账;
  • +
  • 回写 P-01 字段完整:风险等级、验证要求、输出链接、下一步动作、当前阶段五项齐全。
  • +
+
+ +

【拟写 P-11 · 替代关系主记录】—— 只读展示

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#字段拟写入值
1替代类型 substitution_typePin-to-Pin(BT-02 跨品牌)
2原内部料号 original_internal_pnLM2596S-5.0(TI)
3候选内部料号 candidate_internal_pnXL2596S-5.0(XLSEMI)
4推荐等级 recommendation_level条件推荐 (枚举:推荐 / 条件推荐 / 备选 / 不推荐,四态之一)
5推荐理由 recommendation_reason温度范围候选更优,使能阈值待 VAL-2026-0188 闭环
6差异摘要 difference_summary7 项比对:4 MATCH / 1 MISMATCH / 1 UNKNOWN / 1 CONDITIONAL
7替代分析报告 analysis_report_refRPT-2026-0188
8分析状态 analysis_statusFAE确认 (枚举:AI草稿 / FAE确认 / 待客户验证 / 客户已批准 / 不批准 / 作废)
9客户批准状态 customer_approval_status未提交 (由审批流程维护,本活动不产生)
10风险等级 risk_level中
11验证要求 verification_requirementVAL-2026-0188(使能阈值实测)
12证据可信度 confidence_score0.93
13差异规则版本 rule_versionR-2026.07
+
+ +

【拟回写 P-01 · 需求状态】—— 只读展示

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#字段拟写入值
1风险等级 risk_level中 (2 项待闭环)
2验证要求 verification_requirementVAL-2026-0188(使能阈值实测)
3输出链接 output_refRPT-2026-0188
4下一步动作 next_action提交 ACT-GATE-03
5当前阶段 current_stage提交 ACT-GATE-03
+
+ +
审批:本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。通过后去向:在对话区卡提交 ACT-GATE-03 评审放行;放行后结论供 ACT-WORK-08 证据归集消费。
+ +
+ + + +
+
+ + diff --git a/docs/SS02-ACT-WORK-05-html/SS02-SS03-SS04_Demo台账字段细化与统一方案.md b/docs/SS02-ACT-WORK-05-html/SS02-SS03-SS04_Demo台账字段细化与统一方案.md new file mode 100644 index 0000000..3800a1a --- /dev/null +++ b/docs/SS02-ACT-WORK-05-html/SS02-SS03-SS04_Demo台账字段细化与统一方案.md @@ -0,0 +1,632 @@ +# SS02—SS03—SS04 Demo台账字段细化与统一方案 + +## 一、结论摘要 + +本成果以《SS01-02-03-04-台账体系全景-18Activity关联矩阵》的台账编号为唯一编号标准,对六组Demo Activity进行字段级反推和补齐:[1] [2] [3] [4] [5] [6]。结论是:**当前全景体系的台账编号总体可承载Demo,但核心内容描述仍不足以直接支撑执行,必须补充字段级定义、条件必填规则和主记录/子表结构。** + +本方案将Demo中的细分编号按全景标准归并,例如P-01-01→P-01、P-08-01→P-08、P-11-01/P-11-02→P-11的不同关系状态或子对象;不新增与全景体系冲突的台账编号。[1] [2] [3] 对于逐参数差异、逐报价阶梯和逐份单证等重复明细,采用子表承载,避免在单一文本字段中堆叠数据。 + +## 二、编号归并与建模原则 + +> **编号原则:**全景矩阵的44个台账编号是主键标准;Demo中的-01/-02属于局部架构或对象层标识,除非全景矩阵明确存在对应台账,否则不作为新的全景台账编号。 + +|Demo原编号/写法|统一全景编号|统一后的对象/说明| +|---|---|---| +|P-01-01|P-01|客户具体需求与项目任务主台账;任务、需求版本、报价上下文和Activity回链统一归入。| +|P-06-01|P-06|样品交付与芯片验证记录台账;验证事实与样品追溯统一归入。| +|P-08-01|P-08|芯片主数据台账;全链路只认internal_pn。| +|P-09-02|P-09|客户技术与机会档案;技术档案字段统一挂客户ID。| +|P-10-01/02/03|P-10|原厂技术资料库;资料类型由document_type区分。| +|P-11-01/P-11-02|P-11|替代选型与批准关系库;analysis_status与customer_approval_status分离,差异明细设子表。| +|P-04-01/P-04-02|P-04|交易框架与承诺台账;框架主记录与承诺子记录分层。| +|D-01物流F组/交接E组|D-01|交易与执行台账;交接事实与物流节点作为不同对象层,但共享order_id。| + +## 三、Activity覆盖校验 + +下表说明每个Demo的关键读写台账、字段补足点和覆盖结论。[1] [2] [3] [4] [5] [6] + +|Activity|原矩阵台账编号|统一台账编号|主写入台账|关键新增/细化字段|覆盖结论| +|---|---|---|---|---|---| +|ACT-WORK-05 差异评估|P-01-01、P-06-01、P-08-01、P-09-02、P-10-01/02/03、P-11-01、P-11-02、GOV|P-01、P-06、P-08、P-09、P-10、P-11、GOV|P-01、P-11;条件写P-06|requirement_version、input_snapshot_ref、bom_position、normalized_parameter_set、difference_item_id、difference_status、impact_statement、constraint_type、rule_version、confidence_score|已覆盖;差异明细由P-11差异明细子表承载,客户批准状态与差异分析状态分离。| +|ACT-WORK-24 报价方案制定|P-01-01、P-02-01、P-03-01、P-04-01/02、P-06-01、P-08-01、P-11-02、GOV|P-01、P-02、P-03、P-04、P-06、P-08、P-11、GOV|P-02;回写P-01|quote_version、quote_type、quote_valid_until、delivery_commitment、payment_terms、tax_terms、incoterms、exchange_rate、gross_margin_status、rule_version、QuoteLine、blocking_reason、substitution_approval_status|已覆盖;P-02采用主记录+QuoteLine明细结构,正式发布与AI草稿以状态和版本区分。| +|SS03 Activity 1 发起询价|P-01、D-15、P-08、S-03、S-05、S-04|P-01、D-15、P-08、S-03、S-05、S-04|S-04 RFQ层;条件写S-03|source_type/source_id、rfq_batch_id、rfq_line_id、quote_due_at、outbound_content_ref、sent_evidence_ref、reminder_count、last_reminded_at、overdue_flag、temporary_supplier_flag|已覆盖;最小粒度固定为询价批次×供应商×内部料号。| +|SS03 Activity 2 回填报价/比价/定点|P-01、D-15、P-08、S-03、S-05、S-04|P-01、D-15、P-08、S-03、S-05、S-04|S-04 Quote/Decision层|supplier_quote_id、quote_revision_no、comparability_status、comparison_basis_snapshot_ref、baseline_match_status、baseline_unit_price、comparison_currency_code、conversion_rate、price_variance_pct、selected_supplier_id、selected_quote_id、unselected_reason、notification_recipient_ref、score_rule_version|已覆盖;跨币种、无基准、不可比、涨幅>10%、代理证及临时供应商门禁均有字段承载。| +|ACT-11-A2 提货交接与风险转移|D-01、D-03、D-10、D-11、L-02、P-08、D-04|D-01、D-03、D-10、D-11、L-02、P-08、D-04|D-01、D-03;触发D-04相关门禁|allocation_ref、handover_no、receiver_name、sign_time、risk_transfer_time、handover_diff、handover_file_ref、handover_doc_status、handover_notes、document_set_complete、consistency_check_status、allocation_status、last_ship_date|已覆盖;交接事实、风险转移派生、文件状态、单证门禁和库存同步均已落台账。| +|ACT-11-A3 物流执行信息跟踪|D-01、D-10|D-01、D-10|D-01物流节点事实层|logi_pickup/logi_clearance/logi_release/logi_departure(统一节点结构)、track_note、track_visible、logistics_confirmation_channel、logistics_confirmation_interval_days、logistics_max_reminder_count、alert_thresholds|已覆盖;每个节点包含预计时间、实际时间、来源、采集方式、确认状态、异常标记和证据引用。| + +## 四、字段统一总表 + +### 4.1 通用治理字段 + +以下字段建议作为所有涉及Demo台账的通用治理字段继承,但不要求在每张业务台账中重复手工维护。`record_status`只管理记录生命周期;`*_status`管理具体业务过程;`evidence_ref`用于AI和人工结果的可审计回链。 + +|统一字段名|中文字段名|数据类型|必填性|维护/来源|Demo使用|定义/取值/校验规则| +|---|---|---|---|---|---|---| +|record_id|记录ID|Text/AutoNumber|是|系统|所有Demo|每条台账记录的唯一主键;业务编号不能替代数据库主键。| +|record_status|记录状态|Enum|是|台账Owner|所有Demo|有效/草稿/归档/作废;不得用业务过程状态替代记录生命周期状态。| +|record_version|记录版本|Text/Integer|是|系统/Owner|W05/W24/SS03/11-A2/11-A3|每次影响业务判断的修改递增;报价、价格、规则、客户映射必须可回溯。| +|source_type|来源类型|Enum|条件必填|系统/上游Owner|W05/W24/SS03/11-A2|来源对象的类型,如客户需求、采购计划、价格维护、异常补货、订单、外部回复。| +|source_id|来源对象ID|Text/Link|条件必填|系统/上游Owner|W05/W24/SS03/11-A2|与source_type成对出现;不得把说明文字写入ID字段。| +|data_owner|数据Owner|Link|是|台账Owner|所有Demo|负责定义、质量和变更审批的角色或人员。| +|evidence_ref|证据引用|URL/Attachment/Structure|条件必填|AI/PW/Owner|W05/W24/SS03/11-A2/11-A3|指向原始邮件、聊天记录、签署件、数据表、资料页码或其他证据;可多值。| +|created_at|创建时间|DateTime|系统|系统|所有Demo|系统生成。| +|updated_at|更新时间|DateTime|系统|系统|所有Demo|系统生成。| +|last_verified_at|最后核验时间|DateTime|条件必填|Owner/PW/FAE/采购|所有Demo|对影响下游决策的事实标记最近一次人工或规则核验时间。| + +### 4.2 按全景台账编号展开的字段 + +#### P-01 客户具体需求与项目任务主台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|request_id|需求编号|Text/AutoNumber|是|主记录|SS02|W05/W24/SS03-1/SS03-2/D-01|客户需求与项目任务主记录的统一业务键;SS03写入S-04.source_id。|重命名/统一|任务ID、request_id、需求编号| +|customer_id|客户ID|Link|是|主记录|L-02/SS02|W05/W24/SS03-1/SS03-2|统一关联L-02;需求没有客户时需明确内部需求标识。|保留/细化|customer_id、客户| +|project_id|项目ID|Link|条件必填|主记录|P-05/SS02|W05/W24/SS03-1/SS03-2|有项目场景必填;与客户ID共同确定需求归属和结果通知对象。|新增/细化|project_id、客户/项目| +|requirement_version|需求版本|Text|是|主记录|SS02/系统|W05/W24|需求基线版本;差异评估与报价均按版本回溯。|新增/统一|任务描述/需求版本、版本| +|request_type|需求类型|Enum|是|主记录|SS02/系统|W05/W24/SS03|客户需求/RFQ/询价/技术需求/价格维护/其他;采购来源可由source_type补充。|新增/统一|需求类型、RFQ类型| +|request_description|需求描述|LongText|是|主记录|SS02/需求Owner|W05/W24|保留原始需求语义,不能只保留结构化字段。|新增/细化|任务描述| +|input_snapshot_ref|输入快照|URL/Attachment/Structure|W05必填;W24条件|主记录|系统/AI|W05/W24|记录本次评估或报价运行时的输入快照,支持版本回溯。|新增|输入快照| +|bom_id|BOM ID|Link|条件必填|主记录|SS02|W05/W24|有BOM场景必填;与bom_position一并确定物料位置。|新增|BOM范围| +|bom_position|BOM位|Text|条件必填|主记录|SS02/FAE|W05/W24|按BOM位记录原物料、替代物料和报价明细;不同BOM位批准边界不得混用。|新增|BOM位、PN/BOM范围| +|original_internal_pn|原内部料号|Link(P-08)|W05必填;其他条件|主记录|P-08/FAE|W05/W24|差异评估基准;引用P-08.internal_pn。|重命名/统一|原型号/原PN、原PN| +|candidate_internal_pn|候选内部料号|Link(P-08)|W05条件;W24条件|主记录|P-08/FAE|W05/W24|候选替代物料;多个候选应进入P-11子记录而非逗号拼接。|重命名/统一|候选PN、Candidate_PN| +|customer_pn|客户料号|Text|条件必填|主记录|L-02/D-10映射|W24/SS03/D-01|若客户使用客户料号,保留并与internal_pn分开。|新增/统一|customer_mpn、客户料号| +|application_scenario|应用场景|LongText/Enum|W05必填;W24条件|主记录|FAE/客户|W05/W24|解释需求约束、验证条件与报价适用场景。|保留/细化|应用场景、适配场景| +|target_qty|目标数量|Number/Structure|W24/SS03必填|主记录|需求Owner/采购|W24/SS03-1/SS03-2|正式报价、询价与比价的数量基准;阶梯放入数量阶梯子表。|重命名/统一|数量、target_qty、报价类型/数量| +|target_delivery_date|目标交期|Date|W24/SS03必填|主记录|需求Owner/采购|W24/SS03-1/SS03-2|正式报价、询价与比价的日期基准。|重命名/统一|target_delivery_date、目标交期| +|target_price_internal|内部目标价|Currency|否|主记录|SS02|W24/SS03|仅内部判断;默认不对外发送;币种必须同时记录。|保留/细化|target_price_internal、目标价| +|target_price_currency_code|内部目标价币种|ISO 4217 Enum|target_price有值时必填|主记录|SS02|W24/SS03|与target_price_internal成对出现。|新增/统一|currency| +|requested_price_types|所需价格类型|MultiEnum|SS03必填;W24条件|主记录|需求Owner/采购|W24/SS03-1/SS03-2|参考价/量产价/现货价/样品价;上游默认值可补充,删除或替换需留痕。|重命名/统一|price_types_default、price_types_requested、报价类型| +|quote_due_at|期望报价返回时间|DateTime|SS03必填;W24条件|主记录|需求Owner/采购|W24/SS03-1/SS03-2|客户需求来源从P-01读取;内部或维护型询价由采购填写。|重命名/统一|quote_return_due_at、quote_due_at、截止时间| +|technical_constraints|技术约束|Structure|W05/W24条件|主记录|FAE/AI|W05/W24|包含参数、单位、版本、硬约束/软约束,供差异评估和报价前提检查。|新增|参数边界、硬/软约束| +|risk_level|风险等级|Enum|W05/W24条件|主记录|FAE/BDPM|W05/W24|低/中/高/关键;必须记录确认人和时间。|统一|FAE人工判断/风险等级、风险等级| +|verification_requirement|验证/复测/确认要求|LongText/Structure|W05条件;W24条件|主记录|FAE|W05/W24|用于关闭UNKNOWN或CONDITIONAL,并可回链P-06验证记录。|重命名/统一|验证/复测/确认要求、验证要求| +|latest_quote_id|最新报价ID|Link(P-02)|W24/SS03条件|主记录|系统|W24/SS03-2|聚合P-02最新有效报价,不直接复制价格。|新增|关联报价、最新报价状态| +|latest_quote_status|最新报价状态|Lookup/Enum|W24/SS03条件|主记录|系统|W24/SS03-2|由P-02.quote_status/publish_status聚合。|重命名/统一|最新报价状态、报价状态| +|request_status|需求状态|Enum|是|主记录|SS02|W05/W24/SS03|待处理/已响应/已成交/已放弃/关闭;不得与Activity运行状态混用。|重命名/统一|状态机、需求状态| +|current_activity|当前Activity|Link/Enum|W05/W24条件|主记录|系统|W05/W24|如ACT-WORK-05、ACT-WORK-24;用于流程位置显示。|重命名/统一|当前Activity| +|current_stage|当前阶段|Enum/Text|W05/W24条件|主记录|系统/Owner|W05/W24|评估、报价、审批、发布等阶段。|新增/统一|当前阶段| +|next_action|下一步动作|LongText|W05/W24条件|主记录|FAE/BDPM/系统|W05/W24|明确责任人、动作和截止时间。|统一|下一步动作| +|output_ref|标准输出链接|URL/Attachment|W05/W24条件|主记录|系统/FAE/BDPM|W05/W24|差异报告或报价文件的正式输出链接。|重命名/统一|标准输出类型/输出文件链接、输出链接| + +#### P-02 报价与商务记录台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|quote_id|报价ID|Text/AutoNumber|是|报价主记录|系统|W24/SS03-2|报价主记录唯一标识。|保留/细化|报价ID| +|request_id|关联需求编号|Link(P-01)|是|报价主记录|系统|W24/SS03-2|回链P-01.request_id。|重命名/统一|关联任务、关联需求号| +|quote_version|报价版本|Text|是|报价主记录|系统/BDPM|W24|同一需求条件变化创建新版本;旧版本保留不可覆盖。|新增/统一|报价版本、版本| +|quote_type|报价类型|Enum|是|报价主记录|BDPM|W24|正式报价/参考价/项目报价/其他;与price_type(明细价格类型)区分。|新增/统一|报价类型| +|quote_status|报价状态|Enum|是|报价主记录|系统/BDPM|W24/SS03-2|草稿/待审核/已批准/已发布/已过期/已撤回。|重命名/统一|报价发布状态、报价状态| +|quote_currency_code|报价币种|ISO 4217 Enum|是|报价主记录|系统/BDPM|W24|报价金额的统一币种;明细价格若币种不同必须换算并留痕。|重命名/统一|报价价格/币种、currency| +|valid_from|报价生效时间|Date/DateTime|条件必填|报价主记录|BDPM|W24|正式发布报价必须有生效时间。|新增/统一|生效期| +|quote_valid_until|报价有效期至|Date|条件必填|报价主记录|BDPM|W24|不得与P-03价格来源有效期混用。|重命名/统一|报价有效期、valid_until| +|delivery_commitment|报价交期承诺|Date/Text/Structure|是|报价主记录|PW/BDPM|W24|记录交期数值、计算口径、来源和是否为承诺。|重命名/统一|报价交期、交期| +|payment_terms|付款条件|Text/Structure|条件必填|报价主记录|BDPM/P-04|W24|来源于P-04或本次商务确认。|新增/统一|付款| +|tax_terms|税费条件|Text/Structure|条件必填|报价主记录|BDPM|W24|税率、含税/未税口径必须明确。|新增|税费| +|incoterms|贸易术语|Enum|条件必填|报价主记录|BDPM/P-04|W24|使用标准Incoterms枚举并记录版本/地点。|新增/统一|Incoterms、交货条款| +|quote_assumptions|报价前提条件|LongText/Structure|是|报价主记录|BDPM|W24|含付款、税费、Incoterms、MOQ/SPQ、供货边界等。|重命名/细化|报价前提条件| +|technical_boundary|技术边界|LongText/Structure|条件必填|报价主记录|FAE/BDPM|W24|记录替代、验证、规格限制与客户批准状态。|新增|技术边界| +|cost_basis|成本基准|Currency/Structure|条件必填|报价主记录|BDPM/采购|W24|记录成本来源、币种、数量口径和时点。|新增|成本| +|exchange_rate|报价汇率|Number|跨币种时必填|报价主记录|系统/BDPM|W24|统一比较币种时使用;必须关联exchange_rate_source与as_of。|新增|汇率| +|exchange_rate_source|汇率来源|Text/URL|exchange_rate有值时必填|报价主记录|系统/BDPM|W24|记录汇率来源。|新增|汇率| +|exchange_rate_as_of|汇率时点|Date|exchange_rate有值时必填|报价主记录|系统|W24|统一币种计算基准日。|新增|汇率| +|gross_margin_status|毛利状态|Enum/Structure|条件必填|报价主记录|系统/BDPM|W24|未计算/通过/低于阈值/缺成本;口径由GOV规则维护。|新增|毛利状态| +|rule_version|报价规则版本|Text/Link|是|报价主记录|GOV/系统|W24|记录计算、毛利、价格优先级使用的规则版本。|新增/统一|规则版本| +|approval_status|商务审批状态|Enum|是|报价主记录|Gate/审批人|W24|未提交/待审批/已通过/已驳回;通过前不得发布。|重命名/统一|商务联审结论/审批记录| +|approval_comment|商务审批意见|LongText|条件必填|报价主记录|审批人|W24|驳回或有条件通过时必须填写。|新增/统一|审批记录、审批意见| +|publish_status|报价发布状态|Enum|是|报价主记录|系统/Gate|W24|草稿/待审批/已批准/已发布/过期/撤回。|重命名/统一|报价发布状态| +|quote_document_ref|正式报价文件链接|URL/Attachment|已发布时必填|报价主记录|系统/BDPM|W24|只指向正式发布版本。|重命名/统一|报价文件、台账链接| +|blocking_reason|阻断原因|LongText/Structure|条件必填|报价主记录|AI/BDPM|W24|价格过期、冲突、阶梯缺失、技术批准缺失等逐项记录。|新增|阻断原因| +|quote_line_id|报价明细ID|Text/AutoNumber|是|报价明细QuoteLine|系统|W24|最小粒度为报价版本×PN×BOM位×数量阶梯。|新增|QuoteLine| +|internal_pn|内部料号|Link(P-08)|是|报价明细QuoteLine|P-08/FAE|W24|报价物料统一引用P-08。|统一|PN| +|bom_position|BOM位|Text|条件必填|报价明细QuoteLine|FAE|W24|同一报价中不同BOM位不能混淆。|统一|BOM位| +|quantity_tier|数量阶梯|Structure|是|报价明细QuoteLine|AI/BDPM|W24|含min_qty、max_qty或阶梯描述。|拆分/结构化|数量阶梯| +|price_type|价格类型|Enum|是|报价明细QuoteLine|BDPM|W24|参考价/量产价/现货价/样品价等。|新增/统一|price_type| +|unit_price|报价单价|Currency|是|报价明细QuoteLine|系统/BDPM|W24|与currency_code成对存在。|重命名/统一|报价价格、单价| +|currency_code|明细币种|ISO 4217 Enum|是|报价明细QuoteLine|系统/BDPM|W24|明细币种;若与主报价币种不同必须记录换算。|重命名/统一|币种| +|lead_time_days|供货交期天数|Number|条件必填|报价明细QuoteLine|供应商/采购/BDPM|W24|统一为自然日或工作日一种口径,口径写入字段说明。|重命名/统一|LT、报价交期| +|moq_qty|最小起订量|Number|条件必填|报价明细QuoteLine|P-08/S-05/采购|W24|数量比较与前提条件校验。|拆分/统一|MOQ| +|spq_qty|标准包装量|Number|条件必填|报价明细QuoteLine|P-08/S-05/采购|W24|数量比较与包装约束校验。|拆分/统一|SPQ| +|price_source_ref|价格来源引用|Link(P-03/S-05/S-04)|是|报价明细QuoteLine|系统|W24|指向Price List、采购基准或专项询价记录。|新增/统一|来源报价、价格来源| +|substitution_approval_status|替代批准状态|Enum|条件必填|报价明细QuoteLine|P-11|W24|未检查/待验证/已批准/不批准;未满足时阻断发布。|新增|技术替代批准状态| + +#### P-03 Price List台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|price_list_id|PriceList ID|Text/AutoNumber|是|价格记录|系统/BDPM|W24|价格来源唯一标识。|重命名/统一|PriceListID| +|internal_pn|内部料号|Link(P-08)|是|价格记录|P-08/价格Owner|W24|统一引用P-08。|重命名/统一|PN/料号| +|customer_id|适用客户ID|Link(L-02)|条件必填|价格记录|价格Owner|W24|客户专属价必填;公共渠道价可用scope_type标识。|新增|客户或渠道| +|price_scope_type|价格适用范围|Enum|是|价格记录|BDPM|W24|客户专属/渠道/公共/项目。|新增|客户或渠道| +|price_type|价格类型|Enum|是|价格记录|BDPM|W24|与报价所需价格类型对齐。|新增/统一|价格类型| +|list_unit_price|Price List单价|Currency|是|价格记录|价格Owner|W24|与currency_code成对。|重命名/统一|客户专属价/单价| +|currency_code|价格币种|ISO 4217 Enum|是|价格记录|价格Owner|W24|统一币种编码。|重命名/统一|币种| +|release_version|Release版本|Text|是|价格记录|价格Owner|W24|报价时按as_of选取有效版本。|重命名/统一|Release版本、版本| +|valid_from|生效时间|Date|是|价格记录|价格Owner|W24|与valid_to组成有效区间。|保留/细化|生效期| +|valid_to|失效时间|Date|是|价格记录|价格Owner|W24|报价时点必须落在有效区间内。|重命名/统一|失效期、有效期| +|quantity_tier|数量阶梯|Structure|条件必填|价格记录|价格Owner|W24|含适用数量和折扣条件。|拆分/结构化|数量阶梯/折扣条款| +|discount_terms|折扣条款|LongText/Structure|条件必填|价格记录|价格Owner|W24|结构化记录折扣条件和例外。|细化|折扣条款| +|source_quote_id|来源报价ID|Link(P-02/S-04)|条件必填|价格记录|系统/BDPM|W24|能追溯价格来源。|重命名/统一|来源报价| +|price_status|价格状态|Enum|是|价格记录|价格Owner|W24|草稿/有效/暂停/过期/替代。|新增/统一|状态| + +#### P-04 交易框架与承诺台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|framework_id|交易框架ID|Text/AutoNumber|是|框架主记录|商务Owner|W24|统一框架合同主键。|重命名/统一|框架ID| +|customer_id|客户ID|Link(L-02)|是|框架主记录|商务Owner|W24|交易框架归属客户。|保留/统一|客户名称| +|contract_version|合同版本|Text|是|框架主记录|商务Owner|W24|报价采用的合同/框架版本。|重命名/统一|合同版本| +|contract_status|合同状态|Enum|是|框架主记录|商务Owner|W24|草稿/有效/到期/终止。|保留/细化|合同状态| +|account_status|交易账户状态|Enum|是|框架主记录|商务Owner|W24|正常/冻结/待准入/关闭。|重命名/统一|交易账户状态| +|payment_terms|账期与付款条款|Text/Structure|条件必填|框架主记录|商务Owner|W24|供报价继承;与D-04信用台账区分。|重命名/统一|账期条款、付款条件| +|credit_limit|信用额度|Currency|条件必填|框架主记录|商务Owner|W24/11-A2条件|可供信用门禁使用。|保留/细化|信用额度| +|cooperation_scope|合作范围|LongText/Structure|条件必填|框架主记录|商务Owner|W24|定义品牌、产品线、区域等边界。|保留/细化|合作范围| +|incoterms|默认贸易术语|Enum|条件必填|框架主记录|商务Owner|W24|报价若覆盖本条件,回链本字段。|新增/统一|交易条款、Incoterms| +|effective_from|框架生效时间|Date|是|框架主记录|商务Owner|W24|有效区间。|新增/统一|生效期| +|effective_to|框架失效时间|Date|条件必填|框架主记录|商务Owner|W24|有效区间。|新增/统一|生效期| +|commitment_id|承诺ID|Text/AutoNumber|条件必填|承诺子记录|系统/商务Owner|W24|每条价格、交期、数量或条件承诺一个ID。|重命名/统一|承诺ID| +|commitment_party|承诺方|Enum/Link|是|承诺子记录|商务Owner|W24|我方/客户/供应商/其他。|统一|承诺方| +|commitment_type|承诺类型|Enum|是|承诺子记录|商务Owner|W24|价格/交期/数量/质量/付款/其他。|新增/统一|承诺内容| +|commitment_content|承诺内容|LongText/Structure|是|承诺子记录|商务Owner|W24|结构化值+说明,避免只写自由文本。|细化|承诺内容| +|commitment_due_date|承诺期限|Date|条件必填|承诺子记录|商务Owner|W24|到期前可触发提醒。|重命名/统一|期限| +|commitment_status|承诺状态|Enum|是|承诺子记录|商务Owner/系统|W24|未生效/生效/已兑现/逾期/取消。|重命名/统一|状态| +|fulfillment_record_ref|兑现记录引用|Link/URL|条件必填|承诺子记录|系统/履约|W24|回链D-01/D-09等执行事实。|重命名/统一|兑现记录| +|unresolved_risk_list|未决商务风险清单|LongText/Structure|条件必填|主记录|商务Owner|W24|报价前置风险;未关闭时可阻断审批。|重命名/细化|未决风险清单、商务风险| + +#### P-06 样品交付与芯片验证记录台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|validation_id|验证ID|Text/AutoNumber|是|验证主记录|系统|W05/W24|验证记录唯一标识。|保留/统一|Validation_ID、验证编号| +|request_id|关联需求编号|Link(P-01)|条件必填|验证主记录|系统/FAE|W05/W24|回链需求与验证目的。|新增/统一|需求号| +|project_id|关联项目ID|Link(P-05)|条件必填|验证主记录|系统/FAE|W05/W24|项目场景验证必填。|新增/统一|项目| +|customer_id|客户ID|Link(L-02)|条件必填|验证主记录|系统/FAE|W05/W24|客户验证必填。|新增/统一|客户ID| +|validation_internal_pn|验证内部料号|Link(P-08)|是|验证主记录|FAE|W05/W24|统一引用P-08.internal_pn。|重命名/统一|Validation_PN、验证PN| +|validation_type|验证类型|Enum|是|验证主记录|FAE|W05/W24|Pin-to-Pin/功能/可靠性/认证/复测。|新增/统一|验证类型| +|test_condition|测试条件|LongText/Structure|是|验证主记录|FAE/验证Owner|W05|环境、输入、负载、样品状态等。|重命名/统一|测试条件| +|test_parameters|测试参数|Structure|是|验证主记录|FAE/验证Owner|W05|参数名、值、单位、版本必须结构化。|重命名/统一|测试参数| +|test_result|测试结果|LongText/Structure|是|验证主记录|验证Owner|W05|实测数据与结论,必要时附原始数据。|重命名/统一|测试结果| +|validation_status|验证状态|Enum|是|验证主记录|FAE/验证Owner|W05/W24|未开始/进行中/通过/不通过/条件通过/待补证。|新增/统一|通过状态、验证状态| +|issue_risk|问题与风险|LongText/Structure|条件必填|验证主记录|AI/FAE|W05/W24|记录未关闭问题、风险等级、责任人和关闭条件。|重命名/统一|问题与风险| +|sample_version|样品版本|Text|条件必填|验证主记录|验证Owner|W05|样品追溯。|重命名/统一|样品版本| +|sample_lot|样品批次|Text|条件必填|验证主记录|验证Owner|W05|与D-07或样品出库记录回链。|重命名/统一|样品批次、批次| +|evidence_ref|验证证据引用|URL/Attachment/Structure|是|验证主记录|验证Owner/FAE|W05/W24|原始数据、照片、报告、资料页码等。|统一|证据引用| +|next_validation_action|后续验证动作|LongText/Structure|条件必填|验证主记录|FAE|W05/W24|UNKNOWN/CONDITIONAL时必须有关闭动作。|新增|验证建议/缺口| + +#### P-08 芯片主数据台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|internal_pn|内部料号|Text|是|物料主记录|SS02/FAE|W05/W24/SS03/11-A2|全链路料号统一基准;不可使用自由文本替代。|重命名/统一|标准型号PN、pn、mpn| +|customer_pn|客户料号|Text|条件必填|物料主记录|SS02/D-10映射|W24/SS03/11-A2|客户料号映射优先维护在D-10,P-08可作为别名索引。|新增/统一|customer_mpn| +|brand_name|品牌名称|Text/Link|是|物料主记录|FAE/数据Owner|W05/W24/SS03/11-A2|品牌统一引用;外部供应商品牌与原厂制造商分开。|重命名/统一|brand、manufacturer_brand、原厂或品牌| +|manufacturer_name|制造商名称|Text/Link|条件必填|物料主记录|数据Owner|W05/W24|品牌与制造商不一致时分开记录。|新增/统一|原厂| +|specification_description|规格描述|LongText|是|物料主记录|数据Owner/原厂资料|W05/W24|标准规格文本。|重命名/统一|Specification Description、产品描述| +|normalized_parameter_set|标准化参数集|Structure|W05必填;W24条件|物料主记录|FAE/数据Owner|W05/W24|参数名、值、单位、精度、版本,用于硬/软约束比较。|新增|参数名称、单位、版本| +|package|封装|Enum/Text|条件必填|物料主记录|数据Owner|W05/W24/SS03|影响硬约束和报价识别。|保留/细化|封装| +|process_technology|工艺|Text/Enum|条件必填|物料主记录|数据Owner|W05|硬约束比较项。|重命名/统一|工艺| +|lifecycle_status|生命周期状态|Enum|是|物料主记录|数据Owner|W05/W24/SS03|Active/NRND/EOL/停产/风险。|重命名/统一|生命周期状态、lifecycle_status、产品状态| +|eol_pcn_status|EOL/PCN状态|Enum/Structure|条件必填|物料主记录|数据Owner/原厂|W05/W24|记录通知状态、影响和生效时间。|新增/统一|EoL/PCN状态| +|datasheet_ref|Datasheet引用|URL/Link(P-10)|条件必填|物料主记录|P-10/原厂|W05/W24|官方资料链接。|重命名/统一|Datasheet链接| +|datasheet_version|Datasheet版本|Text|datasheet_ref有值时必填|物料主记录|P-10/原厂|W05/W24|用于证据版本回链。|重命名/统一|Datasheet版本/版本| +|application_scenarios|适配应用场景|MultiEnum/Structure|条件必填|物料主记录|FAE/数据Owner|W05/W24|判断物料实际适用性。|重命名/统一|应用场景、适配场景| +|pin_to_pin_relation_ref|Pin-to-Pin关系引用|Link(P-11)|条件必填|物料主记录|FAE|W05/W24|引用P-11关系记录,避免在P-08复制批准结论。|新增/统一|Pin-to-Pin替代关系| + +#### P-09 客户技术与机会档案 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|customer_id|客户ID|Link(L-02)|是|客户技术档案|SS02/FAE|W05/W24|区别于L-02的商务画像,技术档案以客户ID为主键。|保留/统一|客户 ID| +|technical_stack|客户技术栈|Structure|条件必填|客户技术档案|FAE|W05/W24|记录平台、架构、关键技术约束。|重命名/统一|技术栈| +|product_line_direction|产品线技术方向|Text/Structure|条件必填|客户技术档案|FAE/BDPM|W24|报价上下文与应用场景。|重命名/统一|产品线方向| +|bom_brand_usage|BOM品牌使用情况|Structure|条件必填|客户技术档案|FAE|W05/W24|记录客户当前BOM品牌、料号及替代空间。|重命名/统一|BOM品牌| +|validation_process|客户验证流程|LongText/Structure|条件必填|客户技术档案|FAE/客户|W05/W24|支持验证要求和报价前提判断。|重命名/统一|验证流程| +|technical_decision_maker_ref|技术决策人引用|Link(L-08)|条件必填|客户技术档案|FAE|W05/W24|引用联系人,不复制姓名。|重命名/统一|技术决策人| +|rd_capability_assessment|研发能力评估|Enum/LongText|条件必填|客户技术档案|FAE|W05/W24|用于技术支持与验证路径判断。|重命名/统一|研发能力| +|technical_preference_tags|技术偏好标签|MultiEnum|条件必填|客户技术档案|FAE|W05/W24|用于推荐和方案匹配。|重命名/统一|技术偏好标签| +|opportunity_id|机会ID|Text/AutoNumber|条件必填|机会子记录|系统/BDPM|W05/W24|需求或报价对应的机会对象。|新增/统一|机会记录| + +#### P-10 原厂技术资料库 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|document_id|资料ID|Text/AutoNumber|是|资料主记录|系统|W05/W24|资料唯一标识。|新增/统一|资料记录| +|document_type|资料类型|Enum|是|资料主记录|FAE|W05/W24|Datasheet/应用笔记/参考设计/白皮书/样片资料/其他。|新增/统一|资料类型| +|internal_pn|关联内部料号|Link(P-08)|条件必填|资料主记录|P-08/FAE|W05/W24|料号相关资料必须回链P-08。|新增/统一|PN| +|brand_name|品牌名称|Text/Link|是|资料主记录|数据Owner|W05/W24|统一引用P-08.brand_name。|重命名/统一|原厂| +|document_title|资料标题|Text|是|资料主记录|资料Owner|W05/W24|资料可识别名称。|新增|资料| +|document_version|资料版本|Text|是|资料主记录|资料Owner|W05/W24|评估必须回链版本。|重命名/统一|版本| +|document_date|资料发布日期|Date|条件必填|资料主记录|资料Owner|W05/W24|用于时效判断。|新增|更新记录| +|document_ref|资料文件链接|URL/Attachment|是|资料主记录|资料Owner|W05/W24|官方资料文件。|重命名/统一|Datasheet链接、资料| +|citation_locator|证据定位|Text/Structure|W05必填;W24条件|资料主记录|AI/FAE|W05/W24|页码、章节、表格或段落,支持参数证据引用。|新增|证据引用、页码或证据段落| +|source_contact_ref|原厂支持联系人|Link(L-08)/Text|条件必填|资料主记录|FAE|W05/W24|需要补证时的联系人。|重命名/统一|原厂支持联系人| +|document_status|资料状态|Enum|是|资料主记录|资料Owner|W05/W24|有效/过期/待核实/作废。|新增/统一|状态| + +#### P-11 替代选型与批准关系库 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|substitution_relation_id|替代关系ID|Text/AutoNumber|是|替代关系主记录|系统|W05/W24|原P-11-01与P-11-02统一后的关系主键。|新增/统一|P-11-01/P-11-02记录ID| +|original_internal_pn|原内部料号|Link(P-08)|是|替代关系主记录|P-08/FAE|W05/W24|评估基准物料。|重命名/统一|Original_PN、原PN、目标替代料号| +|candidate_internal_pn|候选内部料号|Link(P-08)|是|替代关系主记录|P-08/FAE|W05/W24|被评估物料。|重命名/统一|Candidate_PN、候选PN| +|substitution_type|替代类型|Enum|是|替代关系主记录|FAE|W05/W24|Pin-to-Pin/功能替代/参数替代/其他。|重命名/统一|替代类型、关系类型| +|recommendation_level|推荐等级|Enum|是|替代关系主记录|AI/FAE|W05/W24|推荐/条件推荐/备选/不推荐。|重命名/统一|替代等级、推荐| +|recommendation_reason|推荐理由|LongText|是|替代关系主记录|AI/FAE|W05/W24|说明技术和商务依据。|新增/统一|推荐理由| +|difference_summary|差异摘要|LongText|是|替代关系主记录|AI/FAE|W05|供P-01和后续流程消费。|新增/统一|评论、差异摘要| +|difference_item_id|差异明细ID|Text/AutoNumber|是|差异明细子表|系统|W05|一条参数差异一条记录。|新增|逐参数差异明细| +|parameter_name|标准参数名|Text/Link(GOV)|是|差异明细子表|AI/FAE|W05|参数名称标准化。|新增/统一|参数名称| +|original_value|原物料参数值|Text/Number/Structure|是|差异明细子表|AI|W05|引用P-08或资料证据。|新增|参数A| +|candidate_value|候选物料参数值|Text/Number/Structure|是|差异明细子表|AI|W05|引用P-08或资料证据。|新增|参数B| +|parameter_unit|参数单位|Text/Link(GOV)|条件必填|差异明细子表|AI/FAE|W05|统一单位后比较。|新增/统一|单位| +|constraint_type|约束类型|Enum|是|差异明细子表|GOV/AI|W05|硬约束/软约束。|新增/统一|硬/软约束标识| +|difference_status|差异状态|Enum|是|差异明细子表|AI/FAE|W05|MATCH/MISMATCH/UNKNOWN/CONDITIONAL。|新增/统一|Status、差异状态| +|impact_statement|影响说明|LongText|MISMATCH/CONDITIONAL必填|差异明细子表|AI/FAE|W05|说明对应用、验证、报价的影响。|新增|Impact、影响说明| +|evidence_ref|差异证据引用|URL/Attachment/Structure|是|差异明细子表|AI/FAE|W05|具体资料、页码、测试记录。|统一|证据引用| +|rule_version|差异规则版本|Text/Link(GOV)|是|差异明细子表|GOV/系统|W05|记录评估时采用的硬/软约束规则版本。|新增/统一|GOV规则版本| +|risk_level|替代风险等级|Enum|是|替代关系主记录|FAE/RI|W05/W24|低/中/高/关键。|统一|风险等级| +|verification_requirement|验证要求|LongText/Structure|CONDITIONAL/UNKNOWN必填|替代关系主记录|FAE|W05/W24|与P-06.validation_id或待验证任务回链。|统一|验证要求| +|validation_id|关联验证ID|Link(P-06)|条件必填|替代关系主记录|系统/FAE|W05/W24|验证完成后回链事实。|新增/统一|验证编号、验证结果| +|analysis_status|分析状态|Enum|是|替代关系主记录|系统/FAE|W05/W24|AI草稿/FAE确认/待客户验证/客户已批准/不批准/作废。|新增/统一|写入权限与时机、批准状态| +|customer_approval_status|客户批准状态|Enum|是|替代关系主记录|审批流程|W05/W24|未提交/待批准/已批准/不批准/有条件批准;不由差异评估自动产生。|重命名/统一|批准状态、客户批准| +|customer_approval_at|客户批准时间|DateTime|客户已批准时必填|替代关系主记录|审批流程|W05/W24|客户批准事实时间。|新增/统一|批准时间| +|customer_approval_ref|客户批准凭证|URL/Attachment|客户已批准时必填|替代关系主记录|审批流程|W05/W24|签核邮件、系统审批单或客户文件。|新增/统一|批准凭证| +|customer_feedback|客户反馈|LongText|条件必填|替代关系主记录|FAE/BDPM|W05/W24|记录客户对候选物料的反馈。|保留/细化|客户反馈| +|request_id|关联需求编号|Link(P-01)|条件必填|替代关系主记录|系统|W05/W24|明确候选关系适用的需求。|新增/统一|关联需求| +|project_id|关联项目ID|Link(P-05)|条件必填|替代关系主记录|系统|W05/W24|不同项目批准边界可不同。|新增/统一|关联项目| +|bom_position|关联BOM位|Text|条件必填|替代关系主记录|FAE|W05/W24|批准边界定位。|新增/统一|BOM位| +|analysis_report_ref|替代分析报告|URL/Attachment|条件必填|替代关系主记录|系统/FAE|W05|正式结构化结果或附件回链。|重命名/统一|替代分析报告、报告链接| +|confidence_score|证据可信度|Percent/Enum|条件必填|替代关系主记录|AI/FAE|W05|AI输出可信度,需与人工确认区分。|新增/统一|证据可信度| + +#### S-03 供应商主台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|supplier_id|供应商ID|Text/AutoNumber|是|供应商主记录|SS03|SS03-1/SS03-2|统一供应商主体键;临时供应商定点前必须转正式ID。|保留/统一|supplier_id、Supplier ID| +|supplier_name|供应商名称|Text|是|供应商主记录|采购/SS03|SS03-1/SS03-2|临时供应商可先占位,定点前补齐正式名称。|保留/统一|supplier_name、供应商名称| +|supplier_type|供应商分类|Enum|是|供应商主记录|SS03|SS03-1/SS03-2|原厂/授权代理/贸易/分销/其他。|保留/细化|supplier_type、供应商分类| +|supplier_status|供应商状态|Enum|是|供应商主记录|SS03|SS03-1/SS03-2|临时/正式/停用;临时状态不可定点。|新增/统一|supplier_status| +|temporary_supplier_flag|临时供应商标记|Boolean|是|供应商主记录|采购|SS03-1/SS03-2|临时新增立即可询价,但必须受定点门禁约束。|新增|临时供应商| +|sales_contact_ref|业务联系人引用|Link/Text|SS03-1必填|供应商主记录|采购/供应商Owner|SS03-1|至少一名联系人,用于邮件或即时通讯外发。|重命名/统一|sales_contact、联系人| +|contact_channel|首选联系渠道|Enum|条件必填|供应商主记录|供应商Owner|SS03-1|邮件/即时通讯/电话;需与外发记录一致。|新增/统一|send_channel、确认通道| +|rfq_product_scope|当前询价品牌/料号范围|Structure|临时供应商定点前必填|供应商主记录|采购|SS03-1/SS03-2|允许不预建完整品牌覆盖,但必须覆盖当前询价。|新增/统一|rfq_product_scope| +|authorized_brand_scope|授权品牌范围|Structure|代理/原厂条件必填|供应商主记录|SS03|SS03-1/SS03-2|判断品牌授权和可供范围。|新增/统一|品牌授权| +|agency_certificate_ref|代理证凭证|URL/Attachment|supplier_type=授权代理时必填|供应商主记录|供应商Owner/采购|SS03-2|无有效代理证不得提交定点审批。|保留/细化|agency_certificate_ref、代理证| +|agency_certificate_valid_to|代理证有效期至|Date|代理证有值时必填|供应商主记录|供应商Owner|SS03-2|定点前检查有效期。|新增|代理证| +|qualification_status|准入资质状态|Enum|定点前必填|供应商主记录|SS03|SS03-2|待准入/合格/限制/不合格。|新增/统一|资质/准入| +|compliance_status|合规状态|Enum|定点前必填|供应商主记录|SS03|SS03-2|通过/待核查/不通过。|新增/统一|合规| +|commercial_terms_ref|长期商务规则引用|Link/Structure|条件必填|供应商主记录|SS03|SS03-2|长期条款不在本次询价Activity维护,但应有引用。|新增/统一|商务规则、长期条款| +|supplier_health_score|供应商健康度|Percent/Enum|条件必填|供应商主记录|SS03/SS04|SS03-2|由履约数据计算,不作为单次询价事实覆盖。|新增/统一|供应商绩效/健康度| + +#### S-04 询价、报价、比价与采购决策记录台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|rfq_batch_id|询价批次号|Text/AutoNumber|是|RFQ层|系统/采购|SS03-1/SS03-2|同一批次可含多料号、多供应商;品牌或料号变化必须新开批次。|保留/统一|rfq_batch_id、询价批次号| +|rfq_line_id|询价行ID|Text/AutoNumber|是|RFQ层|系统|SS03-1/SS03-2|最小粒度为询价批次×供应商×内部料号。|保留/统一|rfq_line_id、询价行ID| +|source_type|需求来源类型|Enum|是|RFQ层|采购/上游台账|SS03-1/SS03-2|P-01客户需求/D-15采购计划/价格维护/异常补货。|统一|source_type| +|source_id|来源对象ID|Text/Link|条件必填|RFQ层|系统/上游台账|SS03-1/SS03-2|与source_type成对;P-01写request_id,D-15写purchase_plan_id。|统一|source_id| +|customer_id|客户ID|Link(L-02)|条件必填|RFQ层|P-01/D-15|SS03-1/SS03-2|用于需求归属与通知。|新增/统一|customer_id| +|project_id|项目ID|Link(P-05)|条件必填|RFQ层|P-01/D-15|SS03-1/SS03-2|用于需求归属与通知。|新增/统一|project_id| +|brand_name|询价品牌|Text/Link(P-08)|是|RFQ层|P-08/采购|SS03-1/SS03-2|与internal_pn精确匹配;品牌变化新开批次。|重命名/统一|brand、manufacturer_brand| +|internal_pn|询价内部料号|Link(P-08)|是|RFQ层|P-08/采购|SS03-1/SS03-2|统一引用P-08.internal_pn。|重命名/统一|pn、料号| +|target_qty|目标数量|Number|是|RFQ层|P-01/D-15/采购|SS03-1/SS03-2|比价基准;来自P-01.target_qty或D-15.trigger_qty。|统一|target_qty、purchase_trigger_qty| +|target_delivery_date|目标交期|Date|是|RFQ层|P-01/D-15/采购|SS03-1/SS03-2|比价基准;来自P-01或D-15.requirement_date。|统一|target_delivery_date、requirement_date| +|requested_price_types|所需价格类型|MultiEnum|是|RFQ层|P-01/采购|SS03-1|参考价/量产价/现货价/样品价。|重命名/统一|price_types_requested| +|quote_due_at|报价回复截止时间|DateTime|是|RFQ层|P-01/采购|SS03-1|供应商回复时效基准。|重命名/统一|quote_due_at、期望报价返回时间| +|target_price_internal|内部目标价|Currency|否|RFQ层|P-01|SS03-1/SS03-2|仅内部判断;如实际披露需留痕。|统一|target_price_internal| +|supplier_id|供应商ID|Link(S-03)|是|RFQ层|S-03/采购|SS03-1/SS03-2|临时供应商定点前转正式ID。|统一|supplier_id| +|send_channel|询价发送渠道|Enum|是|RFQ层|采购/系统|SS03-1|邮件/即时通讯/电话。|统一|send_channel| +|sent_at|询价发送时间|DateTime|是|RFQ层|系统|SS03-1|系统生成;未发送不得标记已发送。|保留/细化|sent_at| +|outbound_content_ref|外发内容引用|URL/Attachment|是|RFQ层|系统/采购|SS03-1|记录对外披露内容和附件。|新增/统一|对外披露内容、附件| +|sent_evidence_ref|外发凭证|URL/Attachment|是|RFQ层|系统|SS03-1|原始邮件、聊天发送记录等。|新增/统一|发送凭证、evidence| +|rfq_status|询价状态|Enum|是|RFQ层|系统/采购|SS03-1|草稿/已发送/部分回复/全部回复/已到期未回复/催询中/关闭。|保留/细化|rfq_status| +|reminder_count|催询次数|Integer|是|RFQ层|系统|SS03-1|自动提醒与人工催询均计数。|新增|跟进回复时效| +|last_reminded_at|最后催询时间|DateTime|条件必填|RFQ层|系统/采购|SS03-1|有催询时必填。|新增|催询| +|overdue_flag|询价超期标记|Boolean|是|RFQ层|系统|SS03-1|quote_due_at已过且未完成回复或未发出时触发。|新增|已到期未回复、发起超期| +|supplier_quote_id|供应商报价ID|Text/AutoNumber|是|Quote层|系统|SS03-2|同一询价行可有报价修订历史。|新增/统一|报价记录ID| +|quote_status|供应商报价状态|Enum|是|Quote层|采购|SS03-2|已报价/拒绝报价/未回复/待确认。|保留/细化|quote_status| +|price_type|报价价格类型|Enum|条件必填|Quote层|采购|SS03-2|必须与requested_price_types匹配。|保留/统一|price_type| +|unit_price|报价单价|Currency|已报价时必填|Quote层|采购|SS03-2|与currency_code成对。|重命名/统一|unit_price、价格| +|currency_code|报价币种|ISO 4217 Enum|已报价时必填|Quote层|采购|SS03-2|统一币种比较前保留原币种。|重命名/统一|currency| +|actual_qty|报价适用数量|Number|已报价时条件必填|Quote层|采购|SS03-2|判断与target_qty可比性。|重命名/统一|actual_qty| +|lead_time_days|报价交期天数|Number|已报价时条件必填|Quote层|采购|SS03-2|统一工作日/自然日口径;原始文字可放lead_time_note。|重命名/统一|lead_time、LT| +|lead_time_note|交期说明|LongText|条件必填|Quote层|采购|SS03-2|无法标准化时保留供应商原文。|新增|lead_time| +|moq_qty|MOQ|Number|条件必填|Quote层|采购|SS03-2|最小起订量。|拆分/统一|MOQ-SPQ、MOQ| +|spq_qty|SPQ|Number|条件必填|Quote层|采购|SS03-2|标准包装量。|拆分/统一|MOQ-SPQ、SPQ| +|quote_valid_until|报价有效期至|Date|条件必填|Quote层|采购|SS03-2|报价是否仍可用。|重命名/统一|valid_until、报价有效期| +|special_terms|本次特殊商务条款|LongText/Structure|否|Quote层|采购|SS03-2|一次性条款;长期供应商条款回链S-03。|重命名/细化|special_terms、特殊条款| +|response_at|实际回复时间|DateTime|已回复时必填|Quote层|系统/采购|SS03-2|回复时间事实。|保留/细化|response_at| +|no_quote_reason|未报价原因|LongText|拒绝/未回复时必填|Quote层|采购|SS03-2|拒绝报价或未回复必须填写。|保留/细化|no_quote_reason| +|quote_evidence_ref|原始报价凭证|URL/Attachment|报价/拒绝均必填|Quote层|系统/采购|SS03-2|邮件、聊天截图、供应商报价单。|重命名/统一|evidence、原始沟通凭证| +|quote_revision_no|同轮报价修订号|Integer|是|Quote层|系统/采购|SS03-2|同轮修改保留修订历史,当前报价可聚合展示。|新增|报价与版本| +|comparison_group_id|比价组ID|Text/AutoNumber|是|Decision层|系统|SS03-2|由rfq_batch_id+internal_pn+target_qty+target_delivery_date组成。|重命名/统一|comparison_group| +|comparability_status|可比性状态|Enum|是|Decision层|AI/采购|SS03-2|可直接比较/条件可比/不可比;不可比必须说明原因。|新增|正式比价组、不可直接比较| +|comparison_basis_snapshot_ref|比价基准快照|Structure/URL|是|Decision层|系统|SS03-2|固化目标数量、目标交期、价格类型、汇率和基准版本。|新增|比价组| +|system_score|系统评分|Number|是|Decision层|系统|SS03-2|按询价类型权重计算,0-100。|重命名/统一|score| +|system_recommendation|系统推荐|Enum|是|Decision层|系统|SS03-2|推荐/备选/不推荐。|重命名/统一|recommendation| +|procurement_confirm|采购确认意见|Enum|意见确认时必填|Decision层|采购|SS03-2|认可/调整/否决。|保留/细化|procurement_confirm| +|confirm_reason|确认或调整理由|LongText|与系统推荐不一致时必填|Decision层|采购|SS03-2|采购调整或否决系统建议时必填。|重命名/统一|confirm_reason| +|baseline_id|采购基准ID|Link(S-05)|有基准时必填|Decision层|系统|SS03-2|与品牌、料号、价格类型、数量和有效期匹配。|新增/统一|baseline_id| +|baseline_match_status|基准匹配状态|Enum|是|Decision层|系统|SS03-2|已匹配/无基准/条件匹配/不匹配。|新增|无基准可比、基准比较| +|baseline_unit_price|比较基准单价|Currency|有基准时必填|Decision层|系统|SS03-2|复制当次采用的S-05基准快照,避免历史重算漂移。|新增/统一|benchmark_unit_price| +|comparison_currency_code|比价统一币种|ISO 4217 Enum|跨币种时必填|Decision层|系统|SS03-2|统一币种并回链汇率。|新增|汇率/统一币种| +|conversion_rate|币种换算汇率|Number|跨币种时必填|Decision层|系统|SS03-2|记录换算基准和来源。|新增|汇率| +|price_variance_pct|相对基准涨幅|Percent|有基准时派生|Decision层|系统|SS03-2|(报价-基准)/基准;涨幅>10%标红。|重命名/统一|price_variance_pct、涨幅| +|variance_reason|涨价原因|LongText|涨幅>10%时必填|Decision层|采购|SS03-2|说明供应商涨价、市场、交期或其他原因。|保留/细化|variance_reason、涨价原因| +|selected_supplier_id|中选供应商ID|Link(S-03)|定点通过时必填|Decision层|系统/采购|SS03-2|审批通过的定点对象。|新增/统一|推荐供应商、中选供应商| +|selected_quote_id|中选报价ID|Link(S-04 Quote)|定点通过时必填|Decision层|系统|SS03-2|关联被选中的供应商报价。|新增/统一|定点结论| +|approval_status|定点审批状态|Enum|是|Decision层|审批流|SS03-2|待审批/已通过/已驳回。|重命名/统一|approval_status| +|approval_comment|定点审批意见|LongText|驳回/条件通过时必填|Decision层|审批人|SS03-2|审批意见。|重命名/统一|approval_comment| +|decision_status|定点结论状态|Enum|是|Decision层|系统/采购|SS03-2|待定点/已定点/无法定点/建议关闭/已变更。|重命名/统一|decision_status、定点结论| +|unselected_reason|未选/无法定点原因|LongText|无法定点或未选时必填|Decision层|采购|SS03-2|完整说明比较、准入或供应条件原因。|新增/统一|未选原因、无法定点原因| +|notification_status|结果通知状态|Enum|是|Decision层|系统|SS03-2|待通知/已通知/通知失败。|重命名/统一|notification_status| +|notification_recipient_ref|结果通知对象|Link/Text|定点结论产生时必填|Decision层|系统|SS03-2|按source_type通知P-01负责人、D-15负责人或其他发起人。|新增/统一|通知需求发起人| +|score_rule_version|评分规则版本|Text/Link(GOV)|是|Decision层|GOV/系统|SS03-2|记录不同询价类型的权重版本。|新增/统一|规则版本| + +#### S-05 采购基准与供应能力台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|baseline_id|采购基准ID|Text/AutoNumber|是|基准主记录|系统|SS03-1/SS03-2|基准唯一标识。|保留/统一|baseline_id| +|brand_name|基准品牌|Text/Link(P-08)|是|基准主记录|采购/SS03|SS03-1/SS03-2|与internal_pn精确匹配。|重命名/统一|brand| +|internal_pn|基准内部料号|Link(P-08)|是|基准主记录|P-08/采购|SS03-1/SS03-2|统一引用P-08。|重命名/统一|pn| +|supplier_id|基准供应商ID|Link(S-03)|是|基准主记录|S-03/采购|SS03-1/SS03-2|当前采购基准的供应商。|统一|supplier_id| +|price_type|基准价格类型|Enum|是|基准主记录|采购|SS03-2|必须与本次报价价格类型一致后比较。|统一|price_type| +|baseline_unit_price|采购基准单价|Currency|是|基准主记录|采购|SS03-2|历史/当前有效采购基准,不被单次询价直接覆盖。|重命名/统一|benchmark_unit_price| +|currency_code|基准币种|ISO 4217 Enum|是|基准主记录|采购|SS03-2|与单价成对。|重命名/统一|currency| +|quantity_tier|适用数量阶梯|Structure|是|基准主记录|采购|SS03-2|匹配目标数量。|拆分/结构化|applicable_qty、数量阶梯| +|valid_from|基准生效时间|Date|是|基准主记录|采购|SS03-2|有效期判断。|重命名/统一|effective_from| +|valid_to|基准失效时间|Date|是|基准主记录|采购|SS03-2|有效期判断。|重命名/统一|effective_to| +|lead_time_days|基准交期天数|Number|条件必填|基准主记录|采购|SS03-1/SS03-2|供应能力比较。|重命名/统一|lead_time、LT| +|moq_qty|基准MOQ|Number|条件必填|基准主记录|采购|SS03-2|供应能力比较。|拆分/统一|MOQ| +|spq_qty|基准SPQ|Number|条件必填|基准主记录|采购|SS03-2|供应能力比较。|拆分/统一|SPQ| +|availability_status|可供状态|Enum|是|基准主记录|采购|SS03-1/SS03-2|可供/部分可供/不可供/待确认。|重命名/统一|availability_status、可供状态| +|supply_risk_level|供应风险等级|Enum|条件必填|基准主记录|采购|SS03-2|低/中/高/关键。|新增/统一|供应风险| +|baseline_status|基准状态|Enum|是|基准主记录|采购|SS03-2|Active/暂停/过期/替代。|保留/细化|baseline_status| +|source_quote_ref|基准来源报价引用|Link(S-04/P-02)|条件必填|基准主记录|系统/采购|SS03-2|记录基准形成依据。|重命名/统一|来源报价| +|locked_at|基准锁定时间|DateTime|条件必填|基准主记录|采购|SS03-2|锁定后单次询价不得直接覆盖。|新增|报价锁定基准| + +#### D-15 采购计划与执行需求台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|purchase_plan_id|采购计划ID|Text/AutoNumber|是|采购计划主记录|SS04|SS03-1/SS03-2|内部采购需求唯一引用;写入S-04.source_id。|保留/统一|purchase_plan_id| +|customer_id|客户ID|Link(L-02)|条件必填|采购计划主记录|SS04|SS03-1/SS03-2|客户需求型采购必填。|统一|customer_id| +|project_id|项目ID|Link(P-05)|条件必填|采购计划主记录|SS04|SS03-1/SS03-2|项目型采购必填。|统一|project_id| +|internal_pn|内部料号|Link(P-08)|是|采购计划主记录|P-08/SS04|SS03-1/SS03-2|统一引用P-08。|重命名/统一|pn| +|forecast_version|Forecast版本|Text/Link(D-02)|条件必填|采购计划主记录|D-02/SS04|SS03-1/SS03-2|Forecast来源时必填。|统一|Forecast版本、版本号| +|customer_pickup_demand|客户提货需求|Number/Date/Structure|条件必填|采购计划主记录|SS04|SS03-1/SS03-2|客户提货需求的数量和时间。|重命名/统一|提货需求| +|requirement_date|内部需求日期|Date|是|采购计划主记录|SS04|SS03-1/SS03-2|作为询价目标交期或内部需求时点。|重命名/统一|requirement_date、需求日期| +|inventory_ref|库存引用|Link(D-03)|条件必填|采购计划主记录|系统|SS03-1/SS03-2|用于采购建议量判断。|新增/统一|库存引用| +|in_transit_ref|在途引用|Link(D-03/D-16)|条件必填|采购计划主记录|系统|SS03-1/SS03-2|用于采购建议量判断。|新增/统一|在途引用| +|recommended_purchase_qty|建议采购量|Number|是|采购计划主记录|系统/采购|SS03-1/SS03-2|形成询价目标数量的依据。|新增/统一|建议采购量| +|trigger_qty|采购触发数量|Number|是|采购计划主记录|SS04|SS03-1/SS03-2|进入询价的正式数量输入。|重命名/统一|purchase_trigger_qty、触发数量| +|purchase_type|采购类型|Enum|是|采购计划主记录|SS04|SS03-1/SS03-2|量产/样品/现货/异常补货。|保留/细化|purchase_type| +|urgency_flag|紧急程度|Enum/Boolean|是|采购计划主记录|SS04|SS03-1/SS03-2|普通/紧急/特急;影响评分权重。|保留/细化|urgency_flag| +|plan_status|采购计划状态|Enum|是|采购计划主记录|SS04|SS03-1/SS03-2|有效/待执行/询价中/已满足/关闭。|保留/细化|plan_status| +|triggered_at|触发询价时间|DateTime|条件必填|采购计划主记录|系统|SS03-1|记录进入询价流程的时间。|新增/统一|触发时间| +|po_ref|关联采购订单引用|Link(D-16)|条件必填|采购计划主记录|系统|SS03-2|定点/执行后回链D-16。|重命名/统一|关联后续PO| + +#### L-02 客户主台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|customer_id|客户ID|Text/AutoNumber|是|客户主记录|SS01|W24/SS03/11-A2|所有客户侧台账统一锚点。|保留/统一|唯一ID、customer_id| +|customer_name|客户名称|Text|是|客户主记录|SS01|W05/W24/11-A2|展示名称;法律主体与展示名称如不同需分开。|新增/统一|客户名称、客户| +|legal_entity_name|客户法律主体|Text|条件必填|客户主记录|SS01|11-A2条件|单证、合同或制裁筛查需要时必填。|新增|主体信息| +|customer_status|客户状态|Enum|是|客户主记录|SS01|W24/11-A2|有效/暂停/终止。|新增/统一|状态机| +|default_contact_ref|默认联系人引用|Link(L-08)|条件必填|客户主记录|SS01|11-A2|与D-10提货联系人区分;D-10可覆盖为履约联系人。|新增/统一|联系人| + +#### D-01 交易与执行台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|order_id|订单号|Text/AutoNumber|是|订单主记录|SS04|11-A2/11-A3|订单执行主键。|保留/统一|order_id、订单号| +|request_id|需求编号|Link(P-01)|条件必填|订单主记录|系统|11-A2|回链P-01需求。|统一|需求编号| +|customer_id|客户ID|Link(L-02)|是|订单主记录|系统|11-A2/11-A3|客户名称通过L-02展示。|统一|customer_id| +|project_id|项目ID|Link(P-05)|条件必填|订单主记录|系统|11-A2|项目追溯。|统一|项目| +|internal_pn|内部料号|Link(P-08)|是|订单主记录|P-08/SS04|11-A2|内部料号统一引用P-08。|重命名/统一|mpn、料号| +|customer_pn|客户料号|Text/Link(D-10)|条件必填|订单主记录|D-10/SS04|11-A2|通过D-10映射获得。|重命名/统一|customer_mpn| +|order_qty|订单数量|Number|是|订单主记录|订单/系统|11-A2|交接实发数量核对基准。|保留/统一|order_qty| +|allocation_ref|分配批次引用|Link(D-03)|条件必填|订单主记录|D-03/SS04|11-A2|指向D-03批次分配记录。|新增/统一|inventory_lot| +|promised_delivery_date|承诺交期|Date|条件必填|订单主记录|P-04/SS04|11-A2|订单承诺交期。|重命名/统一|承诺交期| +|trade_term|贸易术语|Enum|条件必填|订单主记录|D-10/P-04|11-A2|EXW等;风险转移逻辑依赖。|新增/统一|BT类型、贸易术语| +|milestones|履约里程碑|Structure|是|订单主记录|系统/SS04|11-A2/11-A3|至少含交接;每个节点有状态、时间、来源、证据。|细化|milestones、里程碑| +|pickup_status|提货状态|Enum|是|提货交接事实层|PW/系统|11-A2|待提货/已预约/已提货/待重新预约/取消。|保留/细化|pickup_status| +|ship_date|发货日期|Date|现场交接时必填|提货交接事实层|PW|11-A2|现场事实;由PW唯一录入。|保留/细化|ship_date| +|shipped_qty|实发数量|Number|现场交接时必填|提货交接事实层|PW|11-A2|现场事实;与order_qty及D-03同步校验。|保留/细化|shipped_qty| +|handover_no|交接单号|Text|交接完成时必填|提货交接事实层|系统|11-A2|格式HD-YYYYMMDD-NNN。|保留/细化|handover_no| +|receiver_name|签收人姓名|Text|交接完成时必填|提货交接事实层|PW|11-A2|双方签署事实。|重命名/统一|receiver、签收人姓名| +|sign_time|交接签署时间|DateTime|交接完成时必填|提货交接事实层|PW|11-A2|EXW风险转移时点的事实基础。|保留/细化|sign_time、签署时间| +|risk_transfer_time|风险转移时点|DateTime|签署后派生|提货交接事实层|系统/AI/PW确认|11-A2/11-A3|EXW场景默认等于sign_time;作为开票和后续追踪锚点。|保留/细化|risk_transfer_time| +|handover_diff|交接差异|Enum/Structure|条件必填|提货交接事实层|PW/RI|11-A2|无差异/数量差异/型号差异/批次差异/包装差异/其他;关联差异单。|保留/细化|handover_diff| +|handover_file_ref|交接单影像/文件|URL/Attachment|交接完成时必填|提货交接事实层|PW|11-A2|必须区分草稿、已签署、已归档。|重命名/统一|handover_file| +|handover_doc_status|交接单文件状态|Enum|有handover_file_ref时必填|提货交接事实层|PW/系统|11-A2|草稿/已签署/已归档/作废;下游只消费已签署或已归档。|新增/统一|文档状态| +|handover_notes|交接注意事项|LongText|条件必填|提货交接事实层|D-10/PW|11-A2|模板或现场特殊注意事项。|新增|交接注意事项| +|logi_pickup|提货节点事实|Structure|11-A3必填|物流节点事实层|AI/PW|11-A3|含estimated_time、actual_time、source、collection_method、confirmation_status、anomaly_flag、evidence_ref。|保留/结构化|logi_pickup| +|logi_clearance|报关节点事实|Structure|11-A3必填|物流节点事实层|AI/PW|11-A3|同上;异常如报关受阻、查验。|保留/结构化|logi_clearance| +|logi_release|放行节点事实|Structure|11-A3必填|物流节点事实层|AI/PW|11-A3|同上。|保留/结构化|logi_release| +|logi_departure|离港节点事实|Structure|11-A3必填|物流节点事实层|AI/PW|11-A3|同上;异常如离港延误。|保留/结构化|logi_departure| +|track_note|物流跟踪/决策备注|LongText|条件必填|物流节点事实层|PW/RI|11-A3|货代不配合时记录降级为到货确认或销售协调等决策。|重命名/统一|track_note、决策记录| +|track_visible|物流信息可查状态|Boolean|四节点完成后必填|物流节点事实层|系统|11-A3|四节点实际事实齐备后为True;不是人工主观勾选。|保留/细化|track_visible| +|invoice_status|开票状态|Enum|是|订单状态层|系统/财务|11-A2|待开票/已触发/已开票/异常;risk_transfer_time为触发锚点。|新增/统一|开票状态| +|acceptance_status|验收状态|Enum|是|订单状态层|履约Owner|11-A2|待验收/已确认/争议/不适用。|新增/统一|验收状态| +|order_status|订单状态|Enum|是|订单状态层|系统/SS04|11-A2/11-A3|已放行/已交接/物流跟踪中/已完成/异常/关闭。|重命名/统一|订单状态机| +|closed_flag|关闭标记|Boolean|是|订单状态层|系统/SS04|11-A2/11-A3|所有必需节点或异常处置完结后关闭。|新增/统一|关闭状态| +|close_reason|关闭原因|LongText|关闭时必填|订单状态层|SS04|11-A2/11-A3|正常完成/取消/转异常流程等。|新增|关闭状态| +|pickup_appointment_time|预约提货时间|DateTime|分支2场景必填|提货交接事实层|D-10/PW|11-A2|用于预约提醒、未按预约提货判断及重新预约。|新增/统一|提货预约时间| +|physical_check_status|实物核对状态|Enum|交接前必填|提货交接事实层|PW|11-A2|未核对/已核对/不一致;核对数量、型号、批次标签和包装。|新增|实物一致性核对| +|diff_handling_result|差异处理结果|Enum/Structure|有差异时必填|提货交接事实层|RI/PW|11-A2|补齐后交接/按实发量交接并标注/暂停并转后续Activity。|新增/统一|差异处理结果| +|ri_gate_decision|RI门禁判断|Enum/LongText|差异或超时升级时必填|提货交接事实层|RI|11-A2/11-A3|放行/补齐后放行/暂停/转异常/降级追踪/联系客户核实。|新增/统一|RI门禁判断、决策记录| +|ri_escalated_at|RI升级时间|DateTime|发生升级时必填|提货交接事实层|系统|11-A2/11-A3|差异、预约超时或节点反馈超时的升级事实。|新增|升级时限| +|node_type|物流节点类型|Enum|是|物流节点子表(适用于logi_*)|系统|11-A3|提货/报关/放行/离港;对应logi_*字段。|新增/统一|四个关键节点| +|estimated_time|节点预计时间|DateTime|是|物流节点子表(适用于logi_*)|系统/AI|11-A3|到达预计时间后触发确认请求。|新增/统一|节点预计时间| +|actual_time|节点实际时间|DateTime|收到事实时必填|物流节点子表(适用于logi_*)|AI/PW|11-A3|收到反馈或人工电话确认的实际时间。|新增/统一|时间| +|source|节点事实来源|Text/Link|是|物流节点子表(适用于logi_*)|AI/PW|11-A3|回复单位、系统或具体联系人;不得只写“外部”。|重命名/统一|来源、Source| +|collection_method|节点采集方式|Enum|是|物流节点子表(适用于logi_*)|AI/PW|11-A3|邮件/微信/即时通讯/电话/人工录入。|重命名/统一|采集方式、Method| +|confirmation_status|节点确认状态|Enum|是|物流节点子表(适用于logi_*)|系统/PW|11-A3|待确认/已确认/超时/异常/已升级。|新增/统一|确认状态| +|anomaly_flag|节点异常标记|Boolean|是|物流节点子表(适用于logi_*)|AI/PW|11-A3|报关受阻、查验、离港延误等为True。|新增/统一|Anomaly Flag、异常节点标记| +|anomaly_type|节点异常类型|Enum/Text|anomaly_flag=True时必填|物流节点子表(适用于logi_*)|AI/PW|11-A3|报关受阻/查验/离港延误/其他。|新增/统一|异常节点| +|node_evidence_ref|节点事实证据|URL/Attachment|已确认或异常时必填|物流节点子表(适用于logi_*)|AI/PW|11-A3|回复原文、电话记录或其他节点证据。|新增/统一|证据引用| +|reminder_count|节点提醒次数|Integer|是|物流节点子表(适用于logi_*)|系统|11-A3|按D-10最大提醒次数控制。|新增/统一|最大提醒次数| +|last_request_at|最后请求时间|DateTime|有请求时必填|物流节点子表(适用于logi_*)|系统|11-A3|支持间隔天数和超时判断。|新增|确认请求| +|node_ri_escalation_status|节点RI升级状态|Enum|超时/异常时必填|物流节点子表(适用于logi_*)|系统/RI|11-A3|未升级/已升级/已核实/已降级。|新增/统一|升级RI门禁| + +#### D-04 回款与信用台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|credit_record_id|信用记录ID|Text/AutoNumber|是|信用门禁主记录|系统|11-A2|回款信用门禁记录主键。|新增/统一|信用记录| +|customer_id|客户ID|Link(L-02)|是|信用门禁主记录|SS04|11-A2|关联客户信用主体。|统一|客户ID| +|payment_terms|账期条款|Text/Structure|是|信用门禁主记录|P-04/SS04|11-A2|从交易框架读取,作为信用判断依据。|重命名/统一|账期条款| +|credit_limit|信用额度|Currency|条件必填|信用门禁主记录|商务/财务|11-A2|与currency_code成对。|统一|信用额度| +|outstanding_amount|应收金额|Currency|是|信用门禁主记录|财务|11-A2|当前应收,需记录币种。|新增/统一|应收| +|paid_amount|已回款金额|Currency|是|信用门禁主记录|财务|11-A2|当前已回款,需记录币种。|新增/统一|已回款| +|overdue_days|逾期天数|Integer|是|信用门禁主记录|系统/财务|11-A2|信用预警触发依据。|保留/细化|逾期天数| +|credit_rating|信用评级|Enum|是|信用门禁主记录|财务/商务|11-A2|正常/关注/高风险/冻结。|保留/细化|信用评级| +|warning_status|信用预警状态|Enum|是|信用门禁主记录|系统/财务|11-A2|无预警/预警/拦截/已解除。|保留/细化|预警状态| + +#### D-03 库存与在途台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|inventory_record_id|库存记录ID|Text/AutoNumber|是|库存批次记录|系统|11-A2|库存事实主键。|新增/统一|库存记录| +|internal_pn|内部料号|Link(P-08)|是|库存批次记录|P-08/SS04|11-A2|统一引用P-08。|重命名/统一|料号| +|manufacturer_lot|原厂批次|Text|条件必填|库存批次记录|供应商/仓库|11-A2|交接单/COC披露。|重命名/统一|mfr_lot、lot| +|date_code|日期码DC|Text|条件必填|库存批次记录|供应商/仓库|11-A2|交接单/COC披露。|重命名/统一|dc、Date Code| +|available_qty|可用数量|Number|是|库存批次记录|仓库|11-A2|可出库库存。|新增/统一|可用| +|inspection_qty|待检数量|Number|是|库存批次记录|仓库|11-A2|状态数量。|新增/统一|待检| +|frozen_qty|冻结数量|Number|是|库存批次记录|仓库|11-A2|状态数量。|新增/统一|冻结| +|in_transit_qty|在途数量|Number|是|库存批次记录|系统|11-A2|与D-16履约关联。|新增/统一|在途| +|allocation_id|分配记录ID|Text/AutoNumber|是|库存批次记录|系统|11-A2|批次占用与订单绑定。|新增/统一|子表2| +|allocated_qty|已分配数量|Number|是|库存批次记录|系统|11-A2|与allocation_status配套。|新增/统一|分配数量| +|allocation_status|分配状态|Enum|是|库存批次记录|系统/PW确认|11-A2|已分配/部分出库/已出库/释放。|重命名/统一|alloc_status| +|shipped_qty|已出库数量|Number|是|库存批次记录|系统/PW确认|11-A2|与D-01.shipped_qty同步。|保留/细化|shipped_qty| +|last_ship_date|最后出库日期|Date|条件必填|库存批次记录|系统/PW确认|11-A2|该批次最后一次出库事实。|保留/细化|last_ship_date| +|moq_qty|库存批次MOQ|Number|条件必填|库存批次记录|S-05/供应商|11-A2|备货决策参考。|新增/统一|MOQ| +|lead_time_days|库存批次交期天数|Number|条件必填|库存批次记录|S-05/供应商|11-A2|备货与在途判断。|新增/统一|交期| +|stocking_decision|备货决策|Enum/Structure|条件必填|库存批次记录|履约Owner|11-A2|维持/补货/冻结/退出。|新增/统一|备货决策| +|exit_plan|退出方案|LongText/Structure|stocking_decision=退出时必填|库存批次记录|履约Owner|11-A2|异常PN或库存退出动作。|新增/统一|退出方案| + +#### D-10 客户履约主数据台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|execution_profile_id|履约参数档案ID|Text/AutoNumber|是|执行参数主记录|SS04|11-A2/11-A3|客户履约参数的版本化主键。|新增/统一|执行参数| +|customer_id|客户ID|Link(L-02)|是|执行参数主记录|SS04|11-A2/11-A3|履约参数按客户维护。|统一|客户| +|incoterms|贸易术语|Enum|是|执行参数主记录|履约Owner|11-A2|EXW等;影响风险转移规则。|新增/统一|贸易术语| +|packaging_requirements|包装要求|LongText/Structure|条件必填|执行参数主记录|履约Owner|11-A2|生成交接单和实物核对依据。|重命名/统一|包装| +|label_requirements|标签要求|LongText/Structure|条件必填|执行参数主记录|履约Owner|11-A2|实物核对依据。|重命名/统一|标签| +|document_requirements|单证要求|Structure|条件必填|执行参数主记录|履约Owner|11-A2|客户格式与单证清单要求。|重命名/统一|单证要求| +|logistics_preference|物流偏好|Structure|条件必填|执行参数主记录|履约Owner|11-A3|节点采集偏好。|重命名/统一|物流偏好| +|service_level|服务等级|Enum/Structure|条件必填|执行参数主记录|履约Owner|11-A3|节点可见性和响应要求。|新增/统一|服务等级| +|alert_thresholds|预警阈值|Structure|条件必填|执行参数主记录|履约Owner|11-A3|节点超时、异常延误等阈值。|新增/统一|预警阈值| +|pickup_contact_ref|提货联系人引用|Link(L-08)/Text|是|执行参数主记录|履约Owner|11-A2/11-A3|用于交接单和未按预约提货提醒。|重命名/统一|pickup_contact| +|pickup_appointment_mode|预约提货方式|Enum|是|执行参数主记录|履约Owner|11-A2|预约/临时/固定窗口等。|重命名/统一|pickup_appt_mode| +|logistics_confirmation_channel|物流节点确认渠道|Enum|是|执行参数主记录|履约Owner|11-A3|邮件/微信/即时通讯/电话。|重命名/统一|logi_channel、确认通道| +|logistics_confirmation_interval_days|节点确认间隔天数|Integer|是|执行参数主记录|履约Owner|11-A3|默认3天;可按客户配置。|重命名/统一|logi_interval| +|logistics_max_reminder_count|最大提醒次数|Integer|是|执行参数主记录|履约Owner|11-A3|默认3次;达到后升级RI。|重命名/统一|logi_max_retry| +|handover_template_ref|交接单模板引用|URL/Attachment|是|执行参数主记录|履约Owner|11-A2|生成交接单草稿。|重命名/统一|handover_tpl| +|handover_notes|交接注意事项|LongText|条件必填|执行参数主记录|履约Owner|11-A2|客户特殊注意事项;生成交接单。|新增/统一|交接注意事项| +|mapping_id|料号映射ID|Text/AutoNumber|是|料号映射子表|系统|11-A2|客户料号与内部料号一对一版本记录。|新增|料号映射| +|customer_pn|客户料号|Text|是|料号映射子表|履约Owner|11-A2|客户侧料号。|重命名/统一|客户料号、customer_mpn| +|internal_pn|内部料号|Link(P-08)|是|料号映射子表|履约Owner|11-A2|内部统一料号。|重命名/统一|internal_pn| +|mapping_version|映射版本|Text|是|料号映射子表|履约Owner|11-A2|料号映射版本受控。|新增/统一|版本| +|mapping_status|映射状态|Enum|是|料号映射子表|履约Owner|11-A2|有效/待确认/过期/作废。|新增/统一|状态| +|difference_flag|映射差异标记|Boolean/Structure|是|料号映射子表|系统/履约Owner|11-A2|客户料号与内部料号无法一一对应时标记并说明。|新增/统一|差异标注| + +#### D-11 出口单证台账 + +|统一字段名|中文字段名|数据类型|必填性|对象层|维护/来源|Demo使用|定义/取值/校验规则|处理建议|原字段/来源名称| +|---|---|---|---|---|---|---|---|---|---| +|document_set_id|单证包ID|Text/AutoNumber|是|单证包主记录|系统|11-A2|订单级单证包唯一标识。|新增/统一|证据包| +|order_id|订单号|Link(D-01)|是|单证包主记录|系统|11-A2|回链D-01订单。|新增/统一|订单号| +|invoice_ref|发票文件引用|URL/Attachment|条件必填|单证包主记录|履约/财务|11-A2|随货单证之一。|重命名/统一|发票| +|packing_list_ref|箱单文件引用|URL/Attachment|条件必填|单证包主记录|履约|11-A2|随货单证之一。|重命名/统一|箱单| +|coc_ref|COC文件引用|URL/Attachment|条件必填|单证包主记录|履约/质量|11-A2|随货单证之一。|重命名/统一|COC| +|certificate_of_origin_ref|产地证文件引用|URL/Attachment|条件必填|单证包主记录|履约|11-A2|按客户或贸易场景要求。|重命名/统一|产地证| +|warranty_certificate_ref|质保书文件引用|URL/Attachment|条件必填|单证包主记录|履约|11-A2|随货单证之一。|重命名/统一|质保书| +|required_document_types|应提供单证类型|MultiEnum|是|单证包主记录|D-10/履约|11-A2|根据D-10.document_requirements生成。|新增/统一|单证清单、客户格式要求| +|document_set_complete|单证包齐套状态|Enum|是|单证包主记录|系统/PW|11-A2|未齐套/已齐套;已齐套至少满足本订单required_document_types。|保留/细化|doc_set_complete| +|consistency_check_status|单证一致性审核状态|Enum|是|单证包主记录|系统/PW|11-A2|未审核/通过/不通过/待补正;核对订单、料号、数量、批次、客户信息。|新增/统一|一致性审核| +|consistency_check_result|单证一致性审核结果|LongText/Structure|审核后必填|单证包主记录|系统/PW|11-A2|记录差异项、责任人和补正动作。|新增/统一|一致性审核记录| +|document_archive_ref|单证包归档引用|URL/Attachment|归档时必填|单证包主记录|系统/履约|11-A2|正式证据包统一归档。|新增/统一|证据包| +|archive_due_date|归档期限|Date|条件必填|单证包主记录|履约Owner|11-A2|依据客户或合规要求。|新增/统一|归档期限| +|document_type|单证类型|Enum|是|单证明细子表|系统|11-A2|发票/箱单/COC/产地证/质保书/其他。|新增/统一|doc_list| +|document_ref|单证文件引用|URL/Attachment|是|单证明细子表|履约|11-A2|单证文件。|重命名/统一|doc_list| +|document_status|单证文件状态|Enum|是|单证明细子表|系统/履约|11-A2|草稿/已核验/已归档/作废。|新增/统一|文档状态| +|document_version|单证版本|Text|是|单证明细子表|系统/履约|11-A2|单证修改必须递增。|新增/统一|版本| + + +## 五、重叠字段与统一命名清单 + +下表列出不同Activity或台账中“含义相同但写法不同”“层级不同却使用同名”“同一字段承载多个语义”的主要问题,以及本次建议的统一口径。 + +|冲突主题|涉及台账/Activity|原有写法|统一字段名|统一口径| +|---|---|---|---|---| +|客户ID|P-01/P-02/P-04/P-06/P-09/S-04/D-01/D-10/L-02;W05/W24/SS03/11-A2|customer_id、客户ID、客户/项目、客户名称|customer_id;展示字段customer_name|ID只存L-02.customer_id;名称通过关联展示,不在业务台账重复维护。| +|项目关联|P-01/P-02/P-04/P-06/P-09/S-04/D-15/D-01;W05/W24/SS03/11-A2|project_id、客户/项目、项目|project_id|项目对象只保留ID关联;项目名称由P-05展示。| +|内部料号|P-01/P-02/P-03/P-06/P-08/P-11/S-04/S-05/D-15/D-01/D-03/D-10;W05/W24/SS03/11-A2|pn、mpn、标准型号PN、Validation_PN、Original_PN、Candidate_PN|internal_pn;原物料用original_internal_pn,候选物料用candidate_internal_pn|全链路只认P-08.internal_pn;不得将mpn、pn、型号混作同一字段。| +|客户料号|P-01/P-08/D-01/D-10;W05/W24/11-A2|customer_mpn、客户料号|customer_pn|客户料号与internal_pn分开;正式映射落D-10料号映射子表。| +|品牌/原厂|P-08/P-10/S-04/S-05/SS03|brand、manufacturer_brand、原厂或品牌|brand_name;制造商另用manufacturer_name|品牌、制造商、供应商不是同一对象;S-04/S-05引用brand_name与supplier_id。| +|数量|P-01/S-04/D-15/D-01/D-03/W24/SS03/11-A2|数量、target_qty、purchase_trigger_qty、order_qty、shipped_qty、actual_qty|target_qty、trigger_qty、order_qty、actual_qty、shipped_qty、allocated_qty|按业务语义分开;数量阶梯统一用quantity_tier子表,不用文本拼接。| +|交期/时间窗|P-01/S-04/D-15/P-02/S-05/W24/SS03|target_delivery_date、requirement_date、quote_return_due_at、quote_due_at、lead_time/LT|target_delivery_date、requirement_date、quote_due_at、lead_time_days|目标日期、需求日期、报价截止时间、供货时长四类概念分开;原始说明写lead_time_note。| +|价格类型|P-01/P-02/P-03/S-04/S-05/W24/SS03|price_types_default、price_types_requested、price_type、报价类型|requested_price_types(需求层);price_type(价格明细层);quote_type(报价主记录层)|需求要什么价格、这一行是什么价格、这份报价属于什么业务类型分别建字段。| +|价格/币种|P-01/P-02/P-03/S-04/S-05/W24/SS03|报价价格/币种、unit_price、benchmark_unit_price、currency|金额字段按对象命名,币种统一currency_code;跨币种另记comparison_currency_code/conversion_rate|金额与币种成对出现;currency不再作为模糊字段名。| +|有效期|P-02/P-03/S-04/S-05/W24/SS03|生效期、失效期、报价有效期、valid_until、effective_from/to|valid_from、valid_to;报价主记录用quote_valid_until|Price List/采购基准的有效区间用valid_from/valid_to;报价对外有效期用quote_valid_until。| +|状态|P-01/P-02/P-03/P-06/P-11/S-04/S-05/D-01/D-03/D-10/D-11/W05/W24/SS03/11-A2/11-A3|状态机、状态、approval_status、publish_status、doc_set_complete|对象业务状态与record_status分离;各对象保留*_status命名|record_status只管记录生命周期;quote_status、approval_status等只管业务流程。| +|版本|P-01/P-02/P-03/P-06/P-08/P-10/P-11/D-10/S-04/W05/W24/SS03/11-A2|版本、需求版本、Release版本、报价版本、Datasheet版本、规则版本|对象字段用requirement_version/quote_version/release_version/document_version/mapping_version;通用用record_version;规则用rule_version|版本必须绑定对象和用途,不再使用裸字段version。| +|证据/附件/链接|P-01/P-02/P-06/P-10/P-11/S-04/D-01/D-11/W05/W24/SS03/11-A2/11-A3|evidence、证据引用、输出文件链接、handover_file、doc_list|evidence_ref;对象文件用*_ref;输出用output_ref;交接单用handover_file_ref|证据引用可多值且必须可定位;业务对象文件链接按对象命名。| +|供应商主体/联系人|S-03/S-04/S-05/SS03|supplier_id、供应商ID、sales_contact、联系人|supplier_id;sales_contact_ref;contact_channel|供应商主体与联系人分离;联系渠道记录在供应商主数据,实际发送渠道记录在S-04.send_channel。| +|询价批次/比价组|S-04/SS03|rfq_batch_id、comparison_group|rfq_batch_id;comparison_group_id|批次是询价过程容器;比价组是同条件可比集合,两者不可互代。| +|MOQ/SPQ|P-02/S-04/S-05/D-03/W24/SS03/11-A2|MOQ-SPQ、MOQ、SPQ|moq_qty、spq_qty|拆分为两个数值字段,适用对象前缀由台账区分。| +|交期LT|S-04/S-05/P-02/SS03/W24|LT、lead_time、交期|lead_time_days;lead_time_note|标准化时长与原始说明分开;统一日历口径由GOV定义。| +|替代关系与批准|P-01/P-02/P-06/P-08/P-11/W05/W24|替代等级、推荐、批准状态、验证结果、P-11-01/P-11-02|recommendation_level、analysis_status、customer_approval_status、validation_status|推荐、分析、验证、客户批准四个阶段分开,P-11作为统一关系主对象。| +|风险等级|P-01/P-02/P-06/P-11/S-05/D-18/W05/W24/SS03|风险等级、供应风险、问题与风险|risk_level(通用决策风险);supply_risk_level(供应能力风险);issue_risk(问题明细)|不同风险对象不共用一个自由文本字段。| +|交接/物流节点|D-01/D-03/D-10/D-11/11-A2/11-A3|milestones、ship_date、logi_pickup、track_note、交接|milestones;交接事实字段;logi_*节点结构;track_note|交接是法律/责任事实;物流节点是后续过程事实,统一回链同一order_id但不混成一个字段。| + +## 六、必须落地的结构化规则 + +|标准项|标准定义|示例| +|---|---|---| +|ID命名|业务对象主键使用<对象>_id;跨台账引用保留同名ID并采用Link。|request_id、supplier_id、order_id| +|料号命名|internal_pn表示内部标准料号;customer_pn表示客户料号;original_internal_pn/candidate_internal_pn只用于替代关系上下文。|P-08.internal_pn、D-10.customer_pn| +|金额与币种|所有金额字段必须与currency_code成对;跨币种比较在决策记录中另记comparison_currency_code与conversion_rate。|unit_price + currency_code| +|数量阶梯|数量阶梯使用结构化子表,至少包含min_qty、max_qty、price或discount;禁止把多个阶梯拼接为自由文本。|P-02 QuoteLine.quantity_tier| +|时间字段|发生时点用*_at(DateTime),业务日期用*_date(Date),持续时长用*_days(Number)。|quote_due_at、target_delivery_date、lead_time_days| +|版本字段|不使用裸version;必须指明对象:requirement_version、quote_version、release_version、document_version、record_version、rule_version。|P-02.quote_version| +|状态字段|record_status管记录生命周期;对象业务流程用*_status;派生可见性用*_flag或*_visible。|record_status、approval_status、track_visible| +|证据字段|evidence_ref表示证据集合;对象文件按对象命名为*_ref;证据必须支持URL/附件/定位信息。|quote_evidence_ref、handover_file_ref、citation_locator| +|节点结构|物流节点统一包含estimated_time、actual_time、source、collection_method、confirmation_status、anomaly_flag、evidence_ref。|D-01.logi_pickup| +|主记录与子表|主记录保存上下文、状态和汇总;逐PN/BOM位/阶梯/参数/单证的重复明细使用子表。|P-02 QuoteLine、P-11差异明细、D-11单证明细| + +## 七、实施优先级与待确认事项 + +**P0:**先落实P-01、P-02、P-11、S-04、D-01的主记录与子表结构,并建立统一ID、料号、版本、状态和证据字段。P-02如果不建立QuoteLine,PN×BOM位×数量阶梯无法稳定支撑报价和版本管理;P-11如果不建立差异明细,差异评估只能保留摘要,无法被AI复核或下游消费;S-04如果不拆RFQ/Quote/Decision三层,询价事实、供应商回复和定点结论会互相覆盖。 + +**P1:**落实条件必填与校验规则,包括代理证、涨价原因、跨币种换算、报价/基准有效期、风险转移时点、交接文件状态、物流节点异常及提醒次数。 + +**P1:**明确以下业务口径后固化到GOV规则:lead_time_days按自然日还是工作日;毛利计算公式和阈值;价格来源优先级;汇率来源与as_of;Incoterms及地点字段;客户批准与FAE分析结论的权限边界;D-10履约参数的客户级/物流公司级配置粒度。 + +**P2:**将S-05基准、D-03库存与D-16采购履约的数据同步关系做成系统接口;将D-01物流节点结构接入客户可查询视图,并将异常节点回链D-18或既有异常归集Activity。 + + +## 八、交付物说明 + +本Markdown用于评审和阅读;配套Excel提供可直接继续维护的“字段总表”“Activity覆盖矩阵”“重叠字段统一”“字段标准”四张工作表,并保留“说明”页作为使用口径。 + + +## 参考附件 + +[1]: /home/ubuntu/upload/SS01-02-03-04-台账体系全景-18Activity关联矩阵(2).html "SS01-02-03-04-台账体系全景:18 Activity关联矩阵" + +[2]: /home/ubuntu/upload/SS02_ACT-WORK-05_差异评估_E04-I01架构版_V1.2(1).html "SS02 ACT-WORK-05 差异评估" + +[3]: /home/ubuntu/upload/SS02_ACT-WORK-24_报价方案制定_E04-I01架构版_V1.2(1).html "SS02 ACT-WORK-24 报价方案制定" + +[4]: /home/ubuntu/upload/SS03询价ActivityDemo(1).html "SS03 询价 Activity Demo" + +[5]: /home/ubuntu/upload/20260826-11-A2架构版(E04-I01v1.2).html "SS04 ACT-11-A2 提货交接与风险转移" + +[6]: /home/ubuntu/upload/20260826-SS04-SOP-11活动描述样板(11-A3).html "SS04 11-A3 物流执行信息跟踪活动描述样板" diff --git a/docs/img/cy/cy-cy.png b/docs/img/cy/cy-cy.png new file mode 100644 index 0000000..cfed26f Binary files /dev/null and b/docs/img/cy/cy-cy.png differ diff --git a/docs/img/cy/cy-qx.png b/docs/img/cy/cy-qx.png new file mode 100644 index 0000000..923cf97 Binary files /dev/null and b/docs/img/cy/cy-qx.png differ diff --git a/docs/img/cy/cy-zz.png b/docs/img/cy/cy-zz.png new file mode 100644 index 0000000..29d65f5 Binary files /dev/null and b/docs/img/cy/cy-zz.png differ diff --git a/docs/superpowers/plans/2026-08-26-act-work-05-part-substitution-assessment.md b/docs/superpowers/plans/2026-08-26-act-work-05-part-substitution-assessment.md new file mode 100644 index 0000000..5bb0c2f --- /dev/null +++ b/docs/superpowers/plans/2026-08-26-act-work-05-part-substitution-assessment.md @@ -0,0 +1,3775 @@ +# ACT-WORK-05 差异评估(`part-substitution-assessment`)实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 SS02 的 ACT-WORK-05「差异评估」做成本仓第三个真活动包:台账建进库、活动装进系统、脚本 e2e 五案例全绿,再在浏览器里把全流程全分支走一遍,每一轮的结论与修改记进 `docs/ACT-WORK-05-端到端测试记录.md`。 + +**Architecture:** 照 E04 / I01 已验证的骨架 —— 清单(契约七件 + 台账声明 + 样例)、案例模型(CMMN 8 项 + 说明书 + 逐步剧本 + 5 段进度)、两枚纯工具(逐参数比对 / 推荐定级,规则同时以 DMN 表随包冻结)、五张 HTML 模版(两张数据工单件 + 执行单件 + 两张验收件)。主数据(P-01/P-08/P-10/P-11/P-06)进平台基线 `MASTER_OBJECTS` 由安装通道物化到 `ledger` schema,活动清单只追加自己要用的列与标记;活动自持单据归 `sub_eval.*`。数据工单 = 零输入执行工单 + 工作区可增删行的件(每张单只对一张表);验收 01 = 决策卡循环;验收 02 = 终局放行。 + +**Tech Stack:** TypeScript(活动包只 import `@sdk` / `@contracts` / `@sfeel`)、vitest、node-postgres(ledger 动态 DDL)、Hono API、React 工作台、tools/e2e-a2.mjs(服务账号 e2e)、claude-in-chrome(浏览器走查)。 + +**Spec:** `docs/SS02-ACT-WORK-05-html/ACT-WORK-05-活动流程与工单说明.md` · `ACT-WORK-05-一键启动入参说明.md` · `SS02-SS03-SS04_Demo台账字段细化与统一方案.md`(字段口径唯一权威)· 四份目标件 HTML · `ACT-WORK-05-全流程分支图.html`(18 条分支)。开发纪律:`docs/活动开发规范.md`、`docs/HTML报告生成规范.md`、`CLAUDE.md` 铁规矩。 + +## Global Constraints + +- 五道门禁以退出码验:`pnpm gates`(= gen:tiers → typecheck → lint → depcruise → api:check → test);不许以 echo 验。 +- §12.5 禁止清单:no `any` / `as unknown as` / `@ts-ignore`;domain 零 IO;不删测试;不动门禁配置。 +- 活动包**只准 import `@sdk` / `@contracts` / `@sfeel`**;产物文本黑名单(`process.env` / `fetch(` / `node:` / `eval(`);模版含 `_<值> / _distinct_<列> / tool_<名>_calls`);模型自写的旗只能占谓词一半。 +- 触达世界的项(交人核对 / 拍板 / 放行)标 `manualActivationRule` 且**不给 doneWhen**;`issueAcceptance` 一任务只能发一次且即终局 → 非终局审核用 `decide` 循环。 +- 执行卡**零输入**:不摆 `fields` / `blocks`,填报全在工作区的件上;一单同一张卡只发一次。 +- 报告模版**不算不判**:`data-slot` / `data-rows="mine:域.对象"` / `data-col` / `data-edit` / `data-class-of` / `data-blank`;主键列必挂 `data-col`;枚举列存 ASCII、人读名由样式表出。 +- 样例:第一条满载;样例名 ≤14 字。 +- 试跑一律用服务账号 `bpos-e2e`(`KEYCLOAK_E2E_CLIENT_ID/SECRET`),测完 `pnpm --filter @app/api reset-runs`;浏览器走查用当前 Chrome 已登录会话。 +- 提交只 `git add` 本任务点名的文件(工作树里有一批未提交的鉴权改动,别顺手带走);提交信息末尾带: + `Co-Authored-By: Claude Fable 5 ` 与 `Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh`。 +- 数据工单口径(用户 2026-08-26 定):**读数点概念作废,一张表一张单**;P-08 是一张表(参数集是它的一个字段);本活动最多 2 张数据工单(P-08 / P-10);S-03 / L-02(启动前)/ P-06(事实类)/ P-09 不派。 +- 本活动只答技术可替代性:不做价格 / 交期 / 数量判断;`customer_approval_status` 只读边界,活动不声明其写权。 + +--- + +## 0. 已锁定的设计决定(执行者不必再问;要改先改这里) + +| # | 决定 | 依据 | +|---|---|---| +| D1 | **守铁律②**:数据工单确认落**本单草稿层**,放行时并入正式表(试跑单 `landMaster` 默认开,放行后主数据真落库);本单后续步骤读得到本单草稿,所以「建了就能拿来评」不受影响。文档里「确认即正式入库、无草稿态」落成「无审核环节、放行并入」。 | `docs/活动开发规范.md:45` 铁律②;E04 录入卡同款 | +| D2 | **数据工单 = 零输入执行工单 + 件**(`data-edit` 表绑 `mine:parts.profile` / `mine:parts.document`,可加行,提交闸 = 声明的必填);卡标题以「数据工单 ·」开头;一张表一张单(P-08 一张、P-10 一张);对话区叫「数据卡」的专属卡皮记 debt。 | 现成机制零基座改动 | +| D3 | **P-08 一张表**:`normalized_parameter_set` 是 `parts.profile` 的 **text 列**,行语法 `参数名 \| 值 \| 单位 \| hard/soft \| 说明`(一行一参数),件上用 textarea 编辑;不建 `parts.parameter` 子表,不用 json(json 列对人填的字符串不归型,textarea 存进去是 JSON 字符串,读写两侧形状不一)。 | 用户 2026-08-26「都是一张表」;`engines.ts coerceToColumn` json 不归型 | +| D4 | datasheet 作**起单选填文件入参** `datasheets`(`format:'file'`,与 A1 `attachments` 同形);传了 AI 在数据工单件上预填,没传人全填(文档 0c:同为合法路径);「件上上传」记 debt。 | 平台文件入口只有起单 `format:'file'` | +| D5 | **一人兼 PW/FAE**:所有卡发给任务负责人;`roles: ['FAE/RI']`;派给第二个人记 debt。 | 平台一任务恰一名负责人 | +| D6 | 存在性检查 = S0 读 `parts.profile` 整表(剧本 `reads`)逐个候选核对 + **`refersTo` 墙**(`substitution.relation.original/candidate_internal_pn → parts.profile.internal_pn`;查无当场拒并指路);不另做基座读数。 | E04 contact_code 同款 | +| D7 | 主数据物化走**平台基线**(`seed-mvp-data.ts` 新增 4 域 6 表)+ 清单**追加声明**(必填 / tally / refersTo / requiredWhen 是活动的授权面);不写 SQL 迁移(ledger 表由声明动态物化)。 | `seed-activities.ts` 主数据先于活动物化 | +| D8 | 枚举列一律 ASCII tally,人读名由样式表出(推荐等级 `recommended/conditional/alternate/not_recommended`;差异状态 `match/mismatch/unknown/conditional`;约束 `hard/soft`;生命周期 `active/nrnd/eol/discontinued/risk`)。 | `data-class-of` 只认 ASCII | +| D9 | 验收 01 非终局 → `decide` 循环(E04 S4 同款);驳回意见可写 P-06「验证要求」行;验收 02 = `submit` 终局,件上摆落库预览(`touched:` 数据源)。 | `issueAcceptance` 只能一次 | +| D10 | GOV 规则 = 包内两张 DMN(文档)+ 两枚纯工具(求值),`rule_version = 'R-2026.07'` 随包冻结。 | quote-intake P-03 同款 | + +## 1. 现有数据库架构与「把台账建进库」的含义 + +**一库四 schema**(`db/init/001-schemas.sql`):`sys`(系统自持,Drizzle 迁移 0001–0033)· `ledger`(台账自生长,**唯一写者** `apps/api/src/modules/ledger/internal/adapter/ledger-store.ts`,动态 DDL)· `mastra` · `queue`。连接 `postgres://jcai:jcai@localhost:54329/jcai_wos`(容器 `jcai-postgres`,已在跑;本机没有 psql,一律 `docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc ""`)。 + +**台账怎么物化**(不写迁移,声明即建表): + +| 声明 | 物理落点 | +|---|---| +| `{domain, object}` | 表 `ledger.<域>__<对象>`(`tableNameOf`),表注释 = `label` | +| `fields[].type` text / number / integer / boolean / date / timestamp / json | text / double precision / bigint / boolean / date / timestamptz / jsonb;列注释 = `label` | +| `primaryKey: true` 各列 | `primary key (...)`;**主键一经建立不得由声明变更** | +| `kind` / `caseKey` | `ledger.__object_meta(domain, object, kind, case_key)`;`reset-runs` 只清 `kind <> 'master'` 的表 | +| 同名对象多份声明 | `schema-merge.ts` 最大化合并、只增不破坏(add-column / 放宽类型);`json` 与标量互斥即拦 | +| 任务草稿 | `ledger.__draft(task_id, domain, object, key_hash, key, patch, baseline, tombstone, updated_at)`;`taskView` = 正式版 + 本单草稿;放行 `releaseWrite` 合并进正式表(试跑单 `land_master=false` 时 master 行只留缓存) | + +**谁在什么时候调 `applyDeclarations`**:`installRealActivities()`(`apps/api/src/app/seed-activities.ts`,`pnpm activities:install` 的真身)先物化 `MASTER_OBJECTS`(平台基线,不挂活动),再逐包 `registry.install(manifest)`(活动声明合并进同名表 + 建自持表 + 写 `__object_meta`),最后 `systemWrite` 播 `MASTER_SAMPLE_ROWS`(按主键幂等)。 + +**现状(2026-08-26 实查)**:`ledger` 里 33 个对象;主数据四域 customer / project / supplier / market(`market.part` 是空占位的老料号表,主键 `pn`,与 P-08 的 `internal_pn` 口径不同,**不复用**,记 debt 待并);E04/I01 各自的 record 表 caseKey=`round_ref`;A1–A7 的表还在但已卸载。 + +**本活动要建进库的**(全部经上面这条路,零 SQL): + +| 全景 | 物理表 | kind | 主键 | 由谁物化 | +|---|---|---|---|---| +| P-08 | `ledger.parts__profile` | master | `internal_pn` | 基线 + 清单追加 | +| P-10 | `ledger.parts__document` | master | `internal_pn, doc_seq` | 基线 + 清单追加 | +| P-01 | `ledger.demand__request` | master | `request_id` | 基线 + 清单追加 | +| P-11 主 | `ledger.substitution__relation` | master | `substitution_relation_id` | 基线 + 清单追加 | +| P-11 子 | `ledger.substitution__difference_item` | master | `substitution_relation_id, difference_item_id` | 基线 + 清单追加(`compare_result` / `unknown_disposition` 两列只在清单) | +| P-06 | `ledger.validation__record` | master | `validation_id` | 基线 + 清单追加 | +| 自持 | `ledger.sub_eval__pn_check` / `__input_gap` / `__verdict` | record(caseKey `eval_ref`) | 见 Task 3 | 清单 | + +验收 SQL(Task 6 用): +```sql +select domain||'.'||object||' '||kind||' '||coalesce(case_key,'') from ledger.__object_meta + where domain in ('parts','demand','substitution','validation','sub_eval') order by 1; +select string_agg(column_name||':'||data_type, ', ' order by ordinal_position) + from information_schema.columns where table_schema='ledger' and table_name='parts__profile'; +select conname||' '||pg_get_constraintdef(oid) from pg_constraint where conrelid='ledger.substitution__difference_item'::regclass; +select internal_pn, brand_name, lifecycle_status from ledger.parts__profile order by 1; +``` + +## 2. 文件结构 + +**新建** +- `activities/part-substitution-assessment/package.json` · `src/index.ts` · `src/manifest.ts`(清单 + DMN)· `src/case-model.ts`(CMMN / NARRATIVE / STEPS / PHASES)· `src/skills.ts` · `src/views/prd-style.ts`(逐字节抄 I01)· `src/views/report.ts`(五张模版)· `__tests__/activity.test.ts` · `__tests__/steps.test.ts` +- `apps/api/src/app/__tests__/first-party-substitution.test.ts`(两枚纯工具) +- `docs/ACT-WORK-05-端到端测试记录.md`(Task 8 逐轮追加)· `docs/数据域字段说明-W05.md` + +**修改** +- `apps/api/src/app/seed-mvp-data.ts`(+4 域 6 表 + 样例料号)· `apps/api/src/app/__tests__/seed-mvp-data.test.ts`(四域 → 八域) +- `apps/api/src/app/tools/first-party.ts`(+2 handler)· `apps/api/src/app/seed-mvp.ts`(+2 工具契约) +- `tools/build-activities.ts`(PACKAGES)· `apps/api/src/app/seed-activities.ts`(REAL_PACKAGES) +- `tools/e2e-a2.mjs`(+`w05` 套件、`stageEdits`、`decide` 值) +- `CLAUDE.md`(现状快照 + 文档地图)· `docs/debt.md` + +--- + +### Task 0: 开分支、确认环境 + +**Files:** 无(只动 git 与环境) + +- [ ] **Step 1: 从 mvp-foundation 开功能分支**(工作树里已有的未提交改动跟着走,不碰它们) + +```bash +cd /Users/xcs/project2/tmp/BPOS-MVP +git checkout -b feat/act-work-05 +git status --short | wc -l # 记下这个数:本计划的提交只 add 自己点名的文件 +``` + +- [ ] **Step 2: 确认库与两套服务在跑** + +```bash +docker ps --format '{{.Names}} {{.Status}}' | grep jcai-postgres +lsof -nP -iTCP:8787 -iTCP:5173 -iTCP:8790 -iTCP:5180 -sTCP:LISTEN | awk '{print $1,$9}' | sort -u +``` +Expected: `jcai-postgres Up`;8787(dev api)与 5173(dev web)在;8790/5180 是 `./stable.sh` 起的稳定版(与 dev 连同一个库,推进器靠跨进程租约互斥 —— 跑 e2e / 浏览器走查时**先停它**,避免两台交替推同一单干扰观察:`./stable.sh stop`;走查完 `./stable.sh start`)。 + +- [ ] **Step 3: 确认 e2e 凭证在环境里**(值不打印) + +```bash +for k in KEYCLOAK_URL KEYCLOAK_REALM KEYCLOAK_E2E_CLIENT_ID KEYCLOAK_E2E_CLIENT_SECRET; do printf '%s=%s\n' "$k" "${(P)k:+set}"; done +``` +Expected: 四个都 `set`。缺了就从开发会话 / `.stable/env` 抄进当前 shell(`.stable/env` 只有 `KEYCLOAK_URL/REALM/CLIENT_ID`,E2E 那两个要问用户要;拿不到就停在这一步,e2e 跑不了)。 + +--- + +### Task 1: 主数据基线 —— P-08 / P-10 / P-01 / P-11 / P-06 进平台声明,加样例料号 + +**Files:** +- Modify: `apps/api/src/app/seed-mvp-data.ts`(`MASTER_OBJECTS` 末尾追加 6 表;`MASTER_DOMAINS` 8 域;`MASTER_SAMPLE_ROWS` 末尾追加 4 行) +- Modify: `apps/api/src/app/__tests__/seed-mvp-data.test.ts:18-19`(四域 → 八域) +- Test: `apps/api/src/app/__tests__/seed-mvp-data.test.ts` + +**Interfaces:** +- Produces: 物理表 `ledger.parts__profile` / `parts__document` / `demand__request` / `substitution__relation` / `substitution__difference_item` / `validation__record`(列名与类型见下);样例行 `parts.profile` × 2(`LM2596S-5.0`, `LM2596S-ADJ`)、`parts.document` × 2。Task 3 的清单只**追加**列,列名与类型必须与这里一字不差(同名异型会被 schema-merge 放宽或拦下)。 + +- [ ] **Step 1: 改测试 —— 八域** + +在 `apps/api/src/app/__tests__/seed-mvp-data.test.ts` 把 `'只有客户 / 项目 / 供应商 / 市场四个域'` 那条改成: + +```ts + it('八个域:客户 / 项目 / 供应商 / 市场 + 料件 / 需求 / 替代 / 验证(SS02 差异评估 2026-08-26 加后四域)', () => { + expect([...MASTER_DOMAINS]).toEqual(['customer', 'project', 'supplier', 'market', 'parts', 'demand', 'substitution', 'validation']); + expect([...new Set(MASTER_OBJECTS.map((o) => o.domain))].sort()).toEqual([...MASTER_DOMAINS].sort()); + }); + + it('SS02 差异评估要读写的六张主数据表都在基线里,主键照《统一方案》', () => { + const pk = (domain: string, object: string) => + MASTER_OBJECTS.find((o) => o.domain === domain && o.object === object)?.fields.filter((f) => f.primaryKey).map((f) => f.name); + expect(pk('parts', 'profile')).toEqual(['internal_pn']); + expect(pk('parts', 'document')).toEqual(['internal_pn', 'doc_seq']); + expect(pk('demand', 'request')).toEqual(['request_id']); + expect(pk('substitution', 'relation')).toEqual(['substitution_relation_id']); + expect(pk('substitution', 'difference_item')).toEqual(['substitution_relation_id', 'difference_item_id']); + expect(pk('validation', 'record')).toEqual(['validation_id']); + }); + + it('样例料号:原 PN 与同族可调版在库,候选 XL2596S-5.0 刻意不播(数据工单那条路要试得出来)', () => { + const parts = MASTER_SAMPLE_ROWS.filter((r) => r.address.domain === 'parts' && r.address.object === 'profile').map((r) => r.key['internal_pn']); + expect(parts).toEqual(['LM2596S-5.0', 'LM2596S-ADJ']); + expect(parts).not.toContain('XL2596S-5.0'); + }); +``` +文件头 `import` 改成 `import { MASTER_DOMAINS, MASTER_OBJECTS, MASTER_SAMPLE_ROWS } from '../seed-mvp-data.ts';`,并把文件头注释里「只有这四个域,不多不少」改为「八个域,多一个就说明有人把单据塞进主数据了」。 + +- [ ] **Step 2: 跑测试确认它红** + +Run: `pnpm vitest run apps/api/src/app/__tests__/seed-mvp-data.test.ts` +Expected: FAIL(`MASTER_DOMAINS` 仍是四域;`pk('parts','profile')` 为 undefined)。 + +- [ ] **Step 3: 在 `seed-mvp-data.ts` 的 `MASTER_OBJECTS` 数组末尾(`market.quote_index` 之后、`];` 之前)追加六张表** + +```ts + /* ── 料件:P-08 芯片主数据 / P-10 原厂技术资料库(SS02 差异评估 2026-08-26 加)── + 字段名与《SS02-SS03-SS04_Demo台账字段细化与统一方案》§4.2 一字不差;全链路只认 internal_pn。 + 参数集是 P-08 的一个字段(用户定:一张表,不建子表),一行一参数: + `参数名 | 值 | 单位 | hard/soft | 说明`。老的 market.part(主键 pn)不复用,记 debt 待并。 */ + master('parts', 'profile', '芯片主数据(P-08)', [ + f('internal_pn', 'text', '内部料号', true), + f('customer_pn', 'text', '客户料号'), + f('brand_name', 'text', '品牌名称'), + f('manufacturer_name', 'text', '制造商名称'), + f('specification_description', 'text', '规格描述'), + f('normalized_parameter_set', 'text', '标准化参数集(一行一参数:参数名 | 值 | 单位 | hard/soft | 说明)'), + f('package', 'text', '封装'), + f('process_technology', 'text', '工艺'), + f('lifecycle_status', 'text', '生命周期状态'), + f('eol_pcn_status', 'text', 'EOL/PCN 状态'), + f('datasheet_ref', 'text', 'Datasheet 引用'), + f('datasheet_version', 'text', 'Datasheet 版本'), + f('application_scenarios', 'text', '适配应用场景'), + f('pin_to_pin_relation_ref', 'text', 'Pin-to-Pin 关系引用'), + ]), + master('parts', 'document', '原厂技术资料库(P-10)', [ + f('internal_pn', 'text', '关联内部料号', true), + f('doc_seq', 'text', '资料序号(同一料号下 01/02…)', true), + f('document_type', 'text', '资料类型'), + f('brand_name', 'text', '品牌名称'), + f('document_title', 'text', '资料标题'), + f('document_version', 'text', '资料版本'), + f('document_date', 'date', '资料发布日期'), + f('document_ref', 'text', '资料文件链接'), + f('citation_locator', 'text', '证据定位(页码/章节/表格)'), + f('source_contact_ref', 'text', '原厂支持联系人'), + f('document_status', 'text', '资料状态'), + ]), + + /* ── 需求:P-01 客户具体需求与项目任务主台账 ── */ + master('demand', 'request', '客户需求主记录(P-01)', [ + f('request_id', 'text', '需求编号', true), + f('customer_code', 'text', '客户编码'), + f('project_code', 'text', '项目编码'), + f('requirement_version', 'text', '需求版本'), + f('request_type', 'text', '需求类型'), + f('request_description', 'text', '需求描述'), + f('input_snapshot_ref', 'text', '输入快照'), + f('bom_id', 'text', 'BOM ID'), + f('bom_position', 'text', 'BOM 位'), + f('original_internal_pn', 'text', '原内部料号'), + f('customer_pn', 'text', '客户料号'), + f('application_scenario', 'text', '应用场景'), + f('technical_constraints', 'text', '技术约束'), + f('risk_level', 'text', '风险等级'), + f('verification_requirement', 'text', '验证/复测/确认要求'), + f('request_status', 'text', '需求状态'), + f('current_activity', 'text', '当前 Activity'), + f('current_stage', 'text', '当前阶段'), + f('next_action', 'text', '下一步动作'), + f('output_ref', 'text', '标准输出链接'), + ]), + + /* ── 替代:P-11 替代选型与批准关系库 —— 主记录 + 差异明细子表(《统一方案》§六:重复明细用子表)── */ + master('substitution', 'relation', '替代关系主记录(P-11)', [ + f('substitution_relation_id', 'text', '替代关系ID', true), + f('request_id', 'text', '关联需求编号'), + f('project_code', 'text', '关联项目编码'), + f('bom_position', 'text', '关联 BOM 位'), + f('original_internal_pn', 'text', '原内部料号'), + f('candidate_internal_pn', 'text', '候选内部料号'), + f('substitution_type', 'text', '替代类型'), + f('recommendation_level', 'text', '推荐等级'), + f('recommendation_reason', 'text', '推荐理由'), + f('difference_summary', 'text', '差异摘要'), + f('risk_level', 'text', '替代风险等级'), + f('verification_requirement', 'text', '验证要求'), + f('validation_id', 'text', '关联验证ID'), + f('analysis_status', 'text', '分析状态'), + /* 客户批准三列由独立审批流维护 —— 基线建列,差异评估活动**不声明写权** */ + f('customer_approval_status', 'text', '客户批准状态'), + f('customer_approval_at', 'timestamp', '客户批准时间'), + f('customer_approval_ref', 'text', '客户批准凭证'), + f('customer_feedback', 'text', '客户反馈'), + f('analysis_report_ref', 'text', '替代分析报告'), + f('confidence_score', 'number', '证据可信度'), + f('rule_version', 'text', '差异规则版本'), + ]), + master('substitution', 'difference_item', '差异明细(P-11 子表)', [ + f('substitution_relation_id', 'text', '替代关系ID', true), + f('difference_item_id', 'text', '差异明细ID', true), + f('parameter_name', 'text', '标准参数名'), + f('original_value', 'text', '原物料参数值'), + f('candidate_value', 'text', '候选物料参数值'), + f('parameter_unit', 'text', '参数单位'), + f('constraint_type', 'text', '约束类型'), + f('difference_status', 'text', '差异状态'), + f('impact_statement', 'text', '影响说明'), + f('risk_level', 'text', '风险等级'), + f('evidence_ref', 'text', '差异证据引用'), + f('confidence', 'number', 'AI 置信度'), + f('rule_version', 'text', '差异规则版本'), + ]), + + /* ── 验证:P-06 样品交付与芯片验证记录(事实类:只能实测产生,数据工单不许建)── */ + master('validation', 'record', '芯片验证记录(P-06)', [ + f('validation_id', 'text', '验证ID', true), + f('request_id', 'text', '关联需求编号'), + f('customer_code', 'text', '客户编码'), + f('validation_internal_pn', 'text', '验证内部料号'), + f('validation_type', 'text', '验证类型'), + f('test_condition', 'text', '测试条件'), + f('test_parameters', 'text', '测试参数'), + f('test_result', 'text', '测试结果'), + f('validation_status', 'text', '验证状态'), + f('issue_risk', 'text', '问题与风险'), + f('sample_version', 'text', '样品版本'), + f('sample_lot', 'text', '样品批次'), + f('evidence_ref', 'text', '验证证据引用'), + f('next_validation_action', 'text', '后续验证动作'), + ]), +``` + +并把 `MASTER_DOMAINS` 改为: + +```ts +/** 八域的呈现次序 —— 谁在买 → 为什么买 → 从哪买 → 买什么 → 料件 → 需求 → 替代 → 验证 */ +export const MASTER_DOMAINS = ['customer', 'project', 'supplier', 'market', 'parts', 'demand', 'substitution', 'validation'] as const; +``` + +- [ ] **Step 4: 在 `MASTER_SAMPLE_ROWS` 数组末尾追加样例料号**(现有最后一个元素之后、`];` 之前) + +```ts + /* + * ── SS02 差异评估的样例料号(2026-08-26)── + * 只播**原 PN**与**同族可调版**两颗:「都在库」那条路靠它们;候选 XL2596S-5.0 与国产 DCDC + * **刻意不播** —— 「候选缺库 → 派数据工单」「硬约束不推荐」两条路要试得出来,库里得真没有。 + * 参数集一行一参数(参数名 | 值 | 单位 | hard/soft | 说明),与 P-08 声明的行语法同一份。 + */ + { + address: { domain: 'parts', object: 'profile' }, + key: { internal_pn: 'LM2596S-5.0' }, + values: { + internal_pn: 'LM2596S-5.0', + brand_name: 'TI', + manufacturer_name: 'Texas Instruments', + specification_description: '降压型 DCDC 稳压器,3A,固定 5.0V,150kHz', + normalized_parameter_set: [ + '输入电压范围 | 4.5~40 | V | hard | 客户输入上限 40V', + '输出电压 | 5.0 | V | hard | 固定版', + '输出电流 | 3 | A | hard | 满载能力', + '开关频率 | 150 | kHz | hard | 影响外围电感/电容', + '静态电流 | 5 | mA | soft | 待机功耗', + '工作温度 | -40~+85 | ℃ | hard | 工业温域', + '使能阈值 | 1.4 | V | hard | ON/OFF 引脚阈值', + ].join('\n'), + package: 'TO-263-5', + process_technology: 'BCD', + lifecycle_status: 'nrnd', + eol_pcn_status: 'PCN-2026-07:转 NRND,2027-01 起不接新设计', + datasheet_ref: 'https://www.ti.com/lit/ds/symlink/lm2596.pdf', + datasheet_version: 'Rev H (2023-09)', + application_scenarios: '工业电源;消费电源', + }, + }, + { + address: { domain: 'parts', object: 'profile' }, + key: { internal_pn: 'LM2596S-ADJ' }, + values: { + internal_pn: 'LM2596S-ADJ', + brand_name: 'TI', + manufacturer_name: 'Texas Instruments', + specification_description: '降压型 DCDC 稳压器,3A,可调输出 1.23~37V,150kHz(LM2596 同族)', + normalized_parameter_set: [ + '输入电压范围 | 4.5~40 | V | hard | 与固定版同', + '输出电压 | 1.23~37 | V | hard | 可调版,靠外围反馈网络定', + '输出电流 | 3 | A | hard | 满载能力', + '开关频率 | 150 | kHz | hard | 影响外围电感/电容', + '静态电流 | 5 | mA | soft | 待机功耗', + '工作温度 | -40~+85 | ℃ | hard | 工业温域', + '使能阈值 | 1.4 | V | hard | ON/OFF 引脚阈值', + ].join('\n'), + package: 'TO-263-5', + process_technology: 'BCD', + lifecycle_status: 'active', + datasheet_ref: 'https://www.ti.com/lit/ds/symlink/lm2596.pdf', + datasheet_version: 'Rev H (2023-09)', + application_scenarios: '工业电源;消费电源', + }, + }, + { + address: { domain: 'parts', object: 'document' }, + key: { internal_pn: 'LM2596S-5.0', doc_seq: '01' }, + values: { + internal_pn: 'LM2596S-5.0', + doc_seq: '01', + document_type: 'datasheet', + brand_name: 'TI', + document_title: 'LM2596 SIMPLE SWITCHER Power Converter 150-kHz 3-A Step-Down Voltage Regulator', + document_version: 'Rev H (2023-09)', + document_ref: 'https://www.ti.com/lit/ds/symlink/lm2596.pdf', + citation_locator: '§6.5 Electrical Characteristics 表 / §6.3 Recommended Operating Conditions', + document_status: 'valid', + }, + }, + { + address: { domain: 'parts', object: 'document' }, + key: { internal_pn: 'LM2596S-ADJ', doc_seq: '01' }, + values: { + internal_pn: 'LM2596S-ADJ', + doc_seq: '01', + document_type: 'datasheet', + brand_name: 'TI', + document_title: 'LM2596 SIMPLE SWITCHER Power Converter 150-kHz 3-A Step-Down Voltage Regulator', + document_version: 'Rev H (2023-09)', + document_ref: 'https://www.ti.com/lit/ds/symlink/lm2596.pdf', + citation_locator: '§6.5 Electrical Characteristics 表(ADJ 列)', + document_status: 'valid', + }, + }, +``` + +同时把 `seed-activities.ts:143` 那行日志改成不依赖下标(新行插在数组末尾,`[1]` 仍是样例客户,不改也不炸;但下一个人再往前插就错)——改为: +```ts + log(` 样例主数据已就位:${MASTER_SAMPLE_ROWS.length} 行(客户 / 项目 / 联系人 / 料号)`); +``` + +- [ ] **Step 5: 跑测试确认绿** + +Run: `pnpm vitest run apps/api/src/app/__tests__/seed-mvp-data.test.ts` +Expected: PASS(含新加 3 条)。 + +- [ ] **Step 6: 类型检查 + 提交** + +```bash +pnpm -s typecheck +git add apps/api/src/app/seed-mvp-data.ts apps/api/src/app/__tests__/seed-mvp-data.test.ts apps/api/src/app/seed-activities.ts +git commit -m "feat(w05): 主数据基线加料件/需求/替代/验证四域六表(P-08/P-10/P-01/P-11/P-06)与样例料号 + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` + +--- + +### Task 2: 两枚纯工具 —— 逐参数比对(compare-parameters)与推荐定级(recommend) + +**Files:** +- Modify: `apps/api/src/app/tools/first-party.ts`(在 `FIRST_PARTY_TOOLS` 之前加两段实现;数组末尾登记两条) +- Create: `apps/api/src/app/tools-w05.ts`(两条工具契约 + 试运行样例) +- Modify: `apps/api/src/app/seed-mvp.ts`(注册循环的数组加进 `W05_TOOLS`) +- Test: `apps/api/src/app/__tests__/first-party-substitution.test.ts`(新建) + +**Interfaces:** +- Produces 工具 `part-substitution-assessment.compare-parameters`: + 入参 `{ candidates: Array<{ candidateInternalPn: string; pairs: Array<{ parameterName: string; originalValue: string; candidateValue: string; unit?: string; constraintType: 'hard'|'soft'; requirement?: string; evidenceRef?: string }> }> }` + 出参 `{ ruleVersion: 'R-2026.07'; results: Array<{ candidateInternalPn; items: Array<{ parameterName; compareResult; differenceStatus: 'match'|'mismatch'|'unknown'|'conditional'; riskLevel: 'low'|'medium'|'high'; note }>; counts: { match; mismatch; unknown; conditional; hardMismatch; softMismatch; configurable } }> }` +- Produces 工具 `part-substitution-assessment.recommend`: + 入参 `{ candidates: Array<{ candidateInternalPn: string; counts: { match; mismatch; unknown; conditional; hardMismatch; softMismatch; configurable } }> }` + 出参 `{ ruleVersion; results: Array<{ candidateInternalPn; recommendationLevel: 'recommended'|'conditional'|'alternate'|'not_recommended'; riskLevel; reason }> }` +- 出参键与台账列同名之处:`differenceStatus → difference_status`、`riskLevel → risk_level`、`recommendationLevel → recommendation_level`(模型照抄,不必改名)。 + +- [ ] **Step 1: 写测试** + +`apps/api/src/app/__tests__/first-party-substitution.test.ts`: + +```ts +/** + * ACT-WORK-05 差异评估的两枚纯工具 —— 「能算的不问 LLM」:同输入必同结论。 + * 用例逐条对应《ACT-WORK-05-活动流程与工单说明》§八的示例矩阵: + * A XL2596S-5.0 条件推荐(使能阈值未披露 → UNKNOWN)/ B LM2596S-ADJ 备选(可调版靠外围配成 5V)/ + * C 国产 DCDC 不推荐(输入上限 32V < 客户 40V,硬约束 MISMATCH)。 + */ +import { describe, expect, it } from 'vitest'; +import { FIRST_PARTY_TOOLS } from '../tools/first-party.ts'; + +const handler = (name: string) => { + const found = FIRST_PARTY_TOOLS.find(([n]) => n === name); + if (found === undefined) throw new Error(`没有工具 ${name}`); + return found[1]; +}; +/** ToolHandler 的入参形状见 `apps/api/src/modules/toolbox/internal/adapter/toolbox-store.ts`:纯工具不用连接目标 */ +const call = async (name: string, input: Record) => { + const out = await handler(name)({ input, connection: {}, operation: undefined, idempotencyKey: undefined }); + if (!out.ok) throw new Error(`工具失败:${JSON.stringify(out)}`); + return out.output as Record; +}; + +type Item = { parameterName: string; compareResult: string; differenceStatus: string; riskLevel: string }; +type Result = { candidateInternalPn: string; items: Item[]; counts: Record }; + +const pair = (parameterName: string, originalValue: string, candidateValue: string, constraintType: 'hard' | 'soft', extra: Record = {}) => ({ + parameterName, + originalValue, + candidateValue, + constraintType, + evidenceRef: 'P-08 · P-10 Rev H', + ...extra, +}); + +describe('compare-parameters:逐参数四态', () => { + it('候选温域更宽 → covers → MATCH 低风险;固定 vs 可调 → configurable → CONDITIONAL;未披露 → UNKNOWN', async () => { + const out = await call('part-substitution-assessment.compare-parameters', { + candidates: [ + { + candidateInternalPn: 'XL2596S-5.0', + pairs: [ + pair('工作温度', '-40~+85', '-40~+125', 'hard', { unit: '℃' }), + pair('输出电压', '5.0', '5.0', 'hard', { unit: 'V' }), + pair('使能阈值', '1.4', '未披露', 'hard', { unit: 'V' }), + pair('静态电流', '5', '4.2', 'soft', { unit: 'mA' }), + ], + }, + { + candidateInternalPn: 'LM2596S-ADJ', + pairs: [pair('输出电压', '5.0', '1.23~37', 'hard', { unit: 'V' })], + }, + ], + }); + const [a, b] = out['results'] as Result[]; + expect(out['ruleVersion']).toBe('R-2026.07'); + const by = (r: Result, name: string) => r.items.find((i) => i.parameterName === name); + expect(by(a, '工作温度')).toMatchObject({ compareResult: 'covers', differenceStatus: 'match', riskLevel: 'low' }); + expect(by(a, '输出电压')).toMatchObject({ compareResult: 'equal', differenceStatus: 'match' }); + expect(by(a, '使能阈值')).toMatchObject({ compareResult: 'unknown', differenceStatus: 'unknown', riskLevel: 'medium' }); + expect(by(a, '静态电流')).toMatchObject({ differenceStatus: 'conditional' }); // 软约束数值不同 → 条件 + expect(a.counts).toMatchObject({ match: 2, unknown: 1, conditional: 1, mismatch: 0, hardMismatch: 0 }); + expect(by(b, '输出电压')).toMatchObject({ compareResult: 'configurable', differenceStatus: 'conditional' }); + expect(b.counts['configurable']).toBe(1); + }); + + it('硬约束范围变窄 → MISMATCH 高风险;客户约束(requirement)不满足也是 MISMATCH', async () => { + const out = await call('part-substitution-assessment.compare-parameters', { + candidates: [ + { + candidateInternalPn: 'CN-DCDC-32V', + pairs: [ + pair('输入电压范围', '4.5~40', '4.5~32', 'hard', { unit: 'V', requirement: '>= 40' }), + pair('封装', 'TO-263-5', 'TO-263-5', 'hard'), + ], + }, + ], + }); + const [c] = out['results'] as Result[]; + expect(c.items[0]).toMatchObject({ compareResult: 'narrower', differenceStatus: 'mismatch', riskLevel: 'high' }); + expect(c.items[1]).toMatchObject({ compareResult: 'equal', differenceStatus: 'match' }); + expect(c.counts).toMatchObject({ mismatch: 1, hardMismatch: 1, match: 1 }); + }); + + it('硬约束相等但没有证据引用 → 不判 MATCH,降为 CONDITIONAL(硬约束项未经证据支持不得判 MATCH)', async () => { + const out = await call('part-substitution-assessment.compare-parameters', { + candidates: [{ candidateInternalPn: 'X', pairs: [{ parameterName: '封装', originalValue: 'TO-263-5', candidateValue: 'TO-263-5', constraintType: 'hard' }] }], + }); + const [x] = out['results'] as Result[]; + expect(x.items[0]).toMatchObject({ compareResult: 'equal', differenceStatus: 'conditional' }); + }); + + it('数值带单位/括号注记也认得:「5.0(固定)」「3A」「150 kHz」;生命周期按活跃度比', async () => { + const out = await call('part-substitution-assessment.compare-parameters', { + candidates: [{ candidateInternalPn: 'X', pairs: [pair('输出电压', '5.0(固定)', '5.0 V', 'hard'), pair('输出电流', '3A', '3', 'hard'), pair('开关频率', '150 kHz', '150', 'hard'), pair('生命周期', 'nrnd', 'active', 'soft')] }], + }); + const [x] = out['results'] as Result[]; + // 生命周期不是数值:候选比原料更活(nrnd → active)算 covers → MATCH,替 NRND 料本就是为了这个 + expect(x.items.map((i) => i.differenceStatus)).toEqual(['match', 'match', 'match', 'match']); + expect(x.items[3]?.compareResult).toBe('covers'); + }); +}); + +describe('recommend:推荐等级四档', () => { + it('硬约束 MISMATCH → 不推荐;有 UNKNOWN → 条件推荐;只有 configurable → 备选;全 MATCH → 推荐', async () => { + const out = await call('part-substitution-assessment.recommend', { + candidates: [ + { candidateInternalPn: 'C', counts: { match: 5, mismatch: 1, unknown: 0, conditional: 0, hardMismatch: 1, softMismatch: 0, configurable: 0 } }, + { candidateInternalPn: 'A', counts: { match: 5, mismatch: 0, unknown: 1, conditional: 0, hardMismatch: 0, softMismatch: 0, configurable: 0 } }, + { candidateInternalPn: 'B', counts: { match: 6, mismatch: 0, unknown: 0, conditional: 1, hardMismatch: 0, softMismatch: 0, configurable: 1 } }, + { candidateInternalPn: 'D', counts: { match: 7, mismatch: 0, unknown: 0, conditional: 0, hardMismatch: 0, softMismatch: 0, configurable: 0 } }, + ], + }); + const results = out['results'] as Array<{ candidateInternalPn: string; recommendationLevel: string; riskLevel: string }>; + expect(results.map((r) => [r.candidateInternalPn, r.recommendationLevel, r.riskLevel])).toEqual([ + ['C', 'not_recommended', 'high'], + ['A', 'conditional', 'medium'], + ['B', 'alternate', 'medium'], + ['D', 'recommended', 'low'], + ]); + }); + + it('既有 UNKNOWN 又有 configurable:UNKNOWN 优先 → 条件推荐(验证闭环前不给备选)', async () => { + const out = await call('part-substitution-assessment.recommend', { + candidates: [{ candidateInternalPn: 'E', counts: { match: 1, mismatch: 0, unknown: 1, conditional: 1, hardMismatch: 0, softMismatch: 0, configurable: 1 } }], + }); + expect((out['results'] as Array<{ recommendationLevel: string }>)[0]?.recommendationLevel).toBe('conditional'); + }); +}); +``` + +- [ ] **Step 2: 跑测试确认红** + +Run: `pnpm vitest run apps/api/src/app/__tests__/first-party-substitution.test.ts` +Expected: FAIL(`没有工具 part-substitution-assessment.compare-parameters`)。 + +- [ ] **Step 3: 在 `first-party.ts` 的 `export const FIRST_PARTY_TOOLS` 之前加实现** + +```ts +/* ── ACT-WORK-05 差异评估:逐参数比对 —— 纯工具(活动私有作用域)───────────────── + * + * 「能算的不问 LLM」:同输入必同结论。规则与活动包 manifest 里的 D01/D02 两张 DMN 同一份; + * 这里是求值器,那里是随包冻结的文档。`RULE_VERSION` 随结论落账(rule_version)。 + * + * 比对语义(按硬/软约束): + * equal / covers(候选范围 ⊇ 原范围) → match,低风险; + * configurable(原是定值、候选是含它的可调范围)→ conditional(要改外围); + * narrower / differs / partial → 硬:mismatch 高风险;软:conditional 中风险; + * 任一侧未披露 → unknown 中风险; + * 硬约束 equal/covers 但没给 evidenceRef → conditional(硬约束未经证据支持不得判 MATCH); + * requirement(客户约束,如 ">= 40")候选不满足 → 一律 mismatch 高风险。 + */ +export const SUBSTITUTION_RULE_VERSION = 'R-2026.07'; + +type Parsed = { kind: 'number'; value: number } | { kind: 'range'; lo: number; hi: number } | { kind: 'text'; value: string } | { kind: 'unknown' }; +type CompareResult = 'equal' | 'covers' | 'configurable' | 'narrower' | 'partial' | 'differs' | 'unknown'; +type DifferenceStatus = 'match' | 'mismatch' | 'unknown' | 'conditional'; +type RiskLevel = 'low' | 'medium' | 'high'; +type ParamPair = { + parameterName: string; + originalValue: string; + candidateValue: string; + unit?: string; + constraintType: 'hard' | 'soft'; + requirement?: string; + evidenceRef?: string; +}; +type CompareCounts = { match: number; mismatch: number; unknown: number; conditional: number; hardMismatch: number; softMismatch: number; configurable: number }; + +const UNKNOWN_WORDS = /^(未披露|未知|未给出|unknown|n\/?a|tbd|待定|-|—|–)$/i; +const NUM = '[-+]?\\d+(?:\\.\\d+)?'; +const RANGE_RE = new RegExp(`^(${NUM})\\s*(?:~|~|-|–|—|至|to)\\s*(${NUM})`, 'i'); +const NUM_RE = new RegExp(`^(${NUM})`); + +export function parseParamValue(raw: unknown): Parsed { + const s = String(raw ?? '').trim().replace(/\s+/g, ' '); + if (s === '' || UNKNOWN_WORDS.test(s)) return { kind: 'unknown' }; + const r = RANGE_RE.exec(s); + if (r !== null) { + const lo = Number(r[1]); + const hi = Number(r[2]); + if (Number.isFinite(lo) && Number.isFinite(hi)) return { kind: 'range', lo: Math.min(lo, hi), hi: Math.max(lo, hi) }; + } + const n = NUM_RE.exec(s); + if (n !== null && Number.isFinite(Number(n[1]))) return { kind: 'number', value: Number(n[1]) }; + return { kind: 'text', value: s.toLowerCase() }; +} + +function compareParsed(o: Parsed, c: Parsed): CompareResult { + if (o.kind === 'unknown' || c.kind === 'unknown') return 'unknown'; + if (o.kind === 'range' && c.kind === 'range') { + if (c.lo === o.lo && c.hi === o.hi) return 'equal'; + if (c.lo <= o.lo && c.hi >= o.hi) return 'covers'; + if (c.lo >= o.lo && c.hi <= o.hi) return 'narrower'; + return c.hi < o.lo || c.lo > o.hi ? 'differs' : 'partial'; + } + if (o.kind === 'number' && c.kind === 'number') return Math.abs(o.value - c.value) <= 1e-9 ? 'equal' : 'differs'; + if (o.kind === 'number' && c.kind === 'range') return c.lo <= o.value && o.value <= c.hi ? 'configurable' : 'differs'; + if (o.kind === 'range' && c.kind === 'number') return o.lo <= c.value && c.value <= o.hi ? 'narrower' : 'differs'; + if (o.kind === 'text' && c.kind === 'text') return o.value === c.value ? 'equal' : 'differs'; + return 'differs'; +} + +/** 客户约束:">= 40" / "<= 85" / "-40~85"(候选必须盖住);解析不了当没给 */ +function satisfiesRequirement(requirement: string | undefined, c: Parsed): boolean | undefined { + if (requirement === undefined || requirement.trim() === '' || c.kind === 'unknown' || c.kind === 'text') return undefined; + const s = requirement.trim(); + const ge = new RegExp(`^(?:>=|≥)\\s*(${NUM})$`).exec(s); + const le = new RegExp(`^(?:<=|≤)\\s*(${NUM})$`).exec(s); + const hi = c.kind === 'range' ? c.hi : c.value; + const lo = c.kind === 'range' ? c.lo : c.value; + if (ge !== null) return hi >= Number(ge[1]); + if (le !== null) return lo <= Number(le[1]); + const req = parseParamValue(s); + if (req.kind === 'range') return lo <= req.lo && hi >= req.hi; + if (req.kind === 'number') return lo <= req.value && hi >= req.value; + return undefined; +} + +/** 生命周期不是数值:候选比原料「更活」算 covers(替 NRND 料本就是为了这个),更差才是 differs */ +const LIFECYCLE_ORDER: Record = { active: 3, nrnd: 2, risk: 2, eol: 1, discontinued: 0 }; +function compareLifecycle(o: string, c: string): CompareResult | undefined { + const ro = LIFECYCLE_ORDER[o.trim().toLowerCase()]; + const rc = LIFECYCLE_ORDER[c.trim().toLowerCase()]; + if (ro === undefined || rc === undefined) return undefined; + return rc === ro ? 'equal' : rc > ro ? 'covers' : 'differs'; +} + +function judgePair(p: ParamPair): { compareResult: CompareResult; differenceStatus: DifferenceStatus; riskLevel: RiskLevel; note: string } { + const o = parseParamValue(p.originalValue); + const c = parseParamValue(p.candidateValue); + const hard = p.constraintType === 'hard'; + let compareResult = compareParsed(o, c); + if (/生命周期|lifecycle/i.test(p.parameterName)) compareResult = compareLifecycle(p.originalValue, p.candidateValue) ?? compareResult; + const req = satisfiesRequirement(p.requirement, c); + if (req === false) { + // 客户约束不满足:比对结果照实留(narrower/differs),结论一律 MISMATCH 高风险 + return { compareResult: compareResult === 'unknown' ? 'differs' : compareResult, differenceStatus: 'mismatch', riskLevel: 'high', note: `不满足客户约束 ${p.requirement ?? ''}` }; + } + if (compareResult === 'unknown') return { compareResult, differenceStatus: 'unknown', riskLevel: 'medium', note: '一侧未披露:标 UNKNOWN,挂验证任务或补来源' }; + if (compareResult === 'equal' || compareResult === 'covers') { + if (hard && (p.evidenceRef === undefined || p.evidenceRef.trim() === '')) { + return { compareResult, differenceStatus: 'conditional', riskLevel: 'medium', note: '硬约束缺证据引用:未经证据支持不判 MATCH' }; + } + return { compareResult, differenceStatus: 'match', riskLevel: 'low', note: compareResult === 'covers' ? '候选范围盖住原范围' : '一致' }; + } + if (compareResult === 'configurable') return { compareResult, differenceStatus: 'conditional', riskLevel: 'medium', note: '候选可调,须改外围把它配成原值' }; + if (compareResult === 'partial') return { compareResult, differenceStatus: 'conditional', riskLevel: hard ? 'medium' : 'low', note: '范围部分重叠,须按应用边界确认' }; + compareResult = compareResult === 'narrower' ? 'narrower' : 'differs'; + return hard + ? { compareResult, differenceStatus: 'mismatch', riskLevel: 'high', note: '硬约束不一致' } + : { compareResult, differenceStatus: 'conditional', riskLevel: 'medium', note: '软约束不一致,须评估影响' }; +} + +const compareParameters: ToolHandler = async ({ input }) => { + const candidates = Array.isArray(input['candidates']) ? (input['candidates'] as Array<{ candidateInternalPn: string; pairs: ParamPair[] }>) : []; + const results = candidates.map((cand) => { + const counts: CompareCounts = { match: 0, mismatch: 0, unknown: 0, conditional: 0, hardMismatch: 0, softMismatch: 0, configurable: 0 }; + const items = (Array.isArray(cand.pairs) ? cand.pairs : []).map((p) => { + const j = judgePair(p); + counts[j.differenceStatus] += 1; + if (j.differenceStatus === 'mismatch') counts[p.constraintType === 'hard' ? 'hardMismatch' : 'softMismatch'] += 1; + if (j.compareResult === 'configurable') counts.configurable += 1; + return { parameterName: p.parameterName, unit: p.unit ?? '', constraintType: p.constraintType, ...j }; + }); + return { candidateInternalPn: cand.candidateInternalPn, items, counts }; + }); + return { ok: true, output: { ruleVersion: SUBSTITUTION_RULE_VERSION, results } }; +}; + +/** + * 推荐定级(D02,FIRST):硬 MISMATCH → 不推荐;有 UNKNOWN → 条件推荐(等验证闭环); + * 有 configurable(改外围配得成)→ 备选;其余 conditional → 条件推荐;全 MATCH → 推荐。与 manifest 里的 D02 表同一份规则。 + */ +const recommend: ToolHandler = async ({ input }) => { + const candidates = Array.isArray(input['candidates']) ? (input['candidates'] as Array<{ candidateInternalPn: string; counts: Partial }>) : []; + const results = candidates.map((cand) => { + const n = (k: keyof CompareCounts) => Number(cand.counts?.[k] ?? 0); + if (n('hardMismatch') > 0) return { candidateInternalPn: cand.candidateInternalPn, recommendationLevel: 'not_recommended', riskLevel: 'high', reason: `硬约束 MISMATCH ${n('hardMismatch')} 项:硬约束失败直接不推荐` }; + if (n('unknown') > 0) return { candidateInternalPn: cand.candidateInternalPn, recommendationLevel: 'conditional', riskLevel: 'medium', reason: `UNKNOWN ${n('unknown')} 项待验证闭环,通过后可升级推荐` }; + if (n('configurable') > 0) return { candidateInternalPn: cand.candidateInternalPn, recommendationLevel: 'alternate', riskLevel: 'medium', reason: `${n('configurable')} 项须改外围配成原值(另有 ${n('conditional') - n('configurable')} 项条件差异):客户接受改设计则启用` }; + if (n('conditional') > 0) return { candidateInternalPn: cand.candidateInternalPn, recommendationLevel: 'conditional', riskLevel: 'medium', reason: `CONDITIONAL ${n('conditional')} 项须按应用边界确认` }; + return { candidateInternalPn: cand.candidateInternalPn, recommendationLevel: 'recommended', riskLevel: 'low', reason: `${n('match')} 项全部 MATCH` }; + }); + return { ok: true, output: { ruleVersion: SUBSTITUTION_RULE_VERSION, results } }; +}; +``` + +在 `FIRST_PARTY_TOOLS` 数组末尾登记: + +```ts + ['part-substitution-assessment.compare-parameters', compareParameters], + ['part-substitution-assessment.recommend', recommend], +``` + +- [ ] **Step 4: 跑测试确认绿** + +Run: `pnpm vitest run apps/api/src/app/__tests__/first-party-substitution.test.ts` +Expected: PASS 6 条。 + +- [ ] **Step 5: 工具契约放独立文件 `apps/api/src/app/tools-w05.ts`(seed-mvp 与 seed-activities 都要用,放 seed-mvp 里会撞循环 import)** + +```ts +/** + * ACT-WORK-05 差异评估的两枚纯工具契约(活动私有作用域)。 + * 两处消费:`seed-mvp.ts`(db:seed 全套播种)与 `seed-activities.ts`(activities:install 装活动时顺带注册 —— + * 「安装即用」包含它声明过的纯工具能调得动;从前 E04 的工具只在 db:seed 里注册,开发机上只跑 + * activities:install 的人第一次跑活动就撞「工具不存在」)。 + */ +const PURE = { + effect: 'read' as const, + errors: [] as Array<{ code: string; retryable: boolean; message: string }>, + idempotent: true, + timeoutMs: 5_000, + retries: 0, + connectionKeys: [] as string[], + pure: true, + ownerActivityId: 'part-substitution-assessment', +}; + +/** 逐参数比对 —— 出参键与台账列同名(difference_status / risk_level),模型照抄 */ +export const COMPARE_PARAMETERS_TOOL = { + ...PURE, + name: 'part-substitution-assessment.compare-parameters', + displayName: '替代料逐参数比对', + version: '1.0.0', + description: + '按硬/软约束逐参数比对原 PN 与候选 PN,输出四态 difference_status(match/mismatch/unknown/conditional)、' + + 'compare_result、risk_level 与各候选的计数。一次可比一批候选(candidates 数组),每对参数可带客户约束 requirement(如 ">= 40")与证据引用 evidenceRef;' + + '硬约束没有证据引用不判 match。规则版本 R-2026.07。', + input: { + type: 'object', + properties: { + candidates: { + type: 'array', + description: '[{candidateInternalPn, pairs:[{parameterName, originalValue, candidateValue, unit?, constraintType:"hard"|"soft", requirement?, evidenceRef?}]}]', + }, + }, + }, + output: { + type: 'object', + properties: { + ruleVersion: { type: 'string' }, + results: { + type: 'array', + description: '[{candidateInternalPn, items:[{parameterName, unit, constraintType, compareResult, differenceStatus, riskLevel, note}], counts:{match,mismatch,unknown,conditional,hardMismatch,softMismatch,configurable}}]', + }, + }, + }, +}; + +/** 推荐定级 —— 出参 recommendationLevel / riskLevel / reason 直接落 P-11 */ +export const RECOMMEND_TOOL = { + ...PURE, + name: 'part-substitution-assessment.recommend', + displayName: '替代推荐定级', + version: '1.0.0', + description: + '按每个候选的比对计数定推荐等级:硬约束 mismatch>0 → not_recommended;unknown>0 → conditional;' + + '只有可调配成(configurable)→ alternate;其余 conditional → conditional;全 match → recommended。', + input: { + type: 'object', + properties: { + candidates: { type: 'array', description: '[{candidateInternalPn, counts:{match,mismatch,unknown,conditional,hardMismatch,softMismatch,configurable}}]' }, + }, + }, + output: { + type: 'object', + properties: { + ruleVersion: { type: 'string' }, + results: { type: 'array', description: '[{candidateInternalPn, recommendationLevel, riskLevel, reason}]' }, + }, + }, +}; + +export const W05_TOOLS = [COMPARE_PARAMETERS_TOOL, RECOMMEND_TOOL] as const; + +/** 试运行审的样例(两件共用一份:并集覆盖各家入参) */ +export const W05_TOOL_SAMPLE = { + input: { + candidates: [ + { + candidateInternalPn: 'XL2596S-5.0', + pairs: [{ parameterName: '输出电压', originalValue: '5.0', candidateValue: '5.0', unit: 'V', constraintType: 'hard', evidenceRef: 'P-08' }], + counts: { match: 1, mismatch: 0, unknown: 0, conditional: 0, hardMismatch: 0, softMismatch: 0, configurable: 0 }, + }, + ], + }, + expectShape: { type: 'object' }, +}; +``` + +然后在 `seed-mvp.ts`:顶部加 `import { W05_TOOLS } from './tools-w05.ts';`,把 `for (const contract of [HISTORY_TOOL, MAIL_TOOL, CLASSIFY_TOOL, PLACEHOLDER_TOOL, COVERAGE_SCORE_TOOL, RANK_KEY_PERSONS_TOOL])` 改为 `for (const contract of [HISTORY_TOOL, MAIL_TOOL, CLASSIFY_TOOL, PLACEHOLDER_TOOL, COVERAGE_SCORE_TOOL, RANK_KEY_PERSONS_TOOL, ...W05_TOOLS])`(那条「读类 1 · 写类 1 · 纯工具 4」的日志改成「纯工具 6」)。 + +- [ ] **Step 6: 门禁子集 + 提交** + +Run: `pnpm -s typecheck && pnpm -s lint && pnpm vitest run apps/api/src/app/__tests__` +Expected: 全绿。 + +```bash +git add apps/api/src/app/tools/first-party.ts apps/api/src/app/seed-mvp.ts apps/api/src/app/tools-w05.ts apps/api/src/app/__tests__/first-party-substitution.test.ts +git commit -m "feat(w05): 差异评估两枚纯工具 —— 逐参数四态比对与推荐定级(R-2026.07) + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` + +--- + +### Task 3: 活动包骨架 + 清单(manifest.ts)+ 结构自测 + +**Files:** +- Create: `activities/part-substitution-assessment/package.json` · `src/index.ts` · `src/constants.ts` · `src/skills.ts` · `src/views/prd-style.ts` · `src/manifest.ts` +- Create: `activities/part-substitution-assessment/__tests__/activity.test.ts` +- 依赖(本任务先以占位存在,Task 4/5 替换):`src/case-model.ts`、`src/views/report.ts` + +**Interfaces:** +- Produces:`manifest`(ActivityManifest)、常量 `RULE_VERSION = 'R-2026.07'`、`D = 'sub_eval'`;台账地址与列名(Task 4 剧本 / Task 5 模版 / Task 7 e2e 全部照这里抄): + - `customer.profile`(customer_code PK;name required;industry/region expect) + - `parts.profile`(internal_pn PK;brand_name★ specification_description★ normalized_parameter_set★ package★ lifecycle_status★ tally `active/nrnd/eol/discontinued/risk`;manufacturer_name / process_technology / eol_pcn_status / datasheet_ref / datasheet_version / application_scenarios expect) + - `parts.document`(internal_pn + doc_seq PK;document_type★ tally `datasheet/app_note/reference_design/white_paper/sample_doc/commitment_letter/other`;document_title★ document_version★ document_ref★ document_status★ tally `valid/expired/pending/void`;citation_locator / brand_name expect) + - `demand.request`(request_id PK;customer_code★ refersTo customer.profile.customer_code;requirement_version★ request_type★ request_description★ input_snapshot_ref★ original_internal_pn★ request_status★ tally `pending/responded/won/abandoned/closed`;application_scenario / bom_position expect;risk_level tally `low/medium/high/critical`、verification_requirement、output_ref、next_action、current_activity 均 `requiredWhen {current_stage:'gate03'}`;current_stage 自由文本) + - `substitution.relation`(substitution_relation_id PK;request_id★;original_internal_pn★ / candidate_internal_pn★ 均 refersTo parts.profile.internal_pn;substitution_type★ tally `pin_to_pin/functional/parametric/other`;recommendation_level★ tally `recommended/conditional/alternate/not_recommended`;recommendation_reason★ difference_summary★;risk_level★ tally `low/medium/high/critical`;verification_requirement requiredWhen {recommendation_level:'conditional'};validation_id;analysis_status★ tally `ai_draft/fae_confirmed/pending_customer/customer_approved/rejected/void`;bom_position / analysis_report_ref / confidence_score expect;rule_version★;eval_ref★) + - `substitution.difference_item`(substitution_relation_id PK distinct refersTo substitution.relation.substitution_relation_id;difference_item_id PK;parameter_name★ original_value★ candidate_value★;parameter_unit expect;constraint_type★ tally `hard/soft`;compare_result expect;difference_status★ tally `match/mismatch/unknown/conditional`;impact_statement★;unknown_disposition requiredWhen {difference_status:'unknown'};risk_level★ tally `low/medium/high`;evidence_ref★;confidence expect;rule_version★) + - `validation.record`(validation_id PK;request_id★;validation_internal_pn★ refersTo parts.profile.internal_pn;validation_type★ tally `pin_to_pin/functional/reliability/certification/retest`;validation_status★ tally `not_started/in_progress/passed/failed/conditional/pending_evidence`;next_validation_action★;test_condition / evidence_ref expect) + - `sub_eval.pn_check`(record,caseKey eval_ref;eval_ref + internal_pn PK;role★ tally `original/candidate`;exists_status★ tally `found/absent/created/abandoned`;abandon_reason requiredWhen {exists_status:'abandoned'};note) + - `sub_eval.input_gap`(record;eval_ref + item_key PK;input_item★;source_ledger expect;status★ tally `gap/default/ok`;detail expect;supplement requiredWhen {status:'gap'};origin) + - `sub_eval.verdict`(record;eval_ref PK;request_id、rule_version、original_internal_pn、candidate_total integer、input_confirmed_total、review_approved_total、landing_prepared_total、relation_total、recommended_total、conditional_total、alternate_total、not_recommended_total、summary) +- Produces 出口 `assessment-released`,载荷 `{ evalRef, requestId, relationTotal, recommendedTotal, conditionalTotal, alternateTotal, notRecommendedTotal }`(全部由 `sub_eval.verdict` 同名列承载)。 +- Produces 视图名:`p08-master-create` / `p10-document-create` / `eval-input-list` / `difference-review` / `landing-confirm`(Task 5 必须导出同名常量 `P08_MASTER_CREATE` / `P10_DOCUMENT_CREATE` / `EVAL_INPUT_LIST` / `DIFFERENCE_REVIEW` / `LANDING_CONFIRM`)。 +- Produces 计划项 id(Task 4 的 CMMN / STEPS 必须一字不差):`S0_bind` `S0d_master_data` `S1_input_confirm` `S2_normalize` `S3_assess` `S4_review` `S5_prepare_landing` `T_release`。 + +- [ ] **Step 1: 建包骨架** + +```bash +mkdir -p activities/part-substitution-assessment/src/views activities/part-substitution-assessment/__tests__ +cat > activities/part-substitution-assessment/package.json <<'EOF' +{ "name": "@activities/part-substitution-assessment", "version": "0.1.0", "private": true, "type": "module", "exports": { ".": "./src/index.ts" } } +EOF +cat > activities/part-substitution-assessment/src/index.ts <<'EOF' +/** + * ACT-WORK-05 · 差异评估 —— 活动包代码入口(ADR-004:默认导出 defineActivity 的返回值)。 + * + * 这个活动没有 `run`。案例活动的推进由 Loop 驱动,行为全部写在清单里: + * 案例模型 + 说明书 + 步骤剧本 + 三条技能 + 台账声明 + 五张 HTML 报告模版。 + * 这个文件之所以还存在,是因为清单的 `entry` 指着它 —— + * 它是**被静态审扫描的产物文本**(查 import 与全局引用,证明活动包零环境能力)。 + */ +import { defineActivity } from '@sdk'; + +export default defineActivity({ name: 'part-substitution-assessment' }); +EOF +# 共享常量单独一个文件:manifest.ts 与 case-model.ts 互相引用会撞 TDZ(E04 的 case-model 不 import manifest,这里两边都要用同一批常量) +cat > activities/part-substitution-assessment/src/constants.ts <<'EOF' +/** 清单与案例模型共用的常量 —— 单独成文件,免得 manifest ↔ case-model 循环 import 撞 TDZ。 */ +export const ACTIVITY_ID_TEXT = 'part-substitution-assessment'; +/** 活动自持单据的域名 */ +export const D = 'sub_eval'; +/** GOV 差异规则版本 —— 随包冻结,随每条结论落账(rule_version) */ +export const RULE_VERSION = 'R-2026.07'; +export const TOOL_COMPARE = 'part-substitution-assessment.compare-parameters'; +export const TOOL_RECOMMEND = 'part-substitution-assessment.recommend'; +EOF +# 视觉底逐字节抄 I01(两包之间没有代码通道,只能各持一份),只换头注 +cp activities/brand-content-toolkit/src/views/prd-style.ts activities/part-substitution-assessment/src/views/prd-style.ts +python3 - <<'PY' +p='activities/part-substitution-assessment/src/views/prd-style.ts' +s=open(p,encoding='utf-8').read() +head_end=s.index('export const PRD_CSS') +new_head='''/** + * ACT-WORK-05 差异评估 · 视觉底 —— 逐字节抄自 I01(`brand-content-toolkit/src/views/prd-style.ts`)。 + * + * 本活动的四份目标件(`docs/SS02-ACT-WORK-05-html/*.html`)与 E04 / I01 的目标件是同一套 + * 设计语言产出的(卡壳 / 卡头渐变 / 判据框 / 表格排版全同);`activities/*` 只准 import + * `@sdk` / `@contracts`,包之间没有代码通道,只能各自持有一份。改这份不要改口径: + * 数据驱动的开关(三档占位 / 缺项红带 / 状态徽 / 编辑闸)在 `report.ts` 的 `BIND_CSS`。 + */ + +''' +open(p,'w',encoding='utf-8').write(new_head+s[head_end:]) +PY +``` + +- [ ] **Step 2: 写技能 `src/skills.ts`** + +```ts +/** + * ACT-WORK-05 差异评估的技能 —— 载方法,不载权力。 + * 剧本写「这一步做什么、写哪几列」,技能写「为什么这么判、拿不准时按哪条走」。 + */ +import { skillIdSchema } from '@contracts'; +import type { Skill } from '@contracts'; + +export const ASSESSMENT_SKILLS: Skill[] = [ + { + id: skillIdSchema.parse('substitution.normalize-parameters'), + label: '把多来源资料整成可比对的标准参数对照表', + applicability: '输入确认之后、逐项比对之前:原 PN 与每个候选的参数集要摆成同名同单位的一对一。', + method: [ + '参数集在 `parts.profile.normalized_parameter_set` 里,一行一参数:`参数名 | 值 | 单位 | hard/soft | 说明`。', + '对照表按**原 PN 的参数名**为骨架:原 PN 有的参数,候选没有对应行 → 候选值写「未披露」(它会成为 UNKNOWN);', + '候选多出来的参数不比(比对只回答「能不能替」,多出来的能力不构成差异)。', + '单位以原 PN 为准:候选单位不同先换算(mV→V、kHz→MHz),换算不了就不改值、在说明里写明 —— 不许静默改数。', + '约束类型以原 PN 那一行的 hard/soft 为准;原 PN 没标的按 GOV 口径:封装 / 工艺 / 电气边界(电压 / 电流 / 温度 / 阈值)一律 hard,其余 soft。', + '**缺参数不得伪造**:查不到的写「未披露」,不写估计值、不写「约」。', + ].join('\n'), + crystallized: [], + knowledgeRefs: ['标准化参数集行语法', '硬/软约束口径'], + version: '1.0.0', + }, + { + id: skillIdSchema.parse('substitution.difference-judgement'), + label: '逐项四态由工具算,证据与影响由你写', + applicability: '对照表齐了,要出 MATCH / MISMATCH / UNKNOWN / CONDITIONAL 时。', + method: [ + '四态**不许自己判**:调 `part-substitution-assessment.compare-parameters`,把每个候选的全部参数对一次性传进去(candidates 数组,一次一批)。', + '每对参数都带 `evidenceRef`(值从哪份记录来:「P-08」「P-10 Rev H §6.5」「数据工单 <单号> · 确认人 <人>」)——硬约束没有证据引用,工具不判 MATCH。', + '客户在应用场景里给了边界(如「输入上限 40V」)就写进那一对的 `requirement`(`>= 40`),候选不满足直接 MISMATCH。', + '工具出参照抄进 `substitution.difference_item`:difference_status / compare_result / risk_level 一字不改;', + '`impact_statement` 你写:MATCH 写「无」,其余说清对应用 / 验证 / 报价的影响;UNKNOWN 必须写 `unknown_disposition`(挂验证任务号或补来源动作),不许悬空。', + '推荐等级同样由工具定(`part-substitution-assessment.recommend`,传每个候选的 counts),`recommendation_reason` 照工具的 reason 扩写,不许把不推荐改成推荐。', + ].join('\n'), + crystallized: [], + knowledgeRefs: ['四态口径', '推荐等级四档'], + version: '1.0.0', + }, + { + id: skillIdSchema.parse('substitution.landing'), + label: '落账只写分析层,客户批准不碰', + applicability: '审核通过之后拟 P-11 / 回写 P-01 时。', + method: [ + '`analysis_status` 从 `ai_draft` 推到 `fae_confirmed`;`customer_approval_status` 由独立审批流维护 —— 本活动**没有这一列的写权**,写了会被拒。', + 'P-01 回写五项:risk_level(取各候选里最高的)、verification_requirement(把所有验证任务号列全)、output_ref(替代分析报告号)、next_action(「提交 ACT-GATE-03 评审放行」)、current_stage 写 `gate03`。', + '规则版本随结论落账:每条 P-11 主记录与差异明细都写 rule_version(工具出参的 ruleVersion),中途规则更新不影响进行中的评估。', + '被打回重做时改的是**台账本身**(理由 / 依据 / 去向),不是把同一份原样再递一遍。', + ].join('\n'), + crystallized: [], + knowledgeRefs: ['analysis_status 与 customer_approval_status 分离'], + version: '1.0.0', + }, +]; +``` + +- [ ] **Step 3: 写清单 `src/manifest.ts`** + +```ts +/** + * 活动包清单 —— ACT-WORK-05 差异评估(案例活动)。 + * + * 业务出处:`docs/SS02-ACT-WORK-05-html/`(活动流程与工单说明 / 一键启动入参说明 / + * 《SS02-SS03-SS04_Demo台账字段细化与统一方案》/ 四份目标件 / 全流程分支图)。 + * 骨架照 E04 / I01 已验证的样子:双级入参 → 活动自持单据 + 共享主数据 → decide 循环做中间审核 → + * 终局放行。三处与前两个活动刻意不同,写在这里免得下一个读者当成疏漏: + * ① **主数据是六张(P-08/P-10/P-01/P-11 主+子/P-06)**,物理表由平台基线 + * (`apps/api/src/app/seed-mvp-data.ts`)物化,这里只**追加**本活动要用的列与标记 + * (required / tally / refersTo / requiredWhen 是活动的授权面;声明合并是纯 add-column)。 + * ② **数据工单**(PN 不在库时让人从零建整条主数据)= 零输入执行工单 + 工作区的件 + * (`mine:parts.profile` / `mine:parts.document` 可加行),确认落草稿层、放行并入正式表 + * (守铁律②;文档里「确认即入库」在本平台落成「无审核环节、放行并入」)。一张表一张单。 + * ③ 逐项四态与推荐等级**由两枚纯工具算**(规则同时以 D01/D02 两张 DMN 随包冻结当文档), + * 模型只负责对照表、证据与影响说明 —— 硬约束 MISMATCH 不可能被措辞改判。 + */ +import type { ActivityManifest, DmnModel } from '@contracts'; +import { activityIdSchema } from '@contracts'; +import { ASSESSMENT_SKILLS } from './skills.ts'; +import { CMMN_XML, NARRATIVE, ASSESSMENT_PHASES, ASSESSMENT_STEPS } from './case-model.ts'; +import { P08_MASTER_CREATE, P10_DOCUMENT_CREATE, EVAL_INPUT_LIST, DIFFERENCE_REVIEW, LANDING_CONFIRM } from './views/report.ts'; +import { ACTIVITY_ID_TEXT, D, RULE_VERSION, TOOL_COMPARE, TOOL_RECOMMEND } from './constants.ts'; + +export { D, RULE_VERSION, TOOL_COMPARE, TOOL_RECOMMEND } from './constants.ts'; +const ACTIVITY_ID = activityIdSchema.parse(ACTIVITY_ID_TEXT); + +/** + * 两张判定表 —— **文档形态**(求值器在 `apps/api/src/app/tools/first-party.ts`,与这里同一份规则): + * D01 逐参数四态、D02 推荐等级。都是 FIRST:规则从上往下第一条命中即出结论,顺序就是优先级。 + */ +export const D01_DMN: DmnModel['tables'][number] = { + id: 'd01-difference-status', + label: '差异四态 D01(R-2026.07)', + hitPolicy: 'FIRST', + inputs: [ + { name: 'compare_result', expression: 'compare_result' }, + { name: 'constraint_type', expression: 'constraint_type' }, + { name: 'evidence_present', expression: 'evidence_present' }, + ], + outputs: [{ name: 'difference_status' }, { name: 'risk_level' }], + rules: [ + { id: 'd01-u', when: ['"unknown"', '-', '-'], then: ['unknown', 'medium'], annotation: '一侧未披露 → UNKNOWN,挂验证任务' }, + { id: 'd01-m0', when: ['"equal","covers"', '"hard"', 'false'], then: ['conditional', 'medium'], annotation: '硬约束缺证据不判 MATCH' }, + { id: 'd01-m', when: ['"equal","covers"', '-', '-'], then: ['match', 'low'], annotation: '一致或候选范围盖住原范围' }, + { id: 'd01-c', when: ['"configurable","partial"', '-', '-'], then: ['conditional', 'medium'], annotation: '可调配成 / 部分重叠 → 条件' }, + { id: 'd01-x', when: ['"narrower","differs"', '"hard"', '-'], then: ['mismatch', 'high'], annotation: '硬约束不一致' }, + { id: 'd01-s', when: ['"narrower","differs"', '"soft"', '-'], then: ['conditional', 'medium'], annotation: '软约束不一致 → 评估影响' }, + ], +}; + +export const D02_DMN: DmnModel['tables'][number] = { + id: 'd02-recommendation', + label: '推荐等级 D02(R-2026.07)', + hitPolicy: 'FIRST', + inputs: [ + { name: 'hard_mismatch_total', expression: 'hard_mismatch_total' }, + { name: 'unknown_total', expression: 'unknown_total' }, + { name: 'configurable_total', expression: 'configurable_total' }, + { name: 'conditional_total', expression: 'conditional_total' }, + ], + outputs: [{ name: 'recommendation_level' }], + rules: [ + { id: 'd02-n', when: ['> 0', '-', '-', '-'], then: ['not_recommended'], annotation: '硬约束失败直接不推荐' }, + { id: 'd02-u', when: ['0', '> 0', '-', '-'], then: ['conditional'], annotation: '有 UNKNOWN → 等验证闭环' }, + { id: 'd02-a', when: ['0', '0', '> 0', '-'], then: ['alternate'], annotation: '只靠改外围配得成 → 备选' }, + { id: 'd02-c', when: ['0', '0', '0', '> 0'], then: ['conditional'], annotation: '其余条件项 → 条件推荐' }, + { id: 'd02-r', when: ['0', '0', '0', '0'], then: ['recommended'], annotation: '全 MATCH → 推荐' }, + ], +}; + +export const ASSESSMENT_DMN: DmnModel = { + drd: [ + { decision: 'd01-difference-status', requires: [], inputs: ['compare_result', 'constraint_type', 'evidence_present'] }, + { decision: 'd02-recommendation', requires: [], inputs: ['hard_mismatch_total', 'unknown_total', 'configurable_total', 'conditional_total'] }, + ], + tables: [D01_DMN, D02_DMN], +}; + +/** 字段声明的短写(与 E04 同一套写法:显式 primaryKey / masked,其余按需) */ +type F = { name: string; type: 'text' | 'number' | 'integer' | 'boolean' | 'date' | 'timestamp' | 'json'; label: string; primaryKey?: boolean; required?: boolean; expect?: boolean; tally?: string[]; auto?: boolean; distinct?: boolean; refersTo?: string; requiredWhen?: { column: string; value: string } }; +const col = (f: F) => ({ primaryKey: false, masked: false, ...f }); + +export const manifest: ActivityManifest = { + identity: { + activityId: ACTIVITY_ID, + name: 'ACT-WORK-05 · 差异评估', + packageVersion: '1.0.0', + category: '需求分析与方案匹配', + sdkVersion: '0.1.0-m5', + checksum: 'dev-checksum-part-substitution-assessment', + }, + + /* ── 契约七件 ────────────────────────────────────────────────── */ + contract: { + activityId: ACTIVITY_ID, + version: '1.0.0', + input: { + /* + * 一键启动只收 4 项(文档《一键启动入参说明》极简原则):客户 / 客户需求 / 原 PN / 候选 PN 数组。 + * 应用场景、BOM 位、替代类型、规则版本、负责人期限**不是入参** —— 启动后在执行工单 01 里补。 + * datasheet 选填文件:传了 AI 在数据工单件上预填,没传人全填(文档 0c:同为合法路径)。 + * `customerName` 只在客户不在库(分支 0a)时用:S0 发数据录入卡建档,名字先摆上去让人核。 + */ + data: { + type: 'object', + required: ['customerCode', 'requestDescription', 'originalInternalPn', 'candidateInternalPns'], + properties: { + customerCode: { type: 'string', title: '客户编码(L-02 客户主台账)', examples: ['CU-0142'] }, + customerName: { type: 'string', title: '客户名称(仅客户不在库时填,启动后先建档)', examples: ['深圳恒星电源有限公司'] }, + requestDescription: { + type: 'string', + title: '客户需求(原始描述 —— 据此新建 P-01 需求主记录 · 版本 V1)', + examples: ['工业电源项目在用 LM2596S-5.0(TI,12V→5V),原厂 PCN 转 NRND,客户要求验证替代料'], + }, + originalInternalPn: { type: 'string', title: '原内部料号(P-08)', examples: ['LM2596S-5.0'] }, + candidateInternalPns: { + type: 'array', + title: '候选内部料号(数组,≥1;不在库的启动后经数据工单创建)', + items: { type: 'string' }, + examples: [['XL2596S-5.0', 'LM2596S-ADJ']], + }, + datasheets: { + type: 'array', + format: 'file', + title: '候选 datasheet(选填;传了 AI 在数据工单里预填,没传人全填)', + items: { + type: 'object', + properties: { + name: { type: 'string', title: '文件名' }, + contentType: { type: 'string', title: '类型' }, + size: { type: 'integer', title: '字节数' }, + ref: { type: 'string', title: '存档号' }, + text: { type: 'string', title: '读出来的文本' }, + note: { type: 'string', title: '读取说明' }, + }, + }, + }, + }, + }, + messageAllowed: true, + messageHint: 'FAE 可在此说明客户的应用边界、验证条件或本轮评估的特殊要求', + }, + output: { + /* 单一必发口:本活动终点是 ACT-GATE-03 评审放行;下游 ACT-WORK-08 尚未实现,按「契约先立」声明 */ + mandatory: [ + { + name: 'assessment-released', + description: '差异评估结论已落账(P-11 替代关系主记录 + 差异明细、P-01 回写)—— 供 ACT-WORK-08 证据归集消费;当前无消费者,载荷 = 已落地事实', + payload: { + type: 'object', + required: ['evalRef', 'requestId', 'relationTotal'], + properties: { + evalRef: { type: 'string', title: '评估案号' }, + requestId: { type: 'string', title: '需求编号(P-01)' }, + relationTotal: { type: 'integer', title: '落账的替代关系条数' }, + recommendedTotal: { type: 'integer', title: '推荐' }, + conditionalTotal: { type: 'integer', title: '条件推荐' }, + alternateTotal: { type: 'integer', title: '备选' }, + notRecommendedTotal: { type: 'integer', title: '不推荐' }, + }, + }, + }, + ], + selective: [], + }, + requiredDeliverables: [ + { key: 'eval-input', label: '评估输入清单', ledger: `${D}.input_gap`, ledgers: [`${D}.input_gap`, `${D}.pn_check`, `${D}.verdict`], editableAtRelease: [] }, + { key: 'difference-items', label: '差异评估结论(P-11 差异明细)', ledger: 'substitution.difference_item', ledgers: ['substitution.difference_item', 'substitution.relation'], editableAtRelease: [] }, + { key: 'landing', label: '拟落账内容(P-11 主记录 + P-01 回写)', ledger: 'substitution.relation', ledgers: ['substitution.relation', 'demand.request'], editableAtRelease: [] }, + ], + /* + * 验收判据 —— L1/L2 只读基座读数;L4 三条是文档验收工单 01/02 里留给人的裁量; + * 「推荐等级与明细一致」「硬约束不判 MATCH」机器已由工具守住,这里仍留 L4 让签字的人过目。 + */ + acceptanceCriteria: [ + { + key: 'input-confirmed', + label: '评估输入已经 PW 补齐确认:执行工单 01 已交回,缺口行的补充内容都有,补不了的留了 UNKNOWN', + level: 'L1', + predicate: `input_confirmed_total > 0 and ${D}_input_gap_rows > 0 and ${D}_input_gap_missing = 0`, + }, + { + key: 'review-approved', + label: '差异评估结论已经 FAE/RI 审核通过:验收工单 01 已过,不处在驳回待改状态', + level: 'L1', + predicate: 'review_approved_total > 0 and substitution_difference_item_rows > 0 and substitution_difference_item_missing = 0', + }, + { + key: 'every-candidate-assessed', + label: '每个未放弃的候选都有一条替代关系记录(N 候选 = N 条 P-11,并行独立落账)', + level: 'L2', + predicate: `substitution_relation_rows > 0 and substitution_relation_rows = count(candidateInternalPns) - ${D}_pn_check_exists_status_abandoned`, + }, + { + key: 'every-item-has-status', + label: '关键参数差异均有明确状态:每条明细都是 MATCH / MISMATCH / UNKNOWN / CONDITIONAL 之一', + level: 'L2', + predicate: + 'substitution_difference_item_rows > 0 and substitution_difference_item_difference_status_match + substitution_difference_item_difference_status_mismatch + ' + + 'substitution_difference_item_difference_status_unknown + substitution_difference_item_difference_status_conditional = substitution_difference_item_rows', + }, + { + key: 'unknown-has-disposition', + label: 'UNKNOWN 项均有去向:每个 UNKNOWN 都挂了验证任务或补来源动作,影响说明与证据引用逐条齐全', + level: 'L2', + predicate: 'substitution_difference_item_missing = 0', + }, + { + key: 'judged-by-rule', + label: '四态与推荐等级由规则算出:逐参数比对工具与推荐定级工具都真调过', + level: 'L2', + predicate: `tool_part_substitution_assessment_compare_parameters_calls > 0 and tool_part_substitution_assessment_recommend_calls > 0`, + }, + { + key: 'analysis-status-confirmed', + label: '分析状态与客户批准状态分离:每条 P-11 只写分析层 analysis_status = FAE 确认,规则版本已锁定随结论落账', + level: 'L2', + predicate: 'substitution_relation_analysis_status_fae_confirmed = substitution_relation_rows and substitution_relation_missing = 0', + }, + { + key: 'p01-writeback-complete', + label: '回写 P-01 字段完整:风险等级、验证要求、输出链接、下一步动作、当前阶段五项齐全', + level: 'L2', + predicate: 'demand_request_rows > 0 and demand_request_missing = 0', + }, + { key: 'evidence-credible', label: '证据链可信:每条结论都能追溯到 P-08 / P-10 / P-06 的具体记录,或本单数据工单的创建记录(人工录入)', level: 'L4' }, + { key: 'hard-not-auto-match', label: '硬约束项未被自动判 MATCH:封装 / 工艺 / 电气边界等硬约束的 MATCH 都有证据支持', level: 'L4' }, + { key: 'recommendation-consistent', label: '推荐等级与差异明细一致:存在 UNKNOWN / CONDITIONAL 的候选没有给「推荐」', level: 'L4' }, + /* L5 是责任关:那张卡本身就是这道关,卡面上不摆它(基座统一滤) */ + { key: 'gate03-release', label: 'ACT-GATE-03 评审放行:签下去 P-11 落账、P-01 回写,结论供下游消费', level: 'L5' }, + ], + revisableAtRelease: [], + prohibitions: [ + { key: 'no-fabricated-parameter', statement: 'UNKNOWN 是一等公民:查不到的参数值必须显式写「未披露」并记去向,不得伪造、不得用估计值或空串冒充' }, + { key: 'no-override-hard-mismatch', statement: '硬约束 MISMATCH 不得被任何措辞改判 MATCH;要改只能补证据后重比' }, + { key: 'no-customer-approval-write', statement: '客户批准状态由独立审批流维护,本活动不写 customer_approval_status(没有这一列的写权)' }, + { key: 'no-commercial-judgement', statement: '本活动只答技术可替代性:不做数量、价格、交期、采购判断' }, + { key: 'no-rebind', statement: '启动绑定的 4 项(客户 / 需求 / 原 PN / 候选数组)工单内不可改;绑错只能退回触发方重新启动' }, + { key: 'no-external-write', statement: '放行前不得触达外部系统 —— 触达世界的动作集中在放行那一刻' }, + ], + authorization: { + discretion: [], + preauthorizedActions: [], + /* + * 基线:数据工单 ≤2 + 候选放弃/创建决策 ≤2 + 新客户录入卡 1 + 执行单 01 + 审核决策 1 + 放行 1 = 8; + * 审核循环预留两轮(+4)= 12。cap 必须严格大于最后一次拍板时的实际次数(E04 实测栽过),留到 16。 + */ + humanCallBudget: 16, + }, + resources: { + tools: [{ name: TOOL_COMPARE }, { name: TOOL_RECOMMEND }], + skills: ASSESSMENT_SKILLS.map((s) => s.id), + models: ['deepseek-v4-flash'], + roles: ['FAE/RI'], + budget: { steps: 90, tokens: 600_000, activeMinutes: 120, humanCalls: 16 }, + }, + }, + + /* ── 声明 ────────────────────────────────────────────────────── */ + declarations: { + models: ['deepseek-v4-flash'], + tools: [{ name: TOOL_COMPARE }, { name: TOOL_RECOMMEND }], + ledger: [ + /* L-02 客户主台账:只在分支 0a(客户不在库)由数据录入卡建一行 —— 录入卡写的列都得声明 */ + { + domain: 'customer', + object: 'profile', + kind: 'master', + label: '客户档案', + fields: [ + col({ name: 'customer_code', type: 'text', label: '客户编码', primaryKey: true }), + col({ name: 'name', type: 'text', label: '客户名称', required: true }), + col({ name: 'industry', type: 'text', label: '所属行业', expect: true }), + col({ name: 'region', type: 'text', label: '区域', expect: true }), + ], + }, + /* P-08 芯片主数据:数据工单建整条;参数集是它的一个字段(一张表,不建子表) */ + { + domain: 'parts', + object: 'profile', + kind: 'master', + label: '芯片主数据(P-08)', + fields: [ + col({ name: 'internal_pn', type: 'text', label: '内部料号', primaryKey: true }), + col({ name: 'brand_name', type: 'text', label: '品牌名称', required: true }), + col({ name: 'manufacturer_name', type: 'text', label: '制造商名称', expect: true }), + col({ name: 'specification_description', type: 'text', label: '规格描述', required: true }), + col({ name: 'normalized_parameter_set', type: 'text', label: '标准化参数集(一行一参数:参数名 | 值 | 单位 | hard/soft | 说明)', required: true }), + col({ name: 'package', type: 'text', label: '封装', required: true }), + col({ name: 'process_technology', type: 'text', label: '工艺', expect: true }), + col({ name: 'lifecycle_status', type: 'text', label: '生命周期状态', required: true, tally: ['active', 'nrnd', 'eol', 'discontinued', 'risk'] }), + col({ name: 'eol_pcn_status', type: 'text', label: 'EOL/PCN 状态', expect: true }), + col({ name: 'datasheet_ref', type: 'text', label: 'Datasheet 引用', expect: true }), + col({ name: 'datasheet_version', type: 'text', label: 'Datasheet 版本', expect: true }), + col({ name: 'application_scenarios', type: 'text', label: '适配应用场景', expect: true }), + ], + }, + /* P-10 原厂技术资料库:数据工单建;评估时只读做证据回链 */ + { + domain: 'parts', + object: 'document', + kind: 'master', + label: '原厂技术资料库(P-10)', + fields: [ + col({ name: 'internal_pn', type: 'text', label: '关联内部料号', primaryKey: true }), + col({ name: 'doc_seq', type: 'text', label: '资料序号(同一料号下 01/02…)', primaryKey: true }), + col({ name: 'document_type', type: 'text', label: '资料类型', required: true, tally: ['datasheet', 'app_note', 'reference_design', 'white_paper', 'sample_doc', 'commitment_letter', 'other'] }), + col({ name: 'brand_name', type: 'text', label: '品牌名称', expect: true }), + col({ name: 'document_title', type: 'text', label: '资料标题', required: true }), + col({ name: 'document_version', type: 'text', label: '资料版本', required: true }), + col({ name: 'document_ref', type: 'text', label: '资料文件链接', required: true }), + col({ name: 'citation_locator', type: 'text', label: '证据定位(页码/章节/表格)', expect: true }), + col({ name: 'document_status', type: 'text', label: '资料状态', required: true, tally: ['valid', 'expired', 'pending', 'void'] }), + ], + }, + /* P-01 需求主记录:S0 新建 V1,放行时回写五项(current_stage = gate03 那一刻五项必填) */ + { + domain: 'demand', + object: 'request', + kind: 'master', + label: '客户需求主记录(P-01)', + fields: [ + col({ name: 'request_id', type: 'text', label: '需求编号', primaryKey: true }), + col({ name: 'customer_code', type: 'text', label: '客户编码', required: true, refersTo: 'customer.profile.customer_code' }), + col({ name: 'requirement_version', type: 'text', label: '需求版本', required: true }), + col({ name: 'request_type', type: 'text', label: '需求类型', required: true }), + col({ name: 'request_description', type: 'text', label: '需求描述', required: true }), + col({ name: 'input_snapshot_ref', type: 'text', label: '输入快照', required: true }), + col({ name: 'original_internal_pn', type: 'text', label: '原内部料号', required: true }), + col({ name: 'bom_position', type: 'text', label: 'BOM 位', expect: true }), + col({ name: 'application_scenario', type: 'text', label: '应用场景', expect: true }), + col({ name: 'technical_constraints', type: 'text', label: '技术约束' }), + col({ name: 'risk_level', type: 'text', label: '风险等级', tally: ['low', 'medium', 'high', 'critical'], requiredWhen: { column: 'current_stage', value: 'gate03' } }), + col({ name: 'verification_requirement', type: 'text', label: '验证/复测/确认要求', requiredWhen: { column: 'current_stage', value: 'gate03' } }), + col({ name: 'request_status', type: 'text', label: '需求状态', required: true, tally: ['pending', 'responded', 'won', 'abandoned', 'closed'] }), + col({ name: 'current_activity', type: 'text', label: '当前 Activity', requiredWhen: { column: 'current_stage', value: 'gate03' } }), + col({ name: 'current_stage', type: 'text', label: '当前阶段' }), + col({ name: 'next_action', type: 'text', label: '下一步动作', requiredWhen: { column: 'current_stage', value: 'gate03' } }), + col({ name: 'output_ref', type: 'text', label: '标准输出链接', requiredWhen: { column: 'current_stage', value: 'gate03' } }), + ], + }, + /* P-11 替代关系主记录:一候选一条;两枚 PN 都是引用墙(查无此料当场拒 —— 存在性检查的硬门) */ + { + domain: 'substitution', + object: 'relation', + kind: 'master', + label: '替代关系主记录(P-11)', + fields: [ + col({ name: 'substitution_relation_id', type: 'text', label: '替代关系ID', primaryKey: true }), + col({ name: 'eval_ref', type: 'text', label: '评估案号', required: true }), + col({ name: 'request_id', type: 'text', label: '关联需求编号', required: true }), + col({ name: 'bom_position', type: 'text', label: '关联 BOM 位', expect: true }), + col({ name: 'original_internal_pn', type: 'text', label: '原内部料号', required: true, refersTo: 'parts.profile.internal_pn' }), + col({ name: 'candidate_internal_pn', type: 'text', label: '候选内部料号', required: true, refersTo: 'parts.profile.internal_pn' }), + col({ name: 'substitution_type', type: 'text', label: '替代类型', required: true, tally: ['pin_to_pin', 'functional', 'parametric', 'other'] }), + col({ name: 'recommendation_level', type: 'text', label: '推荐等级', required: true, tally: ['recommended', 'conditional', 'alternate', 'not_recommended'] }), + col({ name: 'recommendation_reason', type: 'text', label: '推荐理由', required: true }), + col({ name: 'difference_summary', type: 'text', label: '差异摘要', required: true }), + col({ name: 'risk_level', type: 'text', label: '替代风险等级', required: true, tally: ['low', 'medium', 'high', 'critical'] }), + col({ name: 'verification_requirement', type: 'text', label: '验证要求', requiredWhen: { column: 'recommendation_level', value: 'conditional' } }), + col({ name: 'validation_id', type: 'text', label: '关联验证ID' }), + col({ name: 'analysis_status', type: 'text', label: '分析状态', required: true, tally: ['ai_draft', 'fae_confirmed', 'pending_customer', 'customer_approved', 'rejected', 'void'] }), + col({ name: 'analysis_report_ref', type: 'text', label: '替代分析报告', expect: true }), + col({ name: 'confidence_score', type: 'number', label: '证据可信度', expect: true }), + col({ name: 'rule_version', type: 'text', label: '差异规则版本', required: true }), + ], + }, + /* P-11 差异明细子表:一条参数一条记录;relation id 上 distinct 让基座数「几条关系有明细」 */ + { + domain: 'substitution', + object: 'difference_item', + kind: 'master', + label: '差异明细(P-11 子表)', + fields: [ + col({ name: 'substitution_relation_id', type: 'text', label: '替代关系ID', primaryKey: true, distinct: true, refersTo: 'substitution.relation.substitution_relation_id' }), + col({ name: 'difference_item_id', type: 'text', label: '差异明细ID', primaryKey: true }), + col({ name: 'parameter_name', type: 'text', label: '标准参数名', required: true }), + col({ name: 'original_value', type: 'text', label: '原物料参数值', required: true }), + col({ name: 'candidate_value', type: 'text', label: '候选物料参数值', required: true }), + col({ name: 'parameter_unit', type: 'text', label: '参数单位', expect: true }), + col({ name: 'constraint_type', type: 'text', label: '约束类型', required: true, tally: ['hard', 'soft'] }), + col({ name: 'compare_result', type: 'text', label: '比对结果(工具出参)', expect: true }), + col({ name: 'difference_status', type: 'text', label: '差异状态', required: true, tally: ['match', 'mismatch', 'unknown', 'conditional'] }), + col({ name: 'impact_statement', type: 'text', label: '影响说明(MATCH 写「无」)', required: true }), + 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: 'evidence_ref', type: 'text', label: '差异证据引用', required: true }), + col({ name: 'confidence', type: 'number', label: 'AI 置信度', expect: true }), + col({ name: 'rule_version', type: 'text', label: '差异规则版本', required: true }), + ], + }, + /* P-06 验证记录:事实类 —— 本活动只写「验证要求」(状态 not_started),不经数据工单建 */ + { + domain: 'validation', + object: 'record', + kind: 'master', + label: '芯片验证记录(P-06)', + fields: [ + col({ name: 'validation_id', type: 'text', label: '验证ID', primaryKey: true }), + col({ name: 'request_id', type: 'text', label: '关联需求编号', required: true }), + col({ name: 'validation_internal_pn', type: 'text', label: '验证内部料号', required: true, refersTo: 'parts.profile.internal_pn' }), + col({ name: 'validation_type', type: 'text', label: '验证类型', required: true, tally: ['pin_to_pin', 'functional', 'reliability', 'certification', 'retest'] }), + col({ name: 'validation_status', type: 'text', label: '验证状态', required: true, tally: ['not_started', 'in_progress', 'passed', 'failed', 'conditional', 'pending_evidence'] }), + col({ name: 'test_condition', type: 'text', label: '测试条件', expect: true }), + col({ name: 'next_validation_action', type: 'text', label: '后续验证动作', required: true }), + col({ name: 'evidence_ref', type: 'text', label: '验证证据引用', expect: true }), + ], + }, + /* ── 活动自持单据(record,caseKey eval_ref)── */ + { + domain: D, + object: 'pn_check', + kind: 'record', + caseKey: 'eval_ref', + label: '料号存在性核对', + fields: [ + col({ name: 'eval_ref', type: 'text', label: '评估案号', primaryKey: true }), + col({ name: 'internal_pn', type: 'text', label: '内部料号', primaryKey: true }), + col({ name: 'role', type: 'text', label: '角色', required: true, tally: ['original', 'candidate'] }), + col({ name: 'exists_status', type: 'text', label: '在库状态', required: true, tally: ['found', 'absent', 'created', 'abandoned'] }), + col({ name: 'abandon_reason', type: 'text', label: '放弃原因', requiredWhen: { column: 'exists_status', value: 'abandoned' } }), + col({ name: 'note', type: 'text', label: '备注' }), + ], + }, + { + domain: D, + object: 'input_gap', + kind: 'record', + caseKey: 'eval_ref', + label: '评估输入清单', + fields: [ + col({ name: 'eval_ref', type: 'text', label: '评估案号', primaryKey: true }), + col({ name: 'item_key', type: 'text', label: '输入项键', primaryKey: true }), + col({ name: 'input_item', type: 'text', label: '输入项', required: true }), + col({ name: 'source_ledger', type: 'text', label: '来源台账', expect: true }), + col({ name: 'status', type: 'text', label: '状态', required: true, tally: ['gap', 'default', 'ok'] }), + col({ name: 'detail', type: 'text', label: '绑定值 / 缺口说明', expect: true }), + col({ name: 'supplement', type: 'text', label: '补充内容', requiredWhen: { column: 'status', value: 'gap' } }), + col({ name: 'origin', type: 'text', label: '来源(ai/human-added)' }), + ], + }, + { + domain: D, + object: 'verdict', + kind: 'record', + caseKey: 'eval_ref', + label: '本次评估结论', + fields: [ + col({ name: 'eval_ref', type: 'text', label: '评估案号', primaryKey: true }), + col({ name: 'request_id', type: 'text', label: '需求编号' }), + col({ name: 'rule_version', type: 'text', label: '规则版本' }), + col({ name: 'original_internal_pn', type: 'text', label: '原内部料号' }), + col({ name: 'candidate_total', type: 'integer', label: '候选数' }), + col({ name: 'input_confirmed_total', type: 'integer', label: '评估输入已确认' }), + col({ name: 'review_approved_total', type: 'integer', label: '差异结论已审核通过' }), + col({ name: 'landing_prepared_total', type: 'integer', label: '拟落账已备好' }), + col({ name: 'relation_total', type: 'integer', label: '替代关系条数' }), + col({ name: 'recommended_total', type: 'integer', label: '推荐' }), + col({ name: 'conditional_total', type: 'integer', label: '条件推荐' }), + col({ name: 'alternate_total', type: 'integer', label: '备选' }), + col({ name: 'not_recommended_total', type: 'integer', label: '不推荐' }), + col({ name: 'summary', type: 'text', label: '本次评估结论' }), + ], + }, + ], + listeners: [], + harness: { + memoryWindow: 24, + stallAfter: 'PT4H', + nudge: [{ after: 'PT2H', action: 'nudge' }], + retry: { times: 2, backoff: 'PT30S' }, + }, + }, + + /* ── 资产初始版 ──────────────────────────────────────────────── */ + assets: { + businessModel: { cmmnXml: CMMN_XML, narrative: NARRATIVE, dmn: ASSESSMENT_DMN, steps: ASSESSMENT_STEPS, phases: ASSESSMENT_PHASES }, + skills: ASSESSMENT_SKILLS, + /* + * 五个局面各一张: + * · `p08-master-create` —— S0d 摆,数据工单(P-08),件上可加行、可编辑; + * · `p10-document-create` —— S0d 摆,数据工单(P-10),同上; + * · `eval-input-list` —— S1 摆,评估输入清单:绑定区只读 + 缺口表可编辑; + * · `difference-review` —— S2/S3/S4 摆,差异明细 + 各候选推荐等级,只读; + * · `landing-confirm` —— S5/T_release 摆,拟写 P-11 / 拟回写 P-01,只读;放行后即归档件。 + */ + viewTemplates: [ + { name: 'p08-master-create', html: P08_MASTER_CREATE }, + { name: 'p10-document-create', html: P10_DOCUMENT_CREATE }, + { name: 'eval-input-list', html: EVAL_INPUT_LIST }, + { name: 'difference-review', html: DIFFERENCE_REVIEW }, + { name: 'landing-confirm', html: LANDING_CONFIRM }, + ], + cardSchemas: {}, + distillHooks: [{ on: 'terminal', prompt: '归纳本次差异评估:哪些候选、各自推荐等级与关键依据、UNKNOWN 挂了什么验证任务、审核是否被驳回过(为什么)、数据工单建了哪些主数据。' }], + }, + + /* + * ── 样例集:五条形状真不同(常备案例即验收案例)── + * 第一条满载且走「候选缺库 → 数据工单」那条最长的路(测试台落地即预填的是它);样例名 ≤14 字。 + * 库里只播了 LM2596S-5.0 / LM2596S-ADJ 两颗(见 seed-mvp-data.ts),XL2596S-5.0 / CN-DCDC-32V / LM2596S-3.3 刻意不在库。 + */ + samples: [ + { + name: '候选缺库·派数据工单', + input: { + customerCode: 'CU-0142', + requestDescription: '工业电源项目在用 LM2596S-5.0(TI,12V→5V),原厂 PCN 转 NRND,客户要求验证替代料;应用 12V→5V、工业温域、输入上限 40V', + originalInternalPn: 'LM2596S-5.0', + candidateInternalPns: ['XL2596S-5.0', 'LM2596S-ADJ'], + }, + expect: { ports: ['assessment-released'] }, + }, + { + name: '都在库·常路', + input: { + customerCode: 'CU-0142', + requestDescription: 'LM2596S-5.0 转 NRND,客户先评估同族可调版 LM2596S-ADJ 能否替代(愿意改外围);应用 12V→5V、工业温域', + originalInternalPn: 'LM2596S-5.0', + candidateInternalPns: ['LM2596S-ADJ'], + }, + expect: { ports: ['assessment-released'] }, + }, + { + name: '原PN缺库·硬阻塞', + input: { + customerCode: 'CU-0142', + requestDescription: '客户另一块板在用 LM2596S-3.3(3.3V 固定版),同批 PCN,要求评估 LM2596S-ADJ 替代;应用 12V→3.3V', + originalInternalPn: 'LM2596S-3.3', + candidateInternalPns: ['LM2596S-ADJ'], + }, + expect: { ports: ['assessment-released'] }, + }, + { + name: '三候选·含硬约束不推荐', + input: { + customerCode: 'CU-0142', + requestDescription: '工业电源 12V→5V,输入上限 40V,工业温域;三个候选:XL2596S-5.0、LM2596S-ADJ、国产 CN-DCDC-32V(输入上限只有 32V)', + originalInternalPn: 'LM2596S-5.0', + candidateInternalPns: ['XL2596S-5.0', 'LM2596S-ADJ', 'CN-DCDC-32V'], + }, + expect: { ports: ['assessment-released'] }, + }, + { + name: '新客户·先建档', + input: { + customerCode: 'CU-9001', + customerName: '深圳恒星电源有限公司', + requestDescription: '新客户口述:工业电源 12V→5V 在用 LM2596S-5.0,想评估 LM2596S-ADJ', + originalInternalPn: 'LM2596S-5.0', + candidateInternalPns: ['LM2596S-ADJ'], + }, + expect: { ports: ['assessment-released'] }, + }, + ], + + entry: { file: 'index.js', checksum: 'dev-entry-part-substitution-assessment' }, +}; +``` + +- [ ] **Step 4: 先放两份占位(让 Task 3 的测试能跑;Task 4/5 整份替换)** + +`src/case-model.ts` 占位: +```ts +export const CMMN_XML = ''; +export const NARRATIVE = ''; +export const ASSESSMENT_STEPS: ReadonlyArray<{ item: string; intent: string; reads: string[]; writes: string[]; tools: string[]; view?: string; doneWhen?: string; guide: string }> = []; +export const ASSESSMENT_PHASES: ReadonlyArray<{ name: string; items: string[] }> = []; +``` +`src/views/report.ts` 占位: +```ts +export const P08_MASTER_CREATE = '
'; +export const P10_DOCUMENT_CREATE = '
'; +export const EVAL_INPUT_LIST = '
'; +export const DIFFERENCE_REVIEW = '
'; +export const LANDING_CONFIRM = '
'; +``` + +- [ ] **Step 5: 写结构自测 `__tests__/activity.test.ts`**(Task 4/5 落地前,案例模型与模版相关的用例会红 —— 这是预期,Task 5 收尾时全绿) + +```ts +/** + * ACT-WORK-05 差异评估的包内自测 —— **只准 import @sdk / @contracts / @sfeel**。 + * 案例活动没有 `run`,自测不跑回合;测的是清单本身与四处对齐 + * (判据谓词 / 台账声明的授权面 / 决策卡循环骨架 / 报告模版的读写面),结构照 E04 / I01。 + */ +import { describe, expect, it } from 'vitest'; +import { activityManifestSchema } from '@contracts'; +import { createMockHost } from '@sdk'; +import { manifest, D, RULE_VERSION } from '../src/manifest.ts'; +import activity from '../src/index.ts'; + +const ITEMS = ['S0_bind', 'S0d_master_data', 'S1_input_confirm', 'S2_normalize', 'S3_assess', 'S4_review', 'S5_prepare_landing', 'T_release']; +const NO_DONE_WHEN = new Set(['S0d_master_data', 'S1_input_confirm', 'S4_review', 'T_release']); +const VIEWS = ['p08-master-create', 'p10-document-create', 'eval-input-list', 'difference-review', 'landing-confirm']; +const byAddress = (domain: string, object: string) => manifest.declarations.ledger.find((l) => l.domain === domain && l.object === object); +const viewHtml = (name: string) => manifest.assets.viewTemplates.find((t) => t.name === name)?.html ?? ''; + +describe('清单本身合规', () => { + it('过 @contracts 的 schema', () => { + expect(() => activityManifestSchema.parse(manifest)).not.toThrow(); + }); + + it('没有静态监听(等外部回音一律落在执行工单上,listen 在本仓不可用)', () => { + expect(manifest.declarations.listeners).toEqual([]); + }); + + it('L1/L2 判据都给了谓词;L4 三条不给(裁量项);L5 恰一条', () => { + for (const c of manifest.contract.acceptanceCriteria) { + if (c.level === 'L1' || c.level === 'L2') expect(c.predicate, c.key).toBeDefined(); + if (c.level === 'L4') expect(c.predicate, c.key).toBeUndefined(); + } + expect(manifest.contract.acceptanceCriteria.filter((c) => c.level === 'L4')).toHaveLength(3); + expect(manifest.contract.acceptanceCriteria.filter((c) => c.level === 'L5')).toHaveLength(1); + }); + + it('机检谓词只读基座读数与记账旗:不出现活动自报的汇总列名', () => { + const forbidden = ['relation_total', 'candidate_total', 'recommended_total', 'not_recommended_total']; + for (const c of manifest.contract.acceptanceCriteria) { + for (const name of forbidden) expect(c.predicate ?? '', `${c.key} 读了自报数 ${name}`).not.toContain(name); + } + }); + + it('三件必交付物都有台账地址;人环预算严格大于「基线 8 + 两轮审核循环 4」', () => { + expect(manifest.contract.requiredDeliverables).toHaveLength(3); + for (const d of manifest.contract.requiredDeliverables) expect(d.ledger, d.key).toBeDefined(); + expect(manifest.contract.authorization.humanCallBudget).toBeGreaterThan(12); + expect(manifest.contract.resources.budget.humanCalls).toBeGreaterThan(12); + }); + + it('起单只必填 4 项(客户 / 需求 / 原 PN / 候选数组);datasheet 是选填文件', () => { + expect(manifest.contract.input.data['required']).toEqual(['customerCode', 'requestDescription', 'originalInternalPn', 'candidateInternalPns']); + const properties = manifest.contract.input.data['properties'] as Record; + expect(properties['candidateInternalPns']?.type).toBe('array'); + expect(properties['datasheets']?.format).toBe('file'); + }); + + it('五条样例:第一条满载走数据工单那条路,名字 ≤14 字,形状各不相同', () => { + expect(manifest.samples).toHaveLength(5); + for (const s of manifest.samples) expect(s.name.length, s.name).toBeLessThanOrEqual(14); + expect(new Set(manifest.samples.map((s) => JSON.stringify(s.input))).size).toBe(5); + const first = manifest.samples[0]?.input as { candidateInternalPns?: string[] }; + expect(first.candidateInternalPns).toContain('XL2596S-5.0'); + const newCustomer = manifest.samples.find((s) => (s.input as { customerName?: string }).customerName !== undefined); + expect(newCustomer).toBeDefined(); + }); + + it('五张视图模版都声明了,剧本每一步引用的 view 都在其中', () => { + expect(new Set(manifest.assets.viewTemplates.map((v) => v.name))).toEqual(new Set(VIEWS)); + for (const step of manifest.assets.businessModel.steps as ReadonlyArray<{ view?: string }>) { + if (step.view !== undefined) expect(VIEWS, step.view).toContain(step.view); + } + }); +}); + +describe('台账声明:授权面与《统一方案》口径', () => { + it('P-08 一张表:主键 internal_pn,参数集是它的一个必填字段,没有 parts.parameter 子表', () => { + const p08 = byAddress('parts', 'profile'); + expect(p08?.fields.filter((f) => f.primaryKey).map((f) => f.name)).toEqual(['internal_pn']); + expect(p08?.fields.find((f) => f.name === 'normalized_parameter_set')?.required).toBe(true); + expect(byAddress('parts', 'parameter')).toBeUndefined(); + }); + + it('P-11 主记录的原 PN / 候选 PN 都是引用墙(查无此料当场拒 —— 存在性检查的硬门)', () => { + const rel = byAddress('substitution', 'relation'); + for (const name of ['original_internal_pn', 'candidate_internal_pn']) { + expect(rel?.fields.find((f) => f.name === name)?.refersTo, name).toBe('parts.profile.internal_pn'); + } + }); + + it('客户批准状态**不在声明里**:本活动没有它的写权(只读边界)', () => { + const rel = byAddress('substitution', 'relation'); + expect(rel?.fields.map((f) => f.name)).not.toContain('customer_approval_status'); + }); + + it('UNKNOWN 必有去向:unknown_disposition 钉在 difference_status = unknown 的行上;四态是 ASCII tally', () => { + const item = byAddress('substitution', 'difference_item'); + expect(item?.fields.find((f) => f.name === 'unknown_disposition')?.requiredWhen).toEqual({ column: 'difference_status', value: 'unknown' }); + expect(item?.fields.find((f) => f.name === 'difference_status')?.tally).toEqual(['match', 'mismatch', 'unknown', 'conditional']); + expect(item?.fields.find((f) => f.name === 'substitution_relation_id')?.distinct).toBe(true); + }); + + it('P-01 回写五项钉在 current_stage = gate03 那一刻(放行前不拦,放行门 L2 数得出来)', () => { + const p01 = byAddress('demand', 'request'); + for (const name of ['risk_level', 'verification_requirement', 'output_ref', 'next_action', 'current_activity']) { + expect(p01?.fields.find((f) => f.name === name)?.requiredWhen, name).toEqual({ column: 'current_stage', value: 'gate03' }); + } + }); + + it('自持单据都带 caseKey eval_ref 且在主键里;放弃候选必须写原因', () => { + for (const object of ['pn_check', 'input_gap', 'verdict']) { + const t = byAddress(D, object); + expect(t?.kind, object).toBe('record'); + expect(t?.caseKey, object).toBe('eval_ref'); + expect(t?.fields.find((f) => f.name === 'eval_ref')?.primaryKey, object).toBe(true); + } + expect(byAddress(D, 'pn_check')?.fields.find((f) => f.name === 'abandon_reason')?.requiredWhen).toEqual({ column: 'exists_status', value: 'abandoned' }); + }); + + it('规则版本常量随包冻结', () => { + expect(RULE_VERSION).toBe('R-2026.07'); + expect(manifest.assets.businessModel.dmn.tables.map((t) => t.id)).toEqual(['d01-difference-status', 'd02-recommendation']); + }); +}); + +describe('案例模型与清单的对齐', () => { + it('八个计划项,剧本 item 与 CMMN humanTask id 一一对应', () => { + const cmmnIds = [...manifest.assets.businessModel.cmmnXml.matchAll(/ m[1] ?? ''); + expect(cmmnIds).toEqual(ITEMS); + expect((manifest.assets.businessModel.steps as ReadonlyArray<{ item: string }>).map((s) => s.item)).toEqual(ITEMS); + }); + + it('四处不给 doneWhen(数据工单 / 输入确认 / 审核 / 放行),其余四处都给', () => { + for (const s of manifest.assets.businessModel.steps as ReadonlyArray<{ item: string; doneWhen?: string }>) { + if (NO_DONE_WHEN.has(s.item)) expect(s.doneWhen, s.item).toBeUndefined(); + else expect(s.doneWhen, s.item).toBeDefined(); + } + }); + + it('那四项在 CMMN 里都标了人工激活', () => { + for (const id of NO_DONE_WHEN) { + const block = new RegExp(``).exec(manifest.assets.businessModel.cmmnXml)?.[0] ?? ''; + expect(block, id).toContain('manualActivationRule'); + } + }); + + it('除放行外每一项恰好属于一个阶段;5 段 + 放行 = x/6,与文档 0.5/1/2/3/4/5 六步对齐', () => { + const phases = manifest.assets.businessModel.phases ?? []; + expect(phases).toHaveLength(5); + const phaseItems = phases.flatMap((p) => p.items); + expect([...phaseItems].sort()).toEqual([...ITEMS.filter((i) => i !== 'T_release')].sort()); + expect(new Set(phaseItems).size).toBe(phaseItems.length); + }); + + it('说明书点名的计划项都真实存在', () => { + const mentioned = [...manifest.assets.businessModel.narrative.matchAll(/\b(S\d[a-z]?_[a-zA-Z_]+|T_release)\b/g)].map((m) => m[1] ?? ''); + for (const name of mentioned) expect(ITEMS, `说明书提到但不存在的计划项: ${name}`).toContain(name); + }); + + it('两种决策卡骨架都在说明书里:候选放弃/创建(create/abandon)与审核(approve/reject/custom:)', () => { + const n = manifest.assets.businessModel.narrative; + expect(n).toContain('"kind":"decide","itemId":"S0d_master_data"'); + expect(n).toContain('"value":"create"'); + expect(n).toContain('"value":"abandon"'); + expect(n).toContain('"kind":"decide","itemId":"S4_review"'); + expect(n).toContain('"approve"'); + expect(n).toContain('"reject"'); + expect(n).toContain('"custom:"'); + }); + + it('T_release 剧本里 judge criterion 的原文逐字对得上 acceptanceCriteria 的 label', () => { + const tRelease = (manifest.assets.businessModel.steps as ReadonlyArray<{ item: string; guide?: string }>).find((s) => s.item === 'T_release'); + const quoted = [...(tRelease?.guide ?? '').matchAll(/judge criterion="([^"]+)"/g)].map((m) => m[1] ?? ''); + expect(quoted).toHaveLength(3); + const labels = new Set(manifest.contract.acceptanceCriteria.map((c) => c.label)); + for (const q of quoted) expect(labels.has(q), q).toBe(true); + }); +}); + +describe('报告模版:编辑面与只读面各就各位', () => { + it('两张数据工单件可加行可编辑:data-edit / data-edit-add 与主键 data-col 都在', () => { + const p08 = viewHtml('p08-master-create'); + expect(p08).toContain('data-edit="parts.profile"'); + expect(p08).toContain('data-edit-add="parts.profile"'); + expect(p08).toContain('data-col="internal_pn"'); + expect(p08).toContain('data-col="normalized_parameter_set"'); + const p10 = viewHtml('p10-document-create'); + expect(p10).toContain('data-edit="parts.document"'); + expect(p10).toContain('data-col="doc_seq"'); + }); + + it('评估输入清单:缺口表可编辑(supplement),绑定区只读', () => { + const html = viewHtml('eval-input-list'); + expect(html).toContain(`data-edit="${D}.input_gap"`); + expect(html).toContain('data-col="supplement"'); + expect(html).toContain(`data-rows="mine:${D}.pn_check"`); + }); + + it('差异审核与落账确认只读:没有任何 data-edit / data-col', () => { + for (const name of ['difference-review', 'landing-confirm']) { + expect(viewHtml(name), name).not.toContain('data-edit='); + expect(viewHtml(name), name).not.toContain('data-col='); + } + }); + + it('每个可编辑格子的 data-col 都在对应台账对象的声明里', () => { + const cases: Array<[string, string, string]> = [ + ['p08-master-create', 'parts', 'profile'], + ['p10-document-create', 'parts', 'document'], + ['eval-input-list', D, 'input_gap'], + ]; + for (const [view, domain, object] of cases) { + const cols = new Set([...viewHtml(view).matchAll(/data-col="([^"]+)"/g)].map((m) => m[1] ?? '')); + const declared = new Set(byAddress(domain, object)?.fields.map((f) => f.name)); + for (const c of cols) expect(declared.has(c), `${view}: ${c}`).toBe(true); + } + }); + + it('枚举列只用 data-class-of 投影,不摆 ASCII 原值', () => { + expect(viewHtml('difference-review')).toContain('data-class-of="difference_status"'); + expect(viewHtml('difference-review')).toContain('data-class-of="recommendation_level"'); + expect(viewHtml('difference-review')).not.toContain('data-slot="difference_status"'); + }); + + it('宿主拒推含脚本的模版', async () => { + const host = createMockHost({ contract: CONTRACT }); + await expect(host.base.interaction.pushHtml('
')).rejects.toThrow(/无脚本执行权/); + }); +}); + +describe('活动入口', () => { + it('没有 run —— 行为全在清单里', () => { + expect(activity.run).toBeUndefined(); + }); +}); + +const CONTRACT = { + activityId: 'part-substitution-assessment', + version: '1.0.0', + data: {}, + message: undefined, + outputPorts: { mandatory: ['assessment-released'], selective: [] }, + prohibitions: [], + discretion: [], +} as const; +``` + +- [ ] **Step 6: 跑一遍,记下哪些红(只许是案例模型 / 模版相关的那几条)** + +Run: `pnpm vitest run activities/part-substitution-assessment` +Expected:「清单本身合规」「台账声明」两组绿;「案例模型」「报告模版」组红(占位为空)。若「过 @contracts 的 schema」红,先修清单再往下。 + +- [ ] **Step 7: 提交骨架** + +```bash +git add activities/part-substitution-assessment +git commit -m "feat(w05): 差异评估活动包骨架与清单 —— 六张主数据追加声明、三张自持单据、两张 DMN、五条样例 + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` + +--- + +### Task 4: 案例模型 —— CMMN 八项、说明书、逐步剧本、五段进度 + 剧本自测 + +**Files:** +- Replace: `activities/part-substitution-assessment/src/case-model.ts`(整份替换 Task 3 的占位) +- Create: `activities/part-substitution-assessment/__tests__/steps.test.ts` + +**Interfaces:** +- Consumes:Task 3 的台账地址 / 列名 / tally 值、工具名 `TOOL_COMPARE` / `TOOL_RECOMMEND`、视图名。 +- Produces:`CMMN_XML`、`NARRATIVE`、`ASSESSMENT_STEPS`(8 条,`item` 顺序 = CMMN humanTask 顺序)、`ASSESSMENT_PHASES`(5 段)。 +- 案号约定(剧本与 e2e 共用):`eval_ref = 'W05-' + case:openedAt 去分隔符`;`request_id = 'REQ-' + 同一串数字`;`input_snapshot_ref = 'SNAP-' + eval_ref`;关系 id `REL-<数字串>-01/02…`(按候选数组顺序);明细 id `<关系id>-P01/P02…`;验证 id `VAL-<数字串>-01/02…`;报告号 `RPT-<数字串>`。 +- 卡标题约定(e2e 按标题对号):数据工单 P-08 以「数据工单 · 芯片主数据创建」开头;P-10 以「数据工单 · 原厂技术资料创建」开头;执行单以「评估输入与资料补齐确认」开头。 + +- [ ] **Step 1: 写 `src/case-model.ts`** + +```ts +/** + * ACT-WORK-05 差异评估的案例模型 —— **双表示同版本**:CMMN XML(机器面)与案例说明书(叙事面)。 + * + * 业务出处:`docs/SS02-ACT-WORK-05-html/ACT-WORK-05-活动流程与工单说明.md`(步骤 0.5 + 5 步, + * 1 数据(条件)+ 1 执行 + 2 验收)与《SS02-SS03-SS04_Demo台账字段细化与统一方案》(字段口径)。 + * + * 文档六步收成 8 个案例模型项: + * 启动期 → S0_bind(AI:铸案号、建 P-01 V1、锁规则版本、逐个 PN 查库、写存在性核对表) + * 0.5 等待数据 → S0d_master_data(人:数据工单 —— P-08 一张、P-10 一张;候选 ≥2 缺库先决策卡; + * 库里都有就 disable 这一项) + * 1 建立评估上下文 → S1_input_confirm(人:执行工单 01,评估输入清单,件上补缺口) + * 2 读取并标准化 → S2_normalize(AI:P-11 主记录骨架 + 差异明细对照行) + * 3 逐项差异评估 → S3_assess(AI + 两枚工具:四态 / 风险 / 证据 / 影响 / 推荐等级 / 验证任务) + * 4 FAE/RI 审核 → S4_review(人:验收工单 01 = 决策卡循环,驳回则 AI 改台账再发) + * 5 提交评审放行 → S5_prepare_landing(AI:analysis_status 推 fae_confirmed、回写 P-01)+ T_release(人:验收工单 02,终局放行) + * + * 四处不给 doneWhen:S0d(触达人)、S1(交人补缺口)、S4(拍板且可循环)、T_release(签下去)。 + */ +import { D, RULE_VERSION, TOOL_COMPARE, TOOL_RECOMMEND } from './constants.ts'; + +export const CMMN_XML = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update + demand_request_rows > 0 and sub_eval_pn_check_rows > 0 + + + + update + 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 < count(candidateInternalPns) and parts_profile_missing = 0 and parts_document_missing = 0 + + + + update + input_confirmed_total > 0 and sub_eval_input_gap_rows > 0 and sub_eval_input_gap_missing = 0 + + + + update + substitution_relation_rows > 0 and substitution_relation_rows = count(candidateInternalPns) - sub_eval_pn_check_exists_status_abandoned and substitution_difference_item_rows > 0 and substitution_difference_item_distinct_substitution_relation_id = substitution_relation_rows + + + + update + substitution_difference_item_missing = 0 and substitution_relation_missing = 0 and substitution_difference_item_difference_status_match + substitution_difference_item_difference_status_mismatch + substitution_difference_item_difference_status_unknown + substitution_difference_item_difference_status_conditional = substitution_difference_item_rows and tool_part_substitution_assessment_compare_parameters_calls > 0 and tool_part_substitution_assessment_recommend_calls > 0 + + + + update + review_approved_total > 0 and substitution_difference_item_missing = 0 + + + + update + landing_prepared_total > 0 and substitution_relation_analysis_status_fae_confirmed = substitution_relation_rows and substitution_relation_missing = 0 and demand_request_missing = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +/** + * 案例说明书 —— **只写整单一以贯之的事**(逐步做法归剧本)。 + * 卡骨架住在这里而不是剧本里(模型会压拍,压过之后只有说明书还在 —— A1/A6/I01 同一课)。 + */ +export const NARRATIVE = `§0 开案:**判断候选料号在技术上能不能替代原料号,逐参数给出可复核的差异结论,审核后落账**。 +入参: +· \`customerCode\` —— 客户编码(L-02);\`customerName\` 只在客户不在库时给。 +· \`requestDescription\` —— 客户需求原话,据它新建 P-01 需求主记录(版本 V1)。 +· \`originalInternalPn\` —— 原内部料号;\`candidateInternalPns\` —— 候选内部料号数组(≥1,逐个评估、各一条 P-11)。 +· \`datasheets\` —— 选填的真文件,基座已读成文本(每件 \`{name, text?, note}\`);有就拿它在数据工单里预填,没有就人全填。 + +**本活动只回答一件事:技术上哪种料能替**。不做数量 / 价格 / 交期 / 采购判断;客户批准状态由审批流维护,你没有那一列的写权。 + +**四项绑定后不可改**(客户 / 需求 / 原 PN / 候选数组):绑错只能退回启动人重新启动。 + +**能算的不问人也不自己判**:四态(MATCH / MISMATCH / UNKNOWN / CONDITIONAL)与推荐等级 +(推荐 / 条件推荐 / 备选 / 不推荐)全由两枚工具算 —— \`${TOOL_COMPARE}\`、\`${TOOL_RECOMMEND}\`, +一批一次。你负责对照表、证据引用、影响说明、UNKNOWN 的去向。**规则版本 \`${RULE_VERSION}\` 随每条结论落账**。 + +**UNKNOWN 是一等公民**:查不到的参数值写「未披露」,不许估、不许留空串冒充;每个 UNKNOWN 必须写去向 +(挂验证任务号或补来源动作)。硬约束 MISMATCH 不许改判,要改只能补证据后重比。 + +**存在性检查与数据工单**:启动时逐个料号在 \`parts.profile\` 里查(原 PN + 每个候选)。查无记录 = 「完全没有」, +要人从零建整条主数据 —— 发**数据工单**(一张表一张单:P-08 一张、P-10 一张);PN 在库但个别字段缺 = 「有而不全」, +归执行工单 01 补。原 PN 缺 = 硬阻塞必须建;候选缺:候选 ≥2 时先问人放弃还是创建,候选 = 1 时必须建。 +**库里都有就把 S0d_master_data disable 掉**(此单不走这条),别发空单。 + +**六步走**:绑定与存在性核对 → (等待数据)→ 人补评估输入 → 标准化对照表 → 逐项评估 → 人审结论(可循环)→ 拟落账 → 人放行。 + +【本单会用到的卡,全部照下面的骨架发,别自己另编】 + +① 新客户建档(只在 \`customerCode\` 在 \`customer.profile\` 里查无时,S0_bind 里发;编码用入参给的那个,人核名): +{"kind":"entry","itemId":"S0_bind","ask":"客户 不在客户档案里,请核对建档信息后确认", + "ledger":"customer.profile","key":{"customer_code":""}, + "fields":[ + {"key":"name","label":"客户名称","value":"","required":true}, + {"key":"industry","label":"所属行业"}, + {"key":"region","label":"区域"} + ]} +人确认那一下基座已把这一行落进草稿层,**你不许再 write 一遍 customer.profile**。 + +② 候选不在库、候选 ≥2 时,一颗一张决策卡(按候选数组顺序,一次只发一张;候选只有一颗时不问、直接建): +{"kind":"decide","itemId":"S0d_master_data", + "ask":"候选 在芯片主数据里查无记录。是从零建它的主数据(派数据工单),还是本轮放弃这颗候选?", + "options":[{"value":"create","label":"创建主数据(派数据工单)","recommended":true}, + {"value":"abandon","label":"放弃这颗候选"}],"allowCustom":true} +\`value\` 只有 \`"create"\` / \`"abandon"\` / \`"custom:"\` 开头三种:\`"custom:"\` 开头按放弃处理、\`note\` 就是放弃原因; +点了 \`"abandon"\` 没写原因,\`abandon_reason\` 写「负责人本轮放弃(未注明原因)」。 + +③ 数据工单 · P-08(所有要建的料号一起,一张单;零输入,填报全在工作区的件上): +{"kind":"human","itemId":"S0d_master_data","workOrder":"execution","card":{ + "title":"数据工单 · 芯片主数据创建(P-08):<料号列表,顿号分隔>", + "ask":"这些料号在芯片主数据里查无记录,请在工作区的件上把整条记录建齐(一颗一行,可加行)。", + "steps":["每一行:品牌、规格描述、封装、生命周期状态、标准化参数集为必填,缺一格确认不了", + "标准化参数集一行一参数:参数名 | 值 | 单位 | hard/soft | 说明;原厂未披露的值写「未披露」,它会进入 UNKNOWN 并挂验证任务", + "有 datasheet 的把引用与版本一起填(成对);制造商与品牌不一致时分开写", + "改完保存,回这张卡确认 —— 确认后这批主数据即入库,活动从等待数据唤醒"], + "standard":"每颗料号一行,必填列全齐;参数集至少含输入电压范围 / 输出电压 / 输出电流 / 工作温度 / 封装相关的硬约束项。", + "basis":[{"k":"触发原因","v":"按 internal_pn 查 P-08 查无记录(完全没有,不是字段缺失)"}, + {"k":"AI 预填","v":"<有 datasheet 就写「已按 datasheet 预填 N 项,请核对」,没有写「未上传 datasheet,请全填」>"}]}} + +④ 数据工单 · P-10(同一批料号的资料记录,一张单;在 P-08 那张确认之后再发): +{"kind":"human","itemId":"S0d_master_data","workOrder":"execution","card":{ + "title":"数据工单 · 原厂技术资料创建(P-10):<料号列表>", + "ask":"请把这些料号的 datasheet / 承诺函等资料记录补进原厂技术资料库(一份资料一行,可加行)。", + "steps":["资料类型、标题、版本、文件链接、资料状态为必填;证据定位写页码或章节,差异评估的证据引用会指向它", + "同一料号多份资料就多加几行,序号 01/02… 自己续", + "改完保存,回这张卡确认"], + "standard":"每颗料号至少一行 datasheet 记录,必填列全齐。", + "basis":[{"k":"触发原因","v":"按 internal_pn 查 P-10 查无记录"}]}} + +⑤ 执行工单 01 · 评估输入与资料补齐确认(S1_input_confirm;零输入): +{"kind":"human","itemId":"S1_input_confirm","workOrder":"execution","card":{ + "title":"评估输入与资料补齐确认:<原 PN> → 个候选", + "ask":"启动绑定的四项已锁定。请在工作区的件上补齐「待补 / 待确认」行的补充内容,默认值可改可确认。", + "steps":["应用场景、BOM 位是启动时未收的:必须补(写在「补充内容」列)", + "原厂未披露的参数(如使能阈值)补不到就写「挂验证任务」或「留 UNKNOWN」—— 补不了也是合法答案,不拦流程", + "替代类型 / 规则版本 / 负责人期限三行是系统默认,可改可不改", + "加一行 = 补充 AI 没列出的资料项;删一行 = 该项不适用;已绑定的四项不可改", + "改完保存,回这张卡提交"], + "standard":"每一条「待补」行的补充内容都有;补不了的写明理由。", + "basis":[{"k":"已绑定","v":"客户 · 需求 V1 · 原 PN <原 PN> · 候选 颗"}, + {"k":"缺口","v":" 项待补 / 待确认"}]}} + +⑥ 验收工单 01 · 差异评估结论审核(S4_review;决策卡循环,不是终局验收)—— 发之前先在轨迹里找有没有已答过、还没处理的 \`[card-reply]\`: +· \`value\` 是 \`"approve"\` → 写 \`${D}.verdict.review_approved_total = 1\`,finish 这一项,别再发卡; +· \`value\` 是 \`"reject"\` 或以 \`"custom:"\` 开头 → 都算驳回:\`"reject"\` 没写意见,**不许再发卡追问**;\`"custom:"\` 的 \`note\` 就是他的意见, + 按意见改 \`substitution.difference_item\` / \`substitution.relation\`(同 PK 覆盖写,不开新行),意见要挂验证的写一行 \`validation.record\`, + 然后**换一种问法**再发一张(措辞不换会被当成同一张卡静默吞掉); +· 没有 → 才发第一张: +{"kind":"decide","itemId":"S4_review","allowCustom":true, + "ask":"差异评估结论已算出: 个候选、共 条参数比对(MATCH a / MISMATCH b / UNKNOWN c / CONDITIONAL d),推荐等级 <逐候选一句>。请审核。", + "options":[{"value":"approve","label":"通过","recommended":true}, + {"value":"reject","label":"驳回(不写具体意见)"}]} +三个 \`value\` 只有 \`"approve"\` / \`"reject"\` / \`"custom:"\` 开头三种,别造第四种。 + +【一路上要说话】每一拍带一句 say,说业务的话:查了几颗料、缺几颗、对照了几项、四态各几条、推荐等级各是什么。到了要找人的那一拍,先说清这一轮办完了什么、为什么现在需要他。用件上那三个词(必填 / 建议 / 选填)说缺口,只说事实。 + +【怎么把一项干完 · 一拍连做】write 和 tool 都不会自动收步 —— 做完用 finish 宣告(有 doneWhen 的项引擎自己收)。 +动作排进同一拍的 actions 串一口气给;只有发卡 / 提交(挂起)必须停拍;tool 之后同拍其余动作作废。 +**常路 9–12 拍收口**(候选缺库那条路多两张数据工单、多两拍)。 + +§Z 结案:FAE/RI 在验收单上签字,系统把草稿合并进 P-08 / P-10 / P-01 / P-11 / P-06 正式版并发射 \`assessment-released\` 口。`; + +/** + * 步骤剧本 —— 一个计划项一条;`doneWhen` 是「成果已存在」的判据,不是「该不该做」。 + * 四处不给 doneWhen:S0d(发数据工单触达人)、S1(交人补缺口)、S4(拍板且循环)、T_release(签下去)。 + */ +export const ASSESSMENT_STEPS = [ + { + item: 'S0_bind', + intent: '铸案号、建需求 V1、锁规则版本、逐个料号查库、写存在性核对表', + writes: ['demand.request', `${D}.pn_check`, `${D}.verdict`, 'customer.profile'], + reads: ['customerCode', 'customerName', 'requestDescription', 'originalInternalPn', 'candidateInternalPns', 'datasheets', 'case:openedAt', 'customer.profile', 'parts.profile'], + view: 'eval-input-list', + tools: [], + /* + * 「该有几行」也进判据:原 PN + 每个候选各一行核对(count(candidateInternalPns) + 1); + * 少一行 = 一颗料从这一单里凭空消失。count() 对未绑定名回 1 —— candidateInternalPns 是必填入参,恒绑定。 + */ + doneWhen: + `demand_request_rows > 0 and demand_request_missing = 0 and ${D}_verdict_rows > 0 and ` + + `${D}_pn_check_rows = count(candidateInternalPns) + 1 and ${D}_pn_check_missing = 0`, + guide: `第一件事:**把这一单的上下文立起来**,一拍连做(客户不在库时多一张录入卡、多一拍)。 + +【先铸案号】\`eval_ref = 'W05-' + case:openedAt 按 YYYYMMDDHHmmss 格式化\`(\`case:openedAt\` 形如 "2026-08-26 21:03:15",去掉分隔符即得); +\`request_id = 'REQ-' + 同一串数字\`;\`input_snapshot_ref = 'SNAP-' + eval_ref\`。**不许从正式表里别单的案号里挑**,整单只有这一个 eval_ref。 + +【客户在不在库】在 \`customer.profile\` 里找 \`customerCode\`:在 → 直接用;不在 → 照说明书①发数据录入卡(名字用 \`customerName\`), +发完这一拍结束;人确认后这家客户就在草稿层里了,下一拍接着做。**客户不在库也不许自己 write customer.profile**。 + +【写 \`demand.request\` 一行(V1)】request_id、customer_code(= customerCode)、requirement_version = "V1"、request_type = "技术需求"、 +request_description(入参原话,不改写)、input_snapshot_ref、original_internal_pn、request_status = "pending"、current_activity = "ACT-WORK-05"、 +current_stage = "assessing"。application_scenario / bom_position **这一步不写**(启动时未收,S1 由人补)。 + +【逐个料号查库,写 \`${D}.pn_check\`】\`parts.profile\` 整表在你的作用域里,按 \`internal_pn\` 逐个核对:原 PN 一行(role = "original"), +每个候选一行(role = "candidate");在 → exists_status = "found";不在 → exists_status = "absent"(**这一格先写 absent,等数据工单确认后再改**)。 +一颗都不能少:数组里点了几颗候选,就写几行 + 原 PN 一行。note 选填(如「PCN 转 NRND」)。 + +【写 \`${D}.verdict\` 一行】eval_ref、request_id、rule_version = "${RULE_VERSION}"、original_internal_pn、candidate_total = 候选数组长度。 + +写完引擎自己收这一项。收完先看 \`${D}.pn_check\`:有 absent 的 → 下一项是 S0d_master_data(数据工单);一个 absent 都没有 → +**disable S0d_master_data**(reason 写「原 PN 与全部候选都在库,无需建主数据」),然后等 S1 那一项 Enabled。`, + }, + { + item: 'S0d_master_data', + intent: '料号不在库:派数据工单让人从零建整条主数据(P-08 一张、P-10 一张);候选 ≥2 先问放弃还是创建', + writes: ['parts.profile', 'parts.document', `${D}.pn_check`], + reads: [`${D}.pn_check`, 'candidateInternalPns', 'originalInternalPn', 'datasheets', 'parts.profile', 'parts.document'], + view: 'p08-master-create', + tools: [], + /* 触达人,不给 doneWhen;人工激活:发卡就算启动 */ + guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走;都在库就 disable 它(见 S0 剧本)。**先分岔,再动作**: + +【先看轨迹】这一项里已经发过哪些卡、人答了什么(【当前这一步】下面那行引擎事实说得清楚): +· 原 PN absent → **硬阻塞**,不问人,直接进入「建」的名单; +· 候选 absent 且候选数 ≥2 → 每颗 absent 的候选照说明书②各发一张决策卡(一次一张,按数组顺序);人选 "abandon"/"custom:" 的, + 把那一行 \`${D}.pn_check\` 改成 exists_status = "abandoned" + abandon_reason(同 PK 覆盖写),这颗不再评估;选 "create" 的进「建」的名单; +· 候选 absent 且候选只有 1 颗 → 不问,必须建。 +**别把同一颗候选问两遍**:决策卡答过就按答案办。 + +【「建」的名单定了 → 先写骨架行,再发 P-08 数据工单】 +1. 给名单里每颗料写一行 \`parts.profile\` 骨架:key {internal_pn},patch 只带你确有依据的列 —— 有 \`datasheets[].text\` 就从里面读 + brand_name / specification_description / package / lifecycle_status(active/nrnd/eol/discontinued/risk 之一)/ normalized_parameter_set + (一行一参数:参数名 | 值 | 单位 | hard/soft | 说明,原厂没写的值写「未披露」)/ datasheet_ref + datasheet_version;没有 datasheet 就只写 internal_pn + 这一列(其余留给人填,**别编**)。 +2. 同一拍给每颗料写一行 \`parts.document\` 骨架:key {internal_pn, doc_seq: "01"},patch {document_type: "datasheet", document_status: "pending", + brand_name 有就写}。 +3. 同一拍发说明书③那张 P-08 数据工单(title 里列全料号),发完这一拍结束(挂起)。 + +【P-08 那张确认回来了】看轨迹里的 [card-reply]:再发说明书④那张 P-10 数据工单(只发一次),发完这一拍结束。 + +【P-10 那张也确认回来了】把名单里每颗料的 \`${D}.pn_check\` 改成 exists_status = "created"(同 PK 覆盖写),然后 finish 这一项。 +这时 S1_input_confirm 的入场前提(全部料号落定、本单建的主数据不缺必填)自己就成立了 —— 缺必填由提交闸兜着,卡回来时一定是齐的。 + +【这两张卡一单各只发一次】人交回来之后不许再发;答复里没有值(空 values)也算答过 —— 人在件上填的东西已经落草稿层。`, + }, + { + item: 'S1_input_confirm', + intent: '评估输入清单:绑定四项只读核对,缺口行交 PW 在件上补', + writes: [`${D}.input_gap`, `${D}.verdict`, 'demand.request'], + reads: [`${D}.input_gap`, `${D}.pn_check`, 'demand.request', 'parts.profile', 'parts.document', 'candidateInternalPns', 'originalInternalPn'], + view: 'eval-input-list', + tools: [], + /* 交人补缺口,不给 doneWhen;人工激活:发卡就算启动 */ + 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 写料号与在库状态); +· 待补两项(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.profile.normalized_parameter_set\`,值是「未披露」的参数各一行(item_key = unknown_<料号>_<序号>, + input_item 如「XL2596S-5.0 使能阈值」,source_ledger = "P-08",detail 写「原厂未披露(有而不全)」); +· 齐备项(status = "ok"):原 PN 资料版本(source_ledger = "P-10",detail 写 datasheet 版本)。 +写完同一拍照说明书⑤发执行工单,发完这一拍结束(挂起)。 + +【收口】人交回之后只做三件事,然后 finish: +① 读 \`${D}.input_gap\` 现值:把 application_scenario / bom_position 两行的 supplement 抄进 \`demand.request\`(同 PK 覆盖写这两列; + supplement 写「补不了」「UNKNOWN」之类的,这两列不写);人加的行(origin = "human-added")同等作数,写进 technical_constraints 一句汇总。 +② 写 \`${D}.verdict.input_confirmed_total = 1\`。 +③ finish 这一项。**别在这一拍做对照表** —— 那是下一步的活。 + +【这张卡一单只发一次】人交回来之后不许再发;缺必填有提交闸兜着。`, + }, + { + item: 'S2_normalize', + intent: '读取并标准化:每个未放弃的候选一条 P-11 主记录骨架 + 逐参数对照行', + writes: ['substitution.relation', 'substitution.difference_item'], + reads: ['parts.profile', 'parts.document', 'demand.request', `${D}.pn_check`, `${D}.input_gap`, `${D}.verdict`, 'candidateInternalPns', 'originalInternalPn'], + view: 'difference-review', + tools: [], + doneWhen: + `substitution_relation_rows > 0 and substitution_relation_rows = count(candidateInternalPns) - ${D}_pn_check_exists_status_abandoned and ` + + 'substitution_difference_item_rows > 0 and substitution_difference_item_distinct_substitution_relation_id = substitution_relation_rows', + guide: `输入齐了,**把多来源资料整成可比对的对照表**,一拍连做(先写关系,再写明细 —— 明细引用关系 id,顺序反了会被拒)。 + +【每个未放弃的候选一条 \`substitution.relation\` 骨架】(\`${D}.pn_check\` 里 exists_status 是 found / created 的候选;abandoned 的不写) +substitution_relation_id = "REL-<案号数字串>-01/02…"(按候选数组顺序)、eval_ref、request_id、original_internal_pn、candidate_internal_pn、 +substitution_type = "pin_to_pin"(人在评估输入清单里改了替代类型就照改:功能替代 → functional、参数替代 → parametric)、 +analysis_status = "ai_draft"、rule_version = "${RULE_VERSION}"、bom_position(照 demand.request 现值抄,没有不写)。 +recommendation_level / recommendation_reason / difference_summary / risk_level **这一步一格都不写**(S3 的活)。 + +【每条关系逐参数写 \`substitution.difference_item\` 对照行】骨架按**原 PN 的参数集**(\`parts.profile.normalized_parameter_set\`,一行一参数 +「参数名 | 值 | 单位 | hard/soft | 说明」):原 PN 有的每个参数一行 —— difference_item_id = "<关系id>-P01/P02…"、parameter_name、 +original_value、candidate_value(候选参数集里同名那一行的值;候选没有这个参数 → 写「未披露」)、parameter_unit(以原 PN 为准;候选单位不同先换算, +换算不了原值照写、evidence_ref 里注明)、constraint_type(原 PN 那一行的 hard/soft;没标的按口径:封装 / 工艺 / 电压 / 电流 / 温度 / 阈值 hard,其余 soft)、 +rule_version = "${RULE_VERSION}"、evidence_ref(值从哪来:「P-08 · P-10 <版本>」;本单数据工单建的料号写「人工录入 · 数据工单 · P-08」)。 +另加两行**封装**与**生命周期**(constraint_type = hard / soft,值分别取 \`package\` 与 \`lifecycle_status\`)。 +difference_status / compare_result / risk_level / impact_statement / unknown_disposition **这一步不写**。 + +**缺参数不得伪造**:候选那一侧查不到就写「未披露」。写完引擎自己收这一项(每条关系都有明细行才算)。`, + }, + { + item: 'S3_assess', + intent: '逐项差异评估:四态与推荐等级由工具算,证据 / 影响 / UNKNOWN 去向由你写', + writes: ['substitution.relation', 'substitution.difference_item', 'validation.record', `${D}.verdict`], + reads: ['substitution.relation', 'substitution.difference_item', 'demand.request', `${D}.input_gap`, `${D}.verdict`], + view: 'difference-review', + tools: [TOOL_COMPARE, TOOL_RECOMMEND], + doneWhen: + 'substitution_difference_item_missing = 0 and substitution_relation_missing = 0 and ' + + 'substitution_difference_item_difference_status_match + substitution_difference_item_difference_status_mismatch + ' + + 'substitution_difference_item_difference_status_unknown + substitution_difference_item_difference_status_conditional = substitution_difference_item_rows and ' + + 'tool_part_substitution_assessment_compare_parameters_calls > 0 and tool_part_substitution_assessment_recommend_calls > 0', + guide: `对照表铺好了,**固定三拍**:比对 → 定级 → 写盘。**工具调用后同拍其余动作作废**,别把工具和写台账串在一起;两枚工具各只调一次。 + +【第一拍:只调 \`${TOOL_COMPARE}\`,别的什么都不写】把**全部**关系一次打包: +{"candidates":[{"candidateInternalPn":"<候选>","pairs":[{"parameterName":"输入电压范围","originalValue":"4.5~40","candidateValue":"4.5~32", + "unit":"V","constraintType":"hard","requirement":">= 40","evidenceRef":"P-08 · P-10 Rev H"}, …]}, …]} +· pairs 逐条照 \`substitution.difference_item\` 抄(parameterName / originalValue / candidateValue / unit / constraintType / evidenceRef); +· **客户在应用场景里给了边界的参数才写 requirement**(如「输入上限 40V」→ 输入电压范围那一对写 ">= 40";「工业温域」→ 工作温度写 "-40~85"); +· 每一对都带 evidenceRef —— 硬约束没有证据引用工具不判 MATCH,那正是要的。 +调完这一拍结束。 + +【第二拍:只调 \`${TOOL_RECOMMEND}\`】把第一拍出参里每个候选的 counts 原样传:{"candidates":[{"candidateInternalPn":"…","counts":{…}}, …]}。调完结束。 + +【第三拍:写盘,一拍连做,一次都不再调工具】 +① 逐条 \`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)。 +② 每个 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 扩写成人话, + 引具体参数)、difference_summary(「7 项比对:4 MATCH / 1 MISMATCH / 1 UNKNOWN / 1 CONDITIONAL」)、analysis_report_ref = "RPT-<案号数字串>"、 + confidence_score(取该候选各行 confidence 的最小值);recommendation_level = conditional 的行**必须**写 verification_requirement(列出 VAL 号)、validation_id。 +④ \`${D}.verdict\` 同 PK 覆盖写:relation_total、recommended_total / conditional_total / alternate_total / not_recommended_total(数关系表)。 +写完引擎自己收这一项;下一项是审核(S4),**这一拍不要发卡**。 + +**不许把工具的结论改成别的**:不推荐就是不推荐;要改只能补证据后重比。`, + }, + { + item: 'S4_review', + intent: '差异评估结论审核:决策卡循环,通过或驳回 + AI 按意见改台账重发', + writes: ['substitution.relation', 'substitution.difference_item', 'validation.record', `${D}.verdict`], + reads: ['substitution.relation', 'substitution.difference_item', 'validation.record', `${D}.verdict`], + view: 'difference-review', + tools: [], + /* 要人拍板,且这一项内部可能循环 —— 不给 doneWhen */ + guide: `结论算出来了,**该 FAE/RI 审核了** —— 这是验收工单 01,走决策卡(不是终局验收)。**先分岔,再动作**: +轨迹里找这一项已答过、还没处理的 [card-reply]: +· 没有 → 照说明书⑥发第一张决策卡(ask 里把候选数、比对条数、四态各几条、逐候选推荐等级说全),发完这一拍结束。 +· "approve" → 写 \`${D}.verdict.review_approved_total = 1\`,finish 这一项。别再发卡。 +· "reject" / "custom:…" → **驳回**。按意见改台账(同 PK 覆盖写,不开新行、不改行数): + · 意见点名某个参数 → 改那一行 \`substitution.difference_item\` 的 impact_statement / unknown_disposition / evidence_ref;要挂验证的补一行 \`validation.record\`; + · 意见说推荐理由站不住 → 改 \`substitution.relation.recommendation_reason\`(等级不许改 —— 那是工具算的;要改先回 S3 补证据重比,本活动不回头,写清楚为什么维持); + · 没写具体意见("reject")→ 自己挑最站不住的一处改实(证据最含糊的那条 evidence_ref、去向最空的那条 unknown_disposition),**至少改对一处**; + 然后**换一种问法**再发一张决策卡(把改了什么写进 ask),发完结束。 +**基座对着字面比对意见与草稿**:观察里点名了哪一行,就改那一行,不挪到别的行头上。`, + }, + { + item: 'S5_prepare_landing', + intent: '拟落账:P-11 分析状态推到 FAE 确认,回写 P-01 五项', + writes: ['substitution.relation', 'demand.request', `${D}.verdict`], + reads: ['substitution.relation', 'substitution.difference_item', 'validation.record', 'demand.request', `${D}.verdict`], + view: 'landing-confirm', + tools: [], + doneWhen: + 'landing_prepared_total > 0 and substitution_relation_analysis_status_fae_confirmed = substitution_relation_rows and ' + + 'substitution_relation_missing = 0 and demand_request_missing = 0', + guide: `审核通过了,**把拟落账的内容备好**,一拍连做: +① 每条 \`substitution.relation\` 同 PK 覆盖写 analysis_status = "fae_confirmed"(只推分析层;客户批准状态你没有写权,别碰)。 +② \`demand.request\` 同 PK 覆盖写五项 + 状态:risk_level(取各候选 risk_level 里最高的一档)、verification_requirement(把所有 VAL 号与要测什么列全; + 没有 UNKNOWN 写「无需验证」)、output_ref = "RPT-<案号数字串>"、next_action = "提交 ACT-GATE-03 评审放行"、current_activity = "ACT-WORK-05"、 + current_stage = "gate03"、request_status = "responded"。 +③ \`${D}.verdict\` 同 PK 覆盖写:landing_prepared_total = 1、summary 一句人话(几个候选各什么等级、挂了几个验证任务)。 +写完引擎自己收这一项,下一项是放行。**这一拍不要 submit**。`, + }, + { + item: 'T_release', + intent: '拟落账内容已备好,提交 ACT-GATE-03 交 FAE/RI 放行;被打回就改台账再交', + /* 被打回重做时要改的就是这几张 —— 不声明就写不进去 */ + 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。**先看轨迹里有没有「验收被打回重做」**: +有 → 这一拍不是再提交一遍。照意见改**台账本身**(同 PK 覆盖写,不开新行): +· 他说推荐等级与明细不符 → 核对 \`substitution.relation.recommendation_level\` 与该关系明细的四态计数,不符的把 recommendation_reason 改实、把漏挂的验证要求补上; +· 他说 P-01 回写不全 → 补 \`demand.request\` 那五项; +· 他说证据不可信 → 改那几行 \`substitution.difference_item.evidence_ref\`(指到具体资料版本与页码); +· 没写具体意见 → 自己挑最站不住的一处改实,至少改对一处; +改完这一拍再 judge + submit。**基座那道闸:打回之后台账一个字没改就不许再提交。** + +没被打回(或已改过)→ 先裁三条裁量判据,然后 submit,同一拍连做: +· judge criterion="证据链可信:每条结论都能追溯到 P-08 / P-10 / P-06 的具体记录,或本单数据工单的创建记录(人工录入)" —— 逐条看 evidence_ref 指不指得到具体记录; +· judge criterion="硬约束项未被自动判 MATCH:封装 / 工艺 / 电气边界等硬约束的 MATCH 都有证据支持" —— 看 hard 且 match 的行 evidence_ref 是否非空且具体; +· judge criterion="推荐等级与差异明细一致:存在 UNKNOWN / CONDITIONAL 的候选没有给「推荐」" —— 逐候选核对; +**裁成不满足照样能提交** —— 理由连着卡一起交给签字的人。criterion 照判据名整句原样抄。 +这一单没有分支去向,只发一口 \`assessment-released\`,不必你选出口。终局唯放行:你不能自己宣布完成。`, + }, +]; + +/** + * 阶段 —— 进度条的刻度,只给人看。除放行外每个计划项恰属一个阶段; + * 放行那一格由基座统一加,人看到的是 **x/6** —— 与文档的步骤 0.5 / 1 / 2 / 3 / 4 / 5 六步逐格对齐: + * 0.5 启动与存在性检查(等待数据)· 1 建立评估上下文 · 2 读取并标准化 · 3 逐项差异评估 · 4 FAE 审核与补证(含拟落账)· 5 提交评审放行(基座那格) + */ +export const ASSESSMENT_PHASES = [ + { name: '启动与存在性检查', items: ['S0_bind', 'S0d_master_data'] }, + { name: '建立评估上下文', items: ['S1_input_confirm'] }, + { name: '读取并标准化资料', items: ['S2_normalize'] }, + { name: '逐项差异评估', items: ['S3_assess'] }, + { name: 'FAE 审核与补证', items: ['S4_review', 'S5_prepare_landing'] }, +]; +``` + +- [ ] **Step 2: 写剧本自测 `__tests__/steps.test.ts`** + +```ts +/** + * 步骤剧本与呈现面 —— 这段错了不报错,只是活动开始转圈 / 界面开始撒谎(与 E04 / I01 同一套惯例)。 + */ +import { describe, expect, it } from 'vitest'; +import { evaluateCondition } from '@sfeel'; +import { ASSESSMENT_PHASES, ASSESSMENT_STEPS, CMMN_XML, NARRATIVE } from '../src/case-model.ts'; +import { manifest, D } from '../src/manifest.ts'; + +const ITEM_IDS = [...CMMN_XML.matchAll(/<(?:humanTask|decisionTask|stage|milestone)\s+id="([^"]+)"/g)].map((m) => m[1] ?? ''); + +function doneOf(item: string): string { + const found = ASSESSMENT_STEPS.find((s) => s.item === item)?.doneWhen; + if (found === undefined) throw new Error(`${item} 没有 doneWhen`); + return found; +} +/** 谓词写坏了引擎当作「没算出来」——测里也照这个口径 */ +function safeEval(expression: string, scope: Record): boolean { + try { + return evaluateCondition(expression, scope); + } catch { + return false; + } +} +/** 开局局面:入参 + 所有基座读数归零 */ +const fresh = (input: Record): Record => ({ + ...input, + demand_request_rows: 0, + demand_request_missing: 0, + [`${D}_verdict_rows`]: 0, + [`${D}_pn_check_rows`]: 0, + [`${D}_pn_check_missing`]: 0, + [`${D}_pn_check_exists_status_absent`]: 0, + [`${D}_pn_check_exists_status_abandoned`]: 0, + [`${D}_input_gap_rows`]: 0, + [`${D}_input_gap_missing`]: 0, + substitution_relation_rows: 0, + substitution_relation_missing: 0, + substitution_relation_analysis_status_fae_confirmed: 0, + substitution_difference_item_rows: 0, + substitution_difference_item_missing: 0, + substitution_difference_item_distinct_substitution_relation_id: 0, + substitution_difference_item_difference_status_match: 0, + substitution_difference_item_difference_status_mismatch: 0, + substitution_difference_item_difference_status_unknown: 0, + substitution_difference_item_difference_status_conditional: 0, + tool_part_substitution_assessment_compare_parameters_calls: 0, + tool_part_substitution_assessment_recommend_calls: 0, +}); + +describe('剧本与案例模型对得上', () => { + it('每一条剧本都指向一个真实存在的计划项,图上每一步也都有剧本', () => { + for (const step of ASSESSMENT_STEPS) expect(ITEM_IDS, `剧本里的 ${step.item} 在 CMMN 里不存在`).toContain(step.item); + for (const id of ITEM_IDS) expect(ASSESSMENT_STEPS.map((s) => s.item), `计划项 ${id} 没有剧本`).toContain(id); + }); + + it('清单里挂的就是这份剧本与这份阶段', () => { + expect(manifest.assets.businessModel.steps).toBe(ASSESSMENT_STEPS); + expect(manifest.assets.businessModel.phases).toBe(ASSESSMENT_PHASES); + }); +}); + +describe('剧本声明的读写面 ⊆ 清单声明', () => { + const declaredAddresses = new Set(manifest.declarations.ledger.map((d) => `${d.domain}.${d.object}`)); + const declaredTools = new Set(manifest.declarations.tools.map((t) => t.name)); + + it('steps[].writes / reads(台账那几个)全落在声明过的对象上', () => { + for (const step of ASSESSMENT_STEPS) { + for (const address of step.writes) expect(declaredAddresses.has(address), `${step.item} 要写 ${address}`).toBe(true); + for (const address of step.reads.filter((r) => r.includes('.') && !r.startsWith('case:'))) { + expect(declaredAddresses.has(address), `${step.item} 要读 ${address}`).toBe(true); + } + } + }); + + it('steps[].tools 全部是声明过的工具;只有 S3 调工具', () => { + for (const step of ASSESSMENT_STEPS) { + for (const tool of step.tools) expect(declaredTools.has(tool), `${step.item} 要调 ${tool}`).toBe(true); + if (step.item !== 'S3_assess') expect(step.tools, step.item).toEqual([]); + } + expect(ASSESSMENT_STEPS.find((s) => s.item === 'S3_assess')?.tools).toHaveLength(2); + }); + + it('customer_approval_status 在任何剧本里都不许写(只读边界)', () => { + for (const step of ASSESSMENT_STEPS) expect(step.guide, step.item).not.toMatch(/customer_approval_status\s*=/); + }); +}); + +describe('doneWhen —— 开局不许真,干完必须真', () => { + it('**开局一步都不该被自动收掉** —— 五条样例的入参形状逐一试', () => { + for (const sample of manifest.samples) { + for (const step of ASSESSMENT_STEPS) { + if (step.doneWhen === undefined) continue; + expect(safeEval(step.doneWhen, fresh(sample.input as Record)), `样例「${sample.name}」下 ${step.item} 开局就为真`).toBe(false); + } + } + }); + + it('S0:两颗候选 + 原 PN = 3 行核对齐、需求与结论都写了 → 收得掉;少写一颗 → 收不掉', () => { + const base = { candidateInternalPns: ['XL2596S-5.0', 'LM2596S-ADJ'], demand_request_rows: 1, demand_request_missing: 0, [`${D}_verdict_rows`]: 1, [`${D}_pn_check_missing`]: 0 }; + expect(safeEval(doneOf('S0_bind'), { ...base, [`${D}_pn_check_rows`]: 3 })).toBe(true); + expect(safeEval(doneOf('S0_bind'), { ...base, [`${D}_pn_check_rows`]: 2 })).toBe(false); + }); + + it('S2:未放弃的候选各一条关系且每条都有明细 → 收得掉;放弃一颗后关系数随之减一', () => { + const two = { candidateInternalPns: ['A', 'B'], [`${D}_pn_check_exists_status_abandoned`]: 0, substitution_difference_item_rows: 14 }; + expect(safeEval(doneOf('S2_normalize'), { ...two, substitution_relation_rows: 2, substitution_difference_item_distinct_substitution_relation_id: 2 })).toBe(true); + expect(safeEval(doneOf('S2_normalize'), { ...two, substitution_relation_rows: 2, substitution_difference_item_distinct_substitution_relation_id: 1 })).toBe(false); + expect(safeEval(doneOf('S2_normalize'), { ...two, [`${D}_pn_check_exists_status_abandoned`]: 1, substitution_relation_rows: 1, substitution_difference_item_distinct_substitution_relation_id: 1 })).toBe(true); + }); + + it('S3:四态之和 = 明细行数、必填齐、两枚工具都调过 → 收得掉;工具没调过 → 收不掉', () => { + const ok = { + substitution_difference_item_missing: 0, + substitution_relation_missing: 0, + substitution_difference_item_rows: 7, + substitution_difference_item_difference_status_match: 4, + substitution_difference_item_difference_status_mismatch: 1, + substitution_difference_item_difference_status_unknown: 1, + substitution_difference_item_difference_status_conditional: 1, + tool_part_substitution_assessment_compare_parameters_calls: 1, + tool_part_substitution_assessment_recommend_calls: 1, + }; + expect(safeEval(doneOf('S3_assess'), ok)).toBe(true); + expect(safeEval(doneOf('S3_assess'), { ...ok, tool_part_substitution_assessment_recommend_calls: 0 })).toBe(false); + expect(safeEval(doneOf('S3_assess'), { ...ok, substitution_difference_item_difference_status_match: 3 })).toBe(false); + }); + + it('S5:旗 + 全部关系 FAE 确认 + P-01 回写齐 → 收得掉;缺一头都收不掉', () => { + const ok = { landing_prepared_total: 1, substitution_relation_analysis_status_fae_confirmed: 2, substitution_relation_rows: 2, substitution_relation_missing: 0, demand_request_missing: 0 }; + expect(safeEval(doneOf('S5_prepare_landing'), ok)).toBe(true); + expect(safeEval(doneOf('S5_prepare_landing'), { ...ok, substitution_relation_analysis_status_fae_confirmed: 1 })).toBe(false); + expect(safeEval(doneOf('S5_prepare_landing'), { ...ok, demand_request_missing: 1 })).toBe(false); + }); +}); + +describe('说明书与剧本的骨架', () => { + it('五张卡的骨架都住在说明书里(压拍之后只有说明书还在)', () => { + expect(NARRATIVE).toContain('"kind":"entry","itemId":"S0_bind"'); + expect(NARRATIVE).toContain('"kind":"decide","itemId":"S0d_master_data"'); + expect(NARRATIVE).toContain('"title":"数据工单 · 芯片主数据创建(P-08)'); + expect(NARRATIVE).toContain('"title":"数据工单 · 原厂技术资料创建(P-10)'); + expect(NARRATIVE).toContain('"title":"评估输入与资料补齐确认'); + expect(NARRATIVE).toContain('"kind":"decide","itemId":"S4_review"'); + }); + + it('剧本里不引反例原话;S3 固定三拍且写明「工具后同拍作废」', () => { + const s3 = ASSESSMENT_STEPS.find((s) => s.item === 'S3_assess')?.guide ?? ''; + expect(s3).toContain('第一拍'); + expect(s3).toContain('第二拍'); + expect(s3).toContain('第三拍'); + expect(s3).toContain('工具调用后同拍其余动作作废'); + }); + + it('S0 剧本写明库里都有就 disable S0d;S0d 剧本写明候选 ≥2 才问、=1 必建、原 PN 硬阻塞', () => { + const s0 = ASSESSMENT_STEPS.find((s) => s.item === 'S0_bind')?.guide ?? ''; + expect(s0).toContain('disable S0d_master_data'); + const s0d = ASSESSMENT_STEPS.find((s) => s.item === 'S0d_master_data')?.guide ?? ''; + expect(s0d).toContain('硬阻塞'); + expect(s0d).toContain('候选只有 1 颗'); + }); + + it('五段进度,每段名 ≤10 字;除放行外每项恰属一段', () => { + expect(ASSESSMENT_PHASES).toHaveLength(5); + for (const p of ASSESSMENT_PHASES) expect(p.name.length, p.name).toBeLessThanOrEqual(10); + const all = ASSESSMENT_PHASES.flatMap((p) => p.items); + expect(new Set(all).size).toBe(all.length); + expect([...all].sort()).toEqual([...ITEM_IDS.filter((i) => i !== 'T_release')].sort()); + }); +}); +``` + +- [ ] **Step 3: 跑包内测试** + +Run: `pnpm vitest run activities/part-substitution-assessment` +Expected:steps.test 全绿;activity.test 里除「报告模版」组外全绿(模版仍是占位)。 + +- [ ] **Step 4: 提交** + +```bash +git add activities/part-substitution-assessment +git commit -m "feat(w05): 差异评估案例模型 —— CMMN 八项七哨兵、说明书五卡骨架、八条剧本、五段进度 + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` + +--- + +### Task 5: 五张 HTML 模版(目标件平移) + +**Files:** +- Replace: `activities/part-substitution-assessment/src/views/report.ts`(整份替换 Task 3 的占位) +- Test: `activities/part-substitution-assessment/__tests__/activity.test.ts`(Task 3 已写好的「报告模版」组此时转绿) + +**Interfaces:** +- Consumes:Task 3 的台账列名 / tally 值;基座读数名 `parts_profile_rows / parts_profile_missing / parts_profile_missing_keys`、`parts_document_*`、`sub_eval_input_gap_*`、`substitution_relation_rows`、`substitution_difference_item_rows`、四态计数;引擎注入 `case:owner` / `case:taskId`;摊平列 `eval_ref` / `request_id` / `rule_version` / `original_internal_pn`;入参 `customerCode` / `requestDescription` / `originalInternalPn`。 +- Produces:`P08_MASTER_CREATE` / `P10_DOCUMENT_CREATE` / `EVAL_INPUT_LIST` / `DIFFERENCE_REVIEW` / `LANDING_CONFIRM`(字符串)。 +- 平移法(《HTML报告生成规范》§7):目标件 `docs/SS02-ACT-WORK-05-html/*.html` 的版面语言原样搬(它与 E04/I01 目标件同一套壳,`prd-style.ts` 已逐字节持有);把设计稿记号换成平台绑定:`data-rows="in.*/out.*"` → `data-rows="mine:域.对象"`;`data-field` → `data-slot` + 可改格加 `data-col`;`meta.task.owner/id` → `case:owner/case:taskId`;`sd.*` 读数 → 基座读数;`card.deadline`(本仓没有工单期限这个事实)与 `card.id` 单号**不摆**;目标件里 JS 造的行 / 算的计数一律换成 `data-rows` + 基座读数。 + +- [ ] **Step 1: 写 `src/views/report.ts`** + +先把 I01 `activities/brand-content-toolkit/src/views/report.ts` 里 `const BIND_CSS = \`` 到它的闭合反引号(`/** 三层壳照目标件 */` 之前)整段**原样复制**过来作为本文件的 `BIND_CSS`(数据驱动的开关全在那里:三档占位 / 缺项红带 / 编辑闸 / 增删行 / 状态徽基座);然后按下面写其余内容: + +```ts +/** + * ACT-WORK-05 差异评估的 HTML 报告模版 —— **活动自己持有的资产**,随包版本冻结。 + * + * 五个局面各一张: + * · `p08-master-create` —— S0d 摆,数据工单(P-08 芯片主数据),可加行、可编辑; + * · `p10-document-create` —— S0d 摆,数据工单(P-10 原厂技术资料),可加行、可编辑; + * · `eval-input-list` —— S0/S1 摆,评估输入清单:绑定区只读 + 缺口表可编辑; + * · `difference-review` —— S2/S3/S4 摆,各候选推荐等级 + 差异明细,**只读**(通过/驳回在对话区决策卡); + * · `landing-confirm` —— S5/T_release 摆,拟写 P-11 / 拟回写 P-01,**只读**,放行后即归档件。 + * + * 版面语言逐字量自 `docs/SS02-ACT-WORK-05-html/` 四份目标件;取值三纪律:顶层只取汇总列与单值列, + * 逐条走 `mine:域.对象`,列名逐字照 `declarations.ledger` 抄。**模版不算不判**。 + */ +import { PRD_CSS } from './prd-style.ts'; +import { D } from '../constants.ts'; + +/* ==================== 绑定期补的样式(从 I01 report.ts 的 BIND_CSS 原样复制)==================== */ +const BIND_CSS = `…(I01 那一段,原样)…`; + +/* ==================== 本活动的枚举徽标与参数集格子 ==================== */ +const EXTRA_CSS = ` +/* 参数集一行一参数:只读那一份按行换行显示 */ +.proto-root .jc-pre { white-space: pre-line; font-family: var(--mono); font-size: 12px; } +.proto-root textarea.jc-cell { min-height: 120px; font-family: var(--mono); font-size: 12px; } +/* 徽标按列作用域取人读名(同一个 v-conditional 在推荐等级与四态里是两个词) */ +.proto-root .jc-tag.t-role.v-original::after { content: "原 PN"; } +.proto-root .jc-tag.t-role.v-candidate::after { content: "候选"; } +.proto-root .jc-tag.t-exist.v-found::after { content: "在库"; } +.proto-root .jc-tag.t-exist.v-absent::after { content: "不在库"; } +.proto-root .jc-tag.t-exist.v-created::after { content: "本单新建"; } +.proto-root .jc-tag.t-exist.v-abandoned::after { content: "已放弃"; } +.proto-root .jc-tag.t-exist.v-absent { color: var(--danger); background: var(--danger-soft); } +.proto-root .jc-tag.t-exist.v-created { color: var(--ok); background: var(--ok-soft); } +.proto-root .jc-tag.t-gap.v-gap::after { content: "待补*"; } +.proto-root .jc-tag.t-gap.v-default::after { content: "默认·可改"; } +.proto-root .jc-tag.t-gap.v-ok::after { content: "齐备"; } +.proto-root .jc-tag.t-gap.v-gap { color: var(--danger); background: var(--danger-soft); } +.proto-root .jc-tag.t-gap.v-ok { color: var(--ok); background: var(--ok-soft); } +.proto-root .jc-tag.t-life.v-active::after { content: "Active"; } +.proto-root .jc-tag.t-life.v-nrnd::after { content: "NRND"; } +.proto-root .jc-tag.t-life.v-eol::after { content: "EOL"; } +.proto-root .jc-tag.t-life.v-discontinued::after { content: "停产"; } +.proto-root .jc-tag.t-life.v-risk::after { content: "风险"; } +.proto-root .jc-tag.t-doc.v-datasheet::after { content: "Datasheet"; } +.proto-root .jc-tag.t-doc.v-app_note::after { content: "应用笔记"; } +.proto-root .jc-tag.t-doc.v-reference_design::after { content: "参考设计"; } +.proto-root .jc-tag.t-doc.v-white_paper::after { content: "白皮书"; } +.proto-root .jc-tag.t-doc.v-sample_doc::after { content: "样片资料"; } +.proto-root .jc-tag.t-doc.v-commitment_letter::after { content: "承诺函"; } +.proto-root .jc-tag.t-doc.v-other::after { content: "其他"; } +.proto-root .jc-tag.t-docst.v-valid::after { content: "有效"; } +.proto-root .jc-tag.t-docst.v-expired::after { content: "过期"; } +.proto-root .jc-tag.t-docst.v-pending::after { content: "待核实"; } +.proto-root .jc-tag.t-docst.v-void::after { content: "作废"; } +.proto-root .jc-tag.t-sub.v-pin_to_pin::after { content: "Pin-to-Pin"; } +.proto-root .jc-tag.t-sub.v-functional::after { content: "功能替代"; } +.proto-root .jc-tag.t-sub.v-parametric::after { content: "参数替代"; } +.proto-root .jc-tag.t-sub.v-other::after { content: "其他"; } +.proto-root .jc-tag.t-rec.v-recommended::after { content: "推荐"; } +.proto-root .jc-tag.t-rec.v-conditional::after { content: "条件推荐"; } +.proto-root .jc-tag.t-rec.v-alternate::after { content: "备选"; } +.proto-root .jc-tag.t-rec.v-not_recommended::after { content: "不推荐"; } +.proto-root .jc-tag.t-rec.v-recommended { color: var(--ok); background: var(--ok-soft); } +.proto-root .jc-tag.t-rec.v-conditional { color: var(--warn-accent); background: var(--warn-soft); } +.proto-root .jc-tag.t-rec.v-not_recommended { color: var(--danger); background: var(--danger-soft); } +.proto-root .jc-tag.t-anal.v-ai_draft::after { content: "AI 草稿"; } +.proto-root .jc-tag.t-anal.v-fae_confirmed::after { content: "FAE 确认"; } +.proto-root .jc-tag.t-anal.v-pending_customer::after { content: "待客户验证"; } +.proto-root .jc-tag.t-anal.v-customer_approved::after { content: "客户已批准"; } +.proto-root .jc-tag.t-anal.v-rejected::after { content: "不批准"; } +.proto-root .jc-tag.t-anal.v-void::after { content: "作废"; } +.proto-root .jc-tag.t-anal.v-fae_confirmed { color: var(--ok); background: var(--ok-soft); } +.proto-root .jc-tag.t-diff.v-match::after { content: "MATCH"; } +.proto-root .jc-tag.t-diff.v-mismatch::after { content: "MISMATCH"; } +.proto-root .jc-tag.t-diff.v-unknown::after { content: "UNKNOWN"; } +.proto-root .jc-tag.t-diff.v-conditional::after { content: "CONDITIONAL"; } +.proto-root .jc-tag.t-diff.v-match { color: var(--ok); background: var(--ok-soft); } +.proto-root .jc-tag.t-diff.v-mismatch { color: var(--danger); background: var(--danger-soft); } +.proto-root .jc-tag.t-diff.v-unknown { color: var(--ink-3); background: var(--soft); } +.proto-root .jc-tag.t-diff.v-conditional { color: var(--warn-accent); background: var(--warn-soft); } +.proto-root .jc-tag.t-con.v-hard::after { content: "硬约束"; } +.proto-root .jc-tag.t-con.v-soft::after { content: "软约束"; } +.proto-root .jc-tag.t-con.v-hard { color: var(--danger); background: var(--danger-soft); } +.proto-root .jc-tag.t-risk.v-low::after { content: "低"; } +.proto-root .jc-tag.t-risk.v-medium::after { content: "中"; } +.proto-root .jc-tag.t-risk.v-high::after { content: "高"; } +.proto-root .jc-tag.t-risk.v-critical::after { content: "关键"; } +.proto-root .jc-tag.t-risk.v-low { color: var(--ok); background: var(--ok-soft); } +.proto-root .jc-tag.t-risk.v-medium { color: var(--warn-accent); background: var(--warn-soft); } +.proto-root .jc-tag.t-risk.v-high, .proto-root .jc-tag.t-risk.v-critical { color: var(--danger); background: var(--danger-soft); } +/* 绑定区:四项锁定的灰底只读行 */ +.proto-root .jc-bound { display: grid; grid-template-columns: 140px 1fr; gap: 6px 14px; padding: 10px 14px; margin: 8px 0 14px; background: var(--soft); border: 1px solid var(--line-soft); border-radius: 9px; font-size: 13px; } +.proto-root .jc-bound b { color: var(--ink-3); font-weight: 600; } +`; + +/** 三层壳照目标件(收件人 / 关联任务两格由引擎注入;目标件的「最迟交回」与单号本仓没有真数据源,不摆) */ +const doc = (input: { tone: 'human' | 'ledger'; badge: string; tag: string; kicker: string; title: string; step: string; state: string; body: string; foot: string }): string => + ` +
+
+
+
${input.badge}
+
+ ${input.kicker} +

${input.title}

+
收件人: · 关联任务:
+
+
+ ${input.tag} + ${input.step} + ${input.state} +
+
+
${input.body}
+
${input.foot}
+
+
`; + +const part = (label: string, aside = ''): string => `

【${label}】${aside === '' ? '' : ` —— ${aside}`}

`; +const criteria = (title: string, items: readonly string[]): string => + `
${title}
    ${items.map((i) => `
  • ${i}
  • `).join('')}
`; +const flow = (text: string): string => `
${text}
`; +/** 页脚右侧「工单类型:× · <案号>」 */ +const sign = (note: string, tag: string): string => `${note}${tag} · `; +const editGate = (address: string, label: string): string => `
+ ${label} + + + +
`; +const gapbar = (object: string, what: string): string => `
+ 还差 行${what}没填 —— 。按「编辑」开表,红框那几格就是要填的。补齐了才确认得了。 +
`; +const tag = (scope: string, col: string): string => ``; +const cell = (col: string, cls = ''): string => ``; +const ro = (col: string, cls = ''): string => ``; +const select = (col: string, options: ReadonlyArray, required = false): string => + ``; + +/* ==================== 局面 0 · 数据工单 P-08 ==================== */ + +const P08_TABLE = `
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
内部料号品牌制造商规格描述封装工艺生命周期EOL / PCNDatasheet 引用Datasheet 版本适配场景标准化参数集(一行一参数:参数名 | 值 | 单位 | hard/soft | 说明)
${cell('internal_pn', 'mono')}${cell('brand_name')}${cell('manufacturer_name')}${cell('specification_description')}${cell('package', 'mono')}${cell('process_technology')}${select('lifecycle_status', [['active', 'Active'], ['nrnd', 'NRND'], ['eol', 'EOL'], ['discontinued', '停产'], ['risk', '风险']], true)}${tag('t-life', 'lifecycle_status')}${cell('eol_pcn_status')}${cell('datasheet_ref', 'mono')}${cell('datasheet_version')}${cell('application_scenarios')}
`; + +export const P08_MASTER_CREATE = doc({ + tone: 'human', + badge: '数据', + tag: '数据工单', + kicker: 'SS02 · ACT-WORK-05 差异评估 · 数据工单(P-08)', + title: '芯片主数据创建', + step: '步骤 0.5 · 等待数据', + state: `待建 / 行`, + body: + part('做什么', '批量创建 P-08 主数据记录') + + `
+

模板定位:本单是「数据工单」的实例 —— 一单一类(可多条):目标台账 = 芯片主数据(P-08); + 字段即表头、每行一条新记录;标准化参数集是这条记录的一个字段(一行一参数)。

+

触发原因:系统按内部料号查芯片主数据查无记录 —— 是「完全没有」,不是字段缺失(那归执行工单)。

+

AI 预填:上传了 datasheet 的料号 AI 已按资料预填,标着值的格子请核对;没上传的料号全部手填,同为合法路径。

+

纪律:必填列全齐才允许确认;确认后这批记录进本单草稿,放行时并入正式库并留痕(确认人 = 数据责任人);只建主数据,验证记录等事实类不在此建。

+
` + + part('创建什么', '主数据记录(每行一条,*为必填列)') + + gapbar('parts_profile', '必填') + + editGate('parts.profile', `待建 条记录,点「编辑」核对预填、补齐空缺;「+ 加一行」= 再建一条`) + + `
必填列(*)全齐才允许确认;原厂未披露的参数值请直接写「未披露」,它会进入 UNKNOWN 并挂验证任务;删一行 = 该记录不建。改动只落任务草稿层,保存不等于放行。
` + + P08_TABLE + + `
+ + 新行的内部料号必须写(它是这一行的钥匙);其余必填列全部填写后才允许确认。 +
` + + flow('提交 / 确认:本工单只承载内容;确认请在对话区的【执行卡】中进行。确认后活动自动从「等待数据」唤醒。'), + foot: sign('SS02 · ACT-WORK-05 差异评估 · 数据工单 · 芯片主数据创建(P-08)', '工单类型:数据'), +}); + +/* ==================== 局面 0′ · 数据工单 P-10 ==================== */ + +const P10_TABLE = `
+ + + + + + + + + + + + + + + + + + + + + + + + +
关联料号序号资料类型品牌资料标题版本文件链接证据定位状态
${cell('internal_pn', 'mono')}${cell('doc_seq', 'mono')}${select('document_type', [['datasheet', 'Datasheet'], ['app_note', '应用笔记'], ['reference_design', '参考设计'], ['white_paper', '白皮书'], ['sample_doc', '样片资料'], ['commitment_letter', '承诺函'], ['other', '其他']], true)}${tag('t-doc', 'document_type')}${cell('brand_name')}${cell('document_title')}${cell('document_version')}${cell('document_ref', 'mono')}${cell('citation_locator')}${select('document_status', [['valid', '有效'], ['expired', '过期'], ['pending', '待核实'], ['void', '作废']], true)}${tag('t-docst', 'document_status')}
`; + +export const P10_DOCUMENT_CREATE = doc({ + tone: 'human', + badge: '数据', + tag: '数据工单', + kicker: 'SS02 · ACT-WORK-05 差异评估 · 数据工单(P-10)', + title: '原厂技术资料创建', + step: '步骤 0.5 · 等待数据', + state: `待建 / 行`, + body: + part('做什么', '为本单新建的料号登记原厂资料(P-10)') + + `
+

触发原因:按内部料号查原厂技术资料库查无记录。一份资料一行;同一料号多份资料序号 01/02… 自己续。

+

为什么要它:差异明细的证据引用要指到具体资料的版本与页码 —— 没有这一行,结论就追溯不到来源。

+
` + + part('创建什么', '资料记录(每行一条,*为必填列)') + + gapbar('parts_document', '必填') + + editGate('parts.document', `待建 条资料记录,点「编辑」补齐`) + + `
资料类型、标题、版本、文件链接、状态为必填;证据定位写页码或章节。改动只落任务草稿层,保存不等于放行。
` + + P10_TABLE + + `
+ + 关联料号与序号是这一行的钥匙,新行都要写;必填列全齐才允许确认。 +
` + + flow('提交 / 确认:本工单只承载内容;确认请在对话区的【执行卡】中进行。'), + foot: sign('SS02 · ACT-WORK-05 差异评估 · 数据工单 · 原厂技术资料创建(P-10)', '工单类型:数据'), +}); + +/* ==================== 局面 1 · 评估输入清单(S0/S1)==================== */ + +const PN_CHECK_TABLE = `
+ + + + + + + + + + + + + + +
料号角色在库状态备注 / 放弃原因
${ro('internal_pn', 'mono')}${tag('t-role', 'role')}${tag('t-exist', 'exists_status')}
`; + +const GAP_TABLE = `
+ + + + + + + + + + + + + + + + + + +
键输入项来源状态绑定值 / 缺口说明补充内容
${cell('item_key', 'mono')}${cell('input_item')}${cell('source_ledger', 'mono')}${select('status', [['gap', '待补'], ['default', '默认·可改'], ['ok', '齐备']])}${tag('t-gap', 'status')}${cell('detail')}
`; + +export const EVAL_INPUT_LIST = doc({ + tone: 'human', + badge: '执行', + tag: '执行工单', + kicker: 'SS02 · ACT-WORK-05 差异评估 · 执行工单 1/1', + title: '评估输入与资料补齐确认', + step: '步骤 1/5 · 建立评估上下文', + state: `待补 项`, + body: + part('做什么', '启动 4 项已绑定,其余信息在工单里补齐') + + `
+

AI 已完成:系统已按启动传入的 4 项完成绑定 —— 客户、客户需求(新建 P-01 · 版本 V1)、原 PN、候选 PN(数组,逐个评估)—— 并生成输入快照。

+

需要你补齐:启动时未收的信息(应用场景、BOM 位)直接在下表补;默认值(替代类型 / 规则版本 / 负责人期限)可确认可改。本单缺口只覆盖「有而不全」;「完全没有」(料号不在库)已由数据工单创建。未补齐的缺口项将按 UNKNOWN 进入差异评估。

+
` + + part('已绑定', '启动时冻结,工单内不可改 —— 绑错请退回启动人重新启动') + + `
+ 客户 + 客户需求 · V1 · + 原内部料号 + 规则版本 +
` + + PN_CHECK_TABLE + + part('交回什么', '评估输入清单(已绑定项只读核对,缺口项补齐,*为必填)') + + gapbar(`${D}_input_gap`, '补充内容') + + editGate(`${D}.input_gap`, `还有 项待补 / 待确认,点「编辑」在「补充内容」列填入`) + + `
加一行 = 补充 AI 未列出的资料项(新行的「输入项」与「补充内容」必填);删一行 = 该输入项不适用;补不了的写「留 UNKNOWN」或「挂验证任务」—— 那也是一个答案。改动只落任务草稿层,保存不等于放行。
` + + GAP_TABLE + + `
+ + 键写一个英文短名(如 thermal_note);「绑定值 / 缺口说明」栏写清这项资料是什么、AI 为什么拿不到。 +
` + + flow('审批 / 提交:本工单只承载内容;「提交」请在对话区的【执行卡】中进行。提交后去向:进入步骤 2 · AI 读取与标准化,仍缺失的项将按 UNKNOWN 进入差异评估。'), + foot: sign('SS02 · ACT-WORK-05 差异评估 · 执行工单 1/1 · 评估输入与资料补齐确认', '工单类型:执行'), +}); + +/* ==================== 局面 2 · 差异评估结论审核(S2/S3/S4,只读)==================== */ + +const RELATION_TABLE = `
+ + + + + + + + + + + + + + + + + + + + + + +
候选料号替代类型推荐等级推荐理由差异摘要风险验证要求置信度
${ro('candidate_internal_pn', 'mono')}${tag('t-sub', 'substitution_type')}${tag('t-rec', 'recommendation_level')}${ro('recommendation_reason')}${ro('difference_summary')}${tag('t-risk', 'risk_level')}${ro('verification_requirement')}${ro('confidence_score', 'mono')}
`; + +const ITEM_TABLE = `
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
关系参数项原 PN 值候选 PN 值单位约束类型结论风险证据引用影响说明UNKNOWN 去向置信度
${ro('substitution_relation_id', 'mono')}${ro('original_value', 'mono')}${ro('candidate_value', 'mono')}${ro('parameter_unit')}${tag('t-con', 'constraint_type')}${tag('t-diff', 'difference_status')}${tag('t-risk', 'risk_level')}${ro('evidence_ref', 'mono')}${ro('impact_statement')}${ro('unknown_disposition')}${ro('confidence', 'mono')}
`; + +export const DIFFERENCE_REVIEW = doc({ + tone: 'ledger', + badge: '验收', + tag: '验收工单', + kicker: 'SS02 · ACT-WORK-05 差异评估 · 验收工单 1/2', + title: '差异评估结论审核', + step: '步骤 3~4/5 · 逐项差异评估 + FAE/RI 审核', + state: `共 个候选 · 条比对`, + body: + part('AI 结论说明', '结论是怎么算出来的') + + `
+

AI 已完成的计算:按 GOV 评估规则(锁定版本 ),对原 PN + 与每个候选逐项比对,四态结论 —— MATCH / + MISMATCH / + UNKNOWN / + CONDITIONAL ,并给出风险等级、证据引用与置信度。 + 差异明细 = P-11 差异明细子表(一条参数一条记录);当前为 AI 草稿,审核通过前不落账。本单数据工单创建的主数据,其参数证据标注「人工录入」。

+
` + + part('验收判据', '你据此判断「通过 / 驳回」') + + criteria('判断这份差异评估结论是否可信,请逐项核对', [ + '关键参数差异均有明确状态:逐项结论无漏项,每个参数都有四态之一的判定;', + 'UNKNOWN 项均有去向:每个 UNKNOWN 都挂了验证任务或补来源动作,不允许悬空;', + '证据链完整:每项结论可追溯至 P-08 / P-10 / P-06 的具体记录,或本单数据工单的创建记录(人工录入);', + '硬约束项未被自动判 MATCH:封装 / 工艺等硬约束未经证据支持不得判 MATCH。', + ]) + + part('各候选推荐等级', '只读展示 · 每个候选一条 P-11 主记录草稿') + + RELATION_TABLE + + part('AI 产出的差异明细', '只读展示 · P-11 差异明细子表草稿') + + ITEM_TABLE + + flow('审批:本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。审核意见与补充条件写入 P-06(验证要求);通过后去向:进入步骤 5 · 评审放行与落账确认。'), + foot: sign('SS02 · ACT-WORK-05 差异评估 · 验收工单 1/2 · 差异评估结论审核', '工单类型:验收'), +}); + +/* ==================== 局面 3 · 评审放行与落账确认(S5/T_release,只读)==================== */ + +const P11_TABLE = `
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
替代关系ID替代类型原内部料号候选内部料号推荐等级推荐理由差异摘要替代分析报告分析状态客户批准状态风险验证要求证据可信度规则版本
${ro('substitution_relation_id', 'mono')}${tag('t-sub', 'substitution_type')}${ro('original_internal_pn', 'mono')}${ro('candidate_internal_pn', 'mono')}${tag('t-rec', 'recommendation_level')}${ro('recommendation_reason')}${ro('difference_summary')}${ro('analysis_report_ref', 'mono')}${tag('t-anal', 'analysis_status')}未提交 由审批流程维护,本活动不产生${tag('t-risk', 'risk_level')}${ro('verification_requirement')}${ro('confidence_score', 'mono')}${ro('rule_version', 'mono')}
`; + +const P01_TABLE = `
+ + + + + + + + + + + + + + + + + + + + +
需求编号风险等级验证要求输出链接下一步动作当前阶段需求状态
${ro('request_id', 'mono')}${tag('t-risk', 'risk_level')}${ro('verification_requirement')}${ro('output_ref', 'mono')}${ro('next_action')}${ro('current_stage', 'mono')}${ro('request_status', 'mono')}
`; + +export const LANDING_CONFIRM = doc({ + tone: 'ledger', + badge: '验收', + tag: '验收工单', + kicker: 'SS02 · ACT-WORK-05 差异评估 · 验收工单 2/2', + title: '评审放行与落账确认', + step: '步骤 5/5 · 提交 ACT-GATE-03', + state: `拟落账 条 P-11`, + body: + part('AI 结论说明', '本单确认什么') + + `
+

本活动以 ACT-GATE-03 评审放行为终点。本单确认拟落账内容:评估结论层写 P-11 替代关系主记录(每个候选一条)与差异明细子表,需求状态回写 P-01(风险等级 / 验证要求 / 输出链接 / 下一步动作 / 当前阶段)。 + 客户批准不属于本活动 —— 客户批准状态由审批流程维护,本单只写分析层(分析状态 = FAE 确认)。

+
` + + part('验收判据', '你据此判断「通过 / 驳回」') + + criteria('放行与落账前,请逐项核对', [ + '推荐等级与差异明细一致:存在 UNKNOWN / CONDITIONAL 的候选,推荐等级不得给「推荐」;', + '分析状态与客户批准状态分离:本单只写分析层(FAE 确认),客户批准状态保持「未提交」;', + 'GOV 规则版本已锁定:规则版本随结论一并落账;', + '回写 P-01 字段完整:风险等级、验证要求、输出链接、下一步动作、当前阶段五项齐全。', + ]) + + part('拟写 P-11 · 替代关系主记录', '只读展示') + + P11_TABLE + + part('拟回写 P-01 · 需求状态', '只读展示') + + P01_TABLE + + flow('审批:本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。通过后去向:P-11 落账、P-01 回写、放行;结论供 ACT-WORK-08 证据归集消费。'), + foot: sign('SS02 · ACT-WORK-05 差异评估 · 验收工单 2/2 · 评审放行与落账确认', '工单类型:验收'), +}); +``` + +- [ ] **Step 2: 跑包内测试,全绿** + +Run: `pnpm vitest run activities/part-substitution-assessment` +Expected: activity.test + steps.test 全部 PASS(含「报告模版」组 7 条)。 + +- [ ] **Step 3: 全仓门禁子集 + 提交** + +Run: `pnpm -s typecheck && pnpm -s lint && pnpm -s depcruise` +Expected: 全绿(活动包只 import `@sdk`/`@contracts`/`@sfeel` 与包内文件)。 + +```bash +git add activities/part-substitution-assessment +git commit -m "feat(w05): 五张报告模版 —— 两张数据工单件、评估输入清单、差异审核件、落账确认件(目标件平移) + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` + +--- + +### Task 6: 登记进构建与安装通道、装进库、库内验收、测试台冒烟 + +**Files:** +- Modify: `tools/build-activities.ts`(import + `PACKAGES` 加一条) +- Modify: `apps/api/src/app/seed-activities.ts`(`REAL_PACKAGES` 加一条;装包之前注册 `W05_TOOLS`) +- Test: `apps/api/src/app/__tests__/seed-mvp-data.test.ts`(已过);`docker exec … psql` 验收 SQL + +**Interfaces:** +- Consumes:Task 2 的 `W05_TOOLS` / `W05_TOOL_SAMPLE`;Task 3–5 的活动包。 +- Produces:库里装好的 `part-substitution-assessment@1.0.0`、六张主数据表 + 三张自持表、样例料号;测试台「活动」页签里能选到它并起单。 + +- [ ] **Step 1: 登记到构建脚本** + +在 `tools/build-activities.ts` 的 import 区加: +```ts +import { manifest as partSubstitutionAssessment } from '../activities/part-substitution-assessment/src/manifest.ts'; +``` +`PACKAGES` 数组末尾加: +```ts + { dir: 'part-substitution-assessment', manifest: partSubstitutionAssessment }, +``` + +- [ ] **Step 2: 登记到安装通道并顺带注册纯工具** + +`apps/api/src/app/seed-activities.ts`: +```ts +const REAL_PACKAGES = ['crm-relationship-coverage', 'brand-content-toolkit', 'part-substitution-assessment'] as const; +``` +顶部加 `import { W05_TOOLS, W05_TOOL_SAMPLE } from './tools-w05.ts';`,在 `installRealActivities` 里 `const root = activitiesRoot();` 之前插入: +```ts + /* + * **纯工具随活动一起注册**(2026-08-26):从前工具契约只在 db:seed 里注册,只跑 activities:install 的 + * 开发机上,活动清单声明了工具、白名单也发了,`sys.tool` 里却没有那件工具 —— 第一次跑就撞「工具不存在」。 + * 「安装即用」包含它声明过的纯工具能调得动。register 按 name+version 幂等,重复装是 no-op。 + */ + for (const contract of W05_TOOLS) { + const verdict = await container.toolbox.register({ contract, samples: [W05_TOOL_SAMPLE] }); + if (!verdict.accepted) log(` 工具 ${contract.name} 未过两审:${verdict.staticFindings.map((f) => f.message).join('; ')}`); + } +``` +同文件那句 `主数据四域已物化:${MASTER_OBJECTS.length} 张表` 改成 `主数据 ${MASTER_DOMAINS.length} 域已物化:${MASTER_OBJECTS.length} 张表`(import 里把 `MASTER_DOMAINS` 一并从 `./seed-mvp-data.ts` 引进来)—— 日志说「四域」而库里八域,就是一处会撒谎的话。 + +- [ ] **Step 3: 构建 + 装包(这一步同时物化六张主数据表、三张自持表,并播样例料号)** + +```bash +pnpm activities:install 2>&1 | tail -20 +``` +Expected 日志里有:`主数据 8 域已物化:17 张表`(原 11 张 + 6)、`工具 …` 无「未过两审」、`活动包已装:ACT-WORK-05 · 差异评估(part-substitution-assessment@1.0.0) · 已授权工具 2 件`、静态审**无 error**。若出现 `静态审提示:[error] …`: +- `predicate-unbound` 指名 `candidateInternalPns` → 把该谓词里的 `count(candidateInternalPns)` 换成读 `sub_eval.verdict.candidate_total`(模型写)并在 doneWhen/哨兵里保留 `_rows` 那一半;升版本重装; +- `reference-target-unbound` → 检查 `refersTo` 的三个目标列是否与清单里声明的列名一字不差; +- `ledger-kind-invalid` → 自持表的 `caseKey` 必须在主键里。 + +- [ ] **Step 4: 库内验收(表、主键、性质、注释、样例行)** + +```bash +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select domain||'.'||object||' | '||kind||' | '||coalesce(case_key,'-') from ledger.__object_meta where domain in ('parts','demand','substitution','validation','sub_eval') order by 1" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select conrelid::regclass||' '||pg_get_constraintdef(oid) from pg_constraint where contype='p' and conrelid::regclass::text ~ '^ledger\\.(parts|demand|substitution|validation|sub_eval)__' order by 1" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select string_agg(column_name||':'||data_type, ', ' order by ordinal_position) from information_schema.columns where table_schema='ledger' and table_name='substitution__difference_item'" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select obj_description('ledger.parts__profile'::regclass)||' / '||col_description('ledger.parts__profile'::regclass, 6)" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select internal_pn||' | '||brand_name||' | '||lifecycle_status||' | '||left(normalized_parameter_set, 40) from ledger.parts__profile order by 1" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select internal_pn||'/'||doc_seq||' '||document_type||' '||document_version from ledger.parts__document order by 1" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select activity_id||' @'||version||' '||state from sys.activity_package where activity_id='part-substitution-assessment'" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select name||' v'||version from sys.tool where name like 'part-substitution-assessment.%'" +``` +Expected: +- `__object_meta` 9 行:`parts.profile master`、`parts.document master`、`demand.request master`、`substitution.relation master`、`substitution.difference_item master`、`validation.record master`、`sub_eval.pn_check record eval_ref`、`sub_eval.input_gap record eval_ref`、`sub_eval.verdict record eval_ref`; +- 主键:`parts__profile (internal_pn)`、`parts__document (internal_pn, doc_seq)`、`substitution__relation (substitution_relation_id)`、`substitution__difference_item (substitution_relation_id, difference_item_id)`、`sub_eval__pn_check (eval_ref, internal_pn)`、`sub_eval__input_gap (eval_ref, item_key)`、`sub_eval__verdict (eval_ref)`; +- `substitution__difference_item` 的列里含 `compare_result:text` 与 `unknown_disposition:text`(清单追加的两列真长出来了); +- 表注释 `芯片主数据(P-08)`,第 6 列注释是参数集的人读名; +- `parts__profile` 两行(`LM2596S-5.0 | TI | nrnd | 输入电压范围 | 4.5~40 …`、`LM2596S-ADJ | TI | active | …`);`parts__document` 两行; +- `sys.activity_package` 有 `part-substitution-assessment @1.0.0 installed`(state 值以库里 E04 那一行为准,同样即可);`sys.tool` 两件 `v1.0.0`。 +把这几条输出原样贴进 `docs/ACT-WORK-05-端到端测试记录.md` 的「0. 库内验收」节(Task 8 Step 1 建文件时一起补)。 + +- [ ] **Step 5: 测试台冒烟(不起单,只看装没装对)** + +用 claude-in-chrome 打开 `http://localhost:5173/design#/design/testbed/act`:活动下拉里有「ACT-WORK-05 · 差异评估」;选中后表单预填的是样例 0(客户 CU-0142、候选 `["XL2596S-5.0","LM2596S-ADJ"]`),样例钮五枚都显示名字(没有退成「样例 N」),「落真库」勾着。再打开 `http://localhost:5173/design#/design/ledgers` 数据域那一屏,按活动分组里能看到 ACT-WORK-05 名下 9 张表、`parts.profile` 正式 2 行。截图存 `docs/img/w05-testbed-smoke.png`(用 computer 工具 screenshot 后保存)。 + +- [ ] **Step 6: 门禁子集 + 提交** + +Run: `pnpm -s typecheck && pnpm -s lint && pnpm -s depcruise && pnpm vitest run apps/api/src/app/__tests__ activities/part-substitution-assessment` +Expected: 全绿。 + +```bash +git add tools/build-activities.ts apps/api/src/app/seed-activities.ts activities/part-substitution-assessment/dist/manifest.json activities/part-substitution-assessment/dist/index.js +git commit -m "feat(w05): 差异评估活动登记进构建与安装通道;装包时顺带注册纯工具 + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` +(`dist/` 是否入库以 E04 现状为准:`git ls-files activities/crm-relationship-coverage/dist` 有输出就一起 add,没有就不 add。) + +--- + +### Task 7: 脚本 e2e 套件 `w05`(五案例) + +**Files:** +- Modify: `tools/e2e-a2.mjs`(`SUITES` 加 `w05`;`buildEdits` 加 `stageEdits` / `fillWhere`;`expectGateBlocks` 认 `fillWhere`;`main()` 套件名单加 `w05`) + +**Interfaces:** +- Consumes:Task 4 的卡标题约定(P-08 单 `数据工单 · 芯片主数据创建`、P-10 单 `数据工单 · 原厂技术资料创建`、执行单 `评估输入与资料补齐确认`)、决策卡 value(`create` / `abandon` / `approve` / `reject` / `custom:`)、样例序号 0–4。 +- Produces:`node tools/e2e-a2.mjs w05` 5/5 通过;每案例 `released`、被拒 0。 + +- [ ] **Step 1: 扩 `buildEdits` —— 案例级按表的编辑(`stageEdits`)与按条件整行扫空(`fillWhere`)** + +把 `function buildEdits(stage, drafts) {` 改为 `function buildEdits(stage, drafts, kase = {}) {`,并把里面 +`for (const [i, edit] of (stage.edits ?? []).entries()) {` 改为 +```js + // 案例级按表的编辑叠在套件级之上(同一套件不同案例要建的料号不同 —— 参数集不能是同一份) + const edits = [...(stage.edits ?? []), ...((kase.stageEdits ?? {})[stage.address] ?? [])]; + for (const [i, edit] of edits.entries()) { +``` +在 `if (stage.fillAllBlank) { … }` 之后加: +```js + /* + * 按条件整行扫空:只给某一档状态的行填(评估输入清单里只有 status = gap 的行要补充内容, + * default / ok 的行本就不该填 —— fillAllBlank 会把它们也填上,那是替人说了他没说的话)。 + */ + if (stage.fillWhere) { + const { column, equals, values } = stage.fillWhere; + for (const r of rows) { + if (String(r[column] ?? '') === equals && !taken.has(String(r[rowKey]))) { + lines.push({ [rowKey]: String(r[rowKey]), ...values }); + } + } + } +``` +`runCase` 里 `const lines = buildEdits(stage, body.drafts ?? []);` 改为 `const lines = buildEdits(stage, body.drafts ?? [], kase);`。 + +`expectGateBlocks` 里 +```js + const rows = draftRows(body.drafts ?? [], stage.address); + const blank = rows.filter((r) => String(r[column] ?? '').trim() === '').length; +``` +改为 +```js + const rows = draftRows(body.drafts ?? [], stage.address); + // 有 fillWhere 的段只数该档的行:default / ok 行空着是本分,不是「闸没拦」 + const scoped = stage.fillWhere ? rows.filter((r) => String(r[stage.fillWhere.column] ?? '') === stage.fillWhere.equals) : rows; + const blank = scoped.filter((r) => String(r[column] ?? '').trim() === '').length; +``` + +- [ ] **Step 2: 加套件(放在 `i01: {…}` 之后、`};` 之前)** + +```js + /* + * ACT-WORK-05 差异评估 —— 1 数据(条件,P-08 / P-10 各一张)+ 1 执行 + 2 验收(审核走决策卡循环、放行终局)。 + * 库里只播了 LM2596S-5.0 / LM2596S-ADJ:XL2596S-5.0、CN-DCDC-32V、LM2596S-3.3 刻意不在, + * 「候选缺库 / 原 PN 缺库 / 硬约束不推荐」三条路靠它们。e2e 关落真库,主数据不落正式表,案例可反复跑。 + */ + w05: { + activityId: 'part-substitution-assessment', + address: 'sub_eval.input_gap', + blankCol: 'supplement', + stages: [ + { + match: /数据工单 · 芯片主数据创建/, + address: 'parts.profile', + rowKey: 'internal_pn', + blankCol: 'brand_name', + expectGateBlock: true, + edits: [], + // 案例没点名的料号兜底(不该走到这里 —— 每个案例都在 stageEdits 里逐颗给了) + fillAllBlank: { + brand_name: '兜底品牌', + specification_description: '兜底规格(案例没给参数集)', + package: 'TO-263-5', + lifecycle_status: 'active', + normalized_parameter_set: '输入电压范围 | 4.5~40 | V | hard | 兜底', + }, + }, + { + match: /数据工单 · 原厂技术资料创建/, + address: 'parts.document', + rowKey: 'internal_pn', + blankCol: 'document_title', + expectGateBlock: true, + edits: [], + fillAllBlank: { + doc_seq: '01', + document_type: 'datasheet', + document_title: 'Datasheet(e2e 兜底)', + document_version: 'Rev A', + document_ref: 'https://example.com/datasheet.pdf', + citation_locator: 'p.1', + document_status: 'valid', + }, + }, + { + match: /评估输入与资料补齐确认/, + address: 'sub_eval.input_gap', + rowKey: 'item_key', + blankCol: 'supplement', + expectGateBlock: true, + edits: [ + { item_key: 'application_scenario', supplement: '12V→5V、工业温域(-40~85℃)、输入上限 40V' }, + { item_key: 'bom_position', supplement: 'U3 电源位' }, + ], + // 其余 gap 行(原厂未披露的参数)统一「留 UNKNOWN」—— 补不了也是合法答案 + fillWhere: { column: 'status', equals: 'gap', values: { supplement: '原厂未披露,留 UNKNOWN 并挂验证任务' } }, + }, + ], + cases: [ + { + sample: 0, + name: '候选缺库 · XL2596S-5.0 派数据工单(P-08 + P-10)· 审核先点名驳回再通过', + // 决策卡序:候选 ≥2 缺库先问 → create;审核第一张点名意见驳回;第二张通过 + decideSeq: ['create', 'custom:使能阈值那条 UNKNOWN 的去向请写明验证任务号与测试条件', 'approve'], + expectDecision: true, + expectPort: 'assessment-released', + stageEdits: { + 'parts.profile': [ + { + internal_pn: 'XL2596S-5.0', + brand_name: 'XLSEMI', + manufacturer_name: 'XLSEMI', + specification_description: '降压型 DCDC 稳压器,3A,固定 5.0V,150kHz', + package: 'TO-263-5', + process_technology: 'BCD', + lifecycle_status: 'active', + datasheet_ref: 'https://www.xlsemi.com/datasheet/XL2596S.pdf', + datasheet_version: 'Rev C', + application_scenarios: '工业电源;消费电源', + normalized_parameter_set: [ + '输入电压范围 | 4.5~40 | V | hard | 客户上限 40V', + '输出电压 | 5.0 | V | hard | 固定版', + '输出电流 | 3 | A | hard | 满载能力', + '开关频率 | 150 | kHz | hard | 影响外围', + '静态电流 | 4.2 | mA | soft | 待机功耗', + '工作温度 | -40~+125 | ℃ | hard | 工业温域', + '使能阈值 | 未披露 | V | hard | 原厂未给', + ].join('\n'), + }, + ], + 'parts.document': [ + { + internal_pn: 'XL2596S-5.0', + doc_seq: '01', + document_type: 'datasheet', + brand_name: 'XLSEMI', + document_title: 'XL2596S 150KHz 3A Buck DC/DC Converter', + document_version: 'Rev C', + document_ref: 'https://www.xlsemi.com/datasheet/XL2596S.pdf', + citation_locator: 'p.3 Electrical Characteristics', + document_status: 'valid', + }, + ], + }, + // 使能阈值未披露 → UNKNOWN → 条件推荐(工具算的,不是模型说的) + expectRevised: { address: 'substitution.relation', where: 'candidate_internal_pn', equals: 'XL2596S-5.0', column: 'recommendation_level', contains: 'conditional' }, + }, + { + sample: 1, + name: '都在库 · 常路 · 可调版 → 备选 · 审核一次通过', + decide: 'approve', + expectPort: 'assessment-released', + expectRevised: { address: 'substitution.relation', where: 'candidate_internal_pn', equals: 'LM2596S-ADJ', column: 'recommendation_level', contains: 'alternate' }, + }, + { + sample: 2, + name: '原 PN 缺库 · 硬阻塞建档(不问人)· 审核通过', + decide: 'approve', + expectPort: 'assessment-released', + stageEdits: { + 'parts.profile': [ + { + internal_pn: 'LM2596S-3.3', + brand_name: 'TI', + manufacturer_name: 'Texas Instruments', + specification_description: '降压型 DCDC 稳压器,3A,固定 3.3V,150kHz', + package: 'TO-263-5', + process_technology: 'BCD', + lifecycle_status: 'nrnd', + datasheet_ref: 'https://www.ti.com/lit/ds/symlink/lm2596.pdf', + datasheet_version: 'Rev H (2023-09)', + normalized_parameter_set: [ + '输入电压范围 | 4.5~40 | V | hard | 客户上限 40V', + '输出电压 | 3.3 | V | hard | 固定版', + '输出电流 | 3 | A | hard | 满载能力', + '开关频率 | 150 | kHz | hard | 影响外围', + '静态电流 | 5 | mA | soft | 待机功耗', + '工作温度 | -40~+85 | ℃ | hard | 工业温域', + '使能阈值 | 1.4 | V | hard | ON/OFF 阈值', + ].join('\n'), + }, + ], + 'parts.document': [ + { + internal_pn: 'LM2596S-3.3', + doc_seq: '01', + document_type: 'datasheet', + brand_name: 'TI', + document_title: 'LM2596 SIMPLE SWITCHER Power Converter', + document_version: 'Rev H (2023-09)', + document_ref: 'https://www.ti.com/lit/ds/symlink/lm2596.pdf', + citation_locator: '§6.5 Electrical Characteristics(3.3V 列)', + document_status: 'valid', + }, + ], + }, + }, + { + sample: 3, + name: '三候选 · 两颗缺库各问一次 · 国产 32V 硬约束不推荐', + decideSeq: ['create', 'create', 'approve'], + expectDecision: true, + expectPort: 'assessment-released', + stageEdits: { + 'parts.profile': [ + { + internal_pn: 'XL2596S-5.0', + brand_name: 'XLSEMI', + specification_description: '降压型 DCDC 稳压器,3A,固定 5.0V,150kHz', + package: 'TO-263-5', + lifecycle_status: 'active', + normalized_parameter_set: [ + '输入电压范围 | 4.5~40 | V | hard | 客户上限 40V', + '输出电压 | 5.0 | V | hard | 固定版', + '输出电流 | 3 | A | hard | 满载能力', + '开关频率 | 150 | kHz | hard | 影响外围', + '静态电流 | 4.2 | mA | soft | 待机功耗', + '工作温度 | -40~+125 | ℃ | hard | 工业温域', + '使能阈值 | 未披露 | V | hard | 原厂未给', + ].join('\n'), + }, + { + internal_pn: 'CN-DCDC-32V', + brand_name: '国产某厂', + specification_description: '降压型 DCDC,3A,固定 5.0V,输入上限 32V', + package: 'TO-263-5', + lifecycle_status: 'active', + normalized_parameter_set: [ + '输入电压范围 | 4.5~32 | V | hard | 上限 32V', + '输出电压 | 5.0 | V | hard | 固定版', + '输出电流 | 3 | A | hard | 满载能力', + '开关频率 | 150 | kHz | hard | 影响外围', + '静态电流 | 6 | mA | soft | 待机功耗', + '工作温度 | -40~+85 | ℃ | hard | 工业温域', + '使能阈值 | 1.3 | V | hard | ON/OFF 阈值', + ].join('\n'), + }, + ], + 'parts.document': [ + { internal_pn: 'XL2596S-5.0', doc_seq: '01', document_type: 'datasheet', document_title: 'XL2596S Datasheet', document_version: 'Rev C', document_ref: 'https://www.xlsemi.com/datasheet/XL2596S.pdf', citation_locator: 'p.3', document_status: 'valid' }, + { internal_pn: 'CN-DCDC-32V', doc_seq: '01', document_type: 'datasheet', document_title: 'CN-DCDC-32V Datasheet', document_version: 'V1.0', document_ref: 'https://example.com/cn-dcdc-32v.pdf', citation_locator: 'p.2', document_status: 'valid' }, + ], + }, + expectRevised: { address: 'substitution.relation', where: 'candidate_internal_pn', equals: 'CN-DCDC-32V', column: 'recommendation_level', contains: 'not_recommended' }, + }, + { + sample: 4, + name: '新客户 · 录入卡先建档 · 审核通过', + entryValues: { name: '深圳恒星电源有限公司', industry: '工业电源', region: '广东' }, + decide: 'approve', + expectPort: 'assessment-released', + }, + ], + }, +``` + +`main()` 里 `['a1', 'a2', 'a3', 'a4', 'a6', 'a7', 'e04', 'i01']` 改为 `['a1', 'a2', 'a3', 'a4', 'a6', 'a7', 'e04', 'i01', 'w05']`。 + +- [ ] **Step 3: 跑套件(先停稳定版,免得两台后端交替推同一单)** + +```bash +./stable.sh stop +pnpm --filter @app/api reset-runs +node tools/e2e-a2.mjs w05 2>&1 | tee /tmp/w05-e2e-round1.log | tail -60 +``` +Expected: `5/5 通过`;每案例一行 `→ released · 被拒 0 · N 拍 / Mk token · 决策卡 K 张 · 发口 [assessment-released]`,案例 0/3 的 `闸拦住空手交卷(400)—— 如设计` 至少各出现两次(P-08 / P-10 / 执行单),案例 4 有 `录入卡代答`。 +**红了怎么办**(这是设计欠账不是模型的错,修清单 / 剧本 / 说明书,升 `packageVersion` 重装再跑;每一轮都记进 Task 8 的测试记录): +- `熔断交人` / 被拒 > 0 → 看 `轨迹末 12 条`:拒回指向哪一步 — `写了等于没写` 多半是剧本让它重写已有的值;`要先…` 类是墙,照设计律「墙要配路」改;`查无此值`(refersTo)= 存在性核对没做对,看 S0/S0d 剧本; +- `需要你判断下一步` 兜底卡 → 模型输出不成形两次,看 agent-runner 重问日志,多半是 S3 第三拍写盘太长(考虑把 ① 明细与 ③ 关系拆成两拍写); +- `认不出这张执行卡属于哪一段` → 卡标题没按说明书骨架,改说明书③④⑤的 title 或 `stages[].match`; +- `期望口 assessment-released 未在发射名单里` → 放行门某条 L2 没绿,看验收卡判据哪条红(测试台观测面 `cards` 里的判据表)。 + +- [ ] **Step 4: 再跑一遍确认稳定(两轮都 5/5 才算),然后清运行数据、提交** + +```bash +node tools/e2e-a2.mjs w05 2>&1 | tee /tmp/w05-e2e-round2.log | tail -20 +pnpm --filter @app/api reset-runs +git add tools/e2e-a2.mjs +git commit -m "test(w05): e2e 套件 —— 五案例(候选缺库 / 常路 / 原 PN 硬阻塞 / 三候选含不推荐 / 新客户建档) + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` +(活动包若在本任务里改过版本,`git add activities/part-substitution-assessment` 一并提交。) + +--- + +### Task 8: 浏览器全流程全分支走查 + 测试记录文档 + +**Files:** +- Create: `docs/ACT-WORK-05-端到端测试记录.md`(每一轮追加一节;Task 6 的库内验收与 Task 7 的两轮脚本结果也补进来) +- 可能 Modify: `activities/part-substitution-assessment/src/*`(走查逼出的修改,每改必升 `packageVersion` 并 `pnpm activities:install`) + +**Interfaces:** +- Consumes:测试台 `http://localhost:5173/design#/design/testbed/act`(活动页签;「起单」钮;样例钮;「落真库」勾选框默认勾着;「日志」页签看逐拍轨迹)、工作台 `http://localhost:5173/`(左栏任务列表 → 中间工作区的件 → 右侧对话区卡;执行卡钮「提交」,验收卡「重做」/「确认通过」,录入卡「确认」,决策卡按钮 = 选项文案;件上「编辑 / 取消 / 保存 / + 加一行 / ×」)、Chrome 当前已登录会话。 +- Produces:五轮走查记录、18 条分支的覆盖表、逼出的修改清单。 + +- [ ] **Step 1: 建记录文档 + 走查前置** + +```bash +cat > docs/ACT-WORK-05-端到端测试记录.md <<'EOF' +# ACT-WORK-05 差异评估 · 端到端测试记录 + +> 活动包 `part-substitution-assessment`;实施计划 `docs/superpowers/plans/2026-08-26-act-work-05-part-substitution-assessment.md`。 +> 每一轮一节:**日期 · 案例 · 走的路径 · 结果(终态 / 被拒 / 拍数 / token / 人环次数)· 观察到的问题 · 修改(文件 @ 版本)· 复测结论**。 +> 脚本 e2e 与浏览器走查都记;「界面不撒谎」:没跑到的分支写「未覆盖」并说原因,不写「通过」。 + +## 0. 库内验收(2026-08-26) + +(Task 6 Step 4 的 SQL 输出原样贴这里) + +## 1. 脚本 e2e(`node tools/e2e-a2.mjs w05`) + +### 第 1 轮 +(贴 tail -60;每案例一行结论;红了写原因与修改) + +### 第 2 轮(稳定性复跑) + +## 2. 浏览器全流程全分支走查 + +### 分支覆盖表 + +| 分支 | 触发条件 | 覆盖轮次 | 结果 | 备注 | +|---|---|---|---|---| +| 0a | 启动期:新客户无档案 | R5 | | 数据录入卡建档 | +| 0b / 1d | 候选无 P-08 主数据 → 派数据工单 | R2 / R3 | | P-08 + P-10 各一张 | +| 0c | 未传 datasheet → 人全填 | R2 / R3 / R4 | | R2b 另试「传了 datasheet → AI 预填」 | +| 1a | 绑定参数有误 → 退回重启 | R1 | | 工单内四项不可改(件上没有编辑面) | +| 1b | 输入存在缺口(有而不全) | R1 / R2 | | 应用场景 / BOM 位 / 未披露参数 | +| 1c | 缺口补不了 → 留 UNKNOWN | R2 | | 补充内容写「留 UNKNOWN」 | +| 1d(原 PN) | 原 PN 无数据 → 硬阻塞必须建 | R4 | | 不问人 | +| 1d(放弃) | 候选 ≥2 缺库 → 放弃并记原因 | R3 | | | +| 2a | 单位冲突 → 阻断回步骤 1 | — | 未覆盖 | 需要冲突单位的夹具料号;记 debt | +| 2b | 证据版本冲突 → 人工复核 | — | 未覆盖 | 同上 | +| 3a | 硬约束 MISMATCH → 不推荐 | R3 | | CN-DCDC-32V | +| 4a | UNKNOWN 无去向 → 驳回补证 | R2 | | 审核卡点名驳回 | +| 4b | 证据链不全 → 驳回 | R2 | | 同一张卡的第二种意见 | +| 5a | GATE-03 放行 | R1–R5 | | | +| 5b | GATE-03 驳回 → REWORK | R4 | | 验收卡「重做」 | +| 6a | 至少一个推荐 / 备选 | R1 | | 备选 | +| 6b | 全是条件推荐 | R2 | | XL 条件推荐 | +| 6c | 全不推荐 | — | 未覆盖 | 需要一颗全硬约束失败的原 PN 组合;记 debt | +| 6d | 客户不批准 | — | 范围外 | 独立审批流 | + +### R1 · 都在库 · 常路(样例 1) +### R2 · 候选缺库 · 数据工单 · 审核驳回循环(样例 0) +### R2b · 传了 datasheet → AI 预填(样例 0 + 上传) +### R3 · 三候选 · 一颗放弃一颗新建 · 硬约束不推荐(样例 3) +### R4 · 原 PN 缺库 · 硬阻塞 · 放行驳回重做(样例 2) +### R5 · 新客户 · 录入卡建档(样例 4) + +## 3. 结论与欠账 +EOF +./stable.sh stop # 走查期间只留 dev(8787 / 5173),避免两台交替推同一单 +pnpm --filter @app/api reset-runs +``` +用 claude-in-chrome:`ToolSearch "select:mcp__claude-in-chrome__tabs_context_mcp,mcp__claude-in-chrome__navigate,mcp__claude-in-chrome__computer,mcp__claude-in-chrome__read_page,mcp__claude-in-chrome__find,mcp__claude-in-chrome__form_input,mcp__claude-in-chrome__get_page_text,mcp__claude-in-chrome__tabs_create_mcp,mcp__claude-in-chrome__file_upload,mcp__claude-in-chrome__read_console_messages"` → `tabs_context_mcp` → 新开一页 `http://localhost:5173/`。**出现 Keycloak 登录页就停下来请用户在浏览器里登录一次**(凭证只有他有),登录后继续。每轮之间跑「轮间清场」: + +```bash +pnpm --filter @app/api reset-runs +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -c " +delete from ledger.substitution__difference_item where substitution_relation_id like 'REL-%'; +delete from ledger.substitution__relation where substitution_relation_id like 'REL-%'; +delete from ledger.validation__record where validation_id like 'VAL-%'; +delete from ledger.demand__request where request_id like 'REQ-%'; +delete from ledger.parts__document where internal_pn in ('XL2596S-5.0','CN-DCDC-32V','LM2596S-3.3'); +delete from ledger.parts__profile where internal_pn in ('XL2596S-5.0','CN-DCDC-32V','LM2596S-3.3'); +delete from ledger.customer__profile where customer_code = 'CU-9001';" +``` +(浏览器走查「落真库」默认勾着,放行后主数据真落正式表 —— 不清掉,下一轮「候选缺库」那条路就试不出来。) + +**每一轮记录的固定项**:起单时刻与 taskId(测试台起单后地址栏 / 观测面顶部);进度条刻度(应是 x/6,每个人环时条上有格);每张卡的文案是否人话、指导书是否显示;件上编辑 → 保存 → 闸 / 红带是否同源(空着必填按「提交」要被拦并点名行列);对话区 say 是否一轮一句;工作成果快照份数(执行单 / 验收单各一份 + 决策卡);终态与 `被拒 / 拍数 / token`(测试台「日志」页签或 `curl -s -H "authorization: Bearer $TOKEN" http://localhost:8787/api/gov/testbed/activity/ | jq '{terminal,used,rejects:[.trail[]|select(.kind=="action-rejected")|.title]}'`,token 用 e2e 的 `login()` 同款 client_credentials 取);落库验证 SQL(见各轮)。发现问题 → 改清单 / 剧本 / 模版 → 升 `packageVersion` → `pnpm activities:install` → 记录「修改」→ 复跑该轮直到 released 且被拒 0。 + +- [ ] **Step 2: R1 · 都在库 · 常路(样例 1)** + +1. 测试台 → 活动页签 → 活动「ACT-WORK-05 · 差异评估」→ 样例「都在库·常路」→「起单」。记 taskId。 +2. 工作台:左栏点开这一单。等 AI 跑 S0(对话区应出一句「已绑定…原 PN 与候选都在库,不建主数据」之类的话;进度条 1/6 → 2/6)。**分支 1a**:确认工作区「已绑定」区四项灰底只读、没有编辑钮。 +3. 执行卡「评估输入与资料补齐确认」到达:看指导书(五条 steps)与依据;工作区件:料号核对表两行(原 PN 在库 / 候选 在库 徽标),评估输入清单里 `application_scenario` / `bom_position` 两行状态「待补*」、三行「默认·可改」、齐备行。**先按「提交」不填** → 应被闸拦,提示点名缺哪几行(记原文)。 +4. 件上「编辑」→ 两行补充内容各填一句(`12V→5V、工业温域、40V 输入上限` / `U3 电源位`)→「保存」→ 红带消失、闸文案变;再「提交」→ 通过;对话区出现「人已交回」痕。 +5. AI 跑 S2/S3(三拍:比对 → 定级 → 写盘)。工作区换成差异审核件:关系表 1 行(`LM2596S-ADJ` 推荐等级「备选」),明细表 ≥8 行(输出电压 CONDITIONAL,其余 MATCH)。**分支 6a**。 +6. 审核决策卡到达 →「通过」。AI 跑 S5 → 工作区换成落账确认件(拟写 P-11 一行 analysis_status「FAE 确认」,拟回写 P-01 一行 current_stage `gate03`)→ 验收卡到达:判据表 L1/L2 全绿、L4 三条带 AI 裁断与理由。 +7.「确认通过」→ 中间态「字已签下,正在落地」→ 落地结果屏(进了哪几张表各几行、转出 `assessment-released`)→ 终态 released。**分支 5a**。 +8. 工作成果:定稿两份(执行单快照 / 验收单快照)+ 决策卡快照,翻份翻页正常。 +9. 落库验证: +```bash +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select substitution_relation_id||' '||candidate_internal_pn||' '||recommendation_level||' '||analysis_status||' '||rule_version from ledger.substitution__relation" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select count(*)||' 条明细;UNKNOWN '||sum(case when difference_status='unknown' then 1 else 0 end) from ledger.substitution__difference_item" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select request_id||' '||request_status||' '||current_stage||' '||coalesce(risk_level,'')||' '||coalesce(output_ref,'') from ledger.demand__request" +``` +Expected:一条关系 `alternate fae_confirmed R-2026.07`;P-01 一行 `responded gate03 … RPT-…`。 +10. 记进 R1 节;有问题按固定流程修、升版、复跑。 + +- [ ] **Step 3: R2 · 候选缺库 · 数据工单 · 审核驳回循环(样例 0)** + +轮间清场 → 起单样例「候选缺库·派数据工单」。 +1. S0 后对话区应说「XL2596S-5.0 不在库」;进度条停在 1/6「启动与存在性检查」。决策卡「候选 XL2596S-5.0 在芯片主数据里查无记录…」→ 点「创建主数据(派数据工单)」。**分支 0b / 1d**。 +2. 执行卡「数据工单 · 芯片主数据创建(P-08):XL2596S-5.0」到达;工作区 = P-08 件,一行骨架只有料号(**分支 0c 人全填**)。空手「提交」→ 闸拦并点名(记原文)。「编辑」→ 填品牌 / 规格 / 封装 / 生命周期(下拉 Active)/ 参数集(textarea 七行,使能阈值写「未披露」)/ Datasheet 引用与版本 →「保存」→ 红带收 →「+ 加一行」加一颗假料再「×」撤掉(增删行链)→「保存」→「提交」。 +3. 执行卡「数据工单 · 原厂技术资料创建(P-10):XL2596S-5.0」到达;件上一行骨架(类型 Datasheet、状态待核实);填标题 / 版本 / 链接 / 证据定位、状态改「有效」→ 保存 → 提交。进度条进 2/6。 +4. 执行卡「评估输入与资料补齐确认」:清单里多一行「XL2596S-5.0 使能阈值」待补(**分支 1b**);应用场景 / BOM 位填值,使能阈值那行补充内容写「原厂未披露,留 UNKNOWN 挂验证任务」(**分支 1c**)→ 保存 → 提交。 +5. S2/S3 后审核件:两条关系(XL 条件推荐 / ADJ 备选),明细里「使能阈值 UNKNOWN」那行 UNKNOWN 去向写着 VAL 号。**分支 6b**。 +6. 审核决策卡 → 在输入框写意见「使能阈值那条 UNKNOWN 的去向请写明验证任务号与测试条件」提交(= custom 驳回,**分支 4a**)。看:对话区观察「意见按字面点名对上了…」,AI 改那一行(unknown_disposition / validation.record 的 test_condition),换问法再发一张卡;件上那一行确实变了。再驳回一次,意见「证据引用请指到 datasheet 的页码」(**分支 4b**),看 evidence_ref 变化;第三张卡「通过」。 +7. 拟落账 → 验收卡 → 确认通过 → released。落库验证: +```bash +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select internal_pn||' '||brand_name||' '||lifecycle_status from ledger.parts__profile where internal_pn='XL2596S-5.0'" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select validation_id||' '||validation_internal_pn||' '||validation_status||' '||left(next_validation_action,40) from ledger.validation__record" +docker exec -i jcai-postgres psql -U jcai -d jcai_wos -Atc "select candidate_internal_pn||' '||recommendation_level||' '||coalesce(verification_requirement,'') from ledger.substitution__relation order by 1" +``` +Expected:XL 真落了 P-08;一条 VAL `not_started`;XL `conditional` 且验证要求含 VAL 号;ADJ `alternate`。 +8. 记录:人环次数(决策 3 + 数据 2 + 执行 1 + 放行 1 = 7)、拍数、被拒。 + +- [ ] **Step 4: R2b · 传了 datasheet → AI 预填(样例 0 + 上传)** + +轮间清场。先造一份文本 datasheet: +```bash +cat > "$TMPDIR/XL2596S-5.0-datasheet.txt" <<'EOF' +XL2596S-5.0 XLSEMI 150KHz 3A Buck DC/DC Converter Rev C +Package TO-263-5, BCD process, Active +Input voltage range 4.5V to 40V +Output voltage 5.0V fixed, output current 3A +Switching frequency 150kHz, quiescent current 4.2mA +Operating temperature -40 to +125 C +Enable threshold: not specified +EOF +``` +测试台样例 0 → 「候选 datasheet」文件选择器上传这份 txt(claude-in-chrome `file_upload`)→ 起单。看 P-08 数据工单的件:品牌 / 规格 / 封装 / 生命周期 / 参数集**已预填**(参数集里使能阈值是「未披露」),卡上依据写「已按 datasheet 预填 N 项」。核对后直接提交(闸应判「无缺」放行)。其余同 R2,审核直接通过。记录预填了几格、有没有编造(**分支 0c 的另一半**)。 + +- [ ] **Step 5: R3 · 三候选 · 一颗放弃一颗新建 · 硬约束不推荐(样例 3)** + +轮间清场 → 起单样例「三候选·含硬约束不推荐」。 +1. 两张决策卡按数组顺序来:XL2596S-5.0 → 点「放弃这颗候选」(不写原因,看 `abandon_reason` 是否落了「负责人本轮放弃(未注明原因)」)(**分支 1d 放弃**);CN-DCDC-32V →「创建主数据」。 +2. P-08 数据工单只有 CN 一行 → 填(输入电压范围 `4.5~32`)→ 提交;P-10 同。 +3. 执行单 → 补两行 → 提交。审核件:两条关系(ADJ 备选、CN **不推荐**),CN 的「输入电压范围」行 MISMATCH 高风险、影响说明写着 32V < 40V(**分支 3a**);XL 不出现在关系表里、核对表里标「已放弃」。 +4. 审核通过 → 放行 → released。落库:`select candidate_internal_pn||' '||recommendation_level from ledger.substitution__relation order by 1` → `CN-DCDC-32V not_recommended` + `LM2596S-ADJ alternate`;`select internal_pn||' '||exists_status from ledger.sub_eval__pn_check` 在放行前查(record 表放行后仍在,reset-runs 才清)→ XL `abandoned`。 + +- [ ] **Step 6: R4 · 原 PN 缺库 · 硬阻塞 · 放行驳回重做(样例 2)** + +轮间清场 → 起单样例「原PN缺库·硬阻塞」。 +1. S0 后**不出决策卡**,直接来 P-08 数据工单「LM2596S-3.3」(**分支 1d 原 PN**);填(输出电压 3.3)→ 提交;P-10 → 提交。 +2. 执行单 → 审核通过 → 落账确认件 → 验收卡:**点「重做」**,意见写「P-01 的验证要求请把测试条件也写进去」(**分支 5b**)。看:对话区「验收被打回重做:…」,AI 改 `demand.request.verification_requirement`(件上那一格变了),再次提交 → 新验收卡 →「确认通过」→ released。 +3. 若 AI 原样重提(基座闸「打回之后台账一个字没改」拒回)→ 这是剧本 T_release 的欠账,改剧本、升版、复跑。 +4. 落库:`select original_internal_pn||' → '||candidate_internal_pn||' '||recommendation_level from ledger.substitution__relation`;`select internal_pn from ledger.parts__profile where internal_pn='LM2596S-3.3'`。 + +- [ ] **Step 7: R5 · 新客户 · 录入卡建档(样例 4)** + +轮间清场 → 起单样例「新客户·先建档」。 +1. S0 发数据录入卡「客户 CU-9001 不在客户档案里…」:名称预填「深圳恒星电源有限公司」,行业 / 区域空 → 填「工业电源 / 广东」→「确认」(**分支 0a**)。对话区痕「已写入客户档案」;AI 接着写 P-01(customer_code 引用墙这时应过)。 +2. 其余同 R1 → released。落库:`select customer_code||' '||name||' '||coalesce(industry,'') from ledger.customer__profile where customer_code='CU-9001'` → 一行。 +3. 轮间清场(把 CU-9001 删掉)。 + +- [ ] **Step 8: 收口记录** + +每轮结束把「结果 / 问题 / 修改 / 复测」填进对应节;分支覆盖表逐行填「结果」(通过 / 未覆盖 + 原因);「3. 结论与欠账」写:五轮终态与被拒数汇总、逼出的修改列表(文件 @ 版本)、未覆盖分支(2a / 2b / 6c)与建议夹具、留给 Task 9 的 debt。走查中改过的活动包随本任务提交: +```bash +git add activities/part-substitution-assessment docs/ACT-WORK-05-端到端测试记录.md +git commit -m "test(w05): 浏览器全流程全分支走查五轮(R1–R5 + R2b)与测试记录;走查逼出的活动包修订 + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +``` + +--- + +### Task 9: 文档、欠账、全量门禁、收尾 + +**Files:** +- Create: `docs/数据域字段说明-W05.md` +- Modify: `CLAUDE.md`(现状快照加一条;文档权威地图加两行)· `docs/debt.md`(追加一节) + +- [ ] **Step 1: 写 `docs/数据域字段说明-W05.md`**(口径与 `docs/数据域字段说明-E04-I01.md` 同一套:一览表 + 每张表「重点字段(按重要程度)」+「数据结构」;★ = 重点,【人填】= 人在件上 / 录入卡填) + +```markdown +# 数据域字段说明 —— ACT-WORK-05 差异评估 + +> 对应设计中心 → 数据域「ACT-WORK-05 · 差异评估」分组。字段以活动清单的台账声明为准 +> (`activities/part-substitution-assessment/src/manifest.ts` 的 `declarations.ledger`); +> 主数据六张的**物理结构**由平台基线 `apps/api/src/app/seed-mvp-data.ts` 物化(《SS02-SS03-SS04_Demo台账字段细化与统一方案》§4.2 全列), +> 活动只追加自己要用的列与标记。约束词表见 `docs/数据域字段说明-E04-I01.md`「怎么读这份文档」。 + +### 一览 + +| 全景 | 台账地址 | 人读名 | 性质 | 本活动用到的列 | 人填列 | +|---|---|---|---|---|---| +| L-02 | `customer.profile` | 客户档案 | 主数据 · 共享 | 4 | 3(录入卡,仅新客户) | +| P-08 | `parts.profile` | 芯片主数据 | 主数据 · 共享 | 12 | 12(数据工单件上) | +| P-10 | `parts.document` | 原厂技术资料库 | 主数据 · 共享 | 9 | 9(数据工单件上) | +| P-01 | `demand.request` | 客户需求主记录 | 主数据 · 共享 | 17 | 0(应用场景 / BOM 位经执行单由 AI 抄入) | +| P-11 | `substitution.relation` | 替代关系主记录 | 主数据 · 共享 | 17 | 0(只审不改) | +| P-11 子 | `substitution.difference_item` | 差异明细 | 主数据 · 共享 | 15 | 0 | +| P-06 | `validation.record` | 芯片验证记录 | 主数据 · 共享 | 8 | 0(只写验证要求) | +| — | `sub_eval.pn_check` | 料号存在性核对 | 本活动工作台账 | 6 | 0(决策卡定放弃) | +| — | `sub_eval.input_gap` | 评估输入清单 | 本活动工作台账 | 8 | 3(补充内容 / 加行) | +| — | `sub_eval.verdict` | 本次评估结论 | 本活动工作台账 | 14 | 0 | + +## 一、`parts.profile` 芯片主数据(P-08) + +一张表:料号一行,标准化参数集是它的一个字段(一行一参数 `参数名 | 值 | 单位 | hard/soft | 说明`)。 +库里查无记录 → 数据工单,人在件上从零建整条(可加行);确认落草稿层,放行并入正式表。 + +**重点字段(按重要程度)** +1. `internal_pn` ★【人填】主键 —— 全链路只认它,不可自由文本 +2. `normalized_parameter_set` ★【人填】必填 —— 差异评估逐项比对的唯一输入;「未披露」进 UNKNOWN +3. `lifecycle_status` ★【人填】必填 · 取值域 `active/nrnd/eol/discontinued/risk` +4. `package` ★【人填】必填 —— 硬约束 +5. `specification_description` ★【人填】必填 +6. `brand_name` ★【人填】必填 +7. `datasheet_ref` / `datasheet_version` ★【人填】建议填(成对)—— 证据回链 + +**数据结构** + +| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | +|---|---|---|---|---|---| +| `internal_pn` ★ | 内部料号 | text | 主键 | 人(件上)/ 种子 | | +| `brand_name` ★ | 品牌名称 | text | 必填 | 人(件上)/ AI 预填 | | +| `manufacturer_name` | 制造商名称 | text | 建议填 | 人(件上) | 与品牌不一致时分开记 | +| `specification_description` ★ | 规格描述 | text | 必填 | 人(件上)/ AI 预填 | | +| `normalized_parameter_set` ★ | 标准化参数集 | text | 必填 | 人(件上)/ AI 预填 | 一行一参数 | +| `package` ★ | 封装 | text | 必填 | 人(件上)/ AI 预填 | 硬约束 | +| `process_technology` | 工艺 | text | 建议填 | 人(件上) | 硬约束 | +| `lifecycle_status` ★ | 生命周期状态 | text | 必填 · 取值域 | 人(件上,下拉) | | +| `eol_pcn_status` | EOL/PCN 状态 | text | 建议填 | 人(件上) | | +| `datasheet_ref` ★ | Datasheet 引用 | text | 建议填 | 人(件上)/ AI 预填 | | +| `datasheet_version` ★ | Datasheet 版本 | text | 建议填 | 人(件上)/ AI 预填 | 有引用就该有版本 | +| `application_scenarios` | 适配应用场景 | text | 建议填 | 人(件上) | | +``` +(其余九张表照这个格式写全:列名逐字照清单抄,「写入者」按剧本 —— S0 写 P-01 / pn_check / verdict;S0d 人建 P-08 / P-10;S1 人补 input_gap、AI 抄两列进 P-01;S2/S3 写 relation / difference_item / validation;S5 写 relation.analysis_status 与 P-01 五项。`customer_approval_status` 一栏写「审批流维护,本活动无写权」。) + +- [ ] **Step 2: CLAUDE.md** + +「现状快照」末尾加一条(照既有条目的写法:事实 + 账): +```markdown +- **ACT-WORK-05 差异评估补齐到验收线**(2026-08-26,`part-substitution-assessment@<最终版本>`):本仓第三个真活动包, + 也是第一个把 SS02 主数据(P-08 / P-10 / P-01 / P-11 主+子 / P-06)建进库的活动 —— 六张表进平台基线 + `MASTER_OBJECTS`(四域 → 八域),活动只追加列与标记;P-08 一张表(参数集是一个 text 字段,一行一参数)。 + **数据工单** = 零输入执行工单 + 件上可加行(一张表一张单:P-08 / P-10),确认落草稿、放行并入(守铁律②)。 + 四态与推荐等级由两枚纯工具算(`compare-parameters` / `recommend`,规则 R-2026.07 同时以 DMN 随包冻结); + 审核走决策卡循环,放行终局。**e2e 五案例 5/5 released、被拒 0**;浏览器五轮(R1–R5 + R2b)走查见 + `docs/ACT-WORK-05-端到端测试记录.md`(18 条分支覆盖表:2a / 2b / 6c 未覆盖,记 debt)。 + <走查逼出的修改各一句> +``` +「文档权威地图」表里加两行:`docs/数据域字段说明-W05.md`(ACT-WORK-05 十张表字段口径)与 `docs/ACT-WORK-05-端到端测试记录.md`(逐轮测试与修改结论)。 + +- [ ] **Step 3: `docs/debt.md` 追加一节** + +```markdown +## 2026-08-26 · ACT-WORK-05 差异评估落地时留下的欠账 + +- **数据工单还没有自己的卡皮**:对话区里它是一张标题带「数据工单 ·」的执行卡;文档要的「数据卡」 + (`workOrder:'data'`)要走契约四步 + 前端卡皮,MVP 先不做。 +- **datasheet 只能在起单时上传**:平台文件入口只有起单 `format:'file'`;文档说「在数据工单里上传」, + 件上 / 卡上的上传控件是新基座能力。 +- **PW 与 FAE 一人兼**:平台一任务恰一名负责人,执行单与验收单都发给 owner;派给第二个人是新能力。 +- **参数集是 text 列的行语法**,不是《统一方案》字面上的 Structure / 子表:件上用 textarea 一行一参数; + 要按行编辑就得物理拆 `parts.parameter`(用户定「一张表」,先不拆)。 +- **`market.part`(老料号占位表,主键 `pn`)与 `parts.profile`(P-08,主键 `internal_pn`)重叠**: + 前者空且无人读写,待并或退役。 +- **分支 2a 单位冲突 / 2b 证据版本冲突 / 6c 全不推荐 未覆盖**:需要冲突单位与冲突版本的夹具料号、 + 以及一颗全硬约束失败的组合;补夹具后加 e2e 案例。 +- **存在性检查靠模型读整表 + refersTo 墙**:没有「这颗料在不在库」的基座读数;料号表大了整表进上下文会撑爆 + 截断阈值,届时要补一枚 `parts.exists` 读工具或基座读数。 +- **`customer_approval_status` 不在活动声明里**,所以件上那一格是静态文案「未提交」而不是取值 —— 审批流做起来后改成 data-slot。 +``` + +- [ ] **Step 4: 全量门禁(退出码)** + +```bash +pnpm gates; echo "gates exit=$?" +``` +Expected: `gates exit=0`。红了逐个修(常见:lint 的 §12.5 禁止项、depcruise 活动包越界 import、api:check —— 本计划没改 `packages/contracts`,api-report 不该变;若变了说明改了契约,补 `pnpm api:check --update && git add api-report`)。 + +- [ ] **Step 5: 提交、收尾** + +```bash +git add docs/数据域字段说明-W05.md docs/debt.md CLAUDE.md +git commit -m "docs(w05): 数据域字段说明、现状快照、欠账登记 + +Co-Authored-By: Claude Fable 5 +Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh" +git log --oneline mvp-foundation..HEAD +./stable.sh start # 走查完把稳定版起回来(要它带上新活动就 ./stable.sh update) +``` +合并与推送按用户的规矩由用户在终端做(主支发起合并再推主支;本会话无 TTY 推不了 Gitea)—— 收尾时把分支名 `feat/act-work-05` 与提交列表报给用户。 + +--- + +## 自检清单(写完计划后过一遍) + +- [x] 规格覆盖:启动 4 项 / 双通道无感知(渠道落 `origin`)/ 存在性检查 / 数据工单 P-08 + P-10 一表一单 / 候选 ≥2 放弃决策 / 原 PN 硬阻塞 / 执行单 01 缺口补齐与 UNKNOWN 合法放行 / 步骤 2–3 纯 AI 不派单 / 四态 + 风险 + 证据 + 置信度 / 推荐四档 / 验收 01 四判据(决策卡循环、意见写 P-06)/ 验收 02 四判据(终局放行)/ P-11 主 + 子落账 / P-01 回写五项 / analysis_status 与 customer_approval_status 分离 / 规则版本锁定 / 多候选并行各一条 P-11 / 台账建进库(基线 + 清单)/ e2e 五案例 / 浏览器五轮 + 分支覆盖表 / 测试记录文档 —— 各有任务落点。 +- [x] 未覆盖且已明示:2a / 2b / 6c(夹具缺)、6d(范围外)、数据卡卡皮、件上上传、双负责人。 +- [x] 名字一致性:活动 id `part-substitution-assessment`;域 `sub_eval` / `parts` / `demand` / `substitution` / `validation`;视图名五个;计划项八个;工具两个;出口 `assessment-released`;读数名与 tally 值在 Task 3/4/5/7 之间逐字相同(`exists_status: found/absent/created/abandoned`,`status: gap/default/ok`,四态 `match/mismatch/unknown/conditional`,推荐 `recommended/conditional/alternate/not_recommended`)。 +- [x] 每个代码步骤给了完整代码;每个验证步骤给了命令与期望输出;没有「TBD / 后补 / 类似 Task N」。