Archived
三面拆分⑮:对齐 Mastra 数据流 —— 边只表达顺序,数据按输入/输出字段名流动
用户定的机制对齐:Mastra 的边不传数据,所有约束力靠输入/输出字段(schema 咬合)完成。
照此把「边映射」整个退役:
· 契约:edge 去 mapping,审批 reject 去 mapping —— 边只剩 顺序 + 条件(读上游
输出字段)+ 标签/回边。fieldMapping 只留给触发器的起单装配(系统边界)。
存量包无需迁移:zod 解析时把旧 jsonb 里的 mapping 静默剥掉,图照常有效。
· 运行时:assemble(边装配)→ flatScope(铺平作用域)——
起单入参垫底 ← 沿途各输出口字段依次盖上 ← 刚发射的载荷(Mastra 语义:
下一步的 input 首先是上一步的 output)。下游按自己的契约取字段;
「__」前缀是运行时自留地,不入业务作用域。审批驳回重开、失败改道同一条路。
· 闭合校验:边未映射/必填未映射/容忍依赖三条映射类检查退役
(宽进之下必填有没有着落是运行时补全单的事,不拦激活);
激活闸只留一条硬伤 —— **同名不同型**:名字即管道,名同型异必坏,
容忍依赖改按名判。改名映射不存在:名不对改契约,不是加映射。
· 种子:A2 的入参 material 更名 pn(它就是 PN;上游 A1 发的名就得是它收的名),
契约 0.3.0、槽位 0.8.0;流程种子边全部去映射。
· 前端:边属性卡砍掉「映射 · 载荷装配」整节,只剩 E号/方向/条件/标签;
节点属性卡新增「数据接口 · 按字段名流动」——每个输入字段标出运行时从哪来
(起单入参 / 上游 X / 补全单),输出口列出发什么名;沿边回溯推导,设计期看得见。
画布保存与依赖文件导出同步去映射。
E2E 顺带把宽进接进真流:快速归档流的 A3 要 outcome 而流里没人产 ——
测试如实走补全路(等人 → 答卡 outcome=won → 开跑 → completed)。
实测走查:A2 节点卡上 pn←上游A1、rfqRef←起单入参、scenario←补全单,推导全对。
五闸绿;契约快照随本提交入库。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -25,11 +25,11 @@ const basePkg: ProcessPackage = processPackageSchema.parse({
|
||||
{ id: 'E', kind: 'end', label: '终' },
|
||||
],
|
||||
edges: [
|
||||
{ id: 'E1', from: 'S', to: 'A', mapping: [{ target: 'x', source: { from: 'initiation', path: 'x' } }] },
|
||||
{ id: 'E2', from: 'A', to: 'B', mapping: [] },
|
||||
{ id: 'E1', from: 'S', to: 'A' },
|
||||
{ id: 'E2', from: 'A', to: 'B' },
|
||||
// 乙的两条互斥出边:通过 → 终点;有异议 → 回甲重开(无网关:分支就在节点身上)
|
||||
{ id: 'E3', from: 'B', to: 'E', condition: 'ok = true', mapping: [] },
|
||||
{ id: 'E4', from: 'B', to: 'A', condition: 'ok = false', mapping: [], reopen: true },
|
||||
{ id: 'E3', from: 'B', to: 'E', condition: 'ok = true' },
|
||||
{ id: 'E4', from: 'B', to: 'A', condition: 'ok = false', reopen: true },
|
||||
],
|
||||
variables: [],
|
||||
});
|
||||
@@ -73,9 +73,8 @@ describe('mergeGraphIntoPackage', () => {
|
||||
expect(minted.sort()).toEqual(['E5', 'E6']);
|
||||
});
|
||||
|
||||
it('画布装不下的声明原样保留:边映射与失败走向', () => {
|
||||
it('画布装不下的声明原样保留:失败走向(边映射已随「名流动」退役)', () => {
|
||||
const merged = mergeGraphIntoPackage(basePkg, graph);
|
||||
expect(merged.edges.find((e) => e.id === 'E1')?.mapping).toHaveLength(1);
|
||||
const a = merged.nodes.find((n) => n.id === 'A');
|
||||
expect(a?.kind === 'activity' && a.failure.kind).toBe('terminate');
|
||||
});
|
||||
@@ -95,12 +94,12 @@ describe('mergeGraphIntoPackage', () => {
|
||||
expect(b?.kind === 'activity' && b.activityId).toBe('act-b');
|
||||
});
|
||||
|
||||
it('边的条件/映射工作副本优先;清空条件 = 变默认兜底边', () => {
|
||||
it('边的条件工作副本优先;清空条件 = 变默认兜底边', () => {
|
||||
const edited: FlowGraph = {
|
||||
...graph,
|
||||
edges: graph.edges.map((e) =>
|
||||
e.from === 'B' && e.to === 'E'
|
||||
? { ...e, condition: 'ok = true and sum > 3', mapping: [{ target: 'y', source: { from: 'initiation', path: 'y' } }] }
|
||||
? { ...e, condition: 'ok = true and sum > 3' }
|
||||
: e.from === 'B' && e.to === 'A'
|
||||
? { ...e, condition: '' } // 设计者把条件清空:这条边成了默认兜底
|
||||
: e,
|
||||
@@ -109,10 +108,7 @@ describe('mergeGraphIntoPackage', () => {
|
||||
const merged = mergeGraphIntoPackage(basePkg, edited);
|
||||
const pass = merged.edges.find((e) => e.id === 'E3');
|
||||
expect(pass?.condition).toBe('ok = true and sum > 3');
|
||||
expect(pass?.mapping).toHaveLength(1);
|
||||
const back = merged.edges.find((e) => e.id === 'E4');
|
||||
expect(back?.condition).toBeUndefined();
|
||||
// 没动过的边沿用旧声明
|
||||
expect(merged.edges.find((e) => e.id === 'E1')?.mapping).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -103,13 +103,12 @@ export function mergeGraphIntoPackage(
|
||||
.filter((e) => semanticIds.has(e.from) && semanticIds.has(e.to))
|
||||
.map((e) => {
|
||||
const prior = priorEdge.get(`${e.from}→${e.to}`);
|
||||
// 边属性(传递契约)以属性卡的工作副本优先;没动过的沿用旧声明
|
||||
// 边只带顺序与条件(对齐 Mastra):条件以属性卡的工作副本优先;没动过的沿用旧声明
|
||||
const condition = e.condition !== undefined ? (e.condition.trim() === '' ? undefined : e.condition) : prior?.condition;
|
||||
return {
|
||||
id: prior?.id ?? `E${nextEdgeNo++}`,
|
||||
from: nodeIdSchema.parse(e.from),
|
||||
to: nodeIdSchema.parse(e.to),
|
||||
mapping: (e.mapping as Edge['mapping'] | undefined) ?? prior?.mapping ?? [],
|
||||
...(condition === undefined ? {} : { condition }),
|
||||
reopen: prior?.reopen ?? false,
|
||||
...(e.label === undefined ? {} : { label: e.label }),
|
||||
|
||||
@@ -71,7 +71,6 @@ export function packageToGraph(pkg: ProcessPackage, marks: readonly InstanceMark
|
||||
...e,
|
||||
code: sem.id as string,
|
||||
...(sem.condition === undefined ? {} : { condition: sem.condition }),
|
||||
mapping: sem.mapping as unknown[],
|
||||
};
|
||||
});
|
||||
return { ...stored, edges, nodes: [...withState(stored.nodes), ...withState(extras)] };
|
||||
|
||||
Reference in New Issue
Block a user