# 数据域字段说明 —— ACT-WORK-05 差异评估 > 对应设计中心 → 数据域「ACT-WORK-05 · 差异评估」分组。字段以活动清单的台账声明为准 > (`activities/part-substitution-assessment/src/manifest.ts` 的 `declarations.ledger`)。 > **2026-08-29 整体重写**(design spec `docs/superpowers/specs/2026-08-29-act-work-05-rewrite-design.md`) > 把九步案例模型砍成四步、全程零 `guide`(行为改由 `PlanStep.auto` 声明式配方接管,ADR-014), > 台账从旧的十张收成**九张**:两张原有的三单据(`sub_eval.pn_check` / `sub_eval.input_gap` / > `sub_eval.verdict`)与三张外域表(`customer.profile` / `demand.request` / `parts.document`) > 一并退役 —— **本活动这次重写不再声明、不再碰这五张表**;它们的存在性核对与录入不再是 > 这个活动的事(manifest.ts 头注原话)。老文档(见 git 历史)里那几张表的段落已随重写整篇撤下, > 不是遗漏。 > > 九张里有七张(除 `sub_eval.part` / `sub_eval.case` 两张本活动自持的工作台账外)的**物理结构** > 由平台基线 `apps/api/src/app/seed-mvp-data.ts` 的 `MASTER_OBJECTS` 物化,声明合并对基线 > 基本是**纯 add-column**:活动只追加自己要用的列与标记(required / expect / tally / refersTo / > requiredWhen / distinct / auto),不改基线已有列的语义。**唯一的例外是 `type_id` 这条身份链路** > (`parts.chip_type.type_id` / `parts.type_param.type_id` / `parts.profile.chip_type_id` / > `sub_eval.part.chip_type_id` / `sub_eval.case.chip_type_id` / `substitution.relation.chip_type_id` > 六列同源同改)——声明把基线原本 `integer` 的这几列**加宽**成了 `text`,理由与 PG 类型转换 > 细节见 manifest.ts 该处的大注释与本文「怎么读这份文档」倒数第二条。那份声明同时是 > **授权面**:没声明的列写不进去,也不会出现在件上、判据里、数据域的活动分组里。 ## 怎么读这份文档 - **★ = 重点字段**。每张表挑 5–8 个,在「重点字段」小节里**按重要程度从高到低**排; 数据结构表里在列名后打 ★。字段少于 5 个的表(字典、案卷)全部列入重点。 - **【人填】= 人在活动过程中亲手填、提交后入库的字段**(数据工单的件上编辑)。这类字段 **一律进重点**:它们是这一单里唯一不能靠配方补出来的事实。**S3 审核卡的四态/影响说明/ 判断依据/UNKNOWN 去向不带这个标记**——那四格的初值是 AI 判出来的,人只是「审核时可以 就地改」,不是「唯一不能靠 AI 补出来的事实」;两者含义不同,详见各自表格的写入者说明。 - **这个活动的「写入者」词表与 `docs/数据域字段说明-E04-I01.md` 不同,单独在这里定义**—— E04/I01 全程多轮 agent 编排,W05 重写后**全程零 `guide`、AI 只在 S2 出场恰好一次**, 中间没有「模型每拍决定写什么」这件事,套用旧词表反而说不清楚: - **人(件上)** —— 工作区那份 HTML 上点「编辑」改的格子,保存即落草稿层(与 E04/I01 同义) - **AI** —— 整个活动**唯一一次**模型调用(S2 的 `analyze`)的出参,系统按 `lookup: result:items` 原样抄进台账;S3 人可在件上就地改 - **配方** —— `auto.do` 里 `project` 动作按声明(遍历 / `lookup` 现值 / `when` 链 / 字面量) 算出来的值,**不经模型**,每一拍都会重新求值一遍(幂等,值不变则不产生写入痕迹) - **基座** —— 平台自己生成,不问人也不经模型(自增序号等) - 没有「人(录入卡)」「AI(据人的意见改)」「承继」—— 本活动没有录入卡,也没有 「人写自由文本意见 → AI 读意见改具体行」这一步:S3 要改结论,人直接在件上改那一行; T_release 全只读,打回重做要改的只有 `sub_eval.case`(且它也是配方算的,不是人填的)。 - 「约束」一栏的词表与 `docs/数据域字段说明-E04-I01.md`「怎么读这份文档」是**同一套** (主键 / 自增 / 必填 / 建议填 / 取值域 / 引用 / 去重计数 / 脱敏),不重复抄一遍。 `requiredWhen` 类字段统一写「条件必填 · `<字段>=<值>` 时」。 - **`refersTo` 拒得很硬**:值必须在目标表里真实存在,查无此值 `draftWrite` 当场拒 (不是留到验收才发现)。装包时静态审核也会核 `refersTo` 目标地址本身是否存在。 ### 一览 | 全景 | 台账地址 | 人读名 | 性质 | 本活动用到的列 | 人填列 | |---|---|---|---|---|---| | — | `parts.chip_type` | 芯片类型字典 | 主数据 · W05 唯一读写者 | 3 | 2(新建类型时,件上) | | — | `parts.type_param` | 芯片类型核心参数字段 | 主数据 · W05 唯一读写者 | 5 | 5(新建类型 / 追加字段时,件上) | | P-08 | `parts.profile` | 芯片主数据 | 主数据 · W05 唯一读写者 | 8 | 7(在库回填现值 · 缺库从零填,件上) | | P-08 子 | `parts.parameter` | 标准化参数集 | 主数据 · W05 唯一读写者 | 5 | 2(值 · 说明,件上;主键两列虽挂 `data-col` 但由配方铺,人正常操作不改) | | — | `sub_eval.part` | 本单两颗料(存在性与类型核对) | 本活动工作台账 | 6 | 0(配方每拍按入参与 `parts.profile` 现查重算,件上没有它的编辑入口) | | — | `sub_eval.case` | 本单案卷 | 本活动工作台账 | 4 | 0(全部配方算) | | P-11 | `substitution.relation` | 替代关系主记录 | 主数据 · W05 唯一读写者 | 13 | 0(配方按规则算,人不直接改) | | P-11 子 | `substitution.difference_item` | 差异明细 | 主数据 · W05 唯一读写者 | 15 | 4(S3 就地改四态/影响说明/判断依据/UNKNOWN 去向;AI 先判,人可改) | | P-06 | `validation.record` | 芯片验证记录 | 主数据 · W05 唯一读写者 | 7 | 0(配方按四态分布算) | **四步**(全 `auto`,零 `guide`,AI 只在 S2 出场恰好一次): `S1_inputs`(核对两颗料在不在库 / 是否归了同一芯片类型 / 参数是否齐,**缺什么才发一张 数据工单**——五条样例里「都在库·同型·齐备」那一条走的是零卡直通,不是特例)→ `S2_assess`(**整个活动唯一一次模型调用**:逐条核心参数判 MATCH/MISMATCH/UNKNOWN/CONDITIONAL 四态;推荐等级 / 风险 / 摘要 / 验证要求全部由配方按规则算,AI 不参与、不改一字)→ `S3_review`(结论审核,人工激活,件上就地改四态与三个自由文本格,可循环打回重做)→ `T_release`(落账放行,人工激活,全只读,签字后正式落账并发口 `assessment-released`, 可循环打回重做)。三道入场哨兵全读基座数(`sub_eval_part_*` / `substitution_*_rows` / `_missing`),**案例模型里一面记账旗都没有**——「机检只读基座数」这条纪律在本仓被违反过 六次,W05 重写从设计上就不给它第七次机会。 --- ## 一、`parts.chip_type` 芯片类型字典 按芯片类型(而不是按料号)标一次「这一类芯片要比哪几个核心参数、每个参数的说明与硬软 约束」——同一类型下两颗料从此**结构上**共用同一套参数名与约束档,不必再靠说明书措辞去 硬逼「两颗料对齐同一套参数名」(旧设计靠 `parts.parameter.param_name` 自指引用墙撑着, 测三次都没拦住,这次直接把墙的存在理由从结构上抹掉了)。件①型下拉里没有合适的,人在 `input-supplement` 的件②a 块现造一个;S1 配方选中之后**原样自投影一遍**(不论新建还是 挑既有类型都投,幂等、不产生多余写入),保证这一单的草稿层里**恒有这一行**——这既是 「保存即铺格」的前提,也是数据工单确认落地时不留悬空引用的前提(见 `docs/活动开发规范.md` §4.6 关于 `gateLedgers` 的那条纪律)。 **重点字段(按重要程度)** 1. `type_id` ★ 主键 · 自增 —— 声明成 `text` 而不是 `integer`(见文档头注),写入时按 「现有最大 + 1」续号,不进表单、只读态摆「系统生成」 2. `type_name` ★【人填】必填 —— 新建类型时件上现填,已有类型从下拉挑 3. `description` ★【人填】建议填 —— 这个类型是什么(供选型参考) **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `type_id` ★ | 类型编号 | text | 主键 · 自增 | 基座(续号)| 基线原为 `integer`,声明加宽为 `text`(见头注) | | `type_name` ★ | 类型名称 | text | 必填 | **人(件上)** | 新建类型时填 | | `description` ★ | 类型说明 | text | 建议填 | **人(件上)** | | --- ## 二、`parts.type_param` 芯片类型核心参数字段 一类芯片一套字段清单,复合主键 `type_id + param_name`。新建类型(或给已有类型追加字段)时, 人在 `input-supplement` 的件②b 块逐条定下字段名 / 说明 / 硬软约束 / 参考单位;S1 配方同样 把选中类型此刻的字段清单**原样自投影一遍**(与 `parts.chip_type` 同一条道理、同一次修正 —— 挑已有类型时这张表天然可能零行,不投一遍会被「零行 = 没干完」的闸误判)。**这张表是 S2 唯一一次 AI 调用的入参来源**:逐条参数连同 `param_desc`(为什么要紧)一起交给模型判四态。 **重点字段(按重要程度)** 1. `type_id` ★【人填,选择】主键之一 · 引用 `parts.chip_type.type_id` 2. `param_name` ★【人填】主键之一 —— 参数名 3. `param_desc` ★【人填】必填 —— **AI 分析时读的就是它**:参数名本身说不清「它为什么要紧」, 写成参数名的复述等于没写 4. `constraint_type` ★【人填,选择】必填 · 取值域 `hard`/`soft` —— 硬约束 = 不满足就不能用 (烧芯片 / 超温域 / 装不上);软约束 = 影响表现但可权衡(功耗 / 噪声) 5. `param_unit` ★【人填】建议填 —— 参考单位;**S1 铺参数骨架、S2 判定都直接抄它** (不是回填每颗料自己记的单位)——同一类型下两颗料必须按同一个单位比,这是这次比对的基准 **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `type_id` ★ | 所属类型 | text | 主键之一 · 引用 `parts.chip_type.type_id` | **人(件上,下拉选)** | 基线原为 `integer`,声明加宽为 `text` | | `param_name` ★ | 参数名 | text | 主键之一 | **人(件上)** | | | `param_desc` ★ | 参数说明 | text | 必填 | **人(件上)** | AI 判定时读它 | | `constraint_type` ★ | 约束类型 | text | 必填 · 取值域 | **人(件上,下拉选)** | 从 `parts.parameter` 搬到这里按类型标一次(旧设计每颗料各标一次,同一参数两颗料标法不一致就成了可能) | | `param_unit` ★ | 参考单位 | text | 建议填 | **人(件上)** | `parts.parameter` 铺骨架 / S2 判定均直接抄它 | --- ## 三、`parts.profile` 芯片主数据(P-08) 一颗料一行。S1 配方按启动参数(`originalInternalPn` / `candidateInternalPn`)给两颗料各铺 一行骨架:**在库的那颗回填此刻的现值**(人不必重填,但看得到、能改),**要建的那颗全空** 等人填。`chip_type_id` 必填意味着「在库但还没归类型的老料」也会被拦住——那一格回填出来是 空的,人得先给它选一个类型。人不必在件上「加一行」:要建哪两颗料是启动参数定死的事, 行由配方摆好,他只填格子。 **重点字段(按重要程度)** 1. `internal_pn` ★ 主键 —— 全链路只认它;值随两颗料由启动参数定,件上虽挂 `data-col` (行的钥匙)但人正常操作不改它 2. `chip_type_id` ★【人填,选择】必填 · 引用 `parts.chip_type.type_id` —— 两颗料**必须** 归到同一个类型,`sub_eval.part.chip_type_id` 的 `distinct` 判据钉着这一条 3. `brand_name` ★【人填】必填 4. `lifecycle_status` ★【人填,选择】必填 · 取值域 `active`/`nrnd`/`eol`/`discontinued`/`risk` 5. `manufacturer_name` ★【人填】建议填 6. `specification_description` ★【人填】建议填 7. `datasheet_ref` / `datasheet_version` ★【人填】建议填(成对)—— 证据回链 **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `internal_pn` ★ | 内部料号 | text | 主键 | 配方(按入参铺行)| 在库的沿用现值,不在库的等人填别的格子后自然成立 | | `chip_type_id` ★ | 芯片类型 | text | 必填 · 引用 `parts.chip_type.type_id` | 配方(在库回填现值)/ **人(件上,下拉选)** | 基线原为 `integer`,声明加宽为 `text` | | `brand_name` ★ | 品牌名称 | text | 必填 | 配方(在库回填现值)/ **人(件上)** | | | `lifecycle_status` ★ | 生命周期状态 | text | 必填 · 取值域 | 配方(在库回填现值)/ **人(件上,下拉选)** | | | `manufacturer_name` ★ | 制造商名称 | text | 建议填 | 配方(在库回填现值)/ **人(件上)** | | | `specification_description` ★ | 规格描述 | text | 建议填 | 配方(在库回填现值)/ **人(件上)** | | | `datasheet_ref` ★ | Datasheet 引用 | text | 建议填 | 配方(在库回填现值)/ **人(件上)** | | | `datasheet_version` ★ | Datasheet 版本 | text | 建议填 | 配方(在库回填现值)/ **人(件上)** | | > 基线物理表里还留着这次重写不再声明的旧列(`customer_pn` / `normalized_parameter_set` / > `package` / `process_technology` / `eol_pcn_status` / `application_scenarios` / > `pin_to_pin_relation_ref`)——那是**同一张表被本活动更早的旧九步版本用过**的痕迹 > (封装/工艺这次已经变成按类型标在字典里,不再是主记录上单独一列),不是别的活动在共享它; > add-column 合并不删旧列,记录浏览里看到它们空着是正常的,判断一列还算不算数,以 > **当前这份声明**为准。 --- ## 四、`parts.parameter` 标准化参数集(P-08 子表) 一颗料一个核心参数一行,复合主键 `internal_pn + param_name`。S1 配方按「这两颗料 × 选中类型的字段清单」铺骨架(`prune:true`,换类型会把不再相关的陈旧参数行清掉): `param_value`/`param_note` 若在库已有记录就回填现值,没有就留空等人填;`param_unit` **永远**从 `parts.type_param` 字典直接复制,不回填每颗料自己记的单位、也不可编辑 (与块①/②b 不同,这一列连孪生输入框都不给)——同一类型下两颗料必须按同一个基准单位比, S2 判定用的 `unit` 与件上摆的是同一处来源,不允许分叉。 **重点字段(按重要程度)** 1. `internal_pn` ★ 主键之一 · 引用 `parts.profile.internal_pn` —— 挂 `data-col` 是因为它是 行的钥匙,不是人正常操作会去改的格子 2. `param_name` ★ 主键之一 —— 同上,由配方按字典铺,不是人填的自由文本 3. `param_value` ★【人填】必填 —— 差异评估逐项比对的唯一输入;原厂规格书查不到就留空, **留空会被红带点名,但编一个值比留空坏得多**(说明书明写的禁令) 4. `param_unit` ★ 建议填 —— **恒等于字典值**,配方每拍覆盖,手改也会在下一拍被盖回去 5. `param_note` ★【人填】建议填 —— 值的说明 **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `internal_pn` ★ | 所属料号 | text | 主键之一 · 引用 `parts.profile.internal_pn` | 配方(按字典 × 两颗料铺骨架) | 挂 `data-col` 只为行的钥匙 | | `param_name` ★ | 参数名 | text | 主键之一 | 配方 | 同上 | | `param_value` ★ | 值 | text | 必填 | 配方(在库回填现值)/ **人(件上)** | 查不到留空,不许估、不许拿别的参数的值顶替 | | `param_unit` ★ | 单位 | text | 建议填 | 配方(恒抄 `parts.type_param` 字典) | 只读,连孪生输入框都不给 | | `param_note` ★ | 说明 | text | 建议填 | 配方(在库回填现值)/ **人(件上)** | | > 基线物理表里还留着一列这次重写不再声明的 `constraint_type`(旧设计每颗料各标一次约束, > 这次已搬到 `parts.type_param` 按类型标一次)——同一条「add-column 不删旧列」的道理。 --- ## 五、`sub_eval.part` 本单两颗料(存在性与类型核对,本活动工作台账) S1 配方**每一拍都重算**的一张表(不是「写一次就不再动」):按启动参数铺两行(`role` 恰有 `original`/`candidate` 各一行),`found_pn_ref` 现查 `parts.profile` 是否已有这个 `internal_pn`、`exists_status` 据此判 `found`/`absent`、`chip_type_id` 现查在库现值。 **这张表没有任何人工编辑入口**——它是「要评估什么」那张件的**行来源** (`data-rows="mine:sub_eval.part" data-edit="parts.profile"`),人在件上改的是 `parts.profile`,行会在下一拍被这张表原样重铺。 **重点字段(按重要程度)** 1. `exists_status` ★ 必填 · 取值域 `found`/`absent` —— 存在性核对的核心结论,S1 收步与 S2 入场哨兵都读它(`sub_eval_part_exists_status_found = 2` 才算「两颗都在库」) 2. `internal_pn` ★ 主键之一 3. `eval_ref` ★ 主键之一 —— 本单案号,`W05-` + 开案时刻 14 位数字,串联本活动自持的两张 工作台账 4. `role` ★ 必填 · 取值域 `original`/`candidate` 5. `chip_type_id` ★ 必填 · **去重计数**(`distinct`)—— 「两颗料是不是同一个类型」从此是 基座数得出来的数(`sub_eval_part_distinct_chip_type_id`),S1 收步与 S2 入场的硬门 6. `found_pn_ref` ★ **条件必填**(`exists_status=found` 时)· 引用 `parts.profile.internal_pn` —— 「在库」是一次 `lookup` 的结果,不是一句断言;判 `found` 就必须拿得出一个基座查得到 的料号 **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `eval_ref` ★ | 评估案号 | text | 主键之一 | 配方 | | | `internal_pn` ★ | 内部料号 | text | 主键之一 | 配方(按启动参数铺) | | | `role` ★ | 角色 | text | 必填 · 取值域 | 配方 | `original` 恰一行 / `candidate` 恰一行 | | `found_pn_ref` ★ | 在库核对依据 | text | 条件必填 · `exists_status=found` · 引用 `parts.profile.internal_pn` | 配方(现查 `parts.profile`) | 查无此值 `exists_status` 判 `absent` | | `exists_status` ★ | 在库状态 | text | 必填 · 取值域 `found`/`absent` | 配方 | 每拍现算,不设 `created` 档、不靠谁记账 | | `chip_type_id` ★ | 芯片类型 | text | 必填 · 去重计数 | 配方(现查 `parts.profile`) | 基线原为 `integer`,声明加宽为 `text` | --- ## 六、`sub_eval.case` 本单案卷(本活动工作台账) 一单一行的案卷,挂着摆给人看的一句话(`gate_reason`)与收口摘要(`summary`)。**没有一面 记账旗**——完成与否全部由别处的基座读数(`sub_eval_part_*` / `substitution_*_rows` / `_missing`)判定,这张表只是「说清此刻是什么状况」的一处落点。由两步接力写:S1 每拍 写 `eval_ref`/`chip_type_id`/`gate_reason`(供数据工单卡面的「还差什么」摆),T_release 再补一笔 `summary`(供落账确认页的结论卡摘要)。 **重点字段** 1. `eval_ref` ★ 主键 —— `W05-` + 开案时刻 14 位数字(与引擎注给模型铸主键用的 `case:openedAt` 同一函数,跨单唯一) 2. `gate_reason` ★ —— **摆在数据工单件上给人看的那句话**;`when` 链逐级判断此刻缺什么 (两颗料没填全 / 有料号查无记录 / 有料号没归类型 / 两颗料类型不一致 / 还有必填格空着), 都不缺时是空串。**这一笔排在铺骨架那两笔(`parts.profile`/`parts.parameter`)之后 不是随手写的次序**——它读的 `parts_profile_missing`/`parts_parameter_missing` 是骨架刚建出来的基座读数,排前面会读到刷新之前的旧数(详见 `docs/活动开发规范.md` §4.6 新增纪律) 3. `chip_type_id` ★ —— 本单锁定的芯片类型(原料那一颗的),供 S2 组装入参时 `lookup` 4. `summary` ★ —— T_release 写的收口一句话(`<候选PN> 替代 <原PN>:<差异摘要>`),落账 确认页「结论卡」的摘要来源 **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `eval_ref` ★ | 评估案号 | text | 主键 | 配方(S1,按开案时刻铸) | | | `chip_type_id` ★ | 本单锁定的芯片类型 | text | — | 配方(S1) | 供 S2 `lookup` 用 | | `gate_reason` ★ | 入参未就绪原因(件上摆的就是它) | text | — | 配方(S1,`when` 链) | 都不缺时是空串;S1 发卡条件读的正是「非空」 | | `summary` ★ | 结论摘要 | text | — | 配方(T_release) | `<候选PN> 替代 <原PN>:<差异摘要>` | --- ## 七、`substitution.relation` 替代关系主记录(P-11) 一单一行(旧设计是「一个未放弃的候选一条」,新设计**一次评估恰一对一候选**,不再有 「多候选、部分放弃」)。S2 与 S3 共用**同一份**投影(`RELATION_PROJECTION`):S2 首算, 人在 S3 就地改了差异明细的某条四态之后,同一份投影重算一遍——两处各写一份早晚分叉, 分叉的那一刻屏上会同时摆着两个互相矛盾的等级。**推荐等级 / 风险 / 摘要 / 理由 / 验证 要求全部由规则与模板拼,不让 AI 编**——于是不会出现「有硬约束不符却写着推荐」这种事; 人在 S3 改的是差异明细的四态,这一行跟着重算,人**不直接改**这张表本身。 **重点字段(按重要程度)** 1. `substitution_relation_id` ★ 主键 —— `SUB-<案号>` 2. `eval_ref` ★ 必填 —— **本活动新增列(基线没有)**,串联本单 3. `recommendation_level` ★ 必填 · 取值域 `recommended`/`conditional`/`not_recommended` (旧设计还有 `alternate` 档,新设计的判定分类三分,这一档随之退役)—— 配方按 §3.5 的 规则算:有硬约束不符必 `not_recommended`,否则有 UNKNOWN/CONDITIONAL/软约束不符任一 即 `conditional`,全一致才 `recommended` 4. `original_internal_pn` / `candidate_internal_pn` ★ 必填 · 引用 `parts.profile.internal_pn` 5. `risk_level` ★ 必填 · 取值域 `low`/`medium`/`high` —— 随 `recommendation_level` 联动算 6. `verification_requirement` ★ **条件必填**(`recommendation_level=conditional` 时) 7. `rule_version` ★ 必填 —— 随结论落账,锁定 `R-2026.07` 8. `validation_id` ★ —— 关联验证ID(`VAL-<案号>`) **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `substitution_relation_id` ★ | 替代关系ID | text | 主键 | 配方 | `SUB-<案号>` | | `eval_ref` ★ | 评估案号 | text | 必填 | 配方 | 本活动新增列(基线没有) | | `original_internal_pn` ★ | 原内部料号 | text | 必填 · 引用 `parts.profile.internal_pn` | 配方 | | | `candidate_internal_pn` ★ | 候选内部料号 | text | 必填 · 引用 `parts.profile.internal_pn` | 配方 | | | `chip_type_id` ★ | 芯片类型 | text | 必填 | 配方 | 基线原为 `integer`,声明加宽为 `text` | | `recommendation_level` ★ | 推荐等级 | text | 必填 · 取值域 | 配方(按规则算) | 不让 AI 编 | | `recommendation_reason` | 推荐理由 | text | 必填 | 配方(按规则拼句) | | | `difference_summary` | 差异摘要 | text | 必填 | 配方(按规则拼句) | 「共 N 项核心参数:X 项一致 / … 」句式 | | `risk_level` ★ | 替代风险等级 | text | 必填 · 取值域 | 配方(按规则算) | | | `verification_requirement` ★ | 验证要求 | text | 条件必填 · `recommendation_level=conditional` | 配方 | | | `validation_id` | 关联验证ID | text | — | 配方 | `VAL-<案号>` | | `analysis_status` ★ | 分析状态 | text | 必填 | 配方 | 恒 `fae_confirmed`(基线其余档位本活动不写) | | `rule_version` ★ | 差异规则版本 | text | 必填 | 配方 | 锁定 `R-2026.07` | > **只读边界**:基线在这张表上还建了 `customer_approval_status` / `customer_approval_at` / > `customer_approval_ref` / `customer_feedback` 四列,由**独立审批流**维护;本活动的声明 > 里没有这四列的写权(禁令 `no-customer-approval-write`)。基线物理表里还留着这次重写 > 不再声明的 `request_id` / `project_code` / `bom_position` / `substitution_type` / > `analysis_report_ref` / `confidence_score`——那是旧九步版本用过的列,同一条 > 「add-column 不删旧列」的道理。 --- ## 八、`substitution.difference_item` 差异明细(P-11 子表) 一颗料的一条核心参数一行,主键 `substitution_relation_id + difference_item_id`;行由 `parts.type_param` 字典 `each` 出来(`prune:true`)——**字段清单由字典给,AI 的出参只是 拿 `param_name` 去 `lookup` 的一张查表**,它漏一条,那一行的四态就是空,`required` 立刻 变成 `_missing > 0`,提交闸与放行门当场拦住(不会像「AI 出参驱动行」那样漏两条就少两行、 每一行看着都合规)。**S3 是这张表唯一的人工编辑入口**:四态 / 影响说明 / 判断依据 / UNKNOWN 去向可就地改,其余(参数项 / 说明 / 约束 / 原值 / 候选值 / 单位)只读。 **重点字段(按重要程度)** 1. `substitution_relation_id` ★ 主键之一 · **去重计数** · 引用 `substitution.relation.substitution_relation_id` —— 「几条关系已经有明细」就是数它的不同值(恰应为 1) 2. `difference_item_id` ★ 主键之一 3. `difference_status` ★ 必填 · 取值域 `match`/`mismatch`/`unknown`/`conditional` —— **S2 那一次 AI 判定的核心出参** 4. `judgement_class` ★ 必填 · 取值域 `match`/`soft_mismatch`/`hard_mismatch`/`unknown`/`conditional` —— **合成列**(先认 `difference_status`,`mismatch` 再按 `constraint_type` 分硬/软): 「硬约束不符有几条」不是任何单列 `tally` 数得出来的数,`recommendation_level` 的判据 全靠这一列 5. `impact_statement` ★ 必填 —— MATCH 写「无」,其余写「换上去会不会比现在差、差在哪儿」 (不复述参数值) 6. `evidence_note` ★ 必填 —— 判断依据 7. `unknown_disposition` ★ **条件必填**(`difference_status=unknown` 时)—— UNKNOWN 的去向 8. `constraint_type` ★ 必填 · 取值域 `hard`/`soft` —— 承自 `parts.type_param` 字典这一行本身 **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `substitution_relation_id` ★ | 替代关系ID | text | 主键之一 · 去重计数 · 引用 `substitution.relation.substitution_relation_id` | 配方 | 隐藏在件上(`.rowkey`),仅供 `resolveRowKey` 凑主键 | | `difference_item_id` ★ | 差异明细ID | text | 主键之一 | 配方 | `<关系id>-<参数名>` | | `parameter_name` | 标准参数名 | text | 必填 | 配方(抄字典) | | | `parameter_desc` | 参数说明 | text | 建议填 | 配方(抄字典) | 归档件不必回查字典,快照一份 | | `constraint_type` | 约束类型 | text | 必填 · 取值域 | 配方(抄字典) | | | `original_value` | 原物料参数值 | text | 必填 | 配方(`lookup` `parts.parameter`) | | | `candidate_value` | 候选物料参数值 | text | 必填 | 配方(`lookup` `parts.parameter`) | | | `parameter_unit` | 参数单位 | text | 建议填 | 配方(抄字典) | | | `difference_status` ★ | 差异状态 | text | 必填 · 取值域 | **AI**(S2 `analyze` 出参) | | | `judgement_class` ★ | 判定分类 | text | 必填 · 取值域 | 配方(合成列) | 由 `difference_status` × `constraint_type` 算 | | `impact_statement` ★ | 影响说明(MATCH 写「无」) | text | 必填 | **AI** / **人(件上,S3 可改)** | | | `evidence_note` ★ | 判断依据 | text | 必填 | **AI** / **人(件上,S3 可改)** | 基线里这一列旧名是 `evidence_ref`,本活动改用新名 `evidence_note`(不写 P-10 了,AI 能给的是依据不是文件引用) | | `unknown_disposition` ★ | UNKNOWN 去向(验证任务号 / 补来源动作) | text | 条件必填 · `difference_status=unknown` | **AI** / **人(件上,S3 可改)** | | | `risk_level` | 风险等级 | text | 必填 · 取值域 | 配方 | `hard_mismatch`→high,`match`→low,其余 medium | | `rule_version` | 差异规则版本 | text | 必填 | 配方 | 锁定 `R-2026.07` | > 基线物理表里还留着两列这次重写不再声明的旧列:`evidence_ref`(已被 `evidence_note` > 取代,不是同一列改名,是新声明了一列、旧列作废不写)与 `confidence`(AI 置信度,旧设计 > 的工具出参字段,新设计没有这个概念)。 --- ## 九、`validation.record` 芯片验证记录(P-06) 事实类表:本活动只在 S2/S3 给每单挂**一行固定的**「验证要求」占位 (`validation_status` 恒 `not_started`,`validation_type` 恒 `parametric`)——它不是实测 结果,**不许虚构实测**;真正测完、写测试结果是另一件事(下游或线下流程的事)。**一单固定 一条**,即使全部参数都 MATCH、没有待验项,也要写「本轮无待验项」而不是不写这一行 (验收判据 `validation-landed` 钉着 `validation_record_rows = 1`)。 **重点字段(按重要程度)** 1. `validation_id` ★ 主键 —— `VAL-<案号>` 2. `eval_ref` ★ 必填 —— **本活动新增列(基线没有)** 3. `validation_internal_pn` ★ 必填 · 引用 `parts.profile.internal_pn` —— 恒为候选料号 4. `validation_status` ★ 必填 —— 恒 `not_started` 5. `next_validation_action` ★ 必填 —— 要测什么、什么条件;没有待验项写「本轮无待验项」 6. `validation_type` ★ 必填 · 取值域(基线六档,本活动只写 `parametric`) 7. `test_condition` ★ 建议填 —— 有待验项才写测试条件,否则「无」 **数据结构** | 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 | |---|---|---|---|---|---| | `validation_id` ★ | 验证ID | text | 主键 | 配方 | `VAL-<案号>` | | `eval_ref` ★ | 评估案号 | text | 必填 | 配方 | 本活动新增列(基线没有) | | `validation_internal_pn` ★ | 验证内部料号 | text | 必填 · 引用 `parts.profile.internal_pn` | 配方 | 恒为候选料号 | | `validation_type` ★ | 验证类型 | text | 必填 · 取值域 | 配方 | 恒 `parametric`(本活动只挂这一种) | | `validation_status` ★ | 验证状态 | text | 必填 | 配方 | 恒 `not_started` | | `test_condition` | 测试条件 | text | 建议填 | 配方 | 有 UNKNOWN/CONDITIONAL 才写,否则「无」 | | `next_validation_action` ★ | 后续验证动作 | text | 必填 | 配方 | 没有待验项写「本轮无待验项」 | > 基线物理表里还留着这次重写不再声明的一批旧列(`request_id` / `customer_code` / > `test_parameters` / `test_result` / `issue_risk` / `sample_version` / `sample_lot` / > `evidence_ref`)——那批列是「真验证发生后才有的事实」(测试参数、测试结果、样品批次), > 本活动只写「验证要求」占位阶段,不写实测结果,同一条「add-column 不删旧列」的道理。 --- ## 十、人填字段总表(按人办事的那一屏) | 活动 · 屏 | 卡的形式 | 落进哪张表 | 人亲手填的列 | |---|---|---|---| | `S1_inputs` · 数据工单「差异评估的输入还差几格」(条件发卡,零卡直通是常态路径之一) | 执行工单(零输入卡,件上编辑 + 增删行) | `parts.chip_type`(新建类型时)/ `parts.type_param`(新建类型 / 追加字段时)/ `parts.profile` / `parts.parameter` | chip_type:`type_name`/`description`;type_param:`type_id`(选)/`param_name`/`param_desc`/`constraint_type`(选)/`param_unit`;profile:`chip_type_id`(选)/`brand_name`/`lifecycle_status`(选)/`manufacturer_name`/`specification_description`/`datasheet_ref`/`datasheet_version`;parameter:`param_value`/`param_note` | | `S3_review` · 差异评估结论审核(可循环打回重做) | 决策卡(`decide`,`variant:'review'`)+ 件上就地改 | `substitution.difference_item` | `difference_status`(选)/`impact_statement`/`evidence_note`/`unknown_disposition`(AI 先判,人核对后可改) | | `T_release` · 落账放行(可循环打回重做) | 终局验收工单(签字,人工验收清单五条) | 无(全只读) | 无 —— 人只签字或打回重做。**这一步本身不提供改内容的手段**(`writes` 只声明了 `sub_eval.case`,且这张表没有人工编辑入口、全由配方算);要改结论内容,退回 `S3_review` 的件上直接改 | --- ## 十一、在数据域里看这些表时的几条提醒 1. **`sub_eval.part` / `sub_eval.case` 是本活动自持的工作台账**(domain `sub_eval`,与 E04 `crm_coverage.*`、I01 `content_brand.*` 同一类地位),其余七张是共享主数据的物理表 —— 但截至这次重写,**只有 W05 声明并读写它们**(全仓 grep `domain: 'parts'` / `'substitution'` / `'validation'` 唯一命中处就是这个包),称「共享」容易让人以为另有 活动在用,这份文档统一改称「W05 唯一读写者」。 2. **`type_id` 这条身份链路是本仓唯一一处「integer 加宽成 text」的声明**(六列同源: `parts.chip_type.type_id` / `parts.type_param.type_id` / `parts.profile.chip_type_id` / `sub_eval.part.chip_type_id` / `sub_eval.case.chip_type_id` / `substitution.relation.chip_type_id`)。原因是平台的自增续号器只认「整数型主键走一条路、 字符串型主键走另一条路」,而「`auto:true` 又是 `integer` 的主键」这个组合会撞上两处 平台级坑(`resolveRowKey` 抢跑续号 / `coerceToColumn` 按 `Number()` 解析掉前导零导致 `refersTo` 比对失配)——声明层加宽成 `text` 绕开,不是修复那两处平台代码(它们仍带着 坑,见 `docs/debt.md`)。字段说明详见 `parts.chip_type.type_id` 那一行与 manifest.ts 对应大注释。 3. **基线物理表里普遍还留着旧九步版本的列**(见各表小节末尾的提醒框)——`add-column` 合并不删旧列,记录浏览里看到它们空着是正常的;判断一列还算不算数,**以活动清单当前 这份声明为准**,不以物理表结构为准。 4. **`substitution.relation.customer_approval_status` 等四列是只读边界,不是本活动没做完**—— 由**独立审批流**维护,本活动的声明里没有写权(禁令 `no-customer-approval-write`)。 5. **试跑单默认也落真库**:与 E04 / I01 同一个开关(`sys.task.land_master`,测试台起单 默认打开)——跑一遍活动,新建的芯片类型 / 料号 / 参数都会真的进正式表;要它不落, 起单时显式关掉(脚本化 e2e 要求自己传 `false`)。 6. **本活动全程无 agent 多轮编排,零技能、零工具**——`contract.resources.tools` 与 `declarations.tools` 都是 `[]`,`assets.skills` 也是 `[]`(旧两枚工具、两张 DMN、三条 技能戳已随重写整体退役)。全部行为写在 `PlanStep.auto` 声明式配方里,详见 `docs/活动开发规范.md` §4.6。 --- *字段口径以 `activities/part-substitution-assessment/src/manifest.ts` 的 `declarations.ledger` 为准;七张共享主数据的基线物理结构见 `apps/api/src/app/seed-mvp-data.ts` 的 `MASTER_OBJECTS`;四步剧本与配方见 `activities/part-substitution-assessment/src/case-model.ts` 的 `ASSESSMENT_STEPS` 与 `NARRATIVE`;确定性步骤(`auto` 五动词)的通用机制见 `docs/adr/ADR-014-deterministic-steps.md` 与 `docs/活动开发规范.md` §4.6;业务设计源见 `docs/SS02-ACT-WORK-05-html/` (活动流程与工单说明)、`docs/superpowers/specs/2026-08-29-act-work-05-rewrite-design.md` 与《SS02-SS03-SS04_Demo台账字段细化与统一方案》。*