Files
JX-BPOS/activities/quote-alternative/src/manifest.ts
T
ChaiHaoandClaude Opus 5 ade0fa5a94 A2-A7 全部补齐到验收线;顺手把几处「门拦住却不给路」的坑填了
活动:A2 选型 / A3 寻源 / A4 定价 / A6 议价 / A7 归档五件各补成真活动包 ——
契约宽进(input.required 空,归一在活动内)、台账必填声明(含行级 requiredWhen)、
入场哨兵与 L1/L2 判据一律读基座数、两份件(在办可编辑 + 办结静态快照)、
三案例常备、包内自测。e2e 各三案例 released、行动被拒 0、发口正确、提交闸每单真拦。

基座(共享面,改一处影响所有活动,故每处都带回归):
· 重发判断走在站上场之前 —— 人答完那步常已收掉,readyToWork 先抛「不能再往它上面挂活」,
  于是「重发已答过的卡 = 无事发生」这条唯一活路在最需要时够不着,A1 基线当场熔断;
· 决策卡补幂等兜底(与执行工单同一把尺):头两次无事发生,第三次起才算被拒;
· 输出没成形先就地重问一次,原话进工作日志、不再摆上卡面;
· 工具调用读数 tool_<名>_calls —— 「机检只读基座数」这条纪律第一次覆盖到工具;
· 执行卡 steps/standard 补进出参 schema(从前能到卡面全靠厂商 JSON 模式宽松);
· 件的就地更新改逐节点 morph,保存先把新值镜像进只读那份 —— 不闪、不等回声。

A1(基线,全程守住):措辞三档对齐(必填/建议/选填)、禁止拿历史价当客户目标价、
卡骨架从步骤剧本搬进说明书(压成一拍连做时剧本没摊开,会发出没有指导书的裸卡)。

契约两处**新增可选字段**(非破坏):FieldDeclaration.requiredWhen、工单 finalHtml;
api-report 同批更新。docs/adr 与 docs/archive 的 13 份文件在工作树里不见了,
本次予以恢复而非提交删除。

门禁:typecheck / lint / depcruise / test(1366)全绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 12:28:31 +08:00

532 lines
26 KiB
TypeScript

