From b7145c325bfbc6f29885f00690e2848da4651645 Mon Sep 17 00:00:00 2001 From: shad Date: Sun, 14 Jun 2026 12:23:22 +0400 Subject: [PATCH] fix(wizard): use real EA2 flow/defines collections for steps EA2's apply-batch doc collections are restricted to ['data','flow','rule', 'version','view']; there is no 'ea2_node'. Steps are themselves 'flow' documents (kind='definition') linked to the parent flow via the 'defines' edge collection with role='next' (or 'presentation'). handleStructureSave now does two batches: 1) create N step flows, capture server-assigned keys 2) wire parent->step1->...->stepN with create_edge on 'defines' --- src/lib/wizardApi.ts | 31 +++++++++++++++++++------------ src/scenes/Wizard.tsx | 32 ++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/lib/wizardApi.ts b/src/lib/wizardApi.ts index b619e5a..6960392 100644 --- a/src/lib/wizardApi.ts +++ b/src/lib/wizardApi.ts @@ -120,30 +120,37 @@ export const wizardApi = { return api.startTransaction(process_definition_id, business_subject, signal); }, - /** Helpers so call sites don't have to know the discriminated-union shape by heart. */ ops: { - createNode(flowKey: string, node: { _key: string; display_name: string; dispatch_kind: string; agent_capability?: string }): CreateOp { - const { _key, ...rest } = node; + createStep(node: { display_name: string; dispatch_kind: string; agent_capability?: string; description?: string }): CreateOp { + const name = node.display_name.toLowerCase().replace(/[^a-z0-9]+/g, "_").slice(0, 40) || "step"; return { op: "create", - coll: "ea2_node", - data: { _key, flow_key: flowKey, ...rest }, + coll: "flow", + data: { + kind: "definition", + name, + display_name: node.display_name, + status: "draft", + description: node.description || node.display_name, + source_context: "EA2_WIZARD_STEP", + dispatch_kind: node.dispatch_kind, + ...(node.agent_capability ? { agent_capability: node.agent_capability } : {}), + }, }; }, - createEdge(_flowKey: string, fromKey: string, toKey: string, role = "next"): CreateEdgeOp { - // EA2 server rejects `data` on create_edge despite the OpenAPI schema marking it optional. + createStepEdge(parentFlowKey: string, childFlowKey: string): CreateEdgeOp { return { op: "create_edge", - edge_coll: "ea2_edge", - from: `ea2_node/${fromKey}`, - to: `ea2_node/${toKey}`, - role, + edge_coll: "defines", + from: `flow/${parentFlowKey}`, + to: `flow/${childFlowKey}`, + role: "next", }; }, publishFlow(flowKey: string): UpdateOp { return { op: "update", - coll: "ea2_flow", + coll: "flow", key: flowKey, data: { status: "published" }, }; diff --git a/src/scenes/Wizard.tsx b/src/scenes/Wizard.tsx index d739954..a5ac3cc 100644 --- a/src/scenes/Wizard.tsx +++ b/src/scenes/Wizard.tsx @@ -103,23 +103,27 @@ export default function Wizard() { if (!draft.flowKey || !actor) return; setWorking(true); try { - const ops: BatchOperation[] = [ - ...draft.nodes.map(n => - wizardApi.ops.createNode(draft.flowKey!, { - _key: n._key, - display_name: n.display_name, - dispatch_kind: n.dispatch_kind, - agent_capability: n.agent_capability, - }) - ), - ...draft.nodes.slice(0, -1).map((n, i) => - wizardApi.ops.createEdge(draft.flowKey!, n._key, draft.nodes[i + 1]._key, "next") + const createOps: BatchOperation[] = draft.nodes.map(n => + wizardApi.ops.createStep({ + display_name: n.display_name, + dispatch_kind: n.dispatch_kind, + agent_capability: n.agent_capability, + }) + ); + const createRes = await wizardApi.applyBatch(draft.flowKey, createOps, actor); + const createdKeys: string[] = (createRes?.result?.ops || []).map((o: any) => o.key); + const nodesWithKeys = draft.nodes.map((n, i) => ({ ...n, _key: createdKeys[i] || n._key })); + + const linkOps: BatchOperation[] = [ + wizardApi.ops.createStepEdge(draft.flowKey!, nodesWithKeys[0]._key), + ...nodesWithKeys.slice(0, -1).map((n, i) => + wizardApi.ops.createStepEdge(n._key, nodesWithKeys[i + 1]._key) ), ]; + if (linkOps.length) await wizardApi.applyBatch(draft.flowKey, linkOps, actor); - await wizardApi.applyBatch(draft.flowKey, ops, actor); - updateDraft({ step: "Generate" }); - pushToast("ok", "Structure saved"); + updateDraft({ step: "Generate", nodes: nodesWithKeys }); + pushToast("ok", `Saved ${createdKeys.length} steps to EA2`); } catch (err: any) { pushToast("err", `Save failed: ${err.message}`); } finally {