Mastra 编译器落地:流程包 → workflow(路线 B)—— 回边成出口,变体各带入口

按 spike 实测结论(spike/mastra-reopen/FINDINGS.md)落地 M5-1 的编译器:

- **回边不连回去**,落成回边出口步骤:本 run 到此为止,把
  `{reopen, reopenedBy, reopenTarget, payload}` 写进 result,胶合层起新 run。
  1 run = 1 实例,与「重开产生新实例、旧实例留痕不动」一一对应
- **每个回边目标各编译一份「以它为入口」的变体**(`<包id>@<节点id>`)——
  新 run 只能从入口起步;种子 unified-quote 编出 `@start` 与 `@A4` 两份
- 条件出边 → `.branch()` + **XOR 收口步**:Mastra 把未命中的分支键也放进输出
  (值 undefined),收口步过滤并断言恰一条 —— 「恰取一条」运行时照旧成立;
  条件谓词排掉「前面已命中」的情形(Mastra 求值非短路),无条件边作兜底,
  连兜底都没有时留一条「全不中 → 死路诊断」的出路
- 活动点/审批点 → 「起任务 → suspend()」,副作用写在 `if (!resumeData)` 里
  (实测 resume 时 execute 从头重跑,不这么写会起两次任务、旧的成孤儿)
- 编译期全验:悬空边、正向边成环(未标 reopen)当场报 —— 编译只走到得了的边,
  悬空边会落在递归碰不到的角落(实测:多默认边情形下 E9 压根没被编译)
- Mastra 泛型桥接收在两个薄工厂里,不把类型逃逸撒满文件

测试:编译器四例(变体/无回边/成环/悬空)+ app 层一例(种子真图编得出来);
五闸全过。胶合层(FlowRuntime 的 Mastra 实现)与 CONTRACT/debt 对账随后。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-29 00:15:07 +08:00
co-authored by Claude Fable 5
parent ce59e4e1e4
commit 0ba3a80e13
7 changed files with 480 additions and 10 deletions
+20 -8
View File
@@ -84,7 +84,7 @@
- **驳回走向声明式**:审批点的驳回按 `reject` 声明回指定节点重开新实例,不由临场决定
- **凡单必有来源**:`source` 必填;终点按输出声明交付、按来源回执
## M5-1:运行时切面已立(2026-07-28),编译器实现仍欠
## M5-1:运行时切面已立 + Mastra 编译器已落(2026-07-28/29)
「Mastra 编译器作可替换 adapter、usecase 不动」的端口**已经存在**:`port.ts` 的
`FlowRuntime`(begin / onTaskTerminal / applyFailure / onApprovalVerdict /
@@ -93,11 +93,23 @@ refreshState / terminate)。执行面的语义、留痕与副作用编排整体
持久);`process-service` 是薄壳:定义生命周期(保存/激活/校验)+ 起单前置
(取生效版/调用接口校验/工作组快照/建单留痕)+ 五方法委托。
替换路径:`ProcessServiceDeps.runtime` 注入第二实现(`internal/adapter/
workflow-compiler.ts`,编码规范 §6.1:流程包编译为 Mastra `createWorkflow`/
`createStep`,XOR → `.branch()`,AND → `.parallel()`,节点 `suspend()`/`resume()`)。
八例端到端(`unified-quote-flow.it.test.ts`)是两个实现的**等价性护栏**。
已核实的语义冲突清单(编译器必须绕):回边 reopen 无原语(spike 定案)、
`.parallel()` 任一失败即整体失败(step 内自吞,不让 Mastra 感知)、
**编译器**(`internal/adapter/workflow-compiler.ts`,出口 `compilePackage`)已落,
走 spike 定的**路线 B**(证据:`spike/mastra-reopen/FINDINGS.md`,实测):
- **回边不连回去**,落成一枚**回边出口步骤** —— 本 run 到此为止,把
`{ reopen, reopenedBy, reopenTarget, payload }` 写进 `result`,胶合层据此起**新 run**。
理由:①1 run = 1 实例,与「重开产生新实例、旧实例留痕不动」一一对应
(`.dowhile` 那条路引擎会**覆盖上一轮记录**);②迟到/重复回调在 `.dowhile` 下
把整条 run 打成**不可恢复的 failed**,而活动终态本就来自不受控的外部调用链。
- **每个回边目标各编译一份「以它为入口」的变体**(`<包id>@<节点id>`)—— 新 run 只能从入口起步。
- 条件出边 → `.branch()` + **XOR 收口步**:Mastra 把未命中的分支键也放进输出
(值 undefined),收口步过滤并断言恰一条 —— 「恰取一条」在运行时照旧成立。
- 活动点/审批点 → 「起任务 → `suspend()`」,副作用写在 `if (!resumeData)` 里
(resume 时 execute **从头重跑**,不这么写会起两次任务)。
- 编译期全验:悬空边、正向边成环(未标 reopen)一律当场报 —— 编译只走到得了的边,
悬空边可能落在递归碰不到的角落。
替换路径:`ProcessServiceDeps.runtime` 注入第二实现(胶合层 = `FlowRuntime` 的
Mastra 实现,尚欠 —— 见 debt M5-1)。八例端到端是两个实现的**等价性护栏**。
仍须绕的语义冲突:`.parallel()` 任一失败即整体失败(step 内自吞,不让 Mastra 感知)、
三态聚合投影(abnormal 防假成功,Mastra run 无此态)、`node_instance`
投影表必须照写(治理台按行查)。
投影表必须照写(治理台按行查、实例发号权仍在主工程)。