docs(w05): 重写收口 —— 头注核正、字段说明按现状重写、规范补四条 auto 纪律、CLAUDE.md 记战果

Task 12 的非走查部分(浏览器走查卡在登录、归人工,不在本次范围)。

① index.ts 头注仍写着旧的「三条技能 + 五张模版」,重写后是 0 技能 3 张件,核正
(Ruling H25);包内其余「三条技能/五张/两枚工具/九步」等提法逐处核过,均为带日期的
「从…改成…」历史叙述,不是失实断言,未动。

② docs/数据域字段说明-W05.md 整篇重写(不是打补丁)。旧文档写的是重写前的十张表
(customer.profile/parts.document/demand.request 与 sub_eval 三张旧单据,本活动这次
重写已不再声明),现按 manifest.ts 的 declarations.ledger 逐表核对重写:9 张表、66 列、
20 人填列。写入者词表另定一套(人(件上)/AI(S2 唯一一次 analyze)/配方(project 算出来
的)/基座)——E04/I01 那套多轮 agent 编排的词表在这里说不清楚。逐个 data-edit 核对过
report.ts 才确认写入者归属:S3 的四态等列改成人在件上直接编辑(不再是「人写意见→AI 改」),
sub_eval.part/case、substitution.relation、validation.record 四张表没有任何人工编辑
入口、全部配方算。顺手核了 seed-mvp-data.ts 的基线物理列,注了每张表里这次重写不再
声明的旧列(旧九步版本留下的痕迹),并记下 type_id 这条身份链路是本仓唯一一处
integer→text 加宽声明。

③ docs/活动开发规范.md §4.6 补四条纪律(六条→十条):jsonb 会重排 fields 键序、字段
求值改按依赖拓扑排序不能拿书写顺序当依赖顺序;跨笔顺序仍要自己排(一笔读的是另一笔
写出来的数就要排后面);两笔方向相反的依赖拆两笔写中间夹子表;gateLedgers 同时管
「闸要求非空」与「确认时并进正式表」两件事,漏想一半会悬空引用或永久卡死。§6.5 补第 9
条「测试要能咬住」(镜像测试与读真文件测试证明的是两件事;变异测试本身可能是空的;
能 import 真东西就别手抄)。§7 补两行排障(reset-runs 前先捞证据;dev 进程可能在跑
几小时前的代码,连 tsup --watch 监视进程本身都可能已死)。八条内容逐条去任务报告核过
原始事故与提交号,没有凭印象转述。

④ CLAUDE.md 现状快照追加一条:四步全 auto、两张类型字典、三张件、退役清单;e2e 三例
released 被拒 0(候选缺库/都在库/原PN缺库,模型调用恰 1 次);类型不一致那例撞上已记
debt.md 的 prune 死锁,两颗都缺库那例卡在「答完卡不推进」、真因未定位如实记录;
judgement_class 7/7 逐行核对通过;jsonb 键序地基缺陷的发现与修法(拓扑排序,6636abf)。
顺手核正文档地图里 W05 那一行的旧字段数(十张/110列→九张/66列,与②同步)。

