@xdelivered/emberflow 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/bin/commands.test.ts +185 -0
  4. package/bin/commands.ts +306 -0
  5. package/bin/emberflow.mjs +38 -0
  6. package/bin/init.test.ts +165 -0
  7. package/bin/init.ts +297 -0
  8. package/bin/runtime.mjs +105 -0
  9. package/bin/runtime.test.ts +167 -0
  10. package/bin/skillTargets.test.ts +108 -0
  11. package/bin/skillTargets.ts +36 -0
  12. package/bin/skillTemplates.test.ts +37 -0
  13. package/bin/tsconfig.json +23 -0
  14. package/dist/bin/commands.d.ts +24 -0
  15. package/dist/bin/commands.js +252 -0
  16. package/dist/bin/init.d.ts +17 -0
  17. package/dist/bin/init.js +241 -0
  18. package/dist/bin/runtime.d.mts +39 -0
  19. package/dist/bin/runtime.mjs +96 -0
  20. package/dist/bin/skillTargets.d.ts +13 -0
  21. package/dist/bin/skillTargets.js +22 -0
  22. package/dist/server/agents/claudeAdapter.d.ts +8 -0
  23. package/dist/server/agents/claudeAdapter.js +190 -0
  24. package/dist/server/agents/claudeParse.d.ts +2 -0
  25. package/dist/server/agents/claudeParse.js +162 -0
  26. package/dist/server/agents/codexAdapter.d.ts +13 -0
  27. package/dist/server/agents/codexAdapter.js +174 -0
  28. package/dist/server/agents/codexParse.d.ts +2 -0
  29. package/dist/server/agents/codexParse.js +74 -0
  30. package/dist/server/agents/detect.d.ts +23 -0
  31. package/dist/server/agents/detect.js +35 -0
  32. package/dist/server/agents/gitScope.d.ts +24 -0
  33. package/dist/server/agents/gitScope.js +95 -0
  34. package/dist/server/agents/modelRejectionHint.d.ts +8 -0
  35. package/dist/server/agents/modelRejectionHint.js +12 -0
  36. package/dist/server/agents/prompt.d.ts +79 -0
  37. package/dist/server/agents/prompt.js +364 -0
  38. package/dist/server/agents/runManager.d.ts +90 -0
  39. package/dist/server/agents/runManager.js +259 -0
  40. package/dist/server/agents/types.d.ts +15 -0
  41. package/dist/server/agents/types.js +1 -0
  42. package/dist/server/apiStore.d.ts +60 -0
  43. package/dist/server/apiStore.js +249 -0
  44. package/dist/server/auth/enforce.d.ts +1 -0
  45. package/dist/server/auth/enforce.js +6 -0
  46. package/dist/server/auth/types.d.ts +1 -0
  47. package/dist/server/auth/types.js +1 -0
  48. package/dist/server/auth/verifiers.d.ts +1 -0
  49. package/dist/server/auth/verifiers.js +4 -0
  50. package/dist/server/authAttach.d.ts +19 -0
  51. package/dist/server/authAttach.js +79 -0
  52. package/dist/server/cli.d.ts +7 -0
  53. package/dist/server/cli.js +804 -0
  54. package/dist/server/client.d.ts +130 -0
  55. package/dist/server/client.js +247 -0
  56. package/dist/server/environments.d.ts +103 -0
  57. package/dist/server/environments.js +486 -0
  58. package/dist/server/flowStore.d.ts +37 -0
  59. package/dist/server/flowStore.js +117 -0
  60. package/dist/server/httpOperations.d.ts +32 -0
  61. package/dist/server/httpOperations.js +62 -0
  62. package/dist/server/index.d.ts +1 -0
  63. package/dist/server/index.js +1022 -0
  64. package/dist/server/infrastructure.d.ts +51 -0
  65. package/dist/server/infrastructure.js +116 -0
  66. package/dist/server/login.d.ts +21 -0
  67. package/dist/server/login.js +44 -0
  68. package/dist/server/mcp.d.ts +1 -0
  69. package/dist/server/mcp.js +182 -0
  70. package/dist/server/migrateFlows.d.ts +8 -0
  71. package/dist/server/migrateFlows.js +25 -0
  72. package/dist/server/mockHandler.d.ts +15 -0
  73. package/dist/server/mockHandler.js +35 -0
  74. package/dist/server/nodesPayload.d.ts +14 -0
  75. package/dist/server/nodesPayload.js +14 -0
  76. package/dist/server/normalizeFlow.d.ts +21 -0
  77. package/dist/server/normalizeFlow.js +48 -0
  78. package/dist/server/openBrowser.d.ts +8 -0
  79. package/dist/server/openBrowser.js +16 -0
  80. package/dist/server/operationResult.d.ts +12 -0
  81. package/dist/server/operationResult.js +26 -0
  82. package/dist/server/pathGuard.d.ts +1 -0
  83. package/dist/server/pathGuard.js +16 -0
  84. package/dist/server/pathSafety.d.ts +20 -0
  85. package/dist/server/pathSafety.js +35 -0
  86. package/dist/server/projectConfig.d.ts +47 -0
  87. package/dist/server/projectConfig.js +61 -0
  88. package/dist/server/projectMode.d.ts +26 -0
  89. package/dist/server/projectMode.js +44 -0
  90. package/dist/server/redact.d.ts +6 -0
  91. package/dist/server/redact.js +37 -0
  92. package/dist/server/runRegistry.d.ts +84 -0
  93. package/dist/server/runRegistry.js +200 -0
  94. package/dist/server/runtime/RuntimeAdapter.d.ts +22 -0
  95. package/dist/server/runtime/RuntimeAdapter.js +1 -0
  96. package/dist/server/runtime/expressAdapter.d.ts +19 -0
  97. package/dist/server/runtime/expressAdapter.js +56 -0
  98. package/dist/server/scenarioTest.d.ts +14 -0
  99. package/dist/server/scenarioTest.js +66 -0
  100. package/dist/server/subflowRunner.d.ts +32 -0
  101. package/dist/server/subflowRunner.js +72 -0
  102. package/dist/server/testRunner.d.ts +70 -0
  103. package/dist/server/testRunner.js +151 -0
  104. package/dist/src/config.d.ts +3 -0
  105. package/dist/src/config.js +2 -0
  106. package/dist/src/engine/authVerify.d.ts +33 -0
  107. package/dist/src/engine/authVerify.js +63 -0
  108. package/dist/src/engine/diagnostics.d.ts +62 -0
  109. package/dist/src/engine/diagnostics.js +148 -0
  110. package/dist/src/engine/executor.d.ts +151 -0
  111. package/dist/src/engine/executor.js +684 -0
  112. package/dist/src/engine/httpError.d.ts +7 -0
  113. package/dist/src/engine/httpError.js +14 -0
  114. package/dist/src/engine/index.d.ts +11 -0
  115. package/dist/src/engine/index.js +11 -0
  116. package/dist/src/engine/output.d.ts +11 -0
  117. package/dist/src/engine/output.js +20 -0
  118. package/dist/src/engine/publish.d.ts +30 -0
  119. package/dist/src/engine/publish.js +95 -0
  120. package/dist/src/engine/registry.d.ts +34 -0
  121. package/dist/src/engine/registry.js +67 -0
  122. package/dist/src/engine/scenarios.d.ts +12 -0
  123. package/dist/src/engine/scenarios.js +19 -0
  124. package/dist/src/engine/schemaCheck.d.ts +4 -0
  125. package/dist/src/engine/schemaCheck.js +34 -0
  126. package/dist/src/engine/trace.d.ts +12 -0
  127. package/dist/src/engine/trace.js +12 -0
  128. package/dist/src/engine/types.d.ts +308 -0
  129. package/dist/src/engine/types.js +1 -0
  130. package/dist/src/engine/validation.d.ts +26 -0
  131. package/dist/src/engine/validation.js +318 -0
  132. package/dist/src/flows/anomaly-flows.d.ts +23 -0
  133. package/dist/src/flows/anomaly-flows.js +151 -0
  134. package/dist/src/flows/city-sweep-flow.d.ts +10 -0
  135. package/dist/src/flows/city-sweep-flow.js +127 -0
  136. package/dist/src/flows/login-flow.d.ts +13 -0
  137. package/dist/src/flows/login-flow.js +145 -0
  138. package/dist/src/flows/pradar-flows.d.ts +5 -0
  139. package/dist/src/flows/pradar-flows.js +375 -0
  140. package/dist/src/flows/weather-flow.d.ts +12 -0
  141. package/dist/src/flows/weather-flow.js +150 -0
  142. package/dist/src/lib/pathParams.d.ts +3 -0
  143. package/dist/src/lib/pathParams.js +6 -0
  144. package/dist/src/nodes/anomaly.d.ts +2 -0
  145. package/dist/src/nodes/anomaly.js +556 -0
  146. package/dist/src/nodes/flow-control.d.ts +7 -0
  147. package/dist/src/nodes/flow-control.js +270 -0
  148. package/dist/src/nodes/index.d.ts +3 -0
  149. package/dist/src/nodes/index.js +20 -0
  150. package/dist/src/nodes/login.d.ts +7 -0
  151. package/dist/src/nodes/login.js +158 -0
  152. package/dist/src/nodes/loops.d.ts +11 -0
  153. package/dist/src/nodes/loops.js +60 -0
  154. package/dist/src/nodes/pradar/index.d.ts +7 -0
  155. package/dist/src/nodes/pradar/index.js +9 -0
  156. package/dist/src/nodes/pradar/logic.d.ts +189 -0
  157. package/dist/src/nodes/pradar/logic.js +370 -0
  158. package/dist/src/nodes/pradar/nodes.d.ts +42 -0
  159. package/dist/src/nodes/pradar/nodes.js +801 -0
  160. package/dist/src/nodes/requireAuth.d.ts +20 -0
  161. package/dist/src/nodes/requireAuth.js +48 -0
  162. package/dist/src/nodes/response.d.ts +8 -0
  163. package/dist/src/nodes/response.js +33 -0
  164. package/dist/src/nodes/weather.d.ts +33 -0
  165. package/dist/src/nodes/weather.js +260 -0
  166. package/package.json +92 -0
  167. package/server/agentRoute.test.ts +157 -0
  168. package/server/agents/__fixtures__/fake-claude-writer.mjs +23 -0
  169. package/server/agents/__fixtures__/fake-claude.mjs +55 -0
  170. package/server/agents/__fixtures__/fake-codex-writer.mjs +27 -0
  171. package/server/agents/__fixtures__/fake-codex.mjs +52 -0
  172. package/server/agents/__fixtures__/fake-version-empty.mjs +3 -0
  173. package/server/agents/__fixtures__/fake-version-fail.mjs +4 -0
  174. package/server/agents/__fixtures__/fake-version-garbage.mjs +4 -0
  175. package/server/agents/__fixtures__/fake-version-good.mjs +4 -0
  176. package/server/agents/__fixtures__/fake-version-hang.mjs +3 -0
  177. package/server/agents/claudeAdapter.test.ts +127 -0
  178. package/server/agents/claudeAdapter.ts +212 -0
  179. package/server/agents/claudeParse.ts +193 -0
  180. package/server/agents/codexAdapter.test.ts +125 -0
  181. package/server/agents/codexAdapter.ts +198 -0
  182. package/server/agents/codexParse.test.ts +58 -0
  183. package/server/agents/codexParse.ts +126 -0
  184. package/server/agents/detect.test.ts +65 -0
  185. package/server/agents/detect.ts +43 -0
  186. package/server/agents/gitScope.test.ts +129 -0
  187. package/server/agents/gitScope.ts +110 -0
  188. package/server/agents/modelRejectionHint.test.ts +30 -0
  189. package/server/agents/modelRejectionHint.ts +14 -0
  190. package/server/agents/prompt.test.ts +613 -0
  191. package/server/agents/prompt.ts +527 -0
  192. package/server/agents/runManager.test.ts +251 -0
  193. package/server/agents/runManager.ts +286 -0
  194. package/server/agents/types.ts +17 -0
  195. package/server/apiMount.test.ts +591 -0
  196. package/server/apiStore.test.ts +324 -0
  197. package/server/apiStore.ts +241 -0
  198. package/server/auth/enforce.test.ts +61 -0
  199. package/server/auth/enforce.ts +6 -0
  200. package/server/auth/types.ts +5 -0
  201. package/server/auth/verifiers.test.ts +37 -0
  202. package/server/auth/verifiers.ts +9 -0
  203. package/server/authAttach.test.ts +175 -0
  204. package/server/authAttach.ts +99 -0
  205. package/server/cli.ts +847 -0
  206. package/server/cliAuth.test.ts +229 -0
  207. package/server/cliCreate.test.ts +131 -0
  208. package/server/cliDoctor.test.ts +326 -0
  209. package/server/cliTest.test.ts +735 -0
  210. package/server/client.ts +314 -0
  211. package/server/demoProject.test.ts +29 -0
  212. package/server/diagnosticsRoute.test.ts +169 -0
  213. package/server/environments.test.ts +620 -0
  214. package/server/environments.ts +603 -0
  215. package/server/environmentsRoute.test.ts +385 -0
  216. package/server/errorWorkflow.test.ts +205 -0
  217. package/server/flowStore.test.ts +84 -0
  218. package/server/flowStore.ts +125 -0
  219. package/server/httpOperations.test.ts +377 -0
  220. package/server/httpOperations.ts +96 -0
  221. package/server/index.ts +1111 -0
  222. package/server/infrastructure.test.ts +154 -0
  223. package/server/infrastructure.ts +161 -0
  224. package/server/infrastructureRoute.test.ts +105 -0
  225. package/server/login.test.ts +153 -0
  226. package/server/login.ts +67 -0
  227. package/server/mcp.ts +278 -0
  228. package/server/migrateFlows.test.ts +55 -0
  229. package/server/migrateFlows.ts +23 -0
  230. package/server/mockHandler.test.ts +138 -0
  231. package/server/mockHandler.ts +52 -0
  232. package/server/mockRun.test.ts +367 -0
  233. package/server/mockServe.test.ts +467 -0
  234. package/server/nodesPayload.test.ts +29 -0
  235. package/server/nodesPayload.ts +26 -0
  236. package/server/normalizeFlow.test.ts +80 -0
  237. package/server/normalizeFlow.ts +59 -0
  238. package/server/openBrowser.test.ts +20 -0
  239. package/server/openBrowser.ts +22 -0
  240. package/server/operationResult.test.ts +67 -0
  241. package/server/operationResult.ts +27 -0
  242. package/server/pathGuard.test.ts +19 -0
  243. package/server/pathGuard.ts +12 -0
  244. package/server/pathSafety.test.ts +38 -0
  245. package/server/pathSafety.ts +33 -0
  246. package/server/projectConfig.test.ts +67 -0
  247. package/server/projectConfig.ts +101 -0
  248. package/server/projectMode.test.ts +106 -0
  249. package/server/projectMode.ts +59 -0
  250. package/server/redact.test.ts +88 -0
  251. package/server/redact.ts +35 -0
  252. package/server/redactEgress.test.ts +281 -0
  253. package/server/runRegistry.ts +270 -0
  254. package/server/runsAuth.test.ts +119 -0
  255. package/server/runtime/RuntimeAdapter.ts +24 -0
  256. package/server/runtime/expressAdapter.test.ts +38 -0
  257. package/server/runtime/expressAdapter.ts +70 -0
  258. package/server/scenarioTest.test.ts +148 -0
  259. package/server/scenarioTest.ts +88 -0
  260. package/server/setupStatus.test.ts +127 -0
  261. package/server/subflowRunner.ts +104 -0
  262. package/server/testRunner.ts +216 -0
  263. package/server/tsconfig.json +20 -0
  264. package/server/workflowTestRoute.test.ts +297 -0
  265. package/src/App.tsx +233 -0
  266. package/src/components/AgentConsole.tsx +461 -0
  267. package/src/components/BranchRulesEditor.tsx +156 -0
  268. package/src/components/CreateModal.tsx +275 -0
  269. package/src/components/Dock.tsx +487 -0
  270. package/src/components/EnvironmentDialog.tsx +569 -0
  271. package/src/components/EnvironmentPicker.tsx +357 -0
  272. package/src/components/EnvironmentsDialog.tsx +144 -0
  273. package/src/components/ExecutionPager.tsx +67 -0
  274. package/src/components/InfraPanel.test.tsx +103 -0
  275. package/src/components/InfraPanel.tsx +171 -0
  276. package/src/components/Inspector.tsx +1182 -0
  277. package/src/components/Json.tsx +50 -0
  278. package/src/components/JsonModal.tsx +52 -0
  279. package/src/components/LogMessage.tsx +61 -0
  280. package/src/components/NodeRunModal.tsx +213 -0
  281. package/src/components/RunLogPanel.tsx +397 -0
  282. package/src/components/RunbookView.tsx +1085 -0
  283. package/src/components/Scenarios.tsx +476 -0
  284. package/src/components/SettingsDialog.tsx +229 -0
  285. package/src/components/Sidebar.tsx +436 -0
  286. package/src/components/StatusBar.tsx +302 -0
  287. package/src/components/Toolbar.tsx +663 -0
  288. package/src/components/WelcomeDialog.test.tsx +107 -0
  289. package/src/components/WelcomeDialog.tsx +412 -0
  290. package/src/components/agentMarkdown.test.tsx +91 -0
  291. package/src/components/agentMarkdown.tsx +158 -0
  292. package/src/components/ui/badge.tsx +31 -0
  293. package/src/components/ui/button.tsx +46 -0
  294. package/src/components/ui/combobox.tsx +114 -0
  295. package/src/components/ui/command.tsx +82 -0
  296. package/src/components/ui/dialog.tsx +73 -0
  297. package/src/components/ui/input.tsx +20 -0
  298. package/src/components/ui/popover.tsx +32 -0
  299. package/src/components/ui/resizable.tsx +44 -0
  300. package/src/components/ui/tabs.tsx +36 -0
  301. package/src/config.ts +4 -0
  302. package/src/engine/authVerify.ts +81 -0
  303. package/src/engine/diagnostics.test.ts +437 -0
  304. package/src/engine/diagnostics.ts +211 -0
  305. package/src/engine/executor.test.ts +1339 -0
  306. package/src/engine/executor.ts +840 -0
  307. package/src/engine/httpError.test.ts +12 -0
  308. package/src/engine/httpError.ts +16 -0
  309. package/src/engine/index.ts +11 -0
  310. package/src/engine/output.ts +21 -0
  311. package/src/engine/publish.test.ts +176 -0
  312. package/src/engine/publish.ts +127 -0
  313. package/src/engine/registry.test.ts +64 -0
  314. package/src/engine/registry.ts +80 -0
  315. package/src/engine/scenarios.test.ts +55 -0
  316. package/src/engine/scenarios.ts +29 -0
  317. package/src/engine/schemaCheck.test.ts +21 -0
  318. package/src/engine/schemaCheck.ts +35 -0
  319. package/src/engine/trace.test.ts +19 -0
  320. package/src/engine/trace.ts +23 -0
  321. package/src/engine/types.test.ts +22 -0
  322. package/src/engine/types.ts +316 -0
  323. package/src/engine/validation.test.ts +314 -0
  324. package/src/engine/validation.ts +354 -0
  325. package/src/flows/anomaly-flows.test.ts +81 -0
  326. package/src/flows/anomaly-flows.ts +202 -0
  327. package/src/flows/city-sweep-flow.ts +131 -0
  328. package/src/flows/flows.test.ts +30 -0
  329. package/src/flows/login-flow.ts +150 -0
  330. package/src/flows/pradar-flows.test.ts +204 -0
  331. package/src/flows/pradar-flows.ts +419 -0
  332. package/src/flows/weather-flow.ts +154 -0
  333. package/src/globals.css +147 -0
  334. package/src/lib/pathParams.test.ts +24 -0
  335. package/src/lib/pathParams.ts +6 -0
  336. package/src/lib/registerLens.test.ts +79 -0
  337. package/src/lib/registerLens.ts +59 -0
  338. package/src/lib/runbookModel.test.ts +61 -0
  339. package/src/lib/runbookModel.ts +290 -0
  340. package/src/lib/runbookProjection.test.ts +417 -0
  341. package/src/lib/runbookProjection.ts +254 -0
  342. package/src/lib/useTailAnchor.ts +53 -0
  343. package/src/lib/utils.ts +7 -0
  344. package/src/main.tsx +13 -0
  345. package/src/nodes/anomaly.test.ts +98 -0
  346. package/src/nodes/anomaly.ts +637 -0
  347. package/src/nodes/effects-scan.test.ts +44 -0
  348. package/src/nodes/flow-control.test.ts +280 -0
  349. package/src/nodes/flow-control.ts +305 -0
  350. package/src/nodes/index.ts +22 -0
  351. package/src/nodes/login.test.ts +81 -0
  352. package/src/nodes/login.ts +186 -0
  353. package/src/nodes/loops.ts +69 -0
  354. package/src/nodes/pradar/index.ts +11 -0
  355. package/src/nodes/pradar/logic.test.ts +288 -0
  356. package/src/nodes/pradar/logic.ts +582 -0
  357. package/src/nodes/pradar/nodes.test.ts +146 -0
  358. package/src/nodes/pradar/nodes.ts +955 -0
  359. package/src/nodes/requireAuth.test.ts +75 -0
  360. package/src/nodes/requireAuth.ts +55 -0
  361. package/src/nodes/response.test.ts +33 -0
  362. package/src/nodes/response.ts +39 -0
  363. package/src/nodes/traceDetail.test.ts +39 -0
  364. package/src/nodes/weather.test.ts +65 -0
  365. package/src/nodes/weather.ts +306 -0
  366. package/src/store/agentClient.test.ts +103 -0
  367. package/src/store/agentClient.ts +131 -0
  368. package/src/store/apiTree.test.ts +17 -0
  369. package/src/store/apiTree.ts +73 -0
  370. package/src/store/builderStore.test.ts +1234 -0
  371. package/src/store/builderStore.ts +1879 -0
  372. package/src/store/infraClient.ts +63 -0
  373. package/src/store/nodeMeta.test.ts +24 -0
  374. package/src/store/nodeMeta.ts +21 -0
  375. package/src/store/persistence.test.ts +15 -0
  376. package/src/store/persistence.ts +54 -0
  377. package/src/store/serverRunner.ts +381 -0
  378. package/src/store/setupClient.ts +57 -0
  379. package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  380. package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  381. package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  382. package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  383. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  384. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  385. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  386. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  387. package/studio-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  388. package/studio-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  389. package/studio-dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  390. package/studio-dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  391. package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  392. package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  393. package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  394. package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  395. package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  396. package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  397. package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  398. package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  399. package/studio-dist/assets/index-DNJwW-hM.css +1 -0
  400. package/studio-dist/assets/index-O26dKRjW.js +65 -0
  401. package/studio-dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  402. package/studio-dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  403. package/studio-dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  404. package/studio-dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  405. package/studio-dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  406. package/studio-dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  407. package/studio-dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  408. package/studio-dist/favicon.svg +1 -0
  409. package/studio-dist/icons.svg +24 -0
  410. package/studio-dist/index.html +14 -0
  411. package/templates/skills/emberflow-basics/SKILL.md +316 -0
  412. package/templates/skills/emberflow-model-process/SKILL.md +209 -0
  413. package/templates/skills/emberflow-new-workflow/SKILL.md +211 -0
  414. package/templates/skills/emberflow-review-workflow/SKILL.md +148 -0
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
@@ -0,0 +1,24 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg">
2
+ <symbol id="bluesky-icon" viewBox="0 0 16 17">
3
+ <g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
4
+ <defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
5
+ </symbol>
6
+ <symbol id="discord-icon" viewBox="0 0 20 19">
7
+ <path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
8
+ </symbol>
9
+ <symbol id="documentation-icon" viewBox="0 0 21 20">
10
+ <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
11
+ <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
12
+ <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
13
+ </symbol>
14
+ <symbol id="github-icon" viewBox="0 0 19 19">
15
+ <path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
16
+ </symbol>
17
+ <symbol id="social-icon" viewBox="0 0 20 20">
18
+ <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
19
+ <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
20
+ </symbol>
21
+ <symbol id="x-icon" viewBox="0 0 19 19">
22
+ <path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
23
+ </symbol>
24
+ </svg>
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Emberflow</title>
8
+ <script type="module" crossorigin src="/assets/index-O26dKRjW.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DNJwW-hM.css">
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,316 @@
1
+ ---
2
+ name: emberflow-basics
3
+ description: Use whenever working with Emberflow in this project — running the studio, understanding the file layout, adding or editing API operations and scenarios, registering custom nodes, or using the CLI/MCP. The foundation the other emberflow-* skills build on. Trigger on "emberflow", "workflow builder", "operation", "api", "flow", "run a scenario", "emberflow dev".
4
+ metadata:
5
+ version: 2.7.0
6
+ ---
7
+
8
+ # Emberflow basics
9
+
10
+ Emberflow is a visual builder for **API operations** in this project. An operation
11
+ is a JSON file describing a node graph (a "flow"); the project's own code is
12
+ exposed as **nodes**; runs execute on a local runner and are visualised in a
13
+ browser studio. An operation with an `http` trigger is a live HTTP endpoint; one
14
+ without is an internal sub-flow other operations call. This skill is the
15
+ mechanics reference — how the pieces fit and how to drive them correctly.
16
+
17
+ ## File layout (in the consumer project)
18
+
19
+ - `emberflow.config.mjs` (project root) — the contract. Exports `defineConfig({ flowsDir, registerNodes })`.
20
+ - `emberflow/apis/<api>/<folder…>/<operation>.json` — one operation per file, in a tree. `<api>` is the top-level API (e.g. `default`, `billing`); folders under it are free-form grouping.
21
+ - **An operation's `id` is its path relative to the apis dir, without `.json`.** `emberflow/apis/billing/charge.json` → `id: "billing/charge"`; `emberflow/apis/default/greet.json` → `id: "default/greet"`. The store keys ops by their in-file `id` but stores them by path, so the two MUST match — a mismatched id makes the op unreachable.
22
+ - `emberflow/apis/…/<operation>.scenarios.json` — that op's scenarios ("stories": named inputs), a sidecar array alongside the op file. The op file never contains a `scenarios` key.
23
+ - `emberflow/apis/<api>/<folder…>/_meta.json` — optional. Holds the `auth` policy inherited by every operation at or below that directory (see Auth below).
24
+ - `emberflow.environments.json` — **structure only**: named environments, their non-secret `vars`, and each env's `secrets` as a LIST of key NAMES (no values). Safe for you to read and edit. `emberflow.secrets.json` — secret **values only**, shape `{ "<envName>": { "<KEY>": "<value>" } }`, chmod `0600`; you NEVER read or write it (the user sets values via the studio's Manage Environment dialog or `login-environment`). A value of `"$ENV:VAR_NAME"` indirects to `process.env.VAR_NAME` at load. Both files are git-ignored (machine-local). The config and the `emberflow/apis` tree ARE committed.
25
+ - `emberflow/infrastructure.json` — **committed, structure-only** manifest of the infrastructure this project already uses (databases, external APIs, providers) with evidence pointing at the files that prove it. Written by the infrastructure scout (the studio's Infra tab / the `scout-infrastructure` agent intent); READ by agents before authoring operations so they REUSE existing systems and secret NAMES instead of inventing parallel config. Holds env-var NAMES only — never secret values.
26
+
27
+ ## Running it
28
+
29
+ - `npx emberflow dev` — boots the runner **and** the studio in one process and opens the browser (`http://127.0.0.1:8092`). This is the primary loop: edit an operation file or config, the studio picks it up.
30
+ - `npx emberflow run <id> --scenario <name>` — run an operation to completion against a running runner; prints the result JSON. `<id>` is the operation's path-based id (e.g. `billing/charge`).
31
+ - `npx emberflow run <id> --input '{}'` — same as the studio's Plain Run button: it uses only the Input node's `defaults`. Use this to catch missing defaults before relying on scenarios.
32
+ - `npx emberflow mcp` — expose the MCP tools (list/get/save/validate/run/publish operations) to an agent.
33
+ - `npx emberflow init` — scaffold config + an example operation (already done if this project has an `emberflow.config.(mjs|js|ts)`).
34
+
35
+ Always have `emberflow dev` running while authoring — it's how you see and test.
36
+
37
+ ## The config and custom nodes
38
+
39
+ The project's `language:` field (`'javascript'` or `'typescript'`, in
40
+ `emberflow.config.(mjs|js|ts)`) is the **authoritative signal for which
41
+ language to author in** — always match it, whichever scaffold shape below
42
+ the project actually uses.
43
+
44
+ JavaScript (`emberflow.config.mjs`, JSDoc types):
45
+
46
+ ```js
47
+ import { defineConfig } from '@xdelivered/emberflow';
48
+ export default defineConfig({
49
+ language: 'javascript',
50
+ flowsDir: 'emberflow/flows', // anchor: the apis tree lives at emberflow/apis (sibling)
51
+ /** @param {import('@xdelivered/emberflow').NodeRegistry} registry */
52
+ registerNodes(registry) {
53
+ registry.register(
54
+ {
55
+ type: 'Greet', // unique id, referenced by operations
56
+ label: 'Greet',
57
+ description: 'Greets by name.',
58
+ category: 'my-app',
59
+ inputSchema: { fields: [{ name: 'name', type: 'string', required: true }] },
60
+ outputSchema: { fields: [{ name: 'hello', type: 'string' }] },
61
+ // effects: 'mutation' ← add for nodes that write/POST/email; they dry-run under safe mode
62
+ // traceKind: 'db'|'http'|'llm' ← add for nodes that touch infrastructure; mock mode intercepts by this
63
+ },
64
+ async (ctx) => ({ hello: `Hello, ${ctx.input.name}!` }),
65
+ );
66
+ },
67
+ });
68
+ ```
69
+
70
+ TypeScript (`emberflow.config.ts`, typed) — same shape, typed instead of JSDoc:
71
+
72
+ ```ts
73
+ import { defineConfig } from '@xdelivered/emberflow';
74
+ import type { NodeRegistry } from '@xdelivered/emberflow';
75
+ export default defineConfig({
76
+ language: 'typescript',
77
+ flowsDir: 'emberflow/flows',
78
+ registerNodes(registry: NodeRegistry) {
79
+ registry.register(
80
+ { type: 'Greet', label: 'Greet', /* schemas/category/effects/traceKind as above */ },
81
+ async (ctx) => ({ hello: `Hello, ${ctx.input.name}!` }),
82
+ );
83
+ },
84
+ });
85
+ ```
86
+
87
+ - A node = a **definition** (metadata: type/label/schemas/category/tags/effects — what the studio shows) plus an **implementation** (an `async (ctx) => output`). The studio fetches definitions over HTTP; implementations run on the runner (next to your app code, so they can touch your DB/services).
88
+ - `ctx.input` = the node's resolved inputs. `ctx.config` = its per-node config. Return a plain object; downstream nodes read fields off it.
89
+ - Mark side-effecting nodes `effects: 'mutation'` so safe mode can dry-run them,
90
+ and infrastructure-touching nodes with `traceKind` so mock mode can intercept them.
91
+ - **Design for both worlds.** An operation ships with real node implementations
92
+ (so it serves live) AND sidecar `mocks` covering every infra node (so it runs
93
+ in mock mode). Neither substitutes for the other — see Mock mode below.
94
+ - **A mutation node has exactly three behaviours** — mock run → canned output
95
+ from the sidecar; dry-run (`config.commit` false, or safe mode) → a
96
+ `wouldSend`/`wouldWrite` summary, nothing touched; commit
97
+ (`config.commit === true && !ctx.safeMode`) → the REAL side effect. There is
98
+ no fourth state: a commit path that logs "[SIMULATED]" and returns success is
99
+ FORBIDDEN — these are operational APIs, not demos. If the real call can't be
100
+ made (a missing secretRef, no webhook configured), the commit path THROWS a
101
+ clear error naming the missing key. Fake success is worse than failure;
102
+ `doctor` flags simulated commit paths as `simulated-commit`.
103
+
104
+ ## Operation file shape
105
+
106
+ ```json
107
+ {
108
+ "id": "default/greet", "name": "Greet", "version": 1,
109
+ "http": { "method": "GET", "path": "/greet" },
110
+ "nodes": [
111
+ { "id": "in", "type": "Input", "label": "Request", "position": {"x":0,"y":0},
112
+ "config": { "fields": [
113
+ {"name":"params","type":"object"}, {"name":"query","type":"object"},
114
+ {"name":"body","type":"object"}, {"name":"headers","type":"object"}
115
+ ], "defaults": { "query": {"name":"world"} } } },
116
+ { "id": "greet", "type": "Greet", "label": "Greet", "position": {"x":300,"y":0},
117
+ "config": {}, "inputMap": { "name": {"sourceNodeId":"in","sourceField":"query.name"} } },
118
+ { "id": "res", "type": "Response", "label": "Response", "position": {"x":600,"y":0},
119
+ "config": {}, "inputMap": { "body": {"sourceNodeId":"greet","sourceField":"$"} } }
120
+ ],
121
+ "edges": [ {"id":"e1","source":"in","target":"greet"}, {"id":"e2","source":"greet","target":"res"} ],
122
+ "createdAt": "2026-07-05T00:00:00Z", "updatedAt": "2026-07-05T00:00:00Z"
123
+ }
124
+ ```
125
+
126
+ - **`http`** = `{ method, path }`. Present → the operation is mounted as an HTTP endpoint. Absent → it's an internal sub-flow (call it from another op's `Subflow` node). Optional `auth` field (see below).
127
+ - **`Input`** is the entry node. For an HTTP operation the run payload is the request shape `{ params, query, body, headers }` (plus the request body's own fields promoted to the top level, and `user` when auth ran). Read a request part with a dotted `sourceField` like `query.name` or `body.amount`.
128
+ - **`Response`** is the terminus of an HTTP operation. Its input `{ status, body }` becomes the HTTP response; omit `status` and it defaults to `200`.
129
+ - **`inputMap`** wires a node's input field to a source node's output: `{ sourceNodeId, sourceField }`. `sourceField: "$"` means the whole output; otherwise a dot-path into it.
130
+ - **`edges`** draw the graph and, critically, define execution order (topological). **Every inputMap needs a matching edge** — an inputMap without an edge renders as a floating node AND the ordering can't see the dependency. Rule: if node B reads from node A, there is an `A→B` edge.
131
+ - **Input defaults matter.** The studio's Plain Run button sends no request. Every required Input field must either have a sensible `config.defaults` value or the author must intentionally accept/report that Plain Run fails. Do not call an operation done after only scenario runs.
132
+
133
+ ## Built-in node types
134
+
135
+ You'll use these most (custom project nodes come from `registerNodes`):
136
+
137
+ - `Input` — entry; emits the run payload merged over `defaults`. For an HTTP op the payload is `{ params, query, body, headers }`.
138
+ - `Response` — **HTTP terminus.** Input `{ status, body }` → the HTTP response. Use this for any operation with an `http` trigger.
139
+ - `Result` — **internal terminus / display sink.** The collected output of a non-HTTP (internal) flow comes from its `Result` nodes. Use `Response` for HTTP endpoints, `Result` for internal sub-flows and while iterating on logic in the studio.
140
+ - `Conditional`/`Route` — branching. Emit `$branch`; downstream edges carry a `sourceHandle` = branch name and only run when that branch is taken.
141
+ - `ForEach`/`Collect` — loop over an array and gather the results.
142
+ - `Subflow` — runs another operation (by id) with mapped input and emits its output. This is how one operation calls an internal flow.
143
+ - `requireAuth` — verifies the request against an auth policy and attaches `{ user }` for downstream nodes (usually you rely on `_meta.json`/`http.auth` instead; use this node only for in-flow checks).
144
+
145
+ ## Auth
146
+
147
+ Auth is resolved per operation by walking its path from the API root down:
148
+
149
+ - `_meta.json` at any level sets a default `auth` policy `{ scheme: 'bearer'|'apiKey', secretRef, header?, verify? }` inherited by every op at or below it. The nearest ancestor wins.
150
+ - An operation overrides via `http.auth`: a policy object (use that policy), `'none'` (explicitly public, ignoring inherited policy), or `'inherit'`/absent (defer to the nearest `_meta.json`).
151
+ - No `_meta.json` and no `http.auth` → public. A present-but-corrupt `_meta.json` fails closed (the op is denied, not mounted open).
152
+
153
+ ## Environment auth (login once, auto-attach)
154
+
155
+ Separate from operation `auth` above: an *environment* in `emberflow.environments.json`
156
+ (git-ignored) can carry an `auth` block so runs against a real backend authenticate
157
+ automatically instead of failing 401 — `{ attach: {as:'cookie'|'header', name, secretRef},
158
+ login?: {request:{method,url,headers?,bodyRef?}, capture} }`, where `capture` is
159
+ `{from:'set-cookie', cookieName?}` / `{from:'json', path}` / `{from:'header', name}`
160
+ and `bodyRef` names a secret holding the login credentials as a JSON string. Once
161
+ configured and logged in, the runner attaches the stored credential to every
162
+ studio/CLI run against that environment — you never see the secret value, only
163
+ `«secret:KEY»` in run output/logs. Run `login-environment <name>` when an op fails
164
+ 401/unauthorized and `list-environments` shows auth configured but not authenticated
165
+ (also use it after scaffolding a new auth block, to verify capture works). If the
166
+ login target enforces CSRF origin checks (e.g. better-auth), add a trusted `origin`
167
+ header to `login.request.headers`.
168
+
169
+ `attach.prefix` prepends a fixed string to the credential at attach time (e.g.
170
+ `"Bearer "` for a captured token, `"Basic "` when the secret already holds
171
+ `base64(user:pass)`). Set (or clear with `--json 'null'`) an environment's auth
172
+ block from the CLI: `set-environment-auth <name> --json '<EnvAuth JSON>'`.
173
+
174
+ When a user asks you to set up auth for an environment (the studio's Manage
175
+ Environment dialog's "Set up with AI"), the flow is: compose the `EnvAuth`
176
+ JSON from what they describe (curl command, prose, or both) — `attach`
177
+ (`as`, `name`, `secretRef`, optional `prefix`) plus an optional `login`
178
+ (`request` + `capture`) — apply it with `set-environment-auth`, then tell the
179
+ user exactly which secret key(s) to fill in under the dialog's Secrets section
180
+ (a `bodyRef` for the login request body, or the `attach.secretRef` for a
181
+ static key with no login), and verify with `login-environment` once those
182
+ secrets are set. If `login-environment` fails, read the error, adjust the
183
+ auth JSON, re-apply, and retry.
184
+
185
+ Two rules are load-bearing here: **never** edit an environment's `auth`
186
+ block in `emberflow.environments.json` directly — `set-environment-auth` is
187
+ the only way to change it (the rest of the file is structure-only and safe to
188
+ read/edit); and **never** ask for, accept, or handle a credential value
189
+ yourself — secret values live in `emberflow.secrets.json` (chmod `0600`,
190
+ which you never touch), and the user always enters them in the studio dialog,
191
+ never in chat or a CLI arg.
192
+
193
+ ## Scenarios
194
+
195
+ A scenario is `{ id, name, input, description?, expect? }` in the
196
+ `<operation>.scenarios.json` sidecar. For an HTTP operation, `input` is the
197
+ request shape `{ params?, query?, body?, headers? }` — running the scenario
198
+ passes it through the exact same path a live HTTP request uses, so a green
199
+ scenario is real parity. `emberflow run <id> --scenario <name>` runs the op
200
+ with that input. In the studio, the Run split-button's dropdown lists
201
+ scenarios; each row has a play (run) and a step (step-through) button.
202
+ Adding/removing scenarios in the studio round-trips to the sidecar file.
203
+
204
+ **IMPORTANT: when you build or edit an operation, give its scenarios an
205
+ `expect`** so `emberflow test` actually covers them — a scenario with no
206
+ `expect` (or `expect: {}`) is silently skipped, not asserted. `expect` is
207
+ `{ status?, body?, executedNodes? }`: `body` is checked as a **deep subset**
208
+ of the real response (every key you list must match, recursively; extra
209
+ response keys are fine — don't restate the whole payload), `executedNodes`
210
+ lists node ids that must have reached `succeeded`. Every scenario that
211
+ exercises a distinct branch should assert `status` + `executedNodes` for
212
+ that branch, so a suite of green scenarios is proof, not vibes:
213
+
214
+ ```json
215
+ {
216
+ "id": "not-found",
217
+ "name": "unknown id",
218
+ "input": { "params": { "id": "missing" } },
219
+ "expect": { "status": 404, "executedNodes": ["lookup", "notFoundResponse"] }
220
+ }
221
+ ```
222
+
223
+ Run the suite with `npx emberflow test [opId] [--environment NAME]
224
+ [--json]` — in-process, no runner needed, exit `0`/`1`/`2`. It does **not**
225
+ auto-attach environment auth (hermetic by design); a scenario that needs
226
+ auth must carry the header/cookie in its own `input`. It also never fires
227
+ `errorOperation` (below) — treat `emberflow test` failures as assertion
228
+ failures, not incidents.
229
+
230
+ Add `--mock` to run scenarios as Mock runs (`mockRun: true`) instead of for
231
+ real: infrastructure nodes (`traceKind` db/http/llm) return their sidecar
232
+ mock (op-level `mocks`, overlaid by the scenario's own `mocks` per nodeId)
233
+ instead of touching real Postgres/HTTP/LLM, and fail loudly if a scenario
234
+ exercises one with no mock. It's the hermetic way to prove branch coverage
235
+ on infra-heavy ops without live infrastructure — `npx emberflow test <opId>
236
+ --mock`.
237
+
238
+ **After creating or editing any operation, run `npx emberflow doctor
239
+ <opId>` and resolve every finding before you consider the work done.** It
240
+ reports coded diagnostics — `missing-param-default` (a `:param` in the
241
+ HTTP path with no value under the Input node's `defaults.params`; leaves
242
+ the studio Run button disabled), `param-no-real-scenario` (no scenario
243
+ supplies a real value for a path param), `no-expects` (nothing asserts the
244
+ operation). `doctor --fix` seeds missing param defaults as `""`
245
+ placeholders; real ids in scenarios are your job — pull one from the
246
+ project's data when you can. Exit `0` means advisory-only findings; the
247
+ studio shows the same diagnostics as a problems chip in the toolbar.
248
+
249
+ ## Retry and error workflow
250
+
251
+ - **`retry` on a node** — `{ "maxTries": 3, "waitMs": 500 }`, sibling of
252
+ `config`/`optional` in the node JSON. Reach for it on nodes that call a
253
+ flaky external (HTTP, a third-party SDK): `maxTries` is total attempts
254
+ including the first, `waitMs` a fixed delay between them, and only the
255
+ implementation call retries (input/config resolve once). Pair with
256
+ `"optional": true` when the flow should fail soft after retries exhaust
257
+ rather than abort the run.
258
+ - **`errorOperation` in `emberflow.config.mjs`** — an op id to run whenever
259
+ a *server* run (live HTTP endpoint or `POST /runs`) finishes `failed`. It
260
+ receives `{ failedRunId, failedWorkflowId, failedNodeId, error,
261
+ environment }`. Use it for alerting/logging side-effect ops, not for
262
+ business logic — it never fires for its own runs, firing is best-effort,
263
+ and `emberflow test`/scenario runs never trigger it.
264
+
265
+ ## Mock mode
266
+
267
+ Mock is the studio's design-first mode. The environment dropdown's first
268
+ row is Mock; picking it is instant, and picking a real environment while
269
+ mocked goes live behind a "Go live?" confirm. A project with NO
270
+ `emberflow.environments.json` boots in Mock by default (nothing real to
271
+ serve against); creating environments — by hand, or via the dropdown's
272
+ "Manage environments…" modal whose Set-up-with-AI action opens the agent
273
+ panel in environment-setup mode (chat dispatches the `setup-environments`
274
+ intent) — is what unlocks going live. That intent scaffolds
275
+ environment entries, vars and `protected` flags but never secret values.
276
+
277
+ **Mock runs execute.** In Mock, Run/Step/scenario runs execute the flow
278
+ for real — routing, mappings, step-through — except infrastructure nodes
279
+ (`traceKind` `db`/`http`/`llm`) return canned outputs from the scenarios
280
+ sidecar's `mocks` maps instead of calling their implementations. Shape:
281
+ top-level `"mocks": { "<nodeId>": <output> }` (plain Run uses this) plus
282
+ optional per-scenario `mocks` (scenario wins per nodeId); the value is the
283
+ node's OUTPUT verbatim — read the node implementation to match its shape.
284
+ An infra node with no mock FAILS the mock run loudly (nothing real is ever
285
+ touched; no auth attached; subflow children inherit mock mode and use
286
+ their own op-level mocks). `compute`/unmarked nodes run their real logic —
287
+ so set `traceKind` honestly when authoring nodes: an unmarked infra node
288
+ silently executes for real in mock runs. `doctor` reports uncovered infra
289
+ nodes as `missing-node-mock`. When you author scenarios for an op with
290
+ infra nodes, ALWAYS write the mocks maps (cover-operation does this).
291
+ Never put secret values in mocks.
292
+
293
+ Serving is switchable at runtime via `POST /serving {mode}`, `emberflow serving <real|mock>` from the terminal, or the studio's environment dropdown; `emberflow serve --mock` / `dev --mock` mounts every HTTP endpoint but
294
+ answers purely from a scenario's `expect` — no auth, no node execution.
295
+ Selection order: `x-emberflow-scenario` request header (exact scenario
296
+ name) → `__scenario` query param → the first scenario on that op with an
297
+ `expect`. A named-but-unknown scenario is `404`; an op with no mockable
298
+ scenario is `501`. Every response carries `x-emberflow-mock: true`, and the
299
+ server logs a MOCK MODE banner at boot. Useful for pointing a frontend at
300
+ stable canned responses without wiring real backends — but it means an op
301
+ needs at least one scenario with `expect` before `--mock` can serve it.
302
+ Note: mock serves `expect.body` verbatim as the WHOLE response — a partial subset body written for testing is returned as-is.
303
+
304
+ ## Validation
305
+
306
+ The runner validates operations: unknown node types, missing edges/handles, orphan
307
+ regions, self-referential subflows all surface as errors/warnings. A clean op
308
+ runs; an invalid one is rejected. Run the plain Run/default-input path plus one
309
+ scenario per branch, and check the run console — the logs stream node-by-node
310
+ with input/output you can click into.
311
+
312
+ ## Related skills
313
+
314
+ - **emberflow-new-workflow** — design and build a brand-new HTTP operation from a goal.
315
+ - **emberflow-model-process** — port existing functionality into Emberflow: one process as an operation, or a whole subsystem as an API (its "At subsystem scale" section).
316
+ - **emberflow-review-workflow** — review an operation before it ships.
@@ -0,0 +1,209 @@
1
+ ---
2
+ name: emberflow-model-process
3
+ description: Use when modeling or MIGRATING EXISTING functionality into Emberflow — a single process (script, controller, job, runbook) as one operation, or a whole subsystem (a worker, a pipeline, a service's job handlers) as an API of operations. Emphasises verbatim porting, branch fidelity, determinism, and (at subsystem scale) decomposition and seam verification. For designing something brand-new, use emberflow-new-workflow instead.
4
+ metadata:
5
+ version: 2.7.0
6
+ ---
7
+
8
+ # Modeling an existing process as an Emberflow operation
9
+
10
+ Read **emberflow-basics** first. This skill is the discipline for turning a real,
11
+ already-working process into an operation that mirrors it — the goal is
12
+ *fidelity*, not reinvention. Done well, the operation becomes executable
13
+ documentation of the system. Sections 1–6 govern each individual port; when
14
+ the source is a whole subsystem spanning several operations, ALSO apply
15
+ **At subsystem scale** at the end.
16
+
17
+ If the process is (or is triggered by) an HTTP endpoint, model it as an HTTP
18
+ operation: create `emberflow/apis/<api>/<folder…>/<name>.json` with `id` equal to
19
+ that path (`emberflow/apis/billing/charge.json` → `"billing/charge"`), add the
20
+ `http` trigger `{ method, path }` matching the real route, start with an `Input`
21
+ receiving `{ params, query, body, headers }`, and end in a `Response` emitting
22
+ `{ status, body }` — mirror the real endpoint's status codes. A process with no
23
+ HTTP surface is an internal flow (no `http`) ending in `Result`.
24
+
25
+ ## 0. Intake — interview the user before you port
26
+
27
+ The source code tells you the logic; it cannot tell you how the user wants the
28
+ model to run. Interview them BEFORE porting — one short question at a time,
29
+ skipping anything already answered by the project or the source:
30
+
31
+ Check `emberflow/infrastructure.json` FIRST: if the scout has already mapped
32
+ this project's databases, APIs and providers, reuse those systems and their
33
+ secret NAMES instead of re-deriving infrastructure the manifest already names.
34
+
35
+ 1. **Environments — only when the project doesn't already answer it.**
36
+ Environments are PROJECT-level (`emberflow.environments.json` is shared by
37
+ every API and operation) — read it first. If it already covers what the
38
+ port needs, note that and move on; don't re-interview per operation. Ask
39
+ only when the file doesn't exist ("Which environments — local, dev,
40
+ staging, prod? Default? Production-like?") or the port introduces a delta
41
+ (a var or environment nothing carries yet). Production-like →
42
+ `protected: true` (safe mode by default; mutations dry-run). Scaffold via
43
+ `setup-environments`; environment auth via `set-environment-auth`, never a
44
+ hand-edit.
45
+ 2. **Secrets — same scoping.** Existing keys need no questions. For NEW keys
46
+ the ported dependencies introduce (the DB URL, API keys you found in the
47
+ source), propose names and ask whether values differ per environment. You
48
+ only handle `secretRef` NAMES — the user enters values in the studio's
49
+ Manage Environment dialog, never in chat or any file/argument you produce.
50
+ 3. **Mocks.** "Do you have a captured response or a real representative row
51
+ from the source system I should use as the canned data?" Mock values
52
+ sourced from the real system make mock runs exercise real shapes — this is
53
+ the difference between a trustworthy model and a green-but-lying one.
54
+ 4. **Scenarios.** "Beyond one scenario per branch of the source, which
55
+ specific cases do you want named and asserted?" (A known incident, a
56
+ particular account shape, the case that motivated the port.)
57
+
58
+ Confirm the summary before porting. The answers feed sections 4–6.
59
+
60
+ ## 1. Read the source first, fully
61
+
62
+ Find and read the actual implementation — the script, controller, job, or written
63
+ runbook. Identify:
64
+
65
+ - **The steps**, in execution order.
66
+ - **The decisions** — every `if`/`switch`/early-return/threshold. These become
67
+ `Conditional`/`Route` nodes. Note the exact conditions and constants.
68
+ - **The side effects** — DB writes, external POSTs, emails, queue sends. These
69
+ become custom nodes marked `effects: 'mutation'`.
70
+ - **The inputs and external data sources** it reads.
71
+
72
+ Do not skim. A faithful model requires knowing what the code actually does,
73
+ including the edge cases.
74
+
75
+ ## 2. Port logic VERBATIM
76
+
77
+ Copy the real constants, formulas, thresholds, and branch order into your custom
78
+ node implementations — do not "improve", round, or simplify them. If the source
79
+ uses `>= 0.7` and `MAX = 30`, your node uses `>= 0.7` and `30`. The whole value
80
+ is that the flow behaves exactly like the system.
81
+
82
+ - Keep a `PORT NOTE:` comment where your model diverges from the source for a
83
+ real reason (e.g. the original interleaves two passes; the flow does them in
84
+ order). Divergences must be deliberate and documented.
85
+ - Where the source calls an in-process function you can't run here, model it as a
86
+ node with the same inputs/outputs and note the substitution.
87
+
88
+ ## 3. Call out ambiguities — do not silently resolve them
89
+
90
+ If the source is contradictory (a comment says one threshold, the code uses
91
+ another), port the **code** (the source of truth), and surface the discrepancy to
92
+ the user rather than quietly picking one. Dead code, unreachable branches, and
93
+ stale constants are findings worth reporting — not things to tidy away.
94
+
95
+ ## 4. Map the shape onto Emberflow
96
+
97
+ - Request in → an `Input` node reading `{ params, query, body, headers }` (for an
98
+ HTTP op); response out → a `Response` node `{ status, body }` per real exit,
99
+ matching the source's status codes (internal flow → `Result`).
100
+ - Linear steps → a chain of nodes with `inputMap` + edges.
101
+ - Decisions → `Conditional` (ordered rules) or `Route` (switch on a field), one
102
+ branch per real code path, including the else/default. A branch that returns a
103
+ different HTTP status gets its own `Response`.
104
+ - Loops over collections → `ForEach`/`Collect`.
105
+ - Sub-procedures the source factors out → a `Subflow` node calling a separate
106
+ operation (mirror the real call structure — if `handleA()` calls `handleB()`,
107
+ operation A calls operation B).
108
+ - Effects → `effects: 'mutation'` nodes that dry-run by default and only commit
109
+ under an explicit opt-in. The commit path performs the REAL side effect the
110
+ source performs — the same insert, the same webhook, the same send. A
111
+ "[SIMULATED]"-success commit path is forbidden: if the credential isn't set,
112
+ commit THROWS naming the secretRef. The port isn't operational until a
113
+ mutation has committed for real against a real environment (or is explicitly
114
+ reported "operational-pending" with the exact secrets needed).
115
+ - Infrastructure boundaries → set `traceKind` (`'db'|'http'|'llm'`) on every
116
+ node that reads a database, calls an external service, or invokes a model.
117
+ Mock mode intercepts by `traceKind`; an unmarked infra node silently executes
118
+ for real during mock runs.
119
+
120
+ **Reuse a node when one fits; author a new one when none does** — inventing
121
+ nodes is expected, it's how a process becomes real code. The only rule: register
122
+ a node's implementation in `registerNodes` before you reference its `type`; the
123
+ runner rejects an unregistered type (a name with no implementation), but a type
124
+ you register in the same change is exactly how you model the step.
125
+
126
+ ## 5. Make it deterministic and reproducible
127
+
128
+ Real processes read the clock, the DB, live data. For a faithful, testable model,
129
+ lift those into node **inputs** (a `now` timestamp, a fetched snapshot) so a
130
+ scenario can pin them. Every branch of the original gets a scenario whose pinned
131
+ input drives exactly that path — named for the case it reproduces
132
+ ("critically-low", "poor-forecast", "already-ran-today").
133
+ Also set Input `defaults` for every required field needed by the studio's plain
134
+ Run button, or explicitly report that plain Run is intentionally unsupported.
135
+ Scenarios prove branches; defaults prove the unscoped authoring run works.
136
+
137
+ **Model both worlds: real implementations AND mocks.** The ported node
138
+ implementations call the real dependencies (the actual query, the actual API,
139
+ the actual model) — verbatim porting means real code, not canned data in the
140
+ implementation. Then, in the same change, author the sidecar `mocks` maps so
141
+ the model also runs in mock mode: a top-level `"mocks": { "<nodeId>": <output> }`
142
+ covering every `traceKind` infra node, plus per-scenario `mocks` where a branch
143
+ needs different canned data. Source the mock values from realistic outputs of
144
+ the original system (a captured response, a representative row) so mock runs
145
+ exercise the same shapes the real system produces. An infra node with no mock
146
+ fails the mock run loudly; `doctor` reports it as `missing-node-mock`. A model
147
+ that only runs against live infrastructure isn't executable documentation —
148
+ and one that only runs mocked was never proven faithful.
149
+
150
+ ## 6. Prove fidelity
151
+
152
+ Run both: plain Run and every scenario — in mock mode always, and against a
153
+ real environment when one exists. Confirm the flow reaches the same decision
154
+ the real system would for each input. Where you have reference outputs from the
155
+ source system, assert the flow matches them. Run `npx emberflow doctor <opId>`
156
+ and clear every finding (including `missing-node-mock`). Then run
157
+ **emberflow-review-workflow**.
158
+
159
+ ## At subsystem scale (migrating a worker / pipeline / job family)
160
+
161
+ When the source is a whole subsystem, the per-operation discipline above still
162
+ governs each port — these rules govern the campaign around it. The core
163
+ lesson from real migrations: **the serious defects live in the wiring between
164
+ correct nodes, not in the ported logic** — an inputMap reading a field the
165
+ producer never emits, a single object wired where the consumer maps an array,
166
+ `timestamp` vs `date` keys, an identity (signalId, token) dropped between
167
+ phases — and a wrongly-shaped mock masks every one of them.
168
+
169
+ - **Scout first, completely.** Before designing, write down: entry points and
170
+ triggers (cron/queue/HTTP), the phases in execution order with file:line,
171
+ every constant in a table (that table becomes the fidelity checklist all
172
+ reviews check against), external dependencies per phase, and the data
173
+ shapes phases pass each other.
174
+ - **One operation per process boundary.** A queue or cron hop in the source is
175
+ an operation boundary in the model — do NOT collapse an async boundary into
176
+ a synchronous `Subflow` (the fan-out op models the *send*; the per-job op is
177
+ invoked separately). A sub-procedure called in-process IS a `Subflow`.
178
+ - **Inventory existing nodes before authoring new ones** — migrations often
179
+ land where partial models already exist. Reuse and extend.
180
+ - **Producers own their output contracts.** If a consumer needs rows, the
181
+ producer emits rows — inputMap cannot compose objects or wrap arrays. When
182
+ a seam mismatch surfaces, fix the producer node, never the mock.
183
+ - **Mocks are truthful.** A mock is the producer's real emittable output,
184
+ verbatim — never the shape the consumer wishes for; that is exactly how
185
+ wiring bugs go green. A `Subflow` mock uses whatever the child's Result
186
+ terminals actually emit (run the child; don't imagine an aggregate).
187
+ - **Drive branch scenarios through real logic.** Prefer feeding the REAL
188
+ detector/gate a crafted infra mock (a series that genuinely promotes, spend
189
+ that genuinely overspends) over mocking the compute node itself — a
190
+ scenario that cans the detector it claims to prove proves only wiring.
191
+ - **Verify the seams, then the system.** Check every inputMap sourceField
192
+ against the producer's outputSchema as you wire (doctor's
193
+ `inputmap-schema-mismatch` catches misnamed fields but NOT type/shape drift
194
+ on a correctly-named field — that check is yours). After per-op reviews
195
+ (emberflow-review-workflow on every op), run one whole-system review with
196
+ fresh eyes over the full diff, tracing infra-node inputs through REAL node
197
+ implementations with fixture data — mock-green proves nothing about real
198
+ mode. Finish with a real-mode spot check against a real environment (safe
199
+ mode on); if credentials block it, report "real-mode pending" — never imply
200
+ live parity you didn't prove.
201
+
202
+ ## The bar
203
+
204
+ Someone who knows the original system should read the flow and recognise it
205
+ exactly — same steps, same decisions, same constants, same effects — with every
206
+ divergence explicitly marked. If they'd be surprised by a branch, you modeled it wrong.
207
+ For a subsystem migration the same bar holds system-wide, and both worlds must
208
+ work: every scenario green in mock mode, and the real path proven or explicitly
209
+ pending.