/**
* 活动包清单 —— 统一报价 A2 · 替代选型(案例活动 · MVP 简化形)。
*
* 业务素材:`docs/MVP示例工作流.html` §1.5,按 2026-08-02 用户改盘的口径归一:
* **A2 是复杂活动(需要大规模业务建模),MVP 期间不展开** —— 只做一张精美的
* HTML 验收报告 + 一张执行单:AI 挑出 C 类、组好清单交 FAE 在系统外选型填回,
* 替代料查库内行情判稳(稳转 A / 不稳转 B),询价受理记录就此转换成
* **询价受理清单**传给下游。提交就是输出。
*
* ── 这里**去掉了 `GRADE_DMN`(替代等级判定表)**(2026-08-02)────────────────
* 它声明着三个入参(pinCompatible / packageSame / paramDelta),而台账上没有任何列
* 承载它们、剧本里也没有一步调它 —— **一张没有求值器的判定表不是资产,是错觉**:
* 看清单的人以为等级是算出来的,实际全程没人算过。MVP 口径下替代等级的判断
* 整个在 FAE 脑子里(系统外),系统不假装接得住;判稳转标那一步的规则
* (≤90 天且稳 → A,否则 → B)写死在剧本里、依据逐行落表,可回核。
*/
import type { ActivityManifest, DmnModel } from '@contracts';
import { activityIdSchema } from '@contracts';
import { ALTERNATIVE_SKILLS } from './skills.ts';
import { ALT_ROSTER, ALT_WORKSHEET } from './views/report.ts';
import { ALTERNATIVE_PHASES, ALTERNATIVE_STEPS, CMMN_XML, NARRATIVE } from './case-model.ts';
const D = 'quote_alternative';
const ACTIVITY_ID = activityIdSchema.parse('quote-alternative');
/** MVP 口径:A2 没有判定表(见文件头注释)—— 判稳规则写死在剧本,依据逐行落表 */
export const ALT_DMN: DmnModel = { drd: [], tables: [] };
export const manifest: ActivityManifest = {
identity: {
activityId: ACTIVITY_ID,
name: 'A2 · 替代选型',
packageVersion: '4.16.0',
category: '统一报价',
sdkVersion: '0.1.0-m5',
checksum: 'dev-checksum-quote-alternative',
},
/* ── 契约七件 ────────────────────────────────────────────────── */
contract: {
activityId: ACTIVITY_ID,
version: '1.0.0',
input: {
/*
* **宽进到底:一个必填都不设**(2026-08-02 用户当场定:「活动是宽进严出,
* 输入接受任何类型」「活动的输入契约是在活动内部归一+补全的」)。
*
* 来路本就有两种形状:流内(上游放行载荷 —— rfqRef + records + 一堆摊平字段)
* 与单飞(一段需求文字,可能连具体型号都没有)。门口拦谁都不对 ——
* 归一是第一个计划项的活,缺什么在活动内问。
*
* 上一版必填 `rfqRef + altRequests`:而 `alt_request` 表 2026-08-02 已在 A1 退休,
* 那个键只是载荷数组通配规则下的空壳(内容与 records 完全相同)——
* 必填一个不存在的东西,单飞起单永远停在补全卡上。
*/
data: {
type: 'object',
required: [],
properties: {
rfqRef: { type: 'string', title: '询价单号' },
records: { type: 'array', title: '上游受理记录(已打标)' },
subject: { type: 'string', title: '需求主题' },
body: { type: 'string', title: '需求正文' },
attachments: {
type: 'array',
format: 'file',
title: '附件',
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: '客户点名的替代授权、禁用品牌、场景约束等写这里',
},
output: {
/*
* 出口载荷**从落地事实推导**:标量字段必须有台账列承载。
* `needSourcing` ← verdict.need_sourcing —— 流程包 E5(仍需寻源 → A3)读的
* 就是它;上一版声明了这个键而台账没有承载列,**E5 从来没有成立过**:
* 选完型仍要寻价的单一律被默认边送去定价(2026-08-02 探查实证)。
*/
mandatory: [
{
name: 'roster',
payload: {
type: 'object',
required: ['rfqRef', 'rows', 'needSourcing'],
properties: {
rfqRef: { type: 'string', title: '询价单号(单标识 —— 下游台账的主键)' },
rows: { type: 'array', title: '询价受理清单(C 类已转 A/B)' },
needSourcing: { type: 'boolean', title: '仍需寻源(清单里还有 B 类)' },
},
},
description: '询价受理清单(C 类已转 A/B)+ 寻源旗',
},
],
/*
* **两条出边 = 一个 XOR 选择组**(对齐流程包 E5/E6)。
* 每一口都写了 `when`(读基座按值现数的 tally 读数),这一单往哪走是**算出来的**:
* 放行时基座按序求值首个命中即出,卡上只如实告诉人走哪一口、凭哪一条。
*/
selective: [
{
group: 'alt-route',
ports: [
{
name: 'to-sourcing',
payload: {
type: 'object',
required: ['rfqRef', 'rows'],
properties: {
rfqRef: { type: 'string', title: '询价单号' },
rows: { type: 'array', title: '询价受理清单(含待补价的 B 类)' },
},
},
description: '清单里仍有 B 类 —— 去寻源补货源价格',
when: 'quote_alternative_list_type_tag_b > 0',
},
{
name: 'to-pricing',
payload: {
type: 'object',
required: ['rfqRef', 'rows'],
properties: {
rfqRef: { type: 'string', title: '询价单号' },
rows: { type: 'array', title: '询价受理清单(全 A 类)' },
},
},
description: '全 A 类:行情完备 —— 直通成本定价',
when: 'quote_alternative_list_type_tag_b = 0',
},
],
},
],
},
/*
* 必交付物只有一件:**询价受理清单**。选型结论(verdict)是它的结论栏,
* 不是第二份成果 —— `ledgers` 说清这份成果由哪几张表承载(A1 的账:
* 各占一条,人在成果面上会看到两样东西)。
*/
requiredDeliverables: [
{
key: 'alt-roster',
label: '询价受理清单(C 类已转 A/B)',
ledger: `${D}.list`,
/*
* 验收页上让 FAE 就地改的那几列 —— 恰是**他的职权面**:
* 替代料三列是他填的,现标 `type_tag` 是判稳规则出的 ——
* 但算得对不对该由人说了算,他改一颗料的标就是在推翻一次判定(A1 的账:
* 不在名单里的列压根不进写回载荷,人改了等于没改)。
* 去向不在其列:它是现标的函数,改标去向就跟着变。
*/
editableAtRelease: ['alt_pn', 'alt_brand', 'alt_grade', 'alt_diff', 'alt_risk', 'alt_verify', 'alt_note', 'type_tag'],
ledgers: [`${D}.list`, `${D}.verdict`],
},
],
/*
* 验收判据 —— **机检(L1/L2)只读基座数得出来的数**(A1 被违反三次的那条纪律):
* `quote_alternative_list_rows` 基座数行数、`_type_tag_a/b/c` 基座按取值域现数。
* 上一版两条 L2 读的是活动自报的 verdict 列(candidate_total / undocumented_total)——
* 那不是门,是「改哪个数能过」的提示。
*/
acceptanceCriteria: [
{
key: 'roster-complete',
label: '清单齐:上游受理记录逐条承继,说了几条清单里就躺着几行',
level: 'L1',
predicate: 'alt_line_total > 0 and quote_alternative_list_rows = alt_line_total',
},
{
key: 'no-c-left',
label: '选型办完:每颗待选型的 C 类料都已填回替代并转成 A 或 B,或经确认剔除',
level: 'L1',
predicate: 'quote_alternative_list_rows > 0 and quote_alternative_list_type_tag_c = 0',
},
{
key: 'tag-conserved',
label: '定标守恒:清单每一行都有现标,A 类加 B 类恰等于总行数',
level: 'L2',
predicate:
'quote_alternative_list_type_tag_a + quote_alternative_list_type_tag_b = quote_alternative_list_rows',
},
/*
* 来源可溯是**裁量**(L3):替代凭什么、说明留没留,机器数不出来 ——
* 别假装机器判得了。裁成不满足照样能提交,理由连着卡交给签字的人。
* 这句 label 与剧本里 judge 的 criterion **必须逐字一致**(judge 按整句对名)。
*/
{
key: 'alt-traceable',
label: '替代料来源可溯且选型说明已留:以原厂官网规格书或客户预授权为准,每颗替代写明凭什么',
level: 'L3',
},
/* L5 是责任关:那张卡本身就是这道关,卡面上不摆它(基座统一滤) */
{ key: 'fae-release', label: 'FAE 放行:签下去这份清单即转正并流向下游', level: 'L5' },
],
/* 去向不给人改(现标的函数);人在验收页改的是成果本身(editableAtRelease) */
revisableAtRelease: [],
prohibitions: [
{ key: 'no-fake-alt', statement: '没有把握的替代不填:宁可在选型备注写明替代未果,也不编一个候选顶上' },
{ key: 'no-banned', statement: '客户点名禁用的品牌与型号不得作替代' },
{ key: 'no-tag-fudge', statement: '不许为过判据改标:现标只能来自判稳规则,或人在件上的亲手修改' },
],
authorization: {
discretion: [
{ key: 'widen-search', statement: 'FAE 可放宽参数或换角度重新选型;是否接受非原厂品牌、参数降级必须问客户,不得替客户决定' },
],
/* 空着不是省事:这个字段全仓没有一个消费者(债 ⑨),写了也不会被执行 */
preauthorizedActions: [],
/* 一颗芯片一张选型单(按顺序派)—— 打扰上限按料数给:MVP 案例 ≤3 颗 + 放行 + 余量 */
humanCallBudget: 6,
},
resources: {
tools: [{ name: 'crm.history-price-read' }],
skills: ALTERNATIVE_SKILLS.map((s) => s.id),
models: ['deepseek-v4-flash'],
roles: ['FAE'],
/*
* FAE 在系统外选型,回来才继续 —— activeMinutes 给足一个工作日的等待;
* token 按实测估:每颗料一张卡各占两三拍,常路(1-2 颗)8-12 拍,留一倍余量。
*/
budget: { steps: 60, tokens: 300_000, activeMinutes: 480, humanCalls: 6 },
},
},
/* ── 声明 ────────────────────────────────────────────────────── */
declarations: {
models: ['deepseek-v4-flash'],
tools: [{ name: 'crm.history-price-read' }],
ledger: [
/*
* ── 这里**去掉了 `shortlist` / `benchmark` / `request` 三张表**(2026-08-02)──
*
* MVP 口径下初筛清单与对标表都不在系统内产出 —— 选型的实质工作在 FAE
* 系统外,系统收的是**结论**(替代料是哪颗、凭什么),落在清单行的三列上。
* 撤掉声明不 drop 物理表(物化只增不破坏),历史行还在库里。
*/
{
domain: D,
object: 'list',
kind: 'record',
caseKey: 'rfq_ref',
label: '询价受理清单',
fields: [
{ name: 'rfq_ref', type: 'text', primaryKey: true, masked: false, label: '询价单号' },
{ name: 'pn', type: 'text', primaryKey: true, masked: false, label: 'PN' },
{ name: 'customer_pn', type: 'text', primaryKey: false, masked: false, label: '客户料号' },
{ name: 'category', type: 'text', primaryKey: false, masked: false, label: '品类' },
{ name: 'brand', type: 'text', primaryKey: false, masked: false, label: '制造商' },
{ name: 'eau', type: 'integer', primaryKey: false, masked: false, label: '年用量 EAU' },
{ name: 'target_price', type: 'number', primaryKey: false, masked: false, label: '目标价' },
{ name: 'lead_time', type: 'text', primaryKey: false, masked: false, label: '交期' },
/* 客户备注承继自上游 —— 预授权替代、认证要求、禁用约束都在这一格,是选型的第一手依据 */
{ name: 'remark', type: 'text', primaryKey: false, masked: false, label: '客户备注(约束/预授权)' },
/*
* **原标与现标两列同名不同物**:orig_type 是上游受理时的三分类(留痕,本活动不改);
* type_tag 是本活动的产物(C 类判稳后转 A/B)。两列都声明取值域,
* 条数由基座按值现数 —— 判据、分布条、出口谓词读的全是基座读数。
*/
{ name: 'orig_type', type: 'text', primaryKey: false, masked: false, label: '原标(上游三分类)', tally: ['A', 'B', 'C'] },
/*
* **定案三格是待选型行的必填**(2026-08-02 用户:「工作区的 HTML 必填项
* 必须填写了才能提交啊,不填写能算任务完成了吗?」)。
* `requiredWhen` 把必填钉在 orig_type=C 的行(一张执行单、件里一颗一页,
* 每页的定案三格都得齐);A/B 行原样承继,不拦。
* 「替代未果」不走「留空交回」—— 那在闸上就是空手交卷:
* 未果 = 在选型备注写明缘由后交回,判稳那一步按剔除处置(剧本 §S3)。
*/
{ name: 'alt_pn', type: 'text', primaryKey: false, masked: false, label: '替代料 PN', requiredWhen: { column: 'orig_type', value: 'C' } },
{ name: 'alt_brand', type: 'text', primaryKey: false, masked: false, label: '替代料品牌', requiredWhen: { column: 'orig_type', value: 'C' } },
/*
* **替代等级与对标三列**(2026-08-02 用户指出「执行单完全是验收单了」后补齐):
* 选型的定案不只是一个型号 —— FAE 的专业产出是**等级 + 差异 + 风险 + 验证建议**
* (照 MVP 素材对标表的口径)。等级用三个机器码(取值域声明,基座按值现数):
* P2P = 同封装同引脚可直换(核对五项:封装尺寸/引脚定义/电气/温度等级/认证);
* FUNC = 功能兼容(需外围电路或 layout 调整);
* OTHER = 其他(部分兼容,须打样验证)。
* 人读名由模版样式表翻译(徽上色 + 图例),FAE 在卡与件上照码填。
*/
{ name: 'alt_grade', type: 'text', primaryKey: false, masked: false, label: '替代等级(P2P/FUNC/OTHER)', tally: ['P2P', 'FUNC', 'OTHER'], requiredWhen: { column: 'orig_type', value: 'C' } },
{ name: 'alt_diff', type: 'text', primaryKey: false, masked: false, label: '参数差异项', expect: true },
{ name: 'alt_risk', type: 'text', primaryKey: false, masked: false, label: '差异风险与改动评估', expect: true },
{ name: 'alt_verify', type: 'text', primaryKey: false, masked: false, label: '验证建议', expect: true },
/*
* 选型备注是**我方写的注**,不标 expect —— 标了 `_gaps` 恒大于 0,
* 那条门就废了(A1 的 remark 同款教训)。
*/
{ name: 'alt_note', type: 'text', primaryKey: false, masked: false, label: '选型备注(来源/预授权)' },
{ name: 'type_tag', type: 'text', primaryKey: false, masked: false, label: '现标', tally: ['A', 'B', 'C'] },
{ name: 'basis', type: 'text', primaryKey: false, masked: false, label: '转标依据' },
{ name: 'history_days', type: 'integer', primaryKey: false, masked: false, label: '替代料距上次报价天数' },
/*
* **选型排程**(2026-08-02 用户定:「多个芯片按顺序派选型单」)——
* 一颗芯片一张选型单,按序派:WAIT 排队 / NOW 当前在办 / DONE 已定案。
* 只有待选型的料写这一列(A/B 行留空,读数自然为 0);工作单照它组画面
* (队列丸、当前颗的档案、填回表只显当前行),判断全在样式表。
* 它是排程不是编辑面 —— 不进 data-col,人改不到。
*/
],
},
{
/*
* 选型结论 —— **一单一行**:汇总数(报告要素卡与哨兵读)、
* 去向旗(流程包 E5 唯一读的东西)、一句人话结论。
*/
domain: D,
object: 'verdict',
kind: 'record',
caseKey: 'rfq_ref',
label: '选型结论',
fields: [
{ name: 'rfq_ref', type: 'text', primaryKey: true, masked: false, label: '询价单号' },
{ name: 'customer_code', type: 'text', primaryKey: false, masked: false, label: '客户码' },
{ name: 'customer_name', type: 'text', primaryKey: false, masked: false, label: '客户名称' },
{ name: 'alt_line_total', type: 'integer', primaryKey: false, masked: false, label: '清单条目数' },
{ name: 'c_total', type: 'integer', primaryKey: false, masked: false, label: '待选型条数' },
/* 「FAE 的答复处理过了」的凭据 —— 只在人环走完才产生(哨兵按它放行下一步) */
{ name: 'filled_total', type: 'integer', primaryKey: false, masked: false, label: '已填回替代料条数' },
{ name: 'converted_a_total', type: 'integer', primaryKey: false, masked: false, label: '判稳转 A 条数' },
{ name: 'converted_b_total', type: 'integer', primaryKey: false, masked: false, label: '转 B(需寻价)条数' },
{ name: 'skipped_note', type: 'text', primaryKey: false, masked: false, label: '剔除了哪几颗·为什么' },
/* 流程包 E5(仍需寻源 → A3)读的就是它 —— 没有这一列,E5 永远判假(实证过) */
{ name: 'need_sourcing', type: 'boolean', primaryKey: false, masked: false, label: '仍需寻源' },
/* 去向定过了没有 —— 旗是布尔,分不清「没写」与「写了 false」,另记一个计数 */
{ name: 'route_decided_total', type: 'integer', primaryKey: false, masked: false, label: '已定去向' },
{ name: 'summary', type: 'text', primaryKey: false, masked: false, label: '选型结论' },
],
},
],
listeners: [
{
/** 时限过半:给 FAE 与负责人提个醒 —— 静态监听,时限必配 */
origin: 'static',
eventType: 'time',
keyFilter: { kind: 'half-deadline' },
semantic: 'observe',
deadline: 'P2D',
escalation: ['nudge'],
},
{
/** 客户改需求(应用条件/参数变更):记一笔观察,回炉与否由 Agent 裁量 */
origin: 'static',
eventType: 'human',
keyFilter: { kind: 'requirement-change' },
semantic: 'observe',
deadline: 'P5D',
escalation: ['nudge', 'reroute'],
},
],
harness: {
memoryWindow: 24,
// FAE 在系统外干活,等他不算停滞 —— 停滞检测放宽到一天
stallAfter: 'P1D',
nudge: [{ after: 'PT12H', action: 'nudge' }],
retry: { times: 2, backoff: 'PT5M' },
},
},
/* ── 资产初始版 ──────────────────────────────────────────────── */
assets: {
businessModel: {
cmmnXml: CMMN_XML,
narrative: NARRATIVE,
dmn: ALT_DMN,
steps: ALTERNATIVE_STEPS,
phases: ALTERNATIVE_PHASES,
},
skills: ALTERNATIVE_SKILLS,
/*
* 两个局面各一张(2026-08-02 用户指出「执行单完全是验收单了」):
* · `alt-worksheet` 替代选型工作单 —— 归一与 FAE 选型期间摆它:逐颗料的选型档案、
* 替代等级判据、pin-to-pin 核对要点、三层筛指引 + 填回工作表。**这是干活的件**;
* 同一局面在办与归档同一张;局面变了换张(A1 三张同款口径)。
*/
viewTemplates: [
/*
* **每份件两态 1:1**(2026-08-02/03 用户两次校准):在办可编辑 ↔ 办结快照
* 静态归档(finalHtml),这是「1:1」的落点;而**选型作业与收口验收各有各的件**。
*/
{ name: 'alt-worksheet', html: ALT_WORKSHEET },
/*
* 收口件(2026-08-03 用户:「验收工单怎么又变成选型了?」)—— 选型作业与
* 收口验收是两件工作,各有各的件:One Pager 归选型执行单,**整合清单归验收**
* (「清单是所有选型完成后收口才整合的东西」)。「1:1」落在每份件与它的
* 办结快照之间(finalHtml),不是全程只许一张。
*/
{ name: 'alt-roster', html: ALT_ROSTER },
],
cardSchemas: {},
distillHooks: [
{
on: 'terminal',
prompt: '归纳本单替代选型的经验:哪类料找得到替代、FAE 的选型依据形态、判稳转标的分布。',
},
],
},
/* ── 样例集 —— 三条形状真不同(常备案例即验收案例)───────────── */
samples: [
{
/*
* ① 流内常路:上游放行载荷的**真实形状** —— records 里同一颗料出现两次
* (受理记录行 + 分类表行)、还混着结论行;顶层带摊平字段。
* 替代料(客户预授权的 PBY)库内无历史 → 判稳转 B → to-sourcing(E5 复活的那条路)。
*/
name: '流内常路 · Itelma 两颗 C 类(一颗一张选型单按序派 · 替代料无历史 → 转 B 去寻源)',
input: {
rfqRef: 'RFQ MD14.1',
customer_code: 'CU-0142',
customer_name: 'Itelma',
type_c_total: 2,
records: [
{
rfq_ref: 'RFQ MD14.1',
pn: 'BBPY00201209601Y00',
customer_pn: '4-14-01502',
category: '滤波器',
brand: 'YAGEO',
eau: 30000,
remark: '客户预授权替代:PBY201209T-601Y-N(BOM Analog 列)',
type_tag: 'C',
},
{
rfq_ref: 'RFQ MD14.1',
pn: 'BBPY00201209601Y00',
history_days: 47,
market_stable: true,
lifecycle: '要求替代',
type_tag: 'C',
basis: 'EOL/缺货/要求替代 → C',
},
{
rfq_ref: 'RFQ MD14.1',
pn: 'DAG2400L321504XZB01-R0',
customer_pn: '9-00-00251',
category: '天线',
brand: '浙江嘉康',
eau: 30000,
remark: '',
type_tag: 'C',
},
{
rfq_ref: 'RFQ MD14.1',
pn: 'DAG2400L321504XZB01-R0',
history_days: 9999,
market_stable: false,
lifecycle: 'EOL',
type_tag: 'C',
basis: 'EOL/缺货/要求替代 → C',
},
{
rfq_ref: 'RFQ MD14.1',
pn: 'CJTB0101R6N',
customer_pn: '4-00-04102',
category: '集成电路',
brand: 'Jiangsu Changjing',
eau: 60000,
type_tag: 'A',
},
{
rfq_ref: 'RFQ MD14.1',
line_total: 3,
type_a_total: 1,
type_b_total: 0,
type_c_total: 2,
need_alternative: true,
need_sourcing: false,
customer_name: 'Itelma',
},
],
},
expect: { ports: ['roster'] },
},
{
/*
* ② 客户点名替代、替代料**库内行情稳** → 判稳转 A → 全 A 直通定价(to-pricing)。
* FAE 若填回 CJTB0101R6N(库内 43 天·行情稳),就走这条路。
*/
name: '点名替代 · 替代料库内行情稳(转 A 直通定价)',
input: {
rfqRef: 'RFQ-2026-0777',
customer_name: '青岛海洋电子',
records: [
{
rfq_ref: 'RFQ-2026-0777',
pn: 'TL072CDR',
category: '运放',
brand: 'TI',
eau: 3000,
target_price: 22,
remark: '客户点名替代:国产运放优先',
type_tag: 'C',
},
{
rfq_ref: 'RFQ-2026-0777',
pn: 'TL072CDR',
history_days: 9999,
market_stable: false,
lifecycle: '要求替代',
type_tag: 'C',
basis: '客户要求替代 → C',
},
],
},
expect: { ports: ['roster'] },
},
{
/*
* ③ 单飞宽进:没有单号、没有上游清单,只有一段需求(类型 + 参数 + 场景)——
* 归一造案号、需求代号,全标 C,照常走完(输入契约在活动内归一+补全)。
*/
name: '单飞需求 · 只有类型与参数(无单号无清单,宽进归一)',
input: {
subject: '车规运放选型需求',
body: '需要一颗车规级双通道运放:供电 3-36V,AEC-Q100,替代 TL072 系列,年用量约 5K。请选型给替代建议;定案后要给采购去问价。',
},
expect: { ports: ['roster'] },
},
],
entry: { file: 'index.js', checksum: 'dev-entry-quote-alternative' },
};