人手改的那一手真正落得下去;界面不撒谎那几处补齐

手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的:

件上就地改
· 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名,
  于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 ——
  而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。
· **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。
  删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)——
  「没回传就是删了」会把一次不完整的回传变成一次删表。
· **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。
· 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。

判据与门
· 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来,
  人填好的目标价它照样判「客户未提供」的毛病由此消掉。
· 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。
· 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开,
  于是一单没打标就进了放行门。三个数全由基座现数。
· 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。
  **指向两扇打不开的门,比不指还糟。**

界面不撒谎
· 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。
· 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、
  往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。
· 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。
· 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。
· 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。

A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。
五道门禁全绿(971 passed)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
2026-08-02 13:41:44 +08:00
co-authored by Claude Opus 5
parent 94f1c7a4ef
commit 259192f736
228 changed files with 27850 additions and 2847 deletions
+94 -56
View File
@@ -1,77 +1,115 @@
/**
* A6 报价议价的案例模型 —— **双表示同版本**:CMMN XML(机器面)与案例说明书(叙事面)。
* A6 报价议价的案例模型 —— **发出去是触达世界的一下,等回音是立监听**
* (2026-07-31 照 A1 的状态机模式重做)。
*
* 2026-07-29 由步序声明(flow)改写而来。发出报价是**触达外界**的一下,
* 恒需人工激活(AM 4.1 原理一);客户回音是外部事件,靠 userEventListener 等,
* 不靠轮询。三个去向(成交 / 有异议 / 拒绝)由验收放行时的选择组给出。
* 这个活动的要害不是步数,是**两处不可省的边界**:
* · 发正式报价单 = 触达外界 → 必须人工激活(铁律③ / AM 4.1);
* · 客户什么时候回是**外界的事** → 立监听等它,不在这一单里轮询、也不假装等到了。
*
* 三项:
* T_compose —— 把草案落成正式报价单(条款齐全),写进台账;
* T_send —— 发出并等回音:人工激活 → 登记发信意向 + 立监听;
* T_release —— 放行(人工激活),三个去向由输出选择组给出。
*/
export const CMMN_XML = `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL">
<case id="negotiate">
<caseFileModel>
<caseFileItem id="draft" name="报价草案"/>
<caseFileItem id="quote" name="正式报价单"/>
<caseFileItem id="reply" name="客户回音"/>
<caseFileItem id="quote_no" name="报价单号(开案即有)"/>
<caseFileItem id="quote_total" name="报价行条数"/>
</caseFileModel>
<casePlanModel id="plan" name="报价议价" autoComplete="true">
<sentry id="s_draft">
<onPart id="op_df" caseFileItemRef="draft"><standardEvent>create</standardEvent></onPart>
</sentry>
<sentry id="s_quote_ready">
<onPart id="op_qr" caseFileItemRef="quote"><standardEvent>update</standardEvent></onPart>
<ifPart id="if_qr"><condition>missingQuoteFields = 0</condition></ifPart>
</sentry>
<sentry id="s_sent">
<onPart id="op_st" caseFileItemRef="quote"><standardEvent>update</standardEvent></onPart>
<ifPart id="if_st"><condition>sent = true</condition></ifPart>
<sentry id="s_composed">
<onPart id="op_cp" caseFileItemRef="quote_total"><standardEvent>update</standardEvent></onPart>
<ifPart id="if_cp"><condition>quote_total &gt; 0</condition></ifPart>
</sentry>
<stage id="S1" name="成单发出" autoComplete="true">
<entryCriterion id="ec_s1" sentryRef="s_draft"/>
<humanTask id="T_compose" name="人工生成正式报价单">
<itemControl><manualActivationRule id="m_cp"/><repetitionRule id="r_cp"/></itemControl>
</humanTask>
<humanTask id="T_send" name="人工发出报价单">
<entryCriterion id="ec_send" sentryRef="s_quote_ready"/>
<itemControl><manualActivationRule id="m_sd"/></itemControl>
</humanTask>
</stage>
<milestone id="M_sent" name="报价已发出"/>
<stage id="S2" name="跟进议价" autoComplete="true">
<entryCriterion id="ec_s2" sentryRef="s_sent"/>
<humanTask id="T_followup" name="人工按 D+3/D+7 跟进">
<itemControl><manualActivationRule id="m_fu"/><repetitionRule id="r_fu"/></itemControl>
</humanTask>
<planningTable id="pt_s2">
<humanTask id="T_revise" name="人工议价调整"/>
</planningTable>
</stage>
<humanTask id="T_release" name="人工议价结果验收放行">
<itemControl><manualActivationRule id="m_rl"/></itemControl>
<!-- 落正式报价单:条款齐全才叫一份能发出去的件 -->
<humanTask id="T_compose" name="落正式报价单:条款齐全,逐条落台账">
<itemControl><repetitionRule id="r_cp"/></itemControl>
</humanTask>
<timerEventListener id="TL_d3" name="D+3 跟进">
<timerExpression>P3D</timerExpression>
</timerEventListener>
<timerEventListener id="TL_d7" name="D+7 跟进">
<timerExpression>P7D</timerExpression>
</timerEventListener>
<userEventListener id="UL_reply" name="客户回音"/>
<!-- 发出并等回音:**触达外界**,必须人工激活 -->
<humanTask id="T_send" name="发出报价并等客户回音">
<entryCriterion id="ec_sd" sentryRef="s_composed"/>
<itemControl><manualActivationRule id="m_sd"/><repetitionRule id="r_sd"/></itemControl>
</humanTask>
<humanTask id="T_release" name="议价结果验收放行">
<entryCriterion id="ec_rel" sentryRef="s_composed"/>
<itemControl><manualActivationRule id="m_rl"/></itemControl>
</humanTask>
</casePlanModel>
</case>
</definitions>`;
export const NARRATIVE = `§0 开案:拿到报价草案即开案。成品是**一份发出去的正式报价单**,外加一个明确的结果。
export const NARRATIVE = `§0 开案:拿到报价草案即开案。成品是**一份发出去的正式报价单**,外加一个明确的结果
(成交 / 有异议 / 拒绝)—— 那三个去向由放行时的选择组定。
S1 成单发出:把草案写成正式报价单(中英双语 PDF)—— 单价、交期、SPQ、MOQ、贸易条款、
有效期六样缺一不可,有效期按 P-04 注明。**发出这一下必须人工按**:它触达客户,
系统不替人做这个决定。写类动作先登记意向,验收放行的那一刻才真发。
这一单有两处不能含糊:
· **发出去是触达世界的一下** —— 那一项标了人工激活,人不按你发不出去;
· **客户什么时候回是外界的事** —— 立监听等它,别在这一单里轮询,更别假装等到了。
S2 跟进议价:没有跟进的报价等于没报 —— D+3、D+7 两记提醒自动到点,人去问。
客户提异议就议价调整(裁量项:改价、改交期、换料都可能),改完是新一版报价单,重新走发出。
【T_compose 落正式报价单】把草案落成能发出去的件:单价 / 交期 / SPQ / MOQ / 贸易条款 /
有效期,逐条写进 \`quote_negotiate.formal_quote\`,并把 quote_total(条数)写上。
条款缺一样都不算一份能发的报价单 —— 缺了就问,别自己填一个"看起来合理"的。写完 finish。
§Z 结案:验收时**恰选一口**给出去向 —— 成交 / 有异议 / 拒绝或超时。
选哪一口就是这一单的结论,下游网关读的就是它;不选口不许放行,含糊的结果比没结果更坏。`;
【T_send 发出并等回音】**先 start T_send**(人工激活:发出去触达客户)。
然后用 \`mail.send\` 登记发信意向(写类工具:过程中只登记,放行那一刻才真发),
接着 listen 一个 data 事件等客户回音,**时限必配**(比如 P7D)——
不配时限就是个幽灵等待,这一单会永远挂着。
回音到了再看:成交 / 有异议 / 拒绝,分别对应放行时的三个出口。
【提交放行】**先 start T_release**(人工激活),提交前用 judge 裁掉裁量判据
(criterion 照判据名原样写),然后 submit。终局唯放行:你不能自己宣布完成。
【一路上要说话】每一拍带一句 say,说**业务的话**:报了什么价、什么时候发的、客户回了什么。
一两句、口语。别报「写草稿 N 行」这种系统词。`;
/**
* 步骤剧本 —— 与 A1 同一套。
* `T_send` 与 `T_release` 都不给 `doneWhen`:一个触达客户、一个触达世界,都得人按一下。
*/
export const NEGOTIATE_STEPS = [
{
item: 'T_compose',
intent: '把草案落成条款齐全的正式报价单,并结算条数',
writes: ['quote_negotiate.formal_quote', 'quote_negotiate.verdict'],
reads: [],
tools: [],
doneWhen: 'quote_total > 0',
guide: `逐条写进 \`quote_negotiate.formal_quote\`:单价 / 交期 / SPQ / MOQ / 贸易条款 / 有效期,
并把 quote_total 写上。条款缺一样都不算一份能发的报价单 ——
缺了就问,**别自己填一个「看起来合理」的值**。
**同一拍再写一行 \`quote_negotiate.verdict\`**(验收机检读的就是它):
· quote_total = 报价行条数;missing_field_total = 六项条款还缺的条数(要 = 0);
· validity_days = 报价有效期天数(要 > 0);
· followup_total = 已建跟进条数(**要 ≥2** —— D+3 与 D+7 两条,没有跟进的报价等于没报);
· summary 一句人话结论。写不对 submit 会被门挡回来。写完再 finish。
**每一行都要带 \`rfq_ref\`** —— 它是这一单的单标识(起单入参里就有,原样抄进去)。
台账主键第一列就是它:不写,这一行会和**别的单**同料号的那一行撞主键,把人家覆盖掉
(实测栽过:A4 的报价草案主键只有 pn,跑四轮只剩最后一轮的 3 行)。`,
},
{
item: 'T_send',
intent: '发出报价并立监听等客户回音',
writes: [],
reads: [],
tools: ['mail.send'],
// 触达客户 —— 人工激活,不给 doneWhen
guide: `**先 start T_send**(人工激活:发出去触达客户)。
用 \`mail.send\` 登记发信意向(写类工具:只登记,放行那一刻才真发),
再 listen 一个 data 事件等回音,**时限必配**(如 P7D)—— 不配就是幽灵等待,这一单会永远挂着。`,
},
{
item: 'T_release',
intent: '议价有结果,提交放行交 BD 验收',
writes: [],
reads: [],
tools: [],
guide: `**先 start T_release**(人工激活),提交前用 judge 裁掉裁量判据,然后 submit。
三个去向(成交 / 有异议 / 拒绝)由放行时的选择组定。
**台账里要写 outcome**(won / revise / lost)—— 下游三条边读的就是它,不写整条流走不下去。`,
},
];