Archived
A6 三个去向改选择组,「恰选一口」全链打通 —— 整条流第一次真正跑完
G4 是整条流最后的断点:三个去向写成了必交口里的 outcome 字段 + selective: [], 而引擎只在选择组恰发一口时才注入 outcome —— 结构上永远没有判据。 ## 四层贯通 ① 契约:验收工单加 `portChoice`(组名 + 可选出口)。draft 是 Omit 透传,human-tool 零改动。 ② A6 声明:outcome 移出必交口,三个去向成选择组(won/revise/lost 各一口带人读说明)。 契约 0.2.0、包版 0.7.0。G4 三条边一行没改 —— outcome 由放行选口注入落地事实。 ③ 引擎:验收发卡时从活动契约现读选择组上卡(组是契约的事实,不在步的卡面里声明)。 ④ 前端 AcceptForm:选择组渲染为必选单选,不选「通过」不亮;选中的口随放行回传。 ## 「恰选一口」拦在收单,不拦在落地 —— 拦错位置实测翻过车 第一版把 validateSelection 放进 release.land:回传是**同步收单**(200 = 收到裁决), 落地在推进器里**异步**跑 —— 拦晚了就是「工单已答完、任务卡死、推进器循环报错」(实测)。 改到 checkWorkOrderReply(与「驳回必附意见」同族的收单规矩):工单投影自带 portChoice, 放行回传缺口/口不在组内 → 400 同步拒收,工单还活着,人补选就是。 land 里的 validateSelection 留作兜底 —— 这条从 contracts 导出以来从未被执行的不变量, 现在两个岗位都有了。 ## 实测四连 不选口通过 → 400「放行前先选『议价结果』」,工单未被吃掉 选 won → end_won released,流 completed —— **整条流第一次真正跑完** 选 revise → A4#2 重开新实例(A4#1 留痕不动) 选 lost → A7 情报归档 → end_arch released,流 completed 全程 route-dead-end 事件 0 条 typecheck ✓ lint ✓ depcruise ✓ 498 测试 ✓ Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -63,11 +63,21 @@ describe('MVP_SLOTS', () => {
|
||||
expect(Object.keys(p2)).toContain('needSourcing');
|
||||
});
|
||||
|
||||
it('A6 出口带 outcome 三值 —— G4 议价结果的判据', () => {
|
||||
/*
|
||||
* 三个去向是**选择组**,不是必交口里的一个字段:人在放行时恰选一口,
|
||||
* 那一口就是这次运行的 outcome(引擎按选口注入),G4 判的正是它。
|
||||
* 曾写成 formal 口的必填 outcome + selective: [] —— 引擎只在选择组恰发一口时
|
||||
* 才注入 outcome,没有选择组就结构上永远没有判据,G4 三条边全不成立(实测)。
|
||||
*/
|
||||
it('A6 三个去向是选择组 —— 放行恰选一口,选口即 G4 的判据', () => {
|
||||
const a6 = MVP_SLOTS.find((m) => m.identity.activityId === 'quote-negotiation')!;
|
||||
const p6 = (a6.contract.output.mandatory[0]!.payload as { properties?: Record<string, Record<string, unknown>> })
|
||||
.properties ?? {};
|
||||
expect(p6['outcome']?.['enum']).toEqual(['won', 'revise', 'lost']);
|
||||
const groups = a6.contract.output.selective;
|
||||
expect(groups).toHaveLength(1);
|
||||
expect(groups[0]!.group).toBe('议价结果');
|
||||
expect(groups[0]!.ports.map((p) => p.name)).toEqual(['won', 'revise', 'lost']);
|
||||
// outcome 不再是必交口的必填字段 —— 它由选口给出,不靠活动自己写
|
||||
const formal = a6.contract.output.mandatory[0]!.payload as { required?: string[] };
|
||||
expect(formal.required ?? []).not.toContain('outcome');
|
||||
});
|
||||
|
||||
it('槽位不携实现:入口是抛「未实现」的占位,工具声明只有名', () => {
|
||||
|
||||
Reference in New Issue
Block a user