pnpm gates 退出码 0(2447 passed | 34 skipped),与 Task 11 最后一轮记录一致,无回归。
未碰 manifest.ts 里那处与本任务无关的未提交版本号改动(2.1.5→2.1.6,疑似上一个卡在
登录的浏览器走查会话留下)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-30 11:22:56 +08:00
co-authored by Claude Opus 5
parent d420284887
commit 8586f7dac8
4 changed files with 462 additions and 357 deletions
+353 -353
View File
@@ -1,472 +1,472 @@
# 数据域字段说明 —— ACT-WORK-05 差异评估
> 对应设计中心 → 数据域「ACT-WORK-05 · 差异评估」分组。字段以活动清单的台账声明为准
> (`activities/part-substitution-assessment/src/manifest.ts` 的 `declarations.ledger`);
> 主数据六张(`customer.profile` / `parts.profile` / `parts.document` / `demand.request` /
> `substitution.relation` + `difference_item` / `validation.record`)的**物理结构**由平台基线
> `apps/api/src/app/seed-mvp-data.ts` 的 `MASTER_OBJECTS` 物化(字段名与《SS02-SS03-SS04_Demo
> 台账字段细化与统一方案》§4.2 一字不差),声明合并对基线是**纯 add-column**:
> 活动只追加自己要用的列与标记(required / tally / refersTo / requiredWhen / distinct),
> 不改基线已有列的语义。那份声明同时是**授权面**:没声明的列写不进去,也不会出现在件上、
> 判据里、数据域的活动分组里。
> (`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 个,在「重点字段」小节里**按重要程度从高到低**排;
数据结构表里在列名后打 ★。
- **【人填】= 人在活动过程中亲手填、提交后入库的字段**(执行工单 / 数据工单的件上编辑,
或数据录入卡)。这类字段**一律进重点**:它们是这一单里唯一不能靠 AI 补出来的事实。
- 「写入者」一栏的口径与「约束」一栏的词表,与 `docs/数据域字段说明-E04-I01.md`
「怎么读这份文档」是**同一套**,不重复抄一遍:人(件上)/ 人(录入卡)/ AI / 工具 / 基座 /
承继;主键 / 自增 / 必填 / 建议填 / 取值域 / 引用 / 去重计数 / 脱敏。
requiredWhen 类字段(某一列到某个值才必填)统一写「条件必填 · <字段>=<值> 时」——
与 E04-I01 的「发布时必填」是同一种约束机制,ACT-WORK-05 的触发条件不止「发布」一种,
所以逐列写清条件,不套用那个专用词。
- ACT-WORK-05 比 E04/I01 多两种「写入者」写法,单列在这里:
- **AI(据人的意见改)** —— S4 审核决策卡驳回、T_release 打回重做时,人写的是自然语言意见,
不是结构化字段值;基座按字面比对定位到哪一行,AI 照意见改那一行的具体列(同 PK 覆盖写),
意见本身不落进任何一张台账的某一列。
- **AI(抄自 input_gap.supplement)** —— `demand.request` 的 `application_scenario` /
`bom_position` 两列,人是在 `sub_eval.input_gap` 的「补充内容」列作答,S1 收口那一拍才由
AI 抄进 P-01;人没有直接编辑过 `demand.request` 本身。
数据结构表里在列名后打 ★。字段少于 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` 目标地址本身是否存在。
### 一览
| 全景 | 台账地址 | 人读名 | 性质 | 本活动用到的列 | 人填列 |
|---|---|---|---|---|---|
| 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.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(配方按四态分布算) |
九步(0.5 拆两项数据工单):绑定与存在性核对(S0_bind)→(等待数据:S0d_p08_master →
S0d_p10_document,人)→ 人补评估输入(S1_input_confirm)→ 标准化对照表(S2_normalize)→
逐项评估(S3_assess)→ 人审结论(S4_review,可循环)→ 拟落账(S5_prepare_landing)→
人放行(T_release)。四道人环里 S0d_p08_master / S0d_p10_document / S1_input_confirm 三道
往台账里写下人亲手填的事实,S4_review / T_release 两道是审核与签字(人写意见,AI 据意见改台账)。
**四步**(全 `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 重写从设计上就不给它第七次机会。
---
## 一、`customer.profile` 客户档案(L-02 · 共享主数据)
## 一、`parts.chip_type` 芯片类型字典
本活动平时**不建这张表**,只在客户不在库(启动入参 `customerCode` 在 `customer.profile`
里查无记录)那一刻代行建档职责:S0_bind 发一张数据录入卡,人核对名称与行业区域后确认。
除此之外整个活动流程里再不碰这张表。
按芯片类型(而不是按料号)标一次「这一类芯片要比哪几个核心参数、每个参数的说明与硬软
约束」——同一类型下两颗料从此**结构上**共用同一套参数名与约束档,不必再靠说明书措辞去
硬逼「两颗料对齐同一套参数名」(旧设计靠 `parts.parameter.param_name` 自指引用墙撑着,
测三次都没拦住,这次直接把墙的存在理由从结构上抹掉了)。件①型下拉里没有合适的,人在
`input-supplement` 的件②a 块现造一个;S1 配方选中之后**原样自投影一遍**(不论新建还是
挑既有类型都投,幂等、不产生多余写入),保证这一单的草稿层里**恒有这一行**——这既是
「保存即铺格」的前提,也是数据工单确认落地时不留悬空引用的前提(见 `docs/活动开发规范.md`
§4.6 关于 `gateLedgers` 的那条纪律)。
**重点字段(按重要程度)**
1. `customer_code` ★ 主键 —— 值取自启动入参 `customerCode`,AI 不许自编;人在录入卡上核对确认,不是自由填写
2. `name` ★【人填】必填 —— 无名档案进不了正式表(与 E04 同一条墙);`customerName` 有给就 AI 预填,人核对
3. `industry` ★【人填】建议填 —— 所属行业,录入卡上从零填
4. `region` ★【人填】建议填 —— 区域,录入卡上从零填
1. `type_id` ★ 主键 · 自增 —— 声明成 `text` 而不是 `integer`(见文档头注),写入时按
「现有最大 + 1」续号,不进表单、只读态摆「系统生成」
2. `type_name` ★【人填】必填 —— 新建类型时件上现填,已有类型从下拉挑
3. `description` ★【人填】建议填 —— 这个类型是什么(供选型参考)
**数据结构**
| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 |
|---|---|---|---|---|---|
| `customer_code` ★ | 客户编码 | text | 主键 | AI(抄入参 customerCode)/ 人(录入卡确认) | 老客户沿用,不经这张卡 |
| `name` ★ | 客户名称 | text | 必填 | AI 预填(`customerName`)/ 人(录入卡) | 没传 `customerName` 就整格空着让人填 |
| `industry` ★ | 所属行业 | text | 建议填 | 人(录入卡) | |
| `region` ★ | 区域 | text | 建议填 | 人(录入卡) | |
| `type_id` ★ | 类型编号 | text | 主键 · 自增 | 基座(续号)| 基线原为 `integer`,声明加宽为 `text`(见头注) |
| `type_name` ★ | 类型名称 | text | 必填 | **人(件上)** | 新建类型时填 |
| `description` ★ | 类型说明 | text | 建议填 | **人(件上)** | |
---
## 二、`parts.profile` 芯片主数据(P-08 · 共享主数据)
## 二、`parts.type_param` 芯片类型核心参数字段
一张表:料号一行,标准化参数集是它的一个字段(一行一参数
`参数名 | 值 | 单位 | hard/soft | 说明`,值是「未披露」的进 UNKNOWN)。库里按 `internal_pn`
查无记录 → 发**数据工单**,人在工作区的件上从零建整条(可加行);有上传 datasheet 就
AI 先按文本预填、人核对,没有就人全填;确认落草稿层,T_release 放行时才与其余表一起并入
正式版。
一类芯片一套字段清单,复合主键 `type_id + param_name`。新建类型(或给已有类型追加字段)时,
人在 `input-supplement` 的件②b 块逐条定下字段名 / 说明 / 硬软约束 / 参考单位;S1 配方同样
把选中类型此刻的字段清单**原样自投影一遍**(与 `parts.chip_type` 同一条道理、同一次修正 ——
挑已有类型时这张表天然可能零行,不投一遍会被「零行 = 没干完」的闸误判)。**这张表是 S2
唯一一次 AI 调用的入参来源**:逐条参数连同 `param_desc`(为什么要紧)一起交给模型判四态。
**重点字段(按重要程度)**
1. `internal_pn` ★【人填】主键 —— 全链路只认它,不可自由文本
2. `normalized_parameter_set` ★【人填】必填 —— 差异评估逐项比对的唯一输入;「未披露」进 UNKNOWN
3. `lifecycle_status` ★【人填】必填 · 取值域 `active/nrnd/eol/discontinued/risk`
4. `package` ★【人填】必填 —— 硬约束
5. `specification_description` ★【人填】必填
6. `brand_name` ★【人填】必填
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 | 主键 | 人(件上)/ 种子 | 已在库的候选(如样例里预播的 LM2596S-5.0)不经这张单 |
| `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 | 建议填 | 人(件上) | |
| `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.document` 原厂技术资料库(P-10 · 共享主数据)
## 四、`parts.parameter` 标准化参数集(P-08 子表)
按 `internal_pn` 查无资料记录 → 发**数据工单**(与 P-08 那张分开,一张表一张单,
在 P-08 那张确认之后再发);S0d_p08_master 那一步先写骨架(`document_type` /
`document_status` / `brand_name` 三格能填就填),人在 P-10 数据工单的件上补齐其余列。
同一料号多份资料就多加几行,序号 `01`/`02`… 人自己续(不是自增列)。
一颗料一个核心参数一行,复合主键 `internal_pn + param_name`。S1 配方按「这两颗料 ×
选中类型的字段清单」铺骨架(`prune:true`,换类型会把不再相关的陈旧参数行清掉):
`param_value`/`param_note` 若在库已有记录就回填现值,没有就留空等人填;`param_unit`
**永远**从 `parts.type_param` 字典直接复制,不回填每颗料自己记的单位、也不可编辑
(与块①/②b 不同,这一列连孪生输入框都不给)——同一类型下两颗料必须按同一个基准单位比,
S2 判定用的 `unit` 与件上摆的是同一处来源,不允许分叉。
**重点字段(按重要程度)**
1. `document_ref` ★【人填】必填 —— 资料文件链接,差异评估的证据引用要指到它
2. `internal_pn` ★【人填】主键之一 —— 关联料号,与 P-08 同一个内部料号
3. `doc_seq` ★【人填】主键之一 —— 同一料号下 01/02… 人自己续号
4. `document_type` ★【人填】必填 · 取值域 —— `datasheet/app_note/reference_design/white_paper/sample_doc/commitment_letter/other`
5. `document_title` ★【人填】必填
6. `document_version` ★【人填】必填
7. `document_status` ★【人填】必填 · 取值域 `valid/expired/pending/void`
8. `citation_locator` ★【人填】建议填 —— 页码 / 章节 / 表格,证据定位精确到这里
1. `internal_pn` ★ 主键之一 · 引用 `parts.profile.internal_pn` —— 挂 `data-col` 是因为它是
行的钥匙,不是人正常操作会去改的格子
2. `param_name` ★ 主键之一 —— 同上,由配方按字典铺,不是人填的自由文本
3. `param_value` ★【人填】必填 —— 差异评估逐项比对的唯一输入;原厂规格书查不到就留空,
**留空会被红带点名,但编一个值比留空坏得多**(说明书明写的禁令)
4. `param_unit` ★ 建议填 —— **恒等于字典值**,配方每拍覆盖,手改也会在下一拍被盖回去
5. `param_note` ★【人填】建议填 —— 值的说明
**数据结构**
| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 |
|---|---|---|---|---|---|
| `internal_pn` ★ | 关联内部料号 | text | 主键之一 | 人(件上) | 与 P-08 那一行同一个键 |
| `doc_seq` ★ | 资料序号(同一料号下 01/02…) | text | 主键之一 | 人(件上) | 不是自增列,人自己续号 |
| `document_type` ★ | 资料类型 | text | 必填 · 取值域 | AI 预填骨架(S0d_p08_master)/ 人(件上,下拉) | |
| `brand_name` | 品牌名称 | text | 建议填 | AI 预填骨架 / 人(件上) | |
| `document_title` ★ | 资料标题 | text | 必填 | 人(件上) | |
| `document_version` ★ | 资料版本 | text | 必填 | 人(件上) | |
| `document_ref` ★ | 资料文件链接 | text | 必填 | 人(件上) | |
| `citation_locator` ★ | 证据定位(页码/章节/表格) | text | 建议填 | 人(件上) | |
| `document_status` ★ | 资料状态 | text | 必填 · 取值域 | AI 预填骨架(pending)/ 人(件上,下拉) | |
| `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 不删旧列」的道理。
---
## 四、`demand.request` 客户需求主记录(P-01 · 共享主数据)
## 五、`sub_eval.part` 本单两颗料(存在性与类型核对,本活动工作台账)
S0_bind 新建 V1(客户 / 需求描述 / 原 PN 三项照入参原样落,启动时未收的 `application_scenario`
/ `bom_position` 先不写);S1_input_confirm 人在评估输入清单上把这两项的「补充内容」补上,
AI 收口那一拍原样抄回本表;S5_prepare_landing 放行前回写风险等级 / 验证要求 / 输出链接 /
下一步动作 / 当前阶段五项(`current_stage = "gate03"` 那一刻这五项转为必填)。**本表没有一列
是人直接编辑的**——人的手笔经由 `sub_eval.input_gap.supplement` 间接进来。
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. `request_id` ★ 主键 —— 需求编号,与 `eval_ref` 用同一串时刻数字铸出
2. `customer_code` ★ 必填 · 引用 `customer.profile.customer_code`
3. `request_description` ★ 必填 —— 客户需求原话,S0 落一次,不许改写
4. `original_internal_pn` ★ 必填
5. `application_scenario` ★ 建议填 —— 启动时未收,人在评估输入清单补的「补充内容」由 AI 抄进这一格(本表唯一带着人手笔的两列之一)
6. `bom_position` ★ 建议填 —— 同上
7. `risk_level` ★ 条件必填 · `current_stage=gate03` 时 —— 放行回写五项之一,取各候选风险最高档
8. `verification_requirement` ★ 条件必填 · `current_stage=gate03` 时 —— 列出挂的验证任务号
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` 就必须拿得出一个基座查得到
的料号
**数据结构**
| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 |
|---|---|---|---|---|---|
| `request_id` ★ | 需求编号 | text | 主键 | AI | `REQ-` + 案号数字串 |
| `customer_code` ★ | 客户编码 | text | 必填 · 引用 `customer.profile.customer_code` | AI | |
| `requirement_version` | 需求版本 | text | 必填 | AI | 恒 `V1` |
| `request_type` | 需求类型 | text | 必填 | AI | 恒「技术需求」 |
| `request_description` ★ | 需求描述 | text | 必填 | AI | 入参原话,不改写 |
| `input_snapshot_ref` | 输入快照 | text | 必填 | AI | `SNAP-` + 案号数字串 |
| `original_internal_pn` ★ | 原内部料号 | text | 必填 | AI | |
| `bom_position` ★ | BOM 位 | text | 建议填 | AI(抄自 `sub_eval.input_gap.supplement`) | 启动时未收 |
| `application_scenario` ★ | 应用场景 | text | 建议填 | AI(抄自 `sub_eval.input_gap.supplement`) | 启动时未收 |
| `technical_constraints` | 技术约束 | text | — | AI | 人加的 input_gap 行汇总成一句 |
| `risk_level` ★ | 风险等级 | text | 条件必填 · `current_stage=gate03` | AI | 取各候选 `risk_level` 最高档 |
| `verification_requirement` ★ | 验证/复测/确认要求 | text | 条件必填 · `current_stage=gate03` | AI | 列出所有挂的 VAL 号 |
| `request_status` | 需求状态 | text | 必填 · 取值域 | AI | `pending → responded` |
| `current_activity` | 当前 Activity | text | 条件必填 · `current_stage=gate03` | AI | 恒 `ACT-WORK-05` |
| `current_stage` | 当前阶段 | text | — | AI | `assessing → gate03` |
| `next_action` | 下一步动作 | text | 条件必填 · `current_stage=gate03` | AI | 恒「提交 ACT-GATE-03 评审放行」 |
| `output_ref` | 标准输出链接 | text | 条件必填 · `current_stage=gate03` | AI | `RPT-` + 案号数字串 |
| `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` |
---
## 五、`substitution.relation` 替代关系主记录(P-11 · 共享主数据)
## 六、`sub_eval.case` 本单案卷(本活动工作台账)
一个未放弃的候选一条:S2_normalize 写骨架(不含推荐/风险/摘要)、S3_assess 用两枚工具的
出参回填推荐等级与风险(**AI 原样抄,不改一字**)、S4_review 只审不改(人写意见,AI 据
意见改具体列)、S5_prepare_landing 把 `analysis_status` 推到 `fae_confirmed`。**本活动没有
一列是人直接编辑的** —— 人的动作是通过 S4/T_release 的决策卡意见间接改台账。
一单一行的案卷,挂着摆给人看的一句话(`gate_reason`)与收口摘要(`summary`)。**没有一面
记账旗**——完成与否全部由别处的基座读数(`sub_eval_part_*` / `substitution_*_rows` /
`_missing`)判定,这张表只是「说清此刻是什么状况」的一处落点。由两步接力写:S1 每拍
写 `eval_ref`/`chip_type_id`/`gate_reason`(供数据工单卡面的「还差什么」摆),T_release
再补一笔 `summary`(供落账确认页的结论卡摘要)。
**重点字段(按重要程度)**
**重点字段**
1. `substitution_relation_id` ★ 主键
2. `eval_ref` ★ 必填 —— **本活动新增列(基线没有)**,评估案号,与 `pn_check` / `verdict` 同一个 `eval_ref` 串联全单
3. `candidate_internal_pn` ★ 必填 · 引用 `parts.profile.internal_pn`
4. `original_internal_pn` ★ 必填 · 引用 `parts.profile.internal_pn`
5. `recommendation_level` ★ 必填 · 取值域 `recommended/conditional/alternate/not_recommended` —— 工具算,AI 照抄不改一字
6. `analysis_status` ★ 必填 · 取值域 —— 分析层状态,S5 推到 `fae_confirmed`
7. `risk_level` ★ 必填 · 取值域 `low/medium/high/critical` —— 工具算
8. `rule_version` ★ 必填 —— 随结论落账,锁定 `R-2026.07`
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>:<差异摘要>`),落账
确认页「结论卡」的摘要来源
**数据结构**
| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 |
|---|---|---|---|---|---|
| `substitution_relation_id` ★ | 替代关系ID | text | 主键 | AI | `REL-<案号数字串>-01/02…` |
| `eval_ref` ★ | 评估案号 | text | 必填 | AI | 本活动新增列(基线没有) |
| `request_id` | 关联需求编号 | text | 必填 | AI | |
| `bom_position` | 关联 BOM 位 | text | 建议填 | AI | 照 `demand.request` 现值抄 |
| `original_internal_pn` ★ | 原内部料号 | text | 必填 · 引用 `parts.profile.internal_pn` | AI | |
| `candidate_internal_pn` ★ | 候选内部料号 | text | 必填 · 引用 `parts.profile.internal_pn` | AI | |
| `substitution_type` | 替代类型 | text | 必填 · 取值域 | AI | `pin_to_pin/functional/parametric/other` |
| `recommendation_level` ★ | 推荐等级 | text | 必填 · 取值域 | 工具(`recommend`)→ AI 照抄 | |
| `recommendation_reason` | 推荐理由 | text | 必填 | AI(可按人的驳回意见改) | 工具 reason 扩写成人话 |
| `difference_summary` | 差异摘要 | text | 必填 | AI | 「7 项比对:4 MATCH / 1 MISMATCH / 1 UNKNOWN / 1 CONDITIONAL」句式 |
| `risk_level` ★ | 替代风险等级 | text | 必填 · 取值域 | 工具(`recommend`)→ AI 照抄 | |
| `verification_requirement` | 验证要求 | text | 条件必填 · `recommendation_level=conditional` | AI | 列出 VAL 号 |
| `validation_id` | 关联验证ID | text | — | AI | |
| `analysis_status` ★ | 分析状态 | text | 必填 · 取值域 | AI | `ai_draft → fae_confirmed`(其余三档本活动不写) |
| `analysis_report_ref` | 替代分析报告 | text | 建议填 | AI | `RPT-<案号数字串>` |
| `confidence_score` | 证据可信度 | number | 建议填 | AI | 取各行 `confidence` 最小值 |
| `rule_version` ★ | 差异规则版本 | text | 必填 | AI | 锁定 `R-2026.07` |
| `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` 三列,由**独立审批流**维护;本活动的声明里没有这三列的写权
> (禁令 `no-customer-approval-write`)。落账确认那一屏那一格是静态文案
> 「未提交 · 由审批流程维护,本活动不产生」,不是可取值的 `data-slot`
> ——审批流做起来后才改成真读数(见 `docs/debt.md`)。
> `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.difference_item` 差异明细(P-11 子表)
一条参数一条记录。S2_normalize 按原 PN 的参数集写骨架(不含四态/影响)、S3_assess 第一拍
调 `compare-parameters` 拿到四态与风险后**原样抄回** `difference_status` / `compare_result` /
`risk_level`,AI 自己只补写证据引用 / 影响说明 / UNKNOWN 去向;S4_review 驳回时人写的意见由
AI 定位到具体行后改 `impact_statement` / `unknown_disposition` / `evidence_ref`。
`substitution_relation_id` 标了 `distinct`,「几条关系已经有明细」就是数它的不同值数
(S2 收步、S3 收步都读这个数)。
一颗料的一条核心参数一行,主键 `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` ★ 主键之一 · 去重计数 —— 「几条关系已有明细」就是数它的不同值
1. `substitution_relation_id` ★ 主键之一 · **去重计数** · 引用 `substitution.relation.substitution_relation_id` ——
「几条关系已经有明细」就是数它的不同值(恰应为 1)
2. `difference_item_id` ★ 主键之一
3. `difference_status` ★ 必填 · 取值域 `match/mismatch/unknown/conditional` —— 工具算,AI 原样抄
4. `compare_result` ★ 建议填 —— **本活动新增列(基线没有)**,工具出参原样存底,供复核
5. `constraint_type` ★ 必填 · 取值域 `hard/soft` —— 决定这一项差异判得严不严
6. `evidence_ref` ★ 必填 —— 证据引用,验收判据「证据链可信」逐行核对它
7. `impact_statement` ★ 必填 —— MATCH 写「无」,其余写清对应用 / 验证 / 报价的影响
8. `unknown_disposition` ★ 条件必填 · `difference_status=unknown` 时 —— **本活动新增列(基线没有)**,UNKNOWN 的去向(挂验证任务号 / 补来源动作)
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 | 主键之一 · 去重计数 | AI | |
| `difference_item_id` ★ | 差异明细ID | text | 主键之一 | AI | `<关系id>-P01/P02…` |
| `parameter_name` | 标准参数名 | text | 必填 | AI | |
| `original_value` | 原物料参数值 | text | 必填 | AI | |
| `candidate_value` | 候选物料参数值 | text | 必填 | AI | 候选没有这个参数写「未披露」 |
| `parameter_unit` | 参数单位 | text | 建议填 | AI | 以原 PN 为准 |
| `constraint_type` ★ | 约束类型 | text | 必填 · 取值域 | AI | 原 PN 那一行的 hard/soft |
| `compare_result` ★ | 比对结果(工具出参) | text | 建议填 | 工具(`compare-parameters`)→ AI 照抄 | 本活动新增列 |
| `difference_status` ★ | 差异状态 | text | 必填 · 取值域 | 工具(`compare-parameters`)→ AI 照抄 | |
| `impact_statement` ★ | 影响说明(MATCH 写「无」) | text | 必填 | AI | |
| `unknown_disposition` ★ | UNKNOWN 去向(验证任务号 / 补来源动作) | text | 条件必填 · `difference_status=unknown` | AI(可按人的驳回意见改) | 本活动新增列 |
| `risk_level` | 风险等级 | text | 必填 · 取值域 `low/medium/high` | 工具(`compare-parameters`)→ AI 照抄 | |
| `evidence_ref` ★ | 差异证据引用 | text | 必填 | AI(可按人的驳回意见改) | |
| `confidence` | AI 置信度 | number | 建议填 | AI | 0~1 |
| `rule_version` | 差异规则版本 | text | 必填 | AI | 锁定 `R-2026.07` |
| `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 · 共享主数据)
## 九、`validation.record` 芯片验证记录(P-06)
事实类表:本活动只在 S3_assess 给每个 UNKNOWN 差异项挂一行「验证要求」占位
(`validation_status` 恒写 `not_started`),不经数据工单建 —— 真正测完、写测试结果是
另一件事(下游活动或线下流程的事)。S4_review 驳回若要补验证任务,同样由 AI 加一行。
事实类表:本活动只在 S2/S3 给每单挂**一行固定的**「验证要求」占位
(`validation_status` 恒 `not_started`,`validation_type` 恒 `parametric`)——它不是实测
结果,**不许虚构实测**;真正测完、写测试结果是另一件事(下游或线下流程的事)。**一单固定
一条**,即使全部参数都 MATCH、没有待验项,也要写「本轮无待验项」而不是不写这一行
(验收判据 `validation-landed` 钉着 `validation_record_rows = 1`)。
**重点字段(按重要程度)**
1. `validation_id` ★ 主键
2. `validation_internal_pn` ★ 必填 · 引用 `parts.profile.internal_pn`
3. `validation_status` ★ 必填 · 取值域 —— 本活动只写 `not_started`(占位待测)
4. `next_validation_action` ★ 必填 —— 要测什么、什么条件
5. `validation_type` ★ 必填 · 取值域
6. `request_id` ★ 必填 —— 关联需求编号
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 | 主键 | AI | `VAL-<案号数字串>-01/02…` |
| `request_id` ★ | 关联需求编号 | text | 必填 | AI | |
| `validation_internal_pn` ★ | 验证内部料号 | text | 必填 · 引用 `parts.profile.internal_pn` | AI | 候选 |
| `validation_type` ★ | 验证类型 | text | 必填 · 取值域 | AI | 恒 `pin_to_pin`(本活动只挂这一种) |
| `validation_status` ★ | 验证状态 | text | 必填 · 取值域 | AI | 恒 `not_started` |
| `test_condition` | 测试条件 | text | 建议填 | AI | |
| `next_validation_action` ★ | 后续验证动作 | text | 必填 | AI(可按人的驳回意见改) | 要测什么、什么条件 |
| `evidence_ref` | 验证证据引用 | text | 建议填 | AI | |
| `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 不删旧列」的道理。
---
## 八、`sub_eval.pn_check` 料号存在性核对(本活动工作台账)
S0_bind 逐个料号(原 PN + 每个候选)在 `parts.profile` 里核对一遍写下的表:原 PN 一行、
候选各一行。`exists_status` 是这张表的核心结论 —— 两项数据工单的入场哨兵、S1 的入场哨兵
都读它。候选 ≥2 且不在库时,S0d_p08_master 发决策卡问放弃还是创建,人选的答案决定这一行
`exists_status` 改成 `abandoned` 还是等数据工单确认后改成 `created`。
**重点字段(按重要程度)**
1. `exists_status` ★ 必填 · 取值域 `found/absent/created/abandoned` —— 存在性检查的核心结论,S0d 两项与 S1 的入场哨兵都读它
2. `internal_pn` ★ 主键之一
3. `eval_ref` ★ 主键之一 —— 串联本单十张表的案号
4. `role` ★ 必填 · 取值域 `original/candidate`
5. `abandon_reason` ★ 条件必填 · `exists_status=abandoned` 时 —— 人在决策卡选放弃、没写理由时,AI 补写「负责人本轮放弃(未注明原因)」
**数据结构**
| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 |
|---|---|---|---|---|---|
| `eval_ref` ★ | 评估案号 | text | 主键之一 | AI | |
| `internal_pn` ★ | 内部料号 | text | 主键之一 | AI | |
| `role` ★ | 角色 | text | 必填 · 取值域 | AI | `original`(恰一行)/ `candidate` |
| `exists_status` ★ | 在库状态 | text | 必填 · 取值域 | AI | `found`(在库)→ 或 `absent` → `created`/`abandoned`(数据工单/决策卡确认后改) |
| `abandon_reason` | 放弃原因 | text | 条件必填 · `exists_status=abandoned` | AI(据人的决策卡选项改) | 人选放弃未写理由时 AI 补写默认句 |
| `note` | 备注 | text | — | AI | 如「PCN 转 NRND」 |
---
## 九、`sub_eval.input_gap` 评估输入清单(本活动工作台账)
S1_input_confirm 一拍写全:已绑定四项(status=ok)、待补两项(应用场景/BOM 位,status=gap)、
默认可改三项(status=default)、逐颗候选「未披露」参数各一行(status=gap)、齐备项(原 PN 资料
版本,status=ok)。**这张表是本活动唯一让人直接在件上补内容的执行工单载体**:人在
「补充内容」列把 status=gap 的行填上答案,也可以加一行补充 AI 没列出的资料项、删一行标
「不适用」。
**重点字段(按重要程度)**
1. `supplement` ★【人填】条件必填 · `status=gap` 时 —— 缺口行的答案,这一单里唯一不能靠 AI 补出来的事实
2. `status` ★ 必填 · 取值域 `gap/default/ok` —— 决定哪一行必须有 `supplement`
3. `item_key` ★【人填,加行时】主键之一 —— 英文短键;已绑定 / 待补 / 默认 / 待确认各占一批行
4. `input_item` ★【人填,加行时】必填 —— 人读名,加一行 = 补充 AI 没列出的资料项
5. `origin` ★ —— `ai` / `human-added`,标出哪几行是人加的(与 E04 `candidate.origin` 同一口径)
6. `source_ledger` ★ 建议填 —— 缺口来自哪张台账(P-01/P-08/P-09/P-10)
**数据结构**
| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 |
|---|---|---|---|---|---|
| `eval_ref` | 评估案号 | text | 主键之一 | AI | |
| `item_key` ★ | 输入项键 | text | 主键之一 | AI / **人(件上,加行时)** | 英文短键,如 `application_scenario` |
| `input_item` ★ | 输入项 | text | 必填 | AI / **人(件上,加行时)** | 人读名 |
| `source_ledger` ★ | 来源台账 | text | 建议填 | AI | |
| `status` ★ | 状态 | text | 必填 · 取值域 | AI | `gap`(待补)/`default`(默认·可改)/`ok`(齐备) |
| `detail` | 绑定值 / 缺口说明 | text | 建议填 | AI | |
| `supplement` ★ | 补充内容 | text | 条件必填 · `status=gap` | **人(件上)** | 唯一的人手笔答案 |
| `origin` ★ | 来源(ai/human-added) | text | — | AI | |
---
## 十、`sub_eval.verdict` 本次评估结论(本活动工作台账)
一单一行的收口结论,挂着几面「人环走到哪儿了」的记账旗 —— 它们是下一步能不能开工的哨兵
(与 E04 `crm_coverage.verdict` 同一种设计)。
**重点字段(按重要程度)**
1. `input_confirmed_total` ★ —— 评估输入已确认(人环凭据),S2 标准化那一步的入场哨兵之一
2. `review_approved_total` ★ —— 差异结论已审核通过(仅通过时置一),S5 拟落账那一步的入场哨兵
3. `landing_prepared_total` ★ —— 拟落账已备好,T_release 放行门的入场哨兵
4. `summary` ★ —— 本次评估结论,归档件上的那段话
5. `relation_total` ★ —— 替代关系条数
6. `recommended_total` / `conditional_total` / `alternate_total` / `not_recommended_total` ★ —— 四档分布,归档件摘要与放行口 `assessment-released` 载荷都用它
**数据结构**
| 列名 | 人读名 | 类型 | 约束 | 写入者 | 说明 |
|---|---|---|---|---|---|
| `eval_ref` | 评估案号 | text | 主键 | AI | |
| `request_id` | 需求编号 | text | — | AI | |
| `rule_version` | 规则版本 | text | — | AI | 锁定 `R-2026.07` |
| `original_internal_pn` | 原内部料号 | text | — | AI | |
| `candidate_total` | 候选数 | integer | — | AI | 候选数组长度 |
| `input_confirmed_total` ★ | 评估输入已确认 | integer | — | AI | 人环凭据 |
| `review_approved_total` ★ | 差异结论已审核通过 | integer | — | AI | 人环凭据(仅通过时置一) |
| `landing_prepared_total` ★ | 拟落账已备好 | integer | — | AI | S5 写完时置一 |
| `relation_total` ★ | 替代关系条数 | integer | — | AI | |
| `recommended_total` ★ | 推荐 | integer | — | AI | |
| `conditional_total` ★ | 条件推荐 | integer | — | AI | |
| `alternate_total` ★ | 备选 | integer | — | AI | |
| `not_recommended_total` ★ | 不推荐 | integer | — | AI | |
| `summary` ★ | 本次评估结论 | text | — | AI | 归档件上的那段话 |
---
## 十一、人填字段总表(按人办事的那一屏)
## 十、人填字段总表(按人办事的那一屏)
| 活动 · 屏 | 卡的形式 | 落进哪张表 | 人亲手填的列 |
|---|---|---|---|
| S0_bind · 新客户建档(仅客户不在库) | 数据录入卡(entry) | `customer.profile` | `name` · `industry` · `region`(`customer_code` 由入参给,人核对) |
| S0d_p08_master · 候选放弃/创建抉择(候选≥2 且部分缺库) | 决策卡(decide) | 间接改 `sub_eval.pn_check.exists_status` | 选项值(`create`/`abandon`/`custom:`,不直接写字段) |
| S0d_p08_master · 数据工单·芯片主数据创建(P-08) | 执行工单(件上编辑 + 增删行) | `parts.profile` | 全部 12 列(`internal_pn` / `brand_name` / `manufacturer_name` / `specification_description` / `normalized_parameter_set` / `package` / `process_technology` / `lifecycle_status` / `eol_pcn_status` / `datasheet_ref` / `datasheet_version` / `application_scenarios`) |
| S0d_p10_document · 数据工单·原厂技术资料创建(P-10) | 执行工单(件上编辑 + 增删行) | `parts.document` | 全部 9 列(`internal_pn` / `doc_seq` / `document_type` / `brand_name` / `document_title` / `document_version` / `document_ref` / `citation_locator` / `document_status`) |
| S1_input_confirm · 评估输入与资料补齐确认 | 执行工单(件上编辑 + 增删行) | `sub_eval.input_gap` | `supplement`(缺口行必填);加一行时另填 `item_key` / `input_item` |
| S4_review · 差异评估结论审核 | 决策卡循环(通过 / 驳回 + 意见) | 间接改 `substitution.difference_item` / `substitution.relation` / `validation.record` | 意见文本(AI 据此改 `evidence_ref` / `impact_statement` / `unknown_disposition` / `recommendation_reason`;推荐等级不许改) |
| T_release · 评审放行与落账确认 | 验收工单(签字,可打回重做) | `parts.profile` / `parts.document` / `demand.request` / `substitution.relation` + `difference_item` / `validation.record` 并入正式版 | 裁决与意见(打回后 AI 按意见改台账本身,不开新行) |
| `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. **主数据是共享的,物理表里还有别的用途留下的列,本活动不写它们**:
- `customer.profile` 还有 `grade` / `owner` / `payment_terms` / `credit_limit` /
`profile_tags` / `status`(报价链早先声明的客户主台账列)
- `parts.profile` 还有 `customer_pn` / `pin_to_pin_relation_ref`
- `parts.document` 还有 `document_date` / `source_contact_ref`
- `demand.request` 还有 `project_code` / `bom_id` / `customer_pn`
- `substitution.relation` 还有 `project_code`,以及客户批准三列 + `customer_feedback`
(见下条,单独说明只读边界)
- `validation.record` 还有 `customer_code` / `test_parameters` / `test_result` /
`issue_risk` / `sample_version` / `sample_lot` —— 本活动只写「验证要求」占位阶段,
不写实测结果(那是真验证发生后才有的事实)。
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 对应大注释。
2. **本活动往两张主数据表新增了列(基线没有,声明里 add-column 长出来的)**:
`substitution.relation.eval_ref`(评估案号,串联本单十张表)与
`substitution.difference_item.compare_result` / `unknown_disposition`
(工具出参原样存底、UNKNOWN 去向)。这三列只对 ACT-WORK-05 生效,不影响其它活动
对这两张表的读写。
3. **基线物理表里普遍还留着旧九步版本的列**(见各表小节末尾的提醒框)——`add-column`
合并不删旧列,记录浏览里看到它们空着是正常的;判断一列还算不算数,**以活动清单当前
这份声明为准**,不以物理表结构为准。
3. **`customer_approval_status` 是只读边界,不是本活动没做完**:基线在
`substitution.relation` 上建了 `customer_approval_status` / `customer_approval_at` /
`customer_approval_ref` 三列,由**独立审批流**维护;本活动的声明里没有这三列的写权
(禁令 `no-customer-approval-write`)。放行前那一屏那一格是静态文案
「未提交 · 由审批流程维护,本活动不产生」,不是取值的 `data-slot`(见 `docs/debt.md`)。
4. **`substitution.relation.customer_approval_status` 等四列是只读边界,不是本活动没做完**——
由**独立审批流**维护,本活动的声明里没有写权(禁令 `no-customer-approval-write`)。
4. **`parts.profile` 的标准化参数集是一个 text 列,不是子表**:`normalized_parameter_set`
存的是一行一参数的行语法「参数名 | 值 | 单位 | hard/soft | 说明」,四态比对工具的入参
都靠**解析这段文本**,不是关系型的多行记录 —— 用户当场定的口径,《SS02-SS03-SS04》
字面上写的 Structure / 子表在本平台落成这样(见 `docs/debt.md`「参数集是 text 列的行语法」)。
5. **试跑单默认也落真库**:与 E04 / I01 同一个开关(`sys.task.land_master`,测试台起单
默认打开)——跑一遍活动,新建的芯片类型 / 料号 / 参数都会真的进正式表;要它不落,
起单时显式关掉(脚本化 e2e 要求自己传 `false`)。
5. **数据工单是零输入执行工单 + 工作区可加行的件**:料号在 `parts.profile` /
`parts.document` 里查无记录时,人从零建整条记录;确认落草稿层,T_release 放行那一刻
才与其余表一起并入正式版(平台铁律②:写只有草稿与意向,终局唯放行)。一张表一张单:
P-08 一张、P-10 一张。
6. **试跑单默认也落真库**:与 E04 / I01 同一个开关(`sys.task.land_master`,测试台起单
默认打开)—— 跑一遍活动,新建的客户档案、料号、资料记录都会真的进正式表;要它不落,
起单时显式关掉。
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`;业务设计源见 `docs/SS02-ACT-WORK-05-html/`
(活动流程与工单说明 / 一键启动入参说明)与《SS02-SS03-SS04_Demo台账字段细化与统一方案》。*
`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台账字段细化与统一方案》。*
+68 -1
View File
@@ -348,7 +348,7 @@ W05 的两张数据工单(人填的值已入库、必填不缺、逐颗对账)
(读人写的自由文本、落到具体的行上)恰恰是模型该干的活,收在它前面等于把半 auto 的
后半截整个删掉。写半 auto 的步时记住这条:`guide` 不空,这一步答完卡不会被引擎抢先收掉。
**六条纪律**(每一条都是踩出来的):
**十条纪律**(每一条都是踩出来的):
1. **行由确定的那一侧决定,不由 AI 决定。** 明细表要按类型字典 `each` 出来,AI 的出参只是拿主键
`lookup` 的一张查表 —— 它漏一条,那一格就是空,`required` 立刻变成 `_missing > 0`,闸与放行门当场拦住。
@@ -366,6 +366,56 @@ W05 的两张数据工单(人填的值已入库、必填不缺、逐颗对账)
6. **`card` 之后的那几笔,要等人交回之后的那一拍才跑。** 卡真发出去那一拍到此为止;
人交回之后那一拍,`card` 这一笔被跳过、后面的照跑。所以「铺格 → 发卡 → 收口整理」读起来像一拍连做,
其实是两拍 —— 收口那一笔看得见人填的值,这正是它该在的位置。装包时会为此报一条 warn 提醒。
7. **`fields` 里的字段书写顺序不是依赖顺序,不能拿文本次序当保证**(2026-08-30,真跑 e2e
逼出的地基缺陷)。活动清单存进 `sys.activity_package.manifest` 这个 **jsonb 列**之后,
PostgreSQL 按「先短后长、同长字典序」**重排**了每一个嵌套对象的键 —— 而 `project`/`analyze`
的 `fields`(以及 `analyze.input.fields`)原来是靠 `Object.entries` 逐字段求值,后一个
字段能通过 `ctx.computed` 看见前一个已经算出的值,「声明序 = 依赖序」是一条从没写下来、
一进数据库就被打破的隐藏假设,而且**不报错**,只是答案算错了(W05 实证:合成列
`judgement_class` 依赖 `difference_status`,被 jsonb 重排到它前面,读到 `undefined`,
三级 `when` 全部落空、摔进兜底档;`difference_status` 本身走 `lookup:'result:items'`
不依赖字段序,所以是对的 —— 「一个对一个全错」的怪现象由此而来。**隔离测试复现不出来**,
因为它直接读 `dist/manifest.json`,从不经数据库往返 —— 只有真跑 e2e 才会现形)。
**现在字段求值改按依赖关系做拓扑排序**(Kahn 算法,`packages/utils` 的 `topoOrder`),
不是「迭代到不动点」—— 后者是数值收敛判据,会把「结构上真有环、只是两个字段的
`else` 分支恰好在错误状态下互相稳定」这种配方误判成排好了,拓扑排序不会有这个假阴性。
真正的环在**装包静态审**那一关当场拒装,不兜底、不静默放行。**写配方时不要假设
「后一个字段能看见前一个」是因为你把它写在了后面**—— 它能看见,是因为拓扑排序算出来
该这么排;把 `when`/`lookup`/`cat` 引用了哪些字段名想清楚,排序自然是对的,想不清楚就
会在装包时被拒并点名牵涉的字段。
8. **跨笔的依赖仍然是你自己排的顺序**(拓扑排序只管同一笔 `fields` 对象内部的字段序,
`do` 数组里 `project`/`analyze`/`card`/`say` 各笔之间的次序不受它管)—— **一笔读的是
另一笔写出来的数,就必须排在那一笔之后**。W05 的 S1 曾把摆给人看的 `gate_reason`
(案卷那一笔)排在铺主数据/参数骨架那两笔之前:`gate_reason` 的 `when` 链要读
`parts_profile_missing`/`parts_parameter_missing`,而这两个基座读数是骨架那两笔刚
建出来的行撑出来的 —— 排在前面,它读到的是**这一拍刷新之前**的旧数(参数骨架那一刻
还不存在,`_missing` 读到 0),片刻之后骨架一建好又变了。收步与发卡的把关本身没坏
(`card.when` 用的是当下真实读数),坏的是**摆给人看那句话**—— 人最需要解释「还差
什么」的时候,案卷里存的、卡面上摆的,是一句空话。
9. **两笔之间可能存在方向相反的依赖,一趟写不出这个顺序**—— S2 曾把关系表
`substitution.relation` 整笔排在差异明细 `substitution.difference_item` 前面:
差异明细的主键列声明了 `refersTo`(要求指向的关系行已经真实存在),而关系表的
推荐等级/风险/摘要/理由这几个必填列又要读明细表的合成列分布**才算得出来**—— 关系表
排前面,`refersTo` 拦住明细表(「要指的那一行还不存在」);排后面,关系表自己那几个
必填计算列无从算起。**治法是把关系表拆成同主键的两笔投影,中间夹着差异明细**:
第一笔只把主键与不依赖统计的上下文列先摆出来(满足 `refersTo`,这一刻明细表还没有
行,所有分布读数都是基座铺的 0,`when` 链落到默认分支 —— 字段值不对但写得出、不报错);
第二笔排在明细表已经真实写入之后,用**同一份**投影重算一遍,把第一笔的占位值覆盖成
真值。这不是新发明的招式 —— 与 S3 每次人改了四态就要重算关系表,是同一套幂等重算
机制,写配方时复用同一个常量,不要抄两份(抄两份早晚分叉)。
10. **`gateLedgers` 同时管两件事,漏想一半就会出真人会踩到的坑**:①闸要求哪几张表非空
(`_rows > 0`)—— 但「零行 = 没干完」这句话只对**这张卡一定会写**的表成立,列进一张
**只是有时候才写**的表会在合法路径上误拦(W05 吃过这个亏:`parts.chip_type` 若不给
编辑手段就列进闸,挑一个已有类型是主路,这条路上它天然零行,闸永久判「没干完」,
人把别的表填得再全,提交也是 400);②数据工单确认那一刻把闸上点名的表**并进正式表**
(`landsOnConfirm`,§3.1)—— 从闸的名单里删掉一张表只顾了①,却会让「引用它的表已经
落地、它自己没有落地」:别的表用 `refersTo` 指着它,落地那一刻起正式表里就有一行悬空
引用。W05 的解法是让配方**自己把选中的行原样自投影一遍**(不论新建还是选既有的都投,
幂等、不产生多余写入)—— 这样 `mine:` 恒非空,该表**同时**满足「闸不会误拦」与
「确认时有行可落地」两个要求,可以放心留在 `gateLedgers` 里。**卡上闸住的每一张表,
件上必须真有一种写它的手段**(人在件上直接编辑,或配方自己投影进去)—— 否则那颗
「确认」钮永远按不下去,而且错误提示只会说「这张卡就是来建它的」,指向一个界面上
压根摸不到的操作。
---
@@ -560,6 +610,21 @@ A1 是 `customerEmail / subject / body / attachments`)。
「稳定的活动出错」比新功能不 work 伤得多。
8. **用户重复提的要求,说明上一轮没打中病根**:往下再挖一层,别在同一层再修一遍
(「工作成果 1:1」挖了三层才到「办结那一刻存快照」这个真解)。
9. **测试要能咬住,不能只是好看**(2026-08-30,ACT-WORK-05 重写全程验证方法论沉淀):
- **镜像测试证明的是机制,只有读真文件的测试才证明「那个文件此刻是对的」**—— 两码事,
要写两条测试。手写数据喂给一个函数、验证它的行为符合预期,证明的是这段逻辑本身没错;
但生产代码会漂移,fixture 常常把漂移的差别悄悄抹平(实测账:真身是 `f.label ?? f.name`,
手抄件是 `f.name`,fixture 从没暴露过这个差别)。少了一条「import 真身」或「读真文件
逐字比对」的测试,这个洞永远合不上。
- **变异测试本身也可能是空的**—— 撞见过单行行组下 `tr:last-of-type` 与
`tr:first-of-type` 天然等价(表只有一行时,「最后一个」与「第一个」是同一个元素);
往真代码里改一个明摆着错的选择器,6 条「变异」测试全绿零反应,证明测试从没真正执行到
那段逻辑。写变异测试要**先让它红过一次**,不红就是测试没咬住东西,不是代码没问题。
- **能 import 真东西的,一律 import,不要手抄**—— 手抄件从诞生那一刻就开始漂(同一轮
连吃三次这个亏:配方读数函数的手抄漂移、`let` 绑定的手抄漂移、DOM 选择器复刻的手抄
漂移)。跨模块分层边界(如 `activities/` 与 `apps/api/` 互不静态 import)是少数几个
不得不手抄的理由之一;分层边界**内部**(如 `apps/api` 各模块之间)没有理由手抄 ——
通常只是少加了一个 `export`。
## 7. 排障速查表(症状 → 根因 → 处置;全部实测账)
@@ -606,6 +671,8 @@ A1 是 `customerEmail / subject / body / attachments`)。
| 收口前一两拍预算耗尽 | budget 给太紧 | §5.2-⑦ 留一倍余量 |
| 代答脚本交执行卡被拒「必填字段未回填:结果」 | 卡没声明 `fields` 时基座兜底加必填「结果」回执字段 | 交卷 `values.result` 带一句 |
| 验收 pass 后落地判「选择组未选出口」 | routedPort 只在前端回传里顶上(已修:服务端发事件前算有效口) | 升级基座;临时:回传自带 `selectedPorts` |
| 排障时按协议等了、`advancing_until` 也查过是空,还是查不到那条旧任务的证据 | 诊断前先跑了 `reset-runs`,任务行与 `task_memory` 一并清空了 | 排障留痕要在 `reset-runs` 之前捞;要腾新案例的跑道,先把要查的证据记下来再清(2026-08-30) |
| 修完平台代码,行为却和源码对不上,`touch` 源文件也没用 | dev 进程跑的是几小时前的旧代码,连自动重建的 `tsup --watch` 监视进程本身都可能已经退出 | 核 `ps -p <pid> -o lstart=` 与最近一次改动的时间对不对得上;不对就整套重启(kill + 重新 `pnpm dev:api`),别指望 `touch`(2026-08-30) |
## 8. 待实现与已知不一致