数据工单只建名单上的料:拼出来的料号与在库的料号写下来都当场拒(W05 1.4.1)

用户在新一单上看到件上多出一行要人补的料号。查实:模型在数据工单里给一颗**不存在的**
XL2596S-ADJ 铺了空骨架 —— 库里没有、入参里也没有,是把两个候选的名字拼出来的;
件上于是多出一行、红带点名它缺品牌。存在性核对本身判对了(ADJ found、XL absent),
错在「判对了却多写了一行」—— 事实在模型手里,能判对也能写错。

两道门,都在写入那一刻:
- `parts.profile.internal_pn refersTo sub_eval.pn_check.internal_pn` —— 只许建核对表里核过的料号,
  凭空拼出来的当场拒(参数/资料两张子表的 internal_pn 本就引用 profile,随之一并拦住)。
- 契约新增对象声明 `createOnly`(平台侧,~25 行):标了的表本单只建不改,键已在正式表当场拒并指路。
  它守的是另一形(在库的料被当成新料重建)。只管 Agent 的写路径,人在件上的保存不受此限。
纯函数 `createOnlyViolation` 可测(复合主键按全部键列比);活动侧三张 parts 表标上;剧本补一句。
当前在跑那一单的草稿里拼出来的那行已手工撤掉。2100 测 / typecheck / lint / depcruise 绿;api-report 已刷。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh
This commit is contained in:
xcs
2026-08-27 13:28:25 +08:00
co-authored by Claude Fable 5
parent 51c87af583
commit fa27cef60e
8 changed files with 99 additions and 4 deletions
@@ -20,7 +20,7 @@
*/
import { describe, expect, it } from 'vitest';
import { emptyGate, findKnownRow, gateAddressesOf, gateOf, parseRowKey, resolveRowKey, rowChanged, writesOf } from '../routes/desk.ts';
import { missingReadings } from '../wiring/engines.ts';
import { createOnlyViolation, missingReadings } from '../wiring/engines.ts';
/** A1 受理记录的声明(裁到这一段用得着的几列):PN 与年用量必填 */
const RECORD = {
@@ -550,3 +550,27 @@ describe('落不下的行要点名 —— 不许悄悄丢', () => {
).toEqual({ customer_code: 'CU-9527', round_ref: 'COV-1' });
});
});
/**
* 只建不改的表(契约 `createOnly`)—— 2026-08-27 W05:数据工单里模型多铺了一行不该建的料号,
* 件上多出一行要人填的记录、红带点名它缺品牌。这一项守「在库的料被重建」这一形。
*/
describe('createOnly:键已在正式表 → 拒并指路', () => {
const target = { label: '芯片主数据(P-08)', keys: ['internal_pn'] };
const formal = [{ internal_pn: 'LM2596S-ADJ', brand_name: 'TI' }, { internal_pn: 'LM2596S-5.0' }];
it('在库的料号写下来 → 一句带指路的拒回(点名 drop)', () => {
const why = createOnlyViolation(target, { internal_pn: 'LM2596S-ADJ' }, formal);
expect(why).toContain('已经有「internal_pn=LM2596S-ADJ」');
expect(why).toContain('drop');
});
it('库里没有的料号 → 放行;复合主键按全部键列比', () => {
expect(createOnlyViolation(target, { internal_pn: 'XL2596S-5.0' }, formal)).toBeUndefined();
const sub = { label: '标准化参数集', keys: ['internal_pn', 'param_name'] };
const rows = [{ internal_pn: 'LM2596S-ADJ', param_name: '输出电压' }];
expect(createOnlyViolation(sub, { internal_pn: 'LM2596S-ADJ', param_name: '输出电压' }, rows)).toBeDefined();
expect(createOnlyViolation(sub, { internal_pn: 'LM2596S-ADJ', param_name: '使能阈值' }, rows)).toBeUndefined();
expect(createOnlyViolation(sub, { internal_pn: 'XL2596S-5.0', param_name: '输出电压' }, rows)).toBeUndefined();
});
});