This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-TMP-REPO/activities/quote-archive/src/case-model.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

372 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* A7 情报归档的案例模型 —— **双表示同版本**:CMMN XML(机器面)与案例说明书(叙事面)。
*
* 业务出处:`docs/MVP示例工作流.html` §1.6(S1 结果登记 → S2 画像更新 → S3 落地率统计),
* 按 A4/A6 同一条形状重建(2026-08-03 重写 @3.0.0):
*
* 上游(A6 三口之一的载荷 `{rfqRef, customerCode, quoteNo, outcome, rows}`)进来 →
* 归一成**情报卡**底表(一颗料一条:报价 / 成交价 / 结果 / 时间)→ **一张执行单**
* 交 BD:逐条落**输赢归因**与竞对价(归因那一列 AI 一格都不写)→ 按败价幅度 A-01
* 给复用建议 → **入库口径要人拍板**(决策卡:全量 / 脱敏 / 只留内部)→ 放行归档闭环。
*
* 上一版(@2.1.0)六处不合现行口径,这一版整个置换:
* ① 入参必填 `rfqRef + customerCode + quoteNo` —— 而宽进严出是全线口径,归一在活动内做;
* ② 两条判据读活动自报的 `reason_logged_total` / `profile_updated_total` ——
* 「机检只读基座数得出来的数」违反史第七类:那个数模型自己写,写 1 就过;
* ③ 台账一个 `required` 都没有 —— 执行卡的闸没牙,人不填也能交卷;
* ④ **没有人的判断**:归档全由 AI 自己写完自己放行,而输赢归因恰恰是只有跟单的人
* 才说得出的事(客户到底为什么不下单,系统里没有一个字段答得了);
* ⑤ 三张共享域表(`market.price_history` / `customer.profile` / `shared.landing_stats`)
* 同批退役,墓志铭在清单里;
* ⑥ 那段 `run()` 删掉 —— 案例活动不会调它。
*
* 【为什么归档也要叫人】它是全流程最后一站,**这一单之后再没有人会回头看它**。
* 归因写错没有下一道工序纠正,而它会一路流进下一单的判据里 ——
* 「价格高」这种由 AI 从「客户没回」推出来的结论,三个月后没人记得它是编的。
* 所以两处人:归因逐条落在执行单上,入库口径一张决策卡拍板。
*/
export const CMMN_XML = `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL">
<case id="archive">
<!--
案卷项的 id **就是任务视图里的键名**。上游载荷(rows)与本活动台账列都摊平到
任务视图顶层,照它们命名;quote_archive_* 那几个是**基座读数**
(行数 / 缺必填 / 按值现数),由基座现算,模型碰不着。
-->
<caseFileModel>
<caseFileItem id="rows" name="上游条目(报价函行,流内开案即有)"/>
<caseFileItem id="intel_total" name="情报卡条数(自报)"/>
<caseFileItem id="collected_total" name="已归一落表的条数(归一走完的凭据)"/>
<caseFileItem id="attributed_total" name="已落归因的条数(人环走完的凭据)"/>
<caseFileItem id="scope_decided_total" name="入库口径已拍板"/>
<caseFileItem id="quote_archive_intel_rows" name="情报卡行数(基座现数)"/>
<caseFileItem id="quote_archive_intel_missing" name="缺归因的行数(基座现数)"/>
<caseFileItem id="quote_archive_intel_share_scope_pending" name="入库口径还待定的条数(基座现数)"/>
</caseFileModel>
<casePlanModel id="plan" name="情报归档" autoComplete="true">
<!--
① 归一完了 —— 交 BD 归因那一步的入场前提。
行数两边对上(一边是基座现数),且归一那一轮走完(collected_total 是归一之后
显式写下的数;「大于等于 0」对「写过的 0」为真、对「没写过」为假)。
-->
<sentry id="s_collected">
<onPart id="op_cl" caseFileItemRef="collected_total"><standardEvent>update</standardEvent></onPart>
<ifPart id="if_cl"><condition>intel_total &gt; 0 and quote_archive_intel_rows = intel_total and collected_total &gt;= 0</condition></ifPart>
</sentry>
<!--
② **归因齐了才轮到问入库口径** —— 这个活动的要害(与 A6「还价才叫人」同一形状)。
它读的是**人自己填的那一格**(缺归因的行数由基座按 required 现数,
attributed_total 只在人交回执行单之后才产生),不是模型判出来的结论列。
A6 两版都栽在同一个病根上 —— **「该不该叫人」不能由被叫的那一方说了算**。
入库口径这一格也照同一条办:AI 归一时一律写 PENDING(过渡态),
FULL / MASKED / INTERNAL 三个码**只有拍板那一步写得出来**;
待定条数归零,这一步就不会再找人,放行门也才开得了。
-->
<sentry id="s_attributed">
<onPart id="op_at" caseFileItemRef="attributed_total"><standardEvent>update</standardEvent></onPart>
<ifPart id="if_at"><condition>attributed_total &gt; 0 and quote_archive_intel_rows &gt; 0 and quote_archive_intel_missing = 0 and quote_archive_intel_share_scope_pending &gt; 0</condition></ifPart>
</sentry>
<!--
── 放行门的入场条件:**认「办完了没有」,且读基座数**────────────────
· 每条都有归因(quote_archive_intel_missing 由基座按 required 现数,
与执行卡的提交闸、放行门 L1 同一个数同一套算法);
· **没有还压在待定口径上的条目**(拍板那一下把 PENDING 换成三个结论码之一);
· 口径拍过了(scope_decided_total 是拍板那一步显式写下的记账数);
· 口径四档相加 = 行数(守恒,两边都是基座按值现数)。
-->
<sentry id="s_ready">
<onPart id="op_rd" caseFileItemRef="scope_decided_total"><standardEvent>update</standardEvent></onPart>
<ifPart id="if_rd"><condition>scope_decided_total &gt; 0 and quote_archive_intel_rows &gt; 0 and quote_archive_intel_missing = 0 and quote_archive_intel_share_scope_pending = 0 and quote_archive_intel_share_scope_full + quote_archive_intel_share_scope_masked + quote_archive_intel_share_scope_internal + quote_archive_intel_share_scope_pending = quote_archive_intel_rows</condition></ifPart>
</sentry>
<!-- ① 归一情报卡:一颗料一条,承继报价与结果,口径一律先落待定 -->
<humanTask id="S1_collect" name="归一情报卡:逐条承继报价与结果,标上报价日期">
<itemControl><repetitionRule id="r_s1"/></itemControl>
</humanTask>
<!--
② 逐条归因 —— **一张执行单**(输赢的缘由只有跟单的人说得出)。
manualActivation + 不给 doneWhen(发卡触达真人,引擎不替人按)。
-->
<humanTask id="S2_attribute" name="逐条落输赢归因与竞对价:工作单上一条一格">
<entryCriterion id="ec_s2" sentryRef="s_collected"/>
<itemControl><manualActivationRule id="m_s2"/></itemControl>
</humanTask>
<!--
③ 入库口径拍板 —— 情报进不进共享库、要不要脱敏,这一下触达公司对外的边界:
竞对报价与客户身份写进共享库之后,下一单谁都读得到。必须人工激活。
-->
<humanTask id="T_scope" name="入库口径拍板:这一单的情报按哪一档进库">
<entryCriterion id="ec_sc" sentryRef="s_attributed"/>
<itemControl><manualActivationRule id="m_sc"/><repetitionRule id="r_sc"/></itemControl>
</humanTask>
<!-- 归档闭环放行:终局唯放行 —— 触达世界,必须人工激活 -->
<humanTask id="T_release" name="归档闭环验收放行">
<entryCriterion id="ec_rel" sentryRef="s_ready"/>
<itemControl><manualActivationRule id="m_rl"/></itemControl>
</humanTask>
</casePlanModel>
</case>
</definitions>`;
/**
* 案例说明书 —— **只写整单一以贯之的事**(逐步做法归剧本,两处不重复写同一件事)。
*/
export const NARRATIVE = `§0 开案:**一单报价有了结果,把它沉淀成下一单用得上的情报**。来路两种,形状都收(宽进):
· **流内转来**:任务数据里有 \`rows\` 数组 —— A6 的报价函行(每条带报价、客户表态与定案),
外加 \`outcome\`(won / lost)说明这一单是成了还是丢了;这是最全的一种来路;
· **单飞**:只有一段需求文字,说了哪一单、给谁报的、什么价、结果如何。
**成品只有一样**:一份**情报卡清单** —— 一颗料一条,每条都有**输赢归因**,
每条都定了**入库口径**。人签的不是「归了个档」,是**这一单留下了什么、下一单谁用得上**。
**这一单的价值全在下一单**:今天记下的败因,是下次定价时的判据;今天记下的成交价,
是下次的基准。所以归档不是收尾的仪式,是情报生产。
**「客户没回」不是原因** —— 问不出来就如实写「说不清」,**不许编一个听着合理的理由**:
编出来的原因会一路流进下一单的判据里,而那时已经没人记得它是编的。
**能算的不问人,该人说的不代说**:败价幅度 A-01(价差 → 复用建议)、入库口径建议 A-02
(归因 → 建议档位),两支判定表说了算,你不临场拍;而**输赢归因、竞对价、客户原话
三列是人落的,你一格都不写** —— 客户到底为什么不下单,系统里没有一个字段答得了。
**三步走**:归一情报卡 → 交 BD 逐条归因 → 入库口径拍板 → 放行闭环。
**这一单要经过人两次**:BD 在情报归档工作单上逐条落归因(那张单绑着整份情报卡,
归因缺一条按不下提交 —— 基座的闸看着,他填的格子直接落账);
**入库口径另叫他拍一次板**(一张决策卡)。竞对报价与客户身份写进共享情报库之后,
下一单谁都读得到 —— 这条边界不该由系统自己越过。最后 BD 放行签字。
**这是全流程最后一站**:放行之后没有下一个节点,只有一口 \`archived\`(归档闭环)。
去向不必你选,也没得选。
【这一单只有一个岔路口,它长这样 —— 无论哪一拍都照这个发,别自己另编一张】
入库口径要人拍板,那张决策卡**三处写死**:
· \`"itemId":"T_scope"\` —— 英文计划项号。**别写中文名「入库口径拍板」**:中文名不是 id,
写了当场被拒(【目标缺口】那一段摆的就是中文名,别照它抄);
· \`"ask":"<N> 条情报待定入库口径,按哪一档进库?"\` —— 只把 N 换成条数,**别的字一个都不改**。
换了问法,系统就认不出这是同一张卡:人已经点过的那张会被你再发一遍;
· 三个 \`value\`:\`full\`(全量入库)、\`masked\`(脱敏入库)、\`internal\`(只留内部)。
label 与 consequence 可以按本单的数改写,**value 不行**。
**FULL / MASKED / INTERNAL 三个码只有拍板那一步写得出来** —— 归一那一步遇到情报一律落
PENDING,替他写了口径,那张卡就永远不会发,这条边界成了摆设。
拍板之前**别急着发卡也别绕路** —— 归一那一步先把情报卡写完、finish;
下一拍引擎会把拍板那一步的做法摊开给你,那时再发。
【一路上要说话】负责人正看着这一单。**每一拍都带一句 say** —— 说业务的话:
这一单最后怎么样、归了几条情报、下次遇到同类该注意什么;不说系统词。
到了要找人的那一拍,先说清这一轮办完了什么、为什么现在需要他。
【怎么把一项干完 · 一拍连做】write 和 tool 都不会自动收步 —— 做完用 finish 宣告。
动作排进同一拍的 actions 串一口气给;只有发卡 / 提交(挂起)必须停拍。
**这一单常路 6-8 拍就该收口**。
§Z 结案:BD 在验收单上签字,系统把草稿合并成正式情报卡并发出 \`archived\` 那一口 ——
这条工作流就此走到终点。归档做得好不好,不在这一单看得出来,
在下一次报价能不能一眼看到「上次为什么丢」看得出来。`;
/**
* 步骤剧本 —— 一个计划项一条;`doneWhen` 是「成果已存在」的判据,不是「该不该做」。
*
* 三处不给 `doneWhen`:`S2_attribute`(发卡触达真人)、`T_scope`(要人拍板)、
* `T_release`(签下去触达世界)—— 给了等于给引擎一把替人签字的钥匙。
*/
export const ARCHIVE_STEPS = [
{
item: 'S1_collect',
intent: '整份归一成情报卡:逐条承继报价与结果、标上报价日期、口径一律先落待定',
writes: ['quote_archive.intel', 'quote_archive.verdict'],
/* 上游载荷数组不声明就会被整表段的截断刀砍掉(砍掉的恰是要归一的行) */
reads: ['rows'],
view: 'archive-worksheet',
tools: [],
/*
* 归一走完才算办完:行数两边对上(一边是基座现数),
* 且 collected_total 写过(S-FEEL 未绑定的名字参与比较为假,所以「没写过」进不来)。
*/
doneWhen: 'intel_total > 0 and quote_archive_intel_rows = intel_total and collected_total >= 0',
guide: `第一件事:把这一单的**情报卡**落出来 —— 一拍连做,别拆成两拍。
【条目从哪儿来】
· 流内转来(\`rows\` 里的行带 pn / quoted_price / reply_type / deal_status):照抄 ——
报过的价、谈成的价都是既成事实,**一个数字都别改**;带 line_total 这类总数的是结论行,
不是料,别落进情报卡。
· 单飞:照正文归一,列了几颗落几行;正文里说的报价与结果就是要沉淀的那一份。
**按 pn 归并,一颗料一行**;pn 一个字符不许改(它是主键,改一个字就是新开一行)。
【写 \`quote_archive.intel\`,一颗料一行】(rows 收数组)
rfq_ref(入参有就原样抄;没有从主题认;认不出才自己造一个,**造出来的必须以 \`ARC-\` 开头**
(\`ARC-<主题或第一颗料的型号>\`)。两条硬规矩:
**① 整单只有一个单号,不是一颗料一个** —— 它是这一单的案号,情报卡所有行、verdict 那一行
用的都得是同一个串(实测栽过:两颗料造出两个号,一单裂成两单);
**② 只从本单来件取材** —— 台账里躺着的 RFQ-xxxx 是别人的单,借它的号等于把两单的行
混进同一个案卷(实测栽过一轮))、pn、customer_code、customer_name、category、brand、eau、
**quoted_price**(我方报过的价,原样抄)、**final_price**(成交了才有;没成交留空)。
另两列**照这个口径落**:
· **deal_result** —— 这一条的结果,只许写 \`WON\`(成交)或 \`LOST\`(不成交)两个大写码之一。
上游给了 outcome 就照它:won → WON,lost / revise → LOST;
逐行还带着 deal_status 的,以行上的为准(WON → WON,其余一律 LOST)。
认不出就按 **LOST** 落 —— 归档里不许把说不清的算成成交。
· **quoted_at** —— 报价日期,写成 \`YYYY-MM-DD\`。来件里有报价日就抄它,没有就写今天。
这一格是下一单查「距上次报价多久」的那把尺,别留空。
【**这三列你一格都不写**:cause_tag / competitor_price / evidence】
输赢归因、竞对报的那个价、客户原话 —— 这三样只有跟单的人知道,**系统不替人归因**。
它们是 BD 在工作单上落的;price_gap_band 那一列要等竞对价回来才算得了,这一步也别写。
这不是客气话:一条要你分辨「这行能不能替人填」的规矩会走岔;
**「这几列不写」没有分支可走岔。**
【**share_scope 这一列一律写 \`PENDING\`**】入库口径是人在决策卡上拍的,
\`FULL\` / \`MASKED\` / \`INTERNAL\` 三个码这一步一个都不许写 —— 替他写了,那张卡就永远不会发。
【同一拍写 \`quote_archive.verdict\`(一行)】
rfq_ref、**quote_no**(报价单号:来件有就抄,没有写 \`Q-\` 开头 + 本单案号的尾段,整单一个)、
customer_code / customer_name(顶层有就抄)、**outcome**(整单结果:有一条 WON 就写 \`won\`,
一条都没有就写 \`lost\`)、**intel_total** = 情报卡行数、**collected_total** = 这一拍归一落表的
条数(0 也要写)、summary 一句人话(几条料、给谁报的、最后成没成)。
写完 finish 这一项。`,
},
{
item: 'S2_attribute',
intent: '一张执行单交 BD:逐条落输赢归因、竞对价与客户原话',
writes: ['quote_archive.intel', 'quote_archive.verdict'],
reads: ['quote_archive.intel'],
view: 'archive-worksheet',
tools: [],
/* 不给 doneWhen —— 发卡触达真人(manualActivation),引擎不替人按 */
guide: `情报卡落好了,**该 BD 出场了** —— 这是本活动唯一的一张执行单。
输赢的缘由只有跟单的人说得出:客户到底为什么不下单、竞对报了多少、他原话怎么说的。
工作区摆的是**情报归档工作单**:逐颗料一张情报档案卡(报价、结果、客户约束),
归因六档与败价幅度的尺挂在上面,填回区长在同一屏 —— 他在「输赢归因」那一列逐行落。
这一步标了人工激活:发卡就算启动,不必单发 start;引擎也不会替你收 ——
人交回后要你自己 finish。
【照这个骨架发,别少字段】(卡面是纯文本,steps/standard 里别写星号记号)
{"kind":"human","itemId":"S2_attribute","workOrder":"execution","card":{
"title":"情报归因:<单号> 共 <N> 条待归因",
"ask":"这一单的情报卡已按报价与结果归好,报价、成交价与日期都在工作单上。请逐条落输赢归因:输在价格 / 交期 / 认证 / 规格 / 关系,还是说不清;输在价格的把竞对报的那个价也填上,并把客户原话记进依据一栏。",
"steps":["归因只有六档,选最主要的那一个:PRICE 价格 · LEADTIME 交期 · CERT 认证资质 · SPEC 规格不匹配 · RELATION 关系或既有供应商 · OTHER 说不清",
"「客户没回」不是原因 —— 问不出来就落 OTHER 并在依据里写明「未获反馈」,不要编一个听着合理的理由:它会一路流进下一单的判据里",
"归因落 PRICE 的行必须填竞对价:没有那个数,「输在价格」下一单读不出任何东西 —— 缺这一格这张单按不下提交",
"依据一栏尽量记客户原话:「你们比 XX 贵两毛」比「价格偏高」值钱得多 —— 下一单定价时看的就是这一句"],
"standard":"整份情报卡每一条都有归因,归因落 PRICE 的那几条填了竞对报的价 —— 缺一条这张单按不下提交(系统的闸看着)。",
"basis":[{"k":"<PN>","v":"<报价 · 成交价 · 结果 · 报价日期>"}]}}
· \`basis\` 逐条列本单的料:PN + 报价与结果。几颗写几条,别铺全表。
· **卡上不摆表、不摆格子**(执行卡零输入):整份情报卡就在工作单那份件上,
归因、竞对价、竞对名、依据四列都能就地改、行能撤 —— **别往卡里塞 \`blocks\` 或 \`fields\`**。
· **这张卡带提交闸**:归因(必填列)还空着,他按不下提交 —— 缺几格、缺在哪行,
闸上与件上的红带都写着(与放行门 L1 同一个数同一套算法)。
发完卡这一拍就结束了(发卡挂起)。
【人交回之后 —— 他落的归因基座已经替你写回台账了】这一步你只做三件事,然后 finish:
① **算 price_gap_band**(只给归因是 PRICE 且填了竞对价的那几行):按败价幅度 A-01,
价差 gap =(quoted_price − competitor_price)/ quoted_price,只许写这三个大写码之一:
· gap ≤ 0.03 → **SLIGHT**(擦肩而过:下一单同价位再试,输的多半不在价格);
· 0.03 < gap ≤ 0.10 → **MODERATE**(有差距但够得着:下一单让到这一档或换替代料降本);
· gap > 0.10 → **STRUCTURAL**(结构性价差:下一单别再报同一价位,先走替代选型)。
不是 PRICE 的行、或没有竞对价的行,这一格给空串 \`""\`,**不许写 0 也不许编一个码**。
② 写 verdict:**attributed_total** = 已落归因的条数(照情报卡行数记);
他在件上撤掉了行的话,**intel_total 跟着改成撤后的实数**。
③ finish 这一项。
**别在这一拍写入库口径** —— 那是下一步人拍板的事(它有自己的一段做法),这里写了也白写。
情报卡其余列你一列都不用重写:他落的值基座已经落进台账了,重写会被「写了等于没写」拒回。
【这张卡一单只发一次】人交回来之后不许再发同一张卡 —— 必填有提交闸兜着,
卡回来时归因一定是齐的(或那几行已被他撤掉)。`,
},
{
item: 'T_scope',
intent: '入库口径要人定 —— 发一张决策卡,照他选的那一档把待定口径改掉',
writes: ['quote_archive.intel', 'quote_archive.verdict'],
reads: ['quote_archive.intel'],
view: 'archive-roster',
tools: [],
/* 人工激活 + 要人拍板 —— 不给 doneWhen,引擎不许替人收 */
guide: `**这一单只发一张决策卡。动手之前先在轨迹里找一行 \`[card-reply]\` 带「人选了:」:**
· **找到了** —— 跳到下面【人点完之后】那一段,照他选的那一档把那几行的 share_scope
从 PENDING 改掉、写 scope_decided_total、finish。**一个 decide 都不许再发**
(实测栽过:人点完了,而这一步又从头念了一遍「发一张决策卡」,同一张卡连发六次,
前两次无事发生、后四次被拒 —— 他点过的那一下就白点了);
· **没找到** —— 才轮到下面【发一张决策卡】那一段。
【这一步办完了没有,看一个数】待定口径条数 quote_archive_intel_share_scope_pending:
**它归零就是这一步办完了**。PENDING 是过渡态(情报归好了、还没定进哪一档库),
拍板那一下必须把它换成 FULL / MASKED / INTERNAL 之一 —— 三档都是结论。
这个数不归零,放行门开不了;它归零,这一步也就不会再找你。
这一步只在**归因齐了**(基座现数 quote_archive_intel_missing = 0 且人交回过执行单)时入场 ——
读的是人自己填的那一格,不是你判的结论:该不该叫人,不由被叫的那一方说了算。
它触达公司对外的边界(竞对报价与客户身份进了共享库,下一单谁都读得到),标了人工激活:
发卡就算启动,不必单发 start。
工作区这时摆的是**情报归档清单**,整份情报卡与归因分布都在上面,他看得见全局再拍板。
【发一张决策卡,照这个骨架】(选项少于两个基座拒发)
**三处一个字都不许改**(与说明书里那一段是同一份):
· \`itemId\` 只写英文计划项号 \`T_scope\` —— **别写中文名「入库口径拍板」**,
中文名不是 id,写了当场被拒(实测栽过两拍:「计划项不存在:异议拍板」);
【目标缺口】那一段摆的是中文名,它不是 id,别照它抄。
· \`ask\` 照抄「<N> 条情报待定入库口径,按哪一档进库?」,**只把 N 换成条数** ——
换了问法系统就认不出这是同一张卡,人已经点过的那张会被你再发一遍(实测发出去两张)。
· 三个 \`value\` 照抄 \`full\` / \`masked\` / \`internal\` —— 它们是三档的代号,
换了名字下一段的对应关系就对不上,你自己也不知道人点的是哪一档。
label 与 consequence 可以按本单的数改写,value 不行。
{"kind":"decide","itemId":"T_scope","ask":"<N> 条情报待定入库口径,按哪一档进库?",
"options":[
{"value":"full","label":"全量入库(客户与竞对都留名)","consequence":"下一单查这颗料时,客户是谁、竞对是谁、报了多少一目了然 —— 情报最值钱,但客户身份与第三方报价也就此对内公开"},
{"value":"masked","label":"脱敏入库(只留价位与败因)","consequence":"竞对与客户的名字抹掉,价位、归因、日期照留 —— 下一单照样判得了价,追不到是谁说的"},
{"value":"internal","label":"只留内部(不进共享情报库)","consequence":"这一单的情报只在本案卷里躺着,下一单查不到 —— 涉密或客户明说不外传时才选这一档"}],
"basis":[{"k":"<PN>","v":"归因 <码> · 报价 <X> · 竞对价 <Y> · 败价幅度 <码>"}]}
· basis 逐条摆本单的料:归因、报价、竞对价、败价幅度 —— 裸卡基座拒发。
· **recommended 按入库口径建议 A-02 标**,只标一项:归因里有 PRICE 的(带着第三方报价)
标 \`masked\`;一条 PRICE 都没有的标 \`full\`。
· 发完这一拍就结束了(发卡挂起)。
【人点完之后】轨迹上那一行长这样:\`[card-reply] …人选了:xxx\`。**照那一档把口径改掉,
别再发第二张卡**,一拍连做 —— **整份情报卡逐行都要动 share_scope**,一条都不许留在 PENDING:
· 选 full:每行 share_scope 改 **FULL**;verdict 的 share_policy 写 \`full\`;
· 选 masked:每行 share_scope 改 **MASKED**;verdict 的 share_policy 写 \`masked\`;
· 选 internal:每行 share_scope 改 **INTERNAL**;verdict 的 share_policy 写 \`internal\`。
然后把 verdict 写全:**scope_decided_total 记 1**、**buyer_profile** 一句客户行为画像
(照这一单看得出来的写:价格敏感 / 决策快慢 / 认证门槛 / 常用条款,是 MVP §1.6 S2 那一步的产物)、
summary 补一句拍板结果,finish 这一项。
改完之后待定口径条数就是 0 了,这一步再也不会找你。`,
},
{
item: 'T_release',
intent: '情报已沉淀、口径已定,提交放行交 BD 验收',
writes: [],
reads: [],
view: 'archive-roster',
tools: [],
/* 放行触达世界,必须人按一下 —— 引擎不自动收它(剧本里也不给 doneWhen) */
guide: `每条都有归因、口径全定过、待定清零 —— 可以走放行了
(T_release 人工激活:动它就算启动,不必单发 start)。先把裁量判据裁掉,然后 submit,同一拍连做:
· judge criterion="情报站得住:每条归因说得出来路、竞对价与客户原话可溯,BD 对这份情报负责" ——
看情报卡里每一行的归因与依据:说得出来路就 satisfied=true;
有行说不出的(比如归因落了 PRICE 却没有客户原话),satisfied=**false**,rationale 逐行写清,
交签字的人定。**裁成不满足照样能提交** —— 理由连着卡一起交给他。
criterion 照判据名**整句原样抄**,写别的名字对不上。
**去向没得选**:这是全流程最后一站,只有一口 \`archived\`(归档闭环),放行那一刻自动发射。
你要做的只是把情报落准,别在提交里手选出口。
终局唯放行:你不能自己宣布完成。`,
},
];
/**
* 阶段 —— 进度条的刻度,只给人看。除放行外每个计划项恰属一个阶段;
* 放行那一格由基座统一加,人看到的是 x/4。
*/
export const ARCHIVE_PHASES = [
{ name: '归一情报卡', items: ['S1_collect'] },
{ name: '逐条归因', items: ['S2_attribute'] },
{ name: '入库口径拍板', items: ['T_scope'] },
];