Archived
三面拆分⑬:流程包纯编排化 —— 编排与传递契约归包,实现方式归库,激活看双判据
模型(用户定):流程包只决定活动编排 + 活动传递的契约,不决定活动的实现方式; 节点只分活动点与审批点;图只定名称与顺序,属性在卡上填,链接好实现即可运行; 激活 = ①Mastra 式图编排闭合完善 + ②活动装载,两面判据。 schema(库经外科手术清空,无历史包袱,网关彻底出局): - contracts:删 gateway 节点变体 / GATEWAY_KINDS / GATEWAY_KIND_LABEL / 'gateway-deprecated' 违规项 —— 节点四种:start/end(结构件)+ activity/approval - routing:joinReady 整个删除(多入边即异或汇合,没有「等回齐」;AND 待并行组); selectEdges 精简;closure 删 checkGateway;process-service 删汇合等待与网关直传, 节点一律只收装配后载荷(verdict/outcome 判据由系统注入,不走映射) 编排面(画布 = 名称 + 顺序,其余在属性卡): - 元素盘五件:活动点(单一,不再分标准/案例 —— 那是实现方式)/审批点/起止/卡点 - 属性卡:节点类型二分(活动点/审批点);新增「链接活动实现」选择器(装载面, FlowNode.act 经 merge 成为语义 activityId);范式/版本降为只读装载信息 - 边属性可填:条件(S-FEEL)+ 映射(上游输出口/起单载荷/常量三种来源; workflow-var/expression 归系统隐性,不进设计者面)—— FlowEdge.condition/mapping 工作副本,pkg-to-graph 回填、graph-to-pkg 合并,清空条件即变默认兜底边 - 异常看板 → 激活双判据看板:图编排(结构+属性+契约)与活动装载(unknown-activity) 分面亮灯,两面清零激活才亮 - pkg-to-graph 去 paradigms 绿化与网关分支;图例/文案全线「活动点/审批点」 测试:closure 27(joinReady 两组 describe 随机制摘除)、advance-order 按真图形状重写 (去 as never 捏造的网关节点)、graph-to-pkg 9(新增 act 合并 + 边契约合并/清空用例)、 seed 8、端到端八例真库全绿;五闸退出码验过(api:check 提交后转绿)。 库:purge 后重种,唯一 unified-quote@v1 生效(10 节点 14 边,双面判据齐)。 债:M5-5 清账(兼容路径连 schema 一起摘);M5-4(并行组)仍挂。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -20,12 +20,7 @@ export type InstanceMark = {
|
||||
/** 每行最多几个方框 —— 蛇形折行的行宽;取原型 business 预设的观感 */
|
||||
const ROW_WIDTH = 4;
|
||||
|
||||
export function packageToGraph(
|
||||
pkg: ProcessPackage,
|
||||
marks: readonly InstanceMark[] = [],
|
||||
/** 活动范式(activityId → loop|flow):案例活动画绿 —— 范式住活动库,包里只有引用 */
|
||||
paradigms?: ReadonlyMap<string, string>,
|
||||
): FlowGraph {
|
||||
export function packageToGraph(pkg: ProcessPackage, marks: readonly InstanceMark[] = []): FlowGraph {
|
||||
const markOf = new Map(marks.map((m) => [m.nodeId, m]));
|
||||
|
||||
/*
|
||||
@@ -46,38 +41,47 @@ export function packageToGraph(
|
||||
const bottom = stored.nodes.reduce((max, n) => Math.max(max, (n.y ?? 0)), 0) + 90;
|
||||
const extras: FlowNode[] = missing.map((n, i) => ({
|
||||
id: n.id,
|
||||
kind: n.kind === 'gateway' ? ('gateway' as const) : n.kind === 'start' ? ('start' as const) : n.kind === 'end' ? ('end' as const) : ('activity' as const),
|
||||
kind: n.kind === 'start' ? ('start' as const) : n.kind === 'end' ? ('end' as const) : ('activity' as const),
|
||||
label: n.label,
|
||||
...(n.kind === 'gateway' ? { mode: n.gateway === 'parallel' ? ('and' as const) : ('x' as const) } : {}),
|
||||
...(n.kind === 'approval' ? { flavor: 'approval' as const, owner: n.approver.position } : {}),
|
||||
...(n.kind === 'activity' ? { code: n.id, owner: n.owner.position } : {}),
|
||||
...(n.kind === 'activity' ? { code: n.id, owner: n.owner.position, act: n.activityId } : {}),
|
||||
x: 30 + (i % 6) * 160,
|
||||
y: bottom + Math.floor(i / 6) * 60,
|
||||
}));
|
||||
const semanticById = new Map(pkg.nodes.map((n) => [n.id as string, n]));
|
||||
const withState = (list: readonly FlowNode[]) =>
|
||||
list.map((n) => {
|
||||
const state = stateOf(markOf.get(n.id));
|
||||
return state === undefined ? n : { ...n, note: state };
|
||||
const sem = semanticById.get(n.id);
|
||||
// 链接的实现(装载面)以语义为准回填 —— 存证里的 act 只是画布工作痕迹
|
||||
const linked = sem?.kind === 'activity' ? { act: sem.activityId as string } : {};
|
||||
const base = { ...n, ...linked };
|
||||
return state === undefined ? base : { ...base, note: state };
|
||||
});
|
||||
return { ...stored, nodes: [...withState(stored.nodes), ...withState(extras)] };
|
||||
/*
|
||||
* 边属性(条件/映射)从语义边回填到画布边:属性卡编辑的是画布工作副本,
|
||||
* 保存时 graph-to-pkg 又以它为准 —— 回填缺席的话,打开即保存会把契约冲掉。
|
||||
* 经卡点的边按卡点 `to` 认亲(A5→c_reject 即 A5→A4)。
|
||||
*/
|
||||
const calloutTo = new Map(
|
||||
stored.nodes.filter((n) => n.kind === 'callout' && n.to !== undefined).map((n) => [n.id, n.to!]),
|
||||
);
|
||||
const semEdge = new Map(pkg.edges.map((e) => [`${e.from}→${e.to}`, e]));
|
||||
const edges = stored.edges.map((e) => {
|
||||
const sem = semEdge.get(`${e.from}→${calloutTo.get(e.to) ?? e.to}`);
|
||||
if (sem === undefined) return e;
|
||||
return {
|
||||
...e,
|
||||
...(sem.condition === undefined ? {} : { condition: sem.condition }),
|
||||
mapping: sem.mapping as unknown[],
|
||||
};
|
||||
});
|
||||
return { ...stored, edges, nodes: [...withState(stored.nodes), ...withState(extras)] };
|
||||
}
|
||||
|
||||
const nodes: FlowNode[] = pkg.nodes.map((n) => {
|
||||
const state = stateOf(markOf.get(n.id));
|
||||
|
||||
if (n.kind === 'gateway') {
|
||||
return {
|
||||
id: n.id,
|
||||
kind: 'gateway',
|
||||
label: n.label,
|
||||
// 并行网关画 +,异或网关画 ×(判断)
|
||||
mode: n.gateway === 'parallel' ? 'and' : 'x',
|
||||
...(n.sub === undefined ? (n.mode === 'join' ? { sub: '汇合' } : { sub: '分叉' }) : { sub: n.sub }),
|
||||
...(n.basis === undefined ? {} : { basis: n.basis }),
|
||||
...(state === undefined ? {} : { note: state }),
|
||||
};
|
||||
}
|
||||
|
||||
if (n.kind === 'start') return { id: n.id, kind: 'start', label: n.label };
|
||||
if (n.kind === 'end') return { id: n.id, kind: 'end', label: n.label };
|
||||
|
||||
@@ -96,7 +100,7 @@ export function packageToGraph(
|
||||
return {
|
||||
...base,
|
||||
owner: n.owner.position,
|
||||
...(paradigms?.get(n.activityId) === 'loop' ? { flavor: 'case' as const } : {}),
|
||||
act: n.activityId,
|
||||
...(n.in === undefined ? {} : { in: [...n.in] }),
|
||||
...(n.out === undefined ? {} : { out: [...n.out] }),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user