procesio-cli 0.1.1__py3-none-any.whl

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 (647) hide show
  1. procesio_cli/__init__.py +18 -0
  2. procesio_cli/_cli.py +71 -0
  3. procesio_cli/agents/__init__.py +0 -0
  4. procesio_cli/agents/_lib/__init__.py +0 -0
  5. procesio_cli/agents/_lib/actiondef.py +24 -0
  6. procesio_cli/agents/_lib/errors.py +40 -0
  7. procesio_cli/agents/_lib/reversibility.py +236 -0
  8. procesio_cli/agents/_lib/toolrunner.py +141 -0
  9. procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PLAYBOOK.md +62 -0
  10. procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PROCESIO-INTEROP.md +76 -0
  11. procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-TROUBLESHOOTING.md +60 -0
  12. procesio_cli/agents/connector-builder/README.md +51 -0
  13. procesio_cli/agents/connector-builder/agent.yaml +61 -0
  14. procesio_cli/agents/connector-builder/context.py +19 -0
  15. procesio_cli/agents/connector-builder/handlers/__init__.py +1 -0
  16. procesio_cli/agents/connector-builder/handlers/checklist.py +71 -0
  17. procesio_cli/agents/connector-builder/handlers/guidance.py +37 -0
  18. procesio_cli/agents/connector-builder/handlers/nextstep.py +221 -0
  19. procesio_cli/agents/connector-builder/handlers/status.py +35 -0
  20. procesio_cli/agents/connector-builder/knowledge.py +47 -0
  21. procesio_cli/agents/connector-builder/main.py +93 -0
  22. procesio_cli/agents/connector-builder/tests/conftest.py +95 -0
  23. procesio_cli/agents/connector-builder/tests/test_cba_agent.py +148 -0
  24. procesio_cli/agents/orchestrator/__init__.py +0 -0
  25. procesio_cli/agents/orchestrator/bootstraplib.py +166 -0
  26. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/00-INDEX.md +60 -0
  27. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/01-PROCESSES.md +230 -0
  28. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/02-FORMS.md +287 -0
  29. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/03-INTEGRATIONS.md +384 -0
  30. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/04-DATABASE.md +293 -0
  31. procesio_cli/agents/procesio/PROCESIO-API-RELIABILITY-DOCTRINE.md +113 -0
  32. procesio_cli/agents/procesio/PROCESIO-BEST-PRACTICES.md +316 -0
  33. procesio_cli/agents/procesio/PROCESIO-BUILD-AND-TEST-PLAYBOOK.md +217 -0
  34. procesio_cli/agents/procesio/PROCESIO-DATASTORE.md +72 -0
  35. procesio_cli/agents/procesio/PROCESIO-ENVIRONMENTS.md +62 -0
  36. procesio_cli/agents/procesio/PROCESIO-SCHEDULING.md +47 -0
  37. procesio_cli/agents/procesio/PROCESIO-VISUAL-ORGANIZATION.md +175 -0
  38. procesio_cli/agents/procesio/README.md +97 -0
  39. procesio_cli/agents/procesio/__init__.py +0 -0
  40. procesio_cli/agents/procesio/agent.yaml +73 -0
  41. procesio_cli/agents/procesio/audit.py +226 -0
  42. procesio_cli/agents/procesio/checklist.py +135 -0
  43. procesio_cli/agents/procesio/context.py +19 -0
  44. procesio_cli/agents/procesio/handlers/__init__.py +0 -0
  45. procesio_cli/agents/procesio/handlers/audit.py +39 -0
  46. procesio_cli/agents/procesio/handlers/checklist.py +34 -0
  47. procesio_cli/agents/procesio/handlers/guidance.py +36 -0
  48. procesio_cli/agents/procesio/handlers/status.py +33 -0
  49. procesio_cli/agents/procesio/handlers/verify.py +39 -0
  50. procesio_cli/agents/procesio/knowledge.py +115 -0
  51. procesio_cli/agents/procesio/main.py +89 -0
  52. procesio_cli/agents/procesio/tests/__init__.py +0 -0
  53. procesio_cli/agents/procesio/tests/conftest.py +62 -0
  54. procesio_cli/agents/procesio/tests/test_audit.py +91 -0
  55. procesio_cli/agents/procesio/tests/test_checklist.py +28 -0
  56. procesio_cli/agents/procesio/tests/test_guidance.py +78 -0
  57. procesio_cli/agents/procesio/tests/test_manifest_sync.py +48 -0
  58. procesio_cli/agents/procesio/tests/test_verify.py +134 -0
  59. procesio_cli/agents/procesio/tests/test_workspace_scope.py +97 -0
  60. procesio_cli/agents/procesio/verifylib.py +222 -0
  61. procesio_cli/dashboard/README.md +117 -0
  62. procesio_cli/dashboard/__init__.py +18 -0
  63. procesio_cli/dashboard/serve.py +98 -0
  64. procesio_cli/dashboard/server/__init__.py +1 -0
  65. procesio_cli/dashboard/server/config_schemas.py +92 -0
  66. procesio_cli/dashboard/server/google.py +103 -0
  67. procesio_cli/dashboard/server/handler.py +160 -0
  68. procesio_cli/dashboard/server/inventory.py +213 -0
  69. procesio_cli/dashboard/server/jobs.py +118 -0
  70. procesio_cli/dashboard/server/llmtest.py +181 -0
  71. procesio_cli/dashboard/server/procesio_env.py +94 -0
  72. procesio_cli/dashboard/server/routes.py +74 -0
  73. procesio_cli/dashboard/server/runner.py +150 -0
  74. procesio_cli/dashboard/server/security.py +29 -0
  75. procesio_cli/dashboard/server/setup.py +211 -0
  76. procesio_cli/dashboard/server/validate.py +152 -0
  77. procesio_cli/dashboard/tests/conftest.py +16 -0
  78. procesio_cli/dashboard/tests/test_dashboard.py +225 -0
  79. procesio_cli/dashboard/tests/test_procesio_env.py +159 -0
  80. procesio_cli/dashboard/web/actions.js +274 -0
  81. procesio_cli/dashboard/web/api.js +79 -0
  82. procesio_cli/dashboard/web/app.js +385 -0
  83. procesio_cli/dashboard/web/copilot.js +72 -0
  84. procesio_cli/dashboard/web/google.js +121 -0
  85. procesio_cli/dashboard/web/index.html +67 -0
  86. procesio_cli/dashboard/web/procesio_env.js +187 -0
  87. procesio_cli/dashboard/web/styles.css +207 -0
  88. procesio_cli/registry.py +370 -0
  89. procesio_cli/scripts/build-router.py +249 -0
  90. procesio_cli/scripts/build-tool-skill.py +202 -0
  91. procesio_cli/scripts/check-requirements.py +131 -0
  92. procesio_cli/scripts/check-skill-governance.py +174 -0
  93. procesio_cli/scripts/evaluate-skill-routing.py +217 -0
  94. procesio_cli/scripts/evaluate-skills.py +316 -0
  95. procesio_cli/scripts/get-skill.py +122 -0
  96. procesio_cli/scripts/list-skills.py +68 -0
  97. procesio_cli/scripts/list-tools.py +73 -0
  98. procesio_cli/scripts/run-agent.py +59 -0
  99. procesio_cli/scripts/run-tool.py +123 -0
  100. procesio_cli/scripts/secret_scan.py +584 -0
  101. procesio_cli/scripts/set-credential.py +126 -0
  102. procesio_cli/scripts/validate-skills.py +530 -0
  103. procesio_cli/skills/PORTFOLIO.md +134 -0
  104. procesio_cli/skills/__init__.py +6 -0
  105. procesio_cli/skills/agent-skill-engineer/SKILL.md +226 -0
  106. procesio_cli/skills/agent-skill-engineer/evals/evals.json +422 -0
  107. procesio_cli/skills/agent-skill-engineer/references/authoring-standard.md +178 -0
  108. procesio_cli/skills/agent-skill-engineer/references/decision-framework.md +107 -0
  109. procesio_cli/skills/agent-skill-engineer/references/domain-evidence-protocol.md +248 -0
  110. procesio_cli/skills/agent-skill-engineer/references/evaluation-standard.md +191 -0
  111. procesio_cli/skills/agent-skill-engineer/references/expert-lenses.md +167 -0
  112. procesio_cli/skills/agent-skill-engineer/references/field-gate-standard.md +231 -0
  113. procesio_cli/skills/agent-skill-engineer/references/optimization-protocol.md +233 -0
  114. procesio_cli/skills/agent-skill-engineer/references/optimizer-contract.md +268 -0
  115. procesio_cli/skills/agent-skill-engineer/references/portability-standard.md +99 -0
  116. procesio_cli/skills/agent-skill-engineer/references/research-basis.md +128 -0
  117. procesio_cli/skills/agent-skill-engineer/references/review-standard.md +143 -0
  118. procesio_cli/skills/agent-skill-engineer/scripts/audit_skill.py +959 -0
  119. procesio_cli/skills/agent-skill-engineer/scripts/optimize_skill.py +891 -0
  120. procesio_cli/skills/agent-skill-engineer/scripts/scaffold_skill.py +397 -0
  121. procesio_cli/skills/evals/baseline-catalog.json +14 -0
  122. procesio_cli/skills/evals/baseline-routing-v2.json +38 -0
  123. procesio_cli/skills/evals/baseline.json +37 -0
  124. procesio_cli/skills/evals/behavioral.json +367 -0
  125. procesio_cli/skills/evals/field-boundary-development-v1.json +126 -0
  126. procesio_cli/skills/evals/field-closeout-correction-development-v1.json +23 -0
  127. procesio_cli/skills/evals/field-closeout-development-v1.json +89 -0
  128. procesio_cli/skills/evals/portfolio-pressure-training-v1.json +83 -0
  129. procesio_cli/skills/evals/routing.json +363 -0
  130. procesio_cli/skills/evals/validation-baseline.json +4 -0
  131. procesio_cli/skills/procesio-cli/SKILL.md +134 -0
  132. procesio_cli/skills/procesio-cli/evals/evals.json +111 -0
  133. procesio_cli/skills/procesio-cli/evals/workflows.json +16 -0
  134. procesio_cli/skills/procesio-cli/references/connector-lifecycle.md +47 -0
  135. procesio_cli/skills/procesio-cli/references/credentials-admin.md +113 -0
  136. procesio_cli/skills/procesio-cli/references/data-verification.md +71 -0
  137. procesio_cli/skills/procesio-cli/references/documents-files.md +44 -0
  138. procesio_cli/skills/procesio-cli/references/form-e2e.md +57 -0
  139. procesio_cli/skills/procesio-cli/references/operation-contract.md +138 -0
  140. procesio_cli/skills/procesio-cli/references/process-debugging.md +65 -0
  141. procesio_cli/skills/procesio-cli/references/process-lifecycle.md +88 -0
  142. procesio_cli/skills/procesio-cli/references/schedules-webhooks.md +58 -0
  143. procesio_cli/skills/procesio-cli/references/transport-environments.md +53 -0
  144. procesio_cli/skills/procesio-cli-maintainer/SKILL.md +84 -0
  145. procesio_cli/skills/procesio-cli-maintainer/evals/evals.json +116 -0
  146. procesio_cli/skills/procesio-cli-maintainer/references/change-contract.md +34 -0
  147. procesio_cli/skills/procesio-cli-maintainer/references/mcp-compatibility.md +13 -0
  148. procesio_cli/skills/procesio-cli-maintainer/references/skill-authoring.md +51 -0
  149. procesio_cli/skills/procesio-platform-advisor/SKILL.md +82 -0
  150. procesio_cli/skills/procesio-platform-advisor/evals/evals.json +116 -0
  151. procesio_cli/skills/procesio-platform-advisor/references/capability-map.md +22 -0
  152. procesio_cli/skills/procesio-platform-advisor/references/fit-framework.md +35 -0
  153. procesio_cli/skills/procesio-platform-advisor/references/freshness-policy.md +25 -0
  154. procesio_cli/skills/procesio-platform-advisor/references/sizing-method.md +70 -0
  155. procesio_cli/skills/procesio-platform-advisor/scripts/estimate_capacity.py +99 -0
  156. procesio_cli/skills/procesio-setup/SKILL.md +180 -0
  157. procesio_cli/skills/sql-server-optimizer/SKILL.md +86 -0
  158. procesio_cli/skills/sql-server-optimizer/evals/evals.json +116 -0
  159. procesio_cli/skills/sql-server-optimizer/references/db-context.md +44 -0
  160. procesio_cli/skills/sql-server-optimizer/references/generic-mode.md +32 -0
  161. procesio_cli/skills/sql-server-optimizer/references/procesio-mode.md +40 -0
  162. procesio_cli/skills/sql-server-optimizer/references/sql-rules.md +43 -0
  163. procesio_cli/skills/sql-server-optimizer/scripts/export-indexes.sql +178 -0
  164. procesio_cli/skills/sql-server-optimizer/scripts/export-procs-and-functions.sql +118 -0
  165. procesio_cli/skills/sql-server-optimizer/scripts/export-tables.sql +367 -0
  166. procesio_cli/skills/tests/__init__.py +0 -0
  167. procesio_cli/skills/tests/test_agent_skill_engineer.py +240 -0
  168. procesio_cli/skills/tests/test_auth_failure_stop.py +25 -0
  169. procesio_cli/skills/tests/test_capacity_estimator.py +85 -0
  170. procesio_cli/skills/tests/test_field_boundary_learning.py +100 -0
  171. procesio_cli/skills/tests/test_field_closeout_learning.py +123 -0
  172. procesio_cli/skills/tests/test_field_gate_standard.py +76 -0
  173. procesio_cli/skills/tests/test_operational_playbooks.py +51 -0
  174. procesio_cli/skills/tests/test_portfolio_pressure_training.py +35 -0
  175. procesio_cli/skills/tests/test_process_lifecycle_single_run.py +25 -0
  176. procesio_cli/skills/tests/test_skill_evaluator.py +67 -0
  177. procesio_cli/skills/tests/test_skill_optimizer.py +473 -0
  178. procesio_cli/skills/tests/test_skill_portfolio_review.py +70 -0
  179. procesio_cli/skills/tests/test_skill_release_binding.py +204 -0
  180. procesio_cli/skills/tests/test_skill_routing.py +74 -0
  181. procesio_cli/skills/tests/test_skill_scaffold_v2.py +116 -0
  182. procesio_cli/skills/tests/test_validate_skills.py +115 -0
  183. procesio_cli/tools/__init__.py +0 -0
  184. procesio_cli/tools/_lib/README.md +119 -0
  185. procesio_cli/tools/_lib/__init__.py +0 -0
  186. procesio_cli/tools/_lib/accounting.py +120 -0
  187. procesio_cli/tools/_lib/contacts.py +544 -0
  188. procesio_cli/tools/_lib/creds.py +83 -0
  189. procesio_cli/tools/_lib/creds_backends.py +578 -0
  190. procesio_cli/tools/_lib/dbprofiles.py +147 -0
  191. procesio_cli/tools/_lib/dbquery.py +286 -0
  192. procesio_cli/tools/_lib/dbschema.py +225 -0
  193. procesio_cli/tools/_lib/io.py +54 -0
  194. procesio_cli/tools/_lib/manifest.py +297 -0
  195. procesio_cli/tools/_lib/skill_release.py +127 -0
  196. procesio_cli/tools/_lib/skill_resources.py +118 -0
  197. procesio_cli/tools/_lib/userdata.py +228 -0
  198. procesio_cli/tools/_lib/webserialize/__init__.py +16 -0
  199. procesio_cli/tools/_lib/webserialize/admin.py +61 -0
  200. procesio_cli/tools/_lib/webserialize/client.py +264 -0
  201. procesio_cli/tools/_lib/webserialize/config.py +96 -0
  202. procesio_cli/tools/_lib/webserialize/errors.py +27 -0
  203. procesio_cli/tools/_lib/webserialize/framing.py +45 -0
  204. procesio_cli/tools/_lib/webserialize/registry.py +47 -0
  205. procesio_cli/tools/_lib/webserialize/runtime.py +61 -0
  206. procesio_cli/tools/_lib/webserialize/server.py +510 -0
  207. procesio_cli/tools/_lib/websession.py +194 -0
  208. procesio_cli/tools/connector-builder/CONNECTOR-BUILDER-API-NOTES.md +91 -0
  209. procesio_cli/tools/connector-builder/README.md +101 -0
  210. procesio_cli/tools/connector-builder/SKILL.md +248 -0
  211. procesio_cli/tools/connector-builder/__init__.py +7 -0
  212. procesio_cli/tools/connector-builder/actiondef.py +24 -0
  213. procesio_cli/tools/connector-builder/client.py +251 -0
  214. procesio_cli/tools/connector-builder/errors.py +44 -0
  215. procesio_cli/tools/connector-builder/gen_manifest.py +142 -0
  216. procesio_cli/tools/connector-builder/handlers/__init__.py +1 -0
  217. procesio_cli/tools/connector-builder/handlers/admin.py +182 -0
  218. procesio_cli/tools/connector-builder/handlers/auth.py +74 -0
  219. procesio_cli/tools/connector-builder/handlers/builds.py +396 -0
  220. procesio_cli/tools/connector-builder/handlers/common.py +104 -0
  221. procesio_cli/tools/connector-builder/handlers/files.py +111 -0
  222. procesio_cli/tools/connector-builder/handlers/raw.py +38 -0
  223. procesio_cli/tools/connector-builder/handlers/telemetry.py +39 -0
  224. procesio_cli/tools/connector-builder/main.py +94 -0
  225. procesio_cli/tools/connector-builder/tests/conftest.py +146 -0
  226. procesio_cli/tools/connector-builder/tests/test_cb_client.py +86 -0
  227. procesio_cli/tools/connector-builder/tests/test_cb_dispatch.py +105 -0
  228. procesio_cli/tools/connector-builder/tests/test_cb_manifest_sync.py +76 -0
  229. procesio_cli/tools/connector-builder/tests/test_cb_wait.py +123 -0
  230. procesio_cli/tools/connector-builder/tool.yaml +671 -0
  231. procesio_cli/tools/framework-map/.gitattributes +2 -0
  232. procesio_cli/tools/framework-map/README.md +98 -0
  233. procesio_cli/tools/framework-map/SKILL.md +30 -0
  234. procesio_cli/tools/framework-map/app.js +188 -0
  235. procesio_cli/tools/framework-map/fm_builder.py +476 -0
  236. procesio_cli/tools/framework-map/fm_catalog.py +19 -0
  237. procesio_cli/tools/framework-map/fm_ro_catalog.py +94 -0
  238. procesio_cli/tools/framework-map/fm_strings.py +101 -0
  239. procesio_cli/tools/framework-map/main.py +95 -0
  240. procesio_cli/tools/framework-map/styles.css +214 -0
  241. procesio_cli/tools/framework-map/tests/conftest.py +33 -0
  242. procesio_cli/tools/framework-map/tests/test_fm_build.py +126 -0
  243. procesio_cli/tools/framework-map/tests/test_fm_manifest_sync.py +63 -0
  244. procesio_cli/tools/framework-map/tests/test_fm_store_tree.py +75 -0
  245. procesio_cli/tools/framework-map/tool.yaml +61 -0
  246. procesio_cli/tools/mysql/MOVED-TO-CENTRAL.md +29 -0
  247. procesio_cli/tools/mysql/README.md +196 -0
  248. procesio_cli/tools/mysql/SKILL.md +37 -0
  249. procesio_cli/tools/mysql/__init__.py +0 -0
  250. procesio_cli/tools/mysql/connection.py +191 -0
  251. procesio_cli/tools/mysql/main.py +487 -0
  252. procesio_cli/tools/mysql/profiles.template.json +1 -0
  253. procesio_cli/tools/mysql/schema.py +434 -0
  254. procesio_cli/tools/mysql/tests/__init__.py +0 -0
  255. procesio_cli/tools/mysql/tests/conftest.py +21 -0
  256. procesio_cli/tools/mysql/tests/test_manifest_sync.py +68 -0
  257. procesio_cli/tools/mysql/tests/test_mysql.py +201 -0
  258. procesio_cli/tools/mysql/tests/test_mysql_schema.py +337 -0
  259. procesio_cli/tools/mysql/tool.yaml +112 -0
  260. procesio_cli/tools/procesio/DTO-SUBTOOLS-NOTE.md +360 -0
  261. procesio_cli/tools/procesio/FORM-DEV-GUIDE/00-INDEX.md +67 -0
  262. procesio_cli/tools/procesio/FORM-DEV-GUIDE/01-ANATOMY.md +209 -0
  263. procesio_cli/tools/procesio/FORM-DEV-GUIDE/02-CODE-INJECTION.md +309 -0
  264. procesio_cli/tools/procesio/FORM-DEV-GUIDE/03-DOM-CONTRACT.md +370 -0
  265. procesio_cli/tools/procesio/FORM-DEV-GUIDE/04-INTERACTION-RECIPES.md +600 -0
  266. procesio_cli/tools/procesio/FORM-DEV-GUIDE/05-STEPPER-AND-MOTION.md +426 -0
  267. procesio_cli/tools/procesio/FORM-DEV-GUIDE/06-PROCESS-INTEGRATION.md +347 -0
  268. procesio_cli/tools/procesio/FORM-DEV-GUIDE/07-DEPLOY-WORKFLOW.md +294 -0
  269. procesio_cli/tools/procesio/FORM-DEV-GUIDE/08-PITFALLS.md +569 -0
  270. procesio_cli/tools/procesio/FORM-DEV-GUIDE/09-NAVIGATION-AND-HOSTING.md +308 -0
  271. procesio_cli/tools/procesio/PHASE4-E2E-NOTES.md +594 -0
  272. procesio_cli/tools/procesio/PROCESIO-API-CORRECTIONS.md +295 -0
  273. procesio_cli/tools/procesio/PROCESIO-API-NOTES.md +6976 -0
  274. procesio_cli/tools/procesio/PROCESIO-AUTH-NOTES.md +226 -0
  275. procesio_cli/tools/procesio/PROCESIO-CARD-BUILD-NOTES.md +183 -0
  276. procesio_cli/tools/procesio/PROCESIO-CONNECTORS-NOTES.md +141 -0
  277. procesio_cli/tools/procesio/PROCESIO-CUSTOM-ACTION-NOTES.md +179 -0
  278. procesio_cli/tools/procesio/PROCESIO-DOCS-FIX-REPORT.md +123 -0
  279. procesio_cli/tools/procesio/PROCESIO-ENVIRONMENTS-NOTES.md +94 -0
  280. procesio_cli/tools/procesio/PROCESIO-FE-VALIDATION-NOTES.md +107 -0
  281. procesio_cli/tools/procesio/PROCESIO-FOREACH-NOTES.md +199 -0
  282. procesio_cli/tools/procesio/PROCESIO-FORM-API-HANG-NOTE.md +101 -0
  283. procesio_cli/tools/procesio/PROCESIO-FORM-SUBMISSION-NOTES.md +172 -0
  284. procesio_cli/tools/procesio/PROCESIO-HTML-TO-DOCX-NOTES.md +110 -0
  285. procesio_cli/tools/procesio/PROCESIO-INSTANCE-HISTORY-NOTES.md +242 -0
  286. procesio_cli/tools/procesio/PROCESIO-METERING-NOTES.md +201 -0
  287. procesio_cli/tools/procesio/PROCESIO-NODE-CODE-NOTES.md +279 -0
  288. procesio_cli/tools/procesio/PROCESIO-NODE-MODULE-WHITELIST.md +113 -0
  289. procesio_cli/tools/procesio/PROCESIO-PYTHON-ACTION-NOTES.md +165 -0
  290. procesio_cli/tools/procesio/PROCESIO-QUERY-STORE-NOTES.md +826 -0
  291. procesio_cli/tools/procesio/PROCESIO-RECONCILIATION-PATTERNS.md +104 -0
  292. procesio_cli/tools/procesio/PROCESIO-RESOURCE-MODEL-NOTES.md +1100 -0
  293. procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-CRED-OPTIONALITY.md +45 -0
  294. procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-NOTES.md +252 -0
  295. procesio_cli/tools/procesio/PROCESIO-SQL-ACTIONS-NOTES.md +284 -0
  296. procesio_cli/tools/procesio/PROCESIO-USAGE-GUIDE.md +219 -0
  297. procesio_cli/tools/procesio/README.md +341 -0
  298. procesio_cli/tools/procesio/SCHEDULE-INPUT-SECURITY-NOTES.md +63 -0
  299. procesio_cli/tools/procesio/SKILL.md +694 -0
  300. procesio_cli/tools/procesio/__init__.py +1 -0
  301. procesio_cli/tools/procesio/actiondef.py +27 -0
  302. procesio_cli/tools/procesio/assets/html2docx/html2docx.js +2155 -0
  303. procesio_cli/tools/procesio/auth.py +354 -0
  304. procesio_cli/tools/procesio/autogen.py +121 -0
  305. procesio_cli/tools/procesio/client.py +355 -0
  306. procesio_cli/tools/procesio/config.py +96 -0
  307. procesio_cli/tools/procesio/data/endpoints.json +2901 -0
  308. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/01-authentication.md +180 -0
  309. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/02-conventions.md +116 -0
  310. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/ANONYMOUS-SURFACE.md +51 -0
  311. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/README.md +104 -0
  312. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/SYMBOL-INDEX.md +444 -0
  313. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/03-auth-users-workspaces.md +731 -0
  314. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/04-processes.md +913 -0
  315. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/05-actions.md +605 -0
  316. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/06-files.md +237 -0
  317. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/07-forms-documents.md +985 -0
  318. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/08-credentials.md +532 -0
  319. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/09-datastore-datatypes.md +830 -0
  320. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/10-custom-urls.md +319 -0
  321. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/11-subscriptions-resources-analytics.md +455 -0
  322. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/12-webhooks-notifications-misc.md +519 -0
  323. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/13-realtime-signalr.md +153 -0
  324. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/99-internal-and-other-services.md +52 -0
  325. procesio_cli/tools/procesio/docs_info/FormBuilder mock.ts +5725 -0
  326. procesio_cli/tools/procesio/docs_info/Procesio Platform Actions.json +30219 -0
  327. procesio_cli/tools/procesio/docs_info/form-config-enums.ts +92 -0
  328. procesio_cli/tools/procesio/docs_info/form-theme.ts +1603 -0
  329. procesio_cli/tools/procesio/docs_info/process-validation-reference-1.md +2739 -0
  330. procesio_cli/tools/procesio/dto/__init__.py +13 -0
  331. procesio_cli/tools/procesio/dto/credential/builder.py +179 -0
  332. procesio_cli/tools/procesio/dto/credential/config.schema.json +19 -0
  333. procesio_cli/tools/procesio/dto/credential/description.md +193 -0
  334. procesio_cli/tools/procesio/dto/credential/fixtures/rest.config.json +11 -0
  335. procesio_cli/tools/procesio/dto/credential/fixtures/rest.dto.json +28 -0
  336. procesio_cli/tools/procesio/dto/credential/fixtures/rest.template.json +44 -0
  337. procesio_cli/tools/procesio/dto/credential/template.dto.json +16 -0
  338. procesio_cli/tools/procesio/dto/data/_refresh.py +147 -0
  339. procesio_cli/tools/procesio/dto/data/action_catalog.json +30825 -0
  340. procesio_cli/tools/procesio/dto/data/action_families.json +55 -0
  341. procesio_cli/tools/procesio/dto/data/platform_types.json +182 -0
  342. procesio_cli/tools/procesio/dto/datatype/builder.py +223 -0
  343. procesio_cli/tools/procesio/dto/datatype/config.schema.json +47 -0
  344. procesio_cli/tools/procesio/dto/datatype/description.md +85 -0
  345. procesio_cli/tools/procesio/dto/datatype/fixtures/customer.config.json +41 -0
  346. procesio_cli/tools/procesio/dto/datatype/fixtures/customer.dto.json +164 -0
  347. procesio_cli/tools/procesio/dto/datatype/template.dto.json +37 -0
  348. procesio_cli/tools/procesio/dto/document/builder.py +193 -0
  349. procesio_cli/tools/procesio/dto/document/config.schema.json +33 -0
  350. procesio_cli/tools/procesio/dto/document/description.md +124 -0
  351. procesio_cli/tools/procesio/dto/document/fixtures/invoice.config.json +16 -0
  352. procesio_cli/tools/procesio/dto/document/fixtures/invoice.dto.json +32 -0
  353. procesio_cli/tools/procesio/dto/document/template.dto.json +14 -0
  354. procesio_cli/tools/procesio/dto/form/FORM-STYLING-NOTES.md +142 -0
  355. procesio_cli/tools/procesio/dto/form/GOLDEN-DRIFT-NOTES.md +72 -0
  356. procesio_cli/tools/procesio/dto/form/RUN-PROCESS-RESULT-NOTES.md +80 -0
  357. procesio_cli/tools/procesio/dto/form/addelement.py +277 -0
  358. procesio_cli/tools/procesio/dto/form/builder.py +1009 -0
  359. procesio_cli/tools/procesio/dto/form/capture_goldens.py +106 -0
  360. procesio_cli/tools/procesio/dto/form/code_cipher.py +67 -0
  361. procesio_cli/tools/procesio/dto/form/config.schema.json +76 -0
  362. procesio_cli/tools/procesio/dto/form/data_shell.json +1 -0
  363. procesio_cli/tools/procesio/dto/form/description.md +346 -0
  364. procesio_cli/tools/procesio/dto/form/elements/approval.json +259 -0
  365. procesio_cli/tools/procesio/dto/form/elements/assignee.json +137 -0
  366. procesio_cli/tools/procesio/dto/form/elements/button.json +125 -0
  367. procesio_cli/tools/procesio/dto/form/elements/chart.json +341 -0
  368. procesio_cli/tools/procesio/dto/form/elements/chat.json +199 -0
  369. procesio_cli/tools/procesio/dto/form/elements/checkbox.json +153 -0
  370. procesio_cli/tools/procesio/dto/form/elements/column.json +77 -0
  371. procesio_cli/tools/procesio/dto/form/elements/columns.json +78 -0
  372. procesio_cli/tools/procesio/dto/form/elements/datetime-input.json +304 -0
  373. procesio_cli/tools/procesio/dto/form/elements/divider.json +54 -0
  374. procesio_cli/tools/procesio/dto/form/elements/dropdown.json +130 -0
  375. procesio_cli/tools/procesio/dto/form/elements/dynamic-table-row.json +253 -0
  376. procesio_cli/tools/procesio/dto/form/elements/file-upload.json +196 -0
  377. procesio_cli/tools/procesio/dto/form/elements/file-viewer.json +169 -0
  378. procesio_cli/tools/procesio/dto/form/elements/heading.json +79 -0
  379. procesio_cli/tools/procesio/dto/form/elements/icon.json +107 -0
  380. procesio_cli/tools/procesio/dto/form/elements/image.json +174 -0
  381. procesio_cli/tools/procesio/dto/form/elements/input.json +206 -0
  382. procesio_cli/tools/procesio/dto/form/elements/list.json +202 -0
  383. procesio_cli/tools/procesio/dto/form/elements/number-input.json +202 -0
  384. procesio_cli/tools/procesio/dto/form/elements/paragraph.json +79 -0
  385. procesio_cli/tools/procesio/dto/form/elements/radiobox.json +174 -0
  386. procesio_cli/tools/procesio/dto/form/elements/section.json +114 -0
  387. procesio_cli/tools/procesio/dto/form/elements/select.json +217 -0
  388. procesio_cli/tools/procesio/dto/form/elements/side-panel.json +146 -0
  389. procesio_cli/tools/procesio/dto/form/elements/signature-pad.json +141 -0
  390. procesio_cli/tools/procesio/dto/form/elements/static-table-row.json +84 -0
  391. procesio_cli/tools/procesio/dto/form/elements/step.json +105 -0
  392. procesio_cli/tools/procesio/dto/form/elements/stepper.json +126 -0
  393. procesio_cli/tools/procesio/dto/form/elements/tab.json +77 -0
  394. procesio_cli/tools/procesio/dto/form/elements/table.json +279 -0
  395. procesio_cli/tools/procesio/dto/form/elements/tabs.json +114 -0
  396. procesio_cli/tools/procesio/dto/form/elements/textarea.json +213 -0
  397. procesio_cli/tools/procesio/dto/form/enum_map.json +68 -0
  398. procesio_cli/tools/procesio/dto/form/fieldpath.py +284 -0
  399. procesio_cli/tools/procesio/dto/form/fixtures/reg.config.json +32 -0
  400. procesio_cli/tools/procesio/dto/form/fixtures/reg.elements.json +638 -0
  401. procesio_cli/tools/procesio/dto/form/sync_from_mock.py +386 -0
  402. procesio_cli/tools/procesio/dto/framework.py +125 -0
  403. procesio_cli/tools/procesio/dto/process/builder.py +1991 -0
  404. procesio_cli/tools/procesio/dto/process/config.schema.json +159 -0
  405. procesio_cli/tools/procesio/dto/process/description.md +218 -0
  406. procesio_cli/tools/procesio/dto/process/fixtures/greet.config.json +32 -0
  407. procesio_cli/tools/procesio/dto/process/fixtures/greet.dto.json +317 -0
  408. procesio_cli/tools/procesio/dto/process/naming.py +305 -0
  409. procesio_cli/tools/procesio/dto/process/normalize.py +74 -0
  410. procesio_cli/tools/procesio/dto/process/template.dto.json +32 -0
  411. procesio_cli/tools/procesio/dto/refdata.py +87 -0
  412. procesio_cli/tools/procesio/dto/registry.py +27 -0
  413. procesio_cli/tools/procesio/dto/webhook/builder.py +142 -0
  414. procesio_cli/tools/procesio/dto/webhook/config.schema.json +25 -0
  415. procesio_cli/tools/procesio/dto/webhook/description.md +92 -0
  416. procesio_cli/tools/procesio/dto/webhook/fixtures/order.config.json +10 -0
  417. procesio_cli/tools/procesio/dto/webhook/fixtures/order.dto.json +25 -0
  418. procesio_cli/tools/procesio/dto/webhook/template.dto.json +14 -0
  419. procesio_cli/tools/procesio/environments.py +264 -0
  420. procesio_cli/tools/procesio/errors.py +166 -0
  421. procesio_cli/tools/procesio/flowmodel/README.md +37 -0
  422. procesio_cli/tools/procesio/flowmodel/__init__.py +14 -0
  423. procesio_cli/tools/procesio/flowmodel/digest.py +223 -0
  424. procesio_cli/tools/procesio/flowmodel/families.py +34 -0
  425. procesio_cli/tools/procesio/flowmodel/fevalidation.py +1098 -0
  426. procesio_cli/tools/procesio/flowmodel/formdigest.py +232 -0
  427. procesio_cli/tools/procesio/flowmodel/inspect.py +78 -0
  428. procesio_cli/tools/procesio/flowmodel/model.py +82 -0
  429. procesio_cli/tools/procesio/flowmodel/nodeparam.py +871 -0
  430. procesio_cli/tools/procesio/flowmodel/reader.py +191 -0
  431. procesio_cli/tools/procesio/flowmodel/sqlparam.py +410 -0
  432. procesio_cli/tools/procesio/form_code_keys.py +50 -0
  433. procesio_cli/tools/procesio/formlint.py +292 -0
  434. procesio_cli/tools/procesio/gen_manifest.py +118 -0
  435. procesio_cli/tools/procesio/handlers/__init__.py +1 -0
  436. procesio_cli/tools/procesio/handlers/auth_actions.py +103 -0
  437. procesio_cli/tools/procesio/handlers/catalog.py +147 -0
  438. procesio_cli/tools/procesio/handlers/common.py +90 -0
  439. procesio_cli/tools/procesio/handlers/customaction_ops.py +146 -0
  440. procesio_cli/tools/procesio/handlers/datastore_ops.py +482 -0
  441. procesio_cli/tools/procesio/handlers/datatype_ops.py +275 -0
  442. procesio_cli/tools/procesio/handlers/dto_actions.py +258 -0
  443. procesio_cli/tools/procesio/handlers/environment_admin.py +146 -0
  444. procesio_cli/tools/procesio/handlers/fevalidate.py +236 -0
  445. procesio_cli/tools/procesio/handlers/files.py +141 -0
  446. procesio_cli/tools/procesio/handlers/flowgraph.py +229 -0
  447. procesio_cli/tools/procesio/handlers/flowlint.py +269 -0
  448. procesio_cli/tools/procesio/handlers/form_chains.py +336 -0
  449. procesio_cli/tools/procesio/handlers/form_code.py +277 -0
  450. procesio_cli/tools/procesio/handlers/form_element.py +199 -0
  451. procesio_cli/tools/procesio/handlers/form_events.py +441 -0
  452. procesio_cli/tools/procesio/handlers/form_update.py +270 -0
  453. procesio_cli/tools/procesio/handlers/formprocess.py +211 -0
  454. procesio_cli/tools/procesio/handlers/generic.py +135 -0
  455. procesio_cli/tools/procesio/handlers/layout.py +137 -0
  456. procesio_cli/tools/procesio/handlers/nodeparams.py +1018 -0
  457. procesio_cli/tools/procesio/handlers/process_layout.py +189 -0
  458. procesio_cli/tools/procesio/handlers/process_naming.py +213 -0
  459. procesio_cli/tools/procesio/handlers/processes.py +416 -0
  460. procesio_cli/tools/procesio/handlers/profile_admin.py +184 -0
  461. procesio_cli/tools/procesio/handlers/resource_ops.py +447 -0
  462. procesio_cli/tools/procesio/handlers/schedules.py +320 -0
  463. procesio_cli/tools/procesio/handlers/sqlactions.py +232 -0
  464. procesio_cli/tools/procesio/handlers/transport.py +463 -0
  465. procesio_cli/tools/procesio/handlers/usageguide.py +222 -0
  466. procesio_cli/tools/procesio/layout/README.md +196 -0
  467. procesio_cli/tools/procesio/layout/__init__.py +11 -0
  468. procesio_cli/tools/procesio/layout/adapter.py +736 -0
  469. procesio_cli/tools/procesio/layout/cycles.py +186 -0
  470. procesio_cli/tools/procesio/layout/dispatch.py +39 -0
  471. procesio_cli/tools/procesio/layout/elk/elk_runner.mjs +50 -0
  472. procesio_cli/tools/procesio/layout/elk/package-lock.json +21 -0
  473. procesio_cli/tools/procesio/layout/elk/package.json +11 -0
  474. procesio_cli/tools/procesio/layout/elk_engine.py +568 -0
  475. procesio_cli/tools/procesio/layout/engine.py +1311 -0
  476. procesio_cli/tools/procesio/layout/report.py +187 -0
  477. procesio_cli/tools/procesio/main.py +179 -0
  478. procesio_cli/tools/procesio/profiles.py +193 -0
  479. procesio_cli/tools/procesio/reliability.py +193 -0
  480. procesio_cli/tools/procesio/sqlformat.py +167 -0
  481. procesio_cli/tools/procesio/swagger.py +51 -0
  482. procesio_cli/tools/procesio/tests/__init__.py +0 -0
  483. procesio_cli/tools/procesio/tests/conftest.py +129 -0
  484. procesio_cli/tools/procesio/tests/test_auth.py +200 -0
  485. procesio_cli/tools/procesio/tests/test_autogen.py +78 -0
  486. procesio_cli/tools/procesio/tests/test_builder_attrpath_sqlbind.py +180 -0
  487. procesio_cli/tools/procesio/tests/test_builder_custom_action_catalog.py +72 -0
  488. procesio_cli/tools/procesio/tests/test_client.py +114 -0
  489. procesio_cli/tools/procesio/tests/test_client_reliability.py +139 -0
  490. procesio_cli/tools/procesio/tests/test_customaction.py +103 -0
  491. procesio_cli/tools/procesio/tests/test_customaction_test.py +80 -0
  492. procesio_cli/tools/procesio/tests/test_cycles.py +69 -0
  493. procesio_cli/tools/procesio/tests/test_datastore.py +378 -0
  494. procesio_cli/tools/procesio/tests/test_datastore_node.py +94 -0
  495. procesio_cli/tools/procesio/tests/test_dispatch.py +170 -0
  496. procesio_cli/tools/procesio/tests/test_document_page_size.py +19 -0
  497. procesio_cli/tools/procesio/tests/test_dto.py +734 -0
  498. procesio_cli/tools/procesio/tests/test_edit_dryrun_preview.py +96 -0
  499. procesio_cli/tools/procesio/tests/test_elk_layout.py +221 -0
  500. procesio_cli/tools/procesio/tests/test_environments.py +214 -0
  501. procesio_cli/tools/procesio/tests/test_fevalidate_gate.py +104 -0
  502. procesio_cli/tools/procesio/tests/test_fevalidation.py +317 -0
  503. procesio_cli/tools/procesio/tests/test_fieldpath_variables.py +87 -0
  504. procesio_cli/tools/procesio/tests/test_files.py +72 -0
  505. procesio_cli/tools/procesio/tests/test_flow_digest.py +202 -0
  506. procesio_cli/tools/procesio/tests/test_flowlint.py +114 -0
  507. procesio_cli/tools/procesio/tests/test_flowmodel.py +158 -0
  508. procesio_cli/tools/procesio/tests/test_form_add_element.py +256 -0
  509. procesio_cli/tools/procesio/tests/test_form_approval_gaps.py +319 -0
  510. procesio_cli/tools/procesio/tests/test_form_auto_names.py +60 -0
  511. procesio_cli/tools/procesio/tests/test_form_chains.py +217 -0
  512. procesio_cli/tools/procesio/tests/test_form_code_actions.py +188 -0
  513. procesio_cli/tools/procesio/tests/test_form_code_keys.py +92 -0
  514. procesio_cli/tools/procesio/tests/test_form_code_style.py +178 -0
  515. procesio_cli/tools/procesio/tests/test_form_delete_element.py +99 -0
  516. procesio_cli/tools/procesio/tests/test_form_element_config.py +141 -0
  517. procesio_cli/tools/procesio/tests/test_form_event_maps.py +135 -0
  518. procesio_cli/tools/procesio/tests/test_form_event_ops.py +376 -0
  519. procesio_cli/tools/procesio/tests/test_form_event_replace.py +86 -0
  520. procesio_cli/tools/procesio/tests/test_form_events.py +92 -0
  521. procesio_cli/tools/procesio/tests/test_form_parity.py +171 -0
  522. procesio_cli/tools/procesio/tests/test_form_select_source.py +74 -0
  523. procesio_cli/tools/procesio/tests/test_form_sync.py +35 -0
  524. procesio_cli/tools/procesio/tests/test_form_update.py +133 -0
  525. procesio_cli/tools/procesio/tests/test_formlint.py +155 -0
  526. procesio_cli/tools/procesio/tests/test_formlint_css_scope.py +38 -0
  527. procesio_cli/tools/procesio/tests/test_formlint_field_names.py +83 -0
  528. procesio_cli/tools/procesio/tests/test_guards.py +110 -0
  529. procesio_cli/tools/procesio/tests/test_hidden_setting_types.py +39 -0
  530. procesio_cli/tools/procesio/tests/test_html2docx_asset.py +132 -0
  531. procesio_cli/tools/procesio/tests/test_key_handle_terminology.py +45 -0
  532. procesio_cli/tools/procesio/tests/test_layout.py +574 -0
  533. procesio_cli/tools/procesio/tests/test_layout_report.py +88 -0
  534. procesio_cli/tools/procesio/tests/test_manifest_sync.py +68 -0
  535. procesio_cli/tools/procesio/tests/test_nodedelete.py +151 -0
  536. procesio_cli/tools/procesio/tests/test_nodeparam.py +826 -0
  537. procesio_cli/tools/procesio/tests/test_normalize.py +70 -0
  538. procesio_cli/tools/procesio/tests/test_payload_docs.py +86 -0
  539. procesio_cli/tools/procesio/tests/test_process_action_naming.py +170 -0
  540. procesio_cli/tools/procesio/tests/test_process_call_api_seed.py +46 -0
  541. procesio_cli/tools/procesio/tests/test_process_ext.py +373 -0
  542. procesio_cli/tools/procesio/tests/test_process_invariants.py +183 -0
  543. procesio_cli/tools/procesio/tests/test_process_layout.py +117 -0
  544. procesio_cli/tools/procesio/tests/test_process_naming.py +188 -0
  545. procesio_cli/tools/procesio/tests/test_process_query_store.py +172 -0
  546. procesio_cli/tools/procesio/tests/test_processinfo_variable.py +56 -0
  547. procesio_cli/tools/procesio/tests/test_profiles.py +127 -0
  548. procesio_cli/tools/procesio/tests/test_put_projects.py +41 -0
  549. procesio_cli/tools/procesio/tests/test_reliability.py +107 -0
  550. procesio_cli/tools/procesio/tests/test_removal_gate.py +95 -0
  551. procesio_cli/tools/procesio/tests/test_resource_ops.py +173 -0
  552. procesio_cli/tools/procesio/tests/test_save_validity.py +207 -0
  553. procesio_cli/tools/procesio/tests/test_schedule_cron_shape.py +111 -0
  554. procesio_cli/tools/procesio/tests/test_schedules.py +309 -0
  555. procesio_cli/tools/procesio/tests/test_sqlconvert.py +234 -0
  556. procesio_cli/tools/procesio/tests/test_sqlformat.py +83 -0
  557. procesio_cli/tools/procesio/tests/test_sqlparam.py +187 -0
  558. procesio_cli/tools/procesio/tests/test_superseded_actions.py +54 -0
  559. procesio_cli/tools/procesio/tests/test_surgical_concurrency.py +156 -0
  560. procesio_cli/tools/procesio/tests/test_tier4.py +123 -0
  561. procesio_cli/tools/procesio/tests/test_transport.py +147 -0
  562. procesio_cli/tools/procesio/tests/test_transport_data_stores.py +168 -0
  563. procesio_cli/tools/procesio/tests/test_transport_mapper_repair.py +150 -0
  564. procesio_cli/tools/procesio/tests/test_transport_webhook_strip.py +253 -0
  565. procesio_cli/tools/procesio/tests/test_usage_guide.py +96 -0
  566. procesio_cli/tools/procesio/tool.yaml +12486 -0
  567. procesio_cli/tools/sqlserver/MOVED-TO-CENTRAL.md +29 -0
  568. procesio_cli/tools/sqlserver/README.md +192 -0
  569. procesio_cli/tools/sqlserver/SKILL.md +37 -0
  570. procesio_cli/tools/sqlserver/SQLSERVER-SCHEMA-NOTES.md +284 -0
  571. procesio_cli/tools/sqlserver/__init__.py +0 -0
  572. procesio_cli/tools/sqlserver/connection.py +242 -0
  573. procesio_cli/tools/sqlserver/main.py +521 -0
  574. procesio_cli/tools/sqlserver/profiles.template.json +1 -0
  575. procesio_cli/tools/sqlserver/schema.py +600 -0
  576. procesio_cli/tools/sqlserver/tests/__init__.py +0 -0
  577. procesio_cli/tools/sqlserver/tests/conftest.py +21 -0
  578. procesio_cli/tools/sqlserver/tests/test_connection.py +75 -0
  579. procesio_cli/tools/sqlserver/tests/test_dbprofiles.py +92 -0
  580. procesio_cli/tools/sqlserver/tests/test_dbquery.py +220 -0
  581. procesio_cli/tools/sqlserver/tests/test_manifest_sync.py +68 -0
  582. procesio_cli/tools/sqlserver/tests/test_sqlserver.py +326 -0
  583. procesio_cli/tools/sqlserver/tests/test_sqlserver_schema.py +488 -0
  584. procesio_cli/tools/sqlserver/tool.yaml +123 -0
  585. procesio_cli/tools/web/README.md +381 -0
  586. procesio_cli/tools/web/SKILL.md +37 -0
  587. procesio_cli/tools/web/WEB-AUTOMATION-NOTES.md +277 -0
  588. procesio_cli/tools/web/__init__.py +21 -0
  589. procesio_cli/tools/web/actiondef.py +24 -0
  590. procesio_cli/tools/web/api.py +444 -0
  591. procesio_cli/tools/web/brokered.py +107 -0
  592. procesio_cli/tools/web/driver.py +839 -0
  593. procesio_cli/tools/web/drivercontract.py +44 -0
  594. procesio_cli/tools/web/errors.py +54 -0
  595. procesio_cli/tools/web/handlers/__init__.py +1 -0
  596. procesio_cli/tools/web/handlers/archive.py +367 -0
  597. procesio_cli/tools/web/handlers/browse.py +182 -0
  598. procesio_cli/tools/web/handlers/contacts.py +74 -0
  599. procesio_cli/tools/web/handlers/sessmgmt.py +120 -0
  600. procesio_cli/tools/web/main.py +128 -0
  601. procesio_cli/tools/web/sessions.py +253 -0
  602. procesio_cli/tools/web/steps.py +162 -0
  603. procesio_cli/tools/web/tests/__init__.py +0 -0
  604. procesio_cli/tools/web/tests/conftest.py +197 -0
  605. procesio_cli/tools/web/tests/test_api.py +330 -0
  606. procesio_cli/tools/web/tests/test_archive.py +190 -0
  607. procesio_cli/tools/web/tests/test_brokered.py +138 -0
  608. procesio_cli/tools/web/tests/test_dispatch.py +132 -0
  609. procesio_cli/tools/web/tests/test_errors.py +60 -0
  610. procesio_cli/tools/web/tests/test_manifest_sync.py +76 -0
  611. procesio_cli/tools/web/tests/test_page_contacts.py +68 -0
  612. procesio_cli/tools/web/tests/test_sessions.py +145 -0
  613. procesio_cli/tools/web/tests/test_steps.py +210 -0
  614. procesio_cli/tools/web/tool.yaml +184 -0
  615. procesio_cli/tools/xlsx/README.md +34 -0
  616. procesio_cli/tools/xlsx/SKILL.md +28 -0
  617. procesio_cli/tools/xlsx/__init__.py +0 -0
  618. procesio_cli/tools/xlsx/main.py +129 -0
  619. procesio_cli/tools/xlsx/reader.py +91 -0
  620. procesio_cli/tools/xlsx/tests/__init__.py +0 -0
  621. procesio_cli/tools/xlsx/tests/conftest.py +39 -0
  622. procesio_cli/tools/xlsx/tests/test_xlsx.py +103 -0
  623. procesio_cli/tools/xlsx/tool.yaml +30 -0
  624. procesio_cli/webplatform/aat_mcp/AAT-MCP-NOTES.md +244 -0
  625. procesio_cli/webplatform/aat_mcp/README.md +78 -0
  626. procesio_cli/webplatform/aat_mcp/bridge.py +705 -0
  627. procesio_cli/webplatform/aat_mcp/chat_server.py +194 -0
  628. procesio_cli/webplatform/aat_mcp/chat_surface.snapshot.json +668 -0
  629. procesio_cli/webplatform/aat_mcp/chat_surface.yaml +372 -0
  630. procesio_cli/webplatform/aat_mcp/gate.py +68 -0
  631. procesio_cli/webplatform/aat_mcp/http_server.py +139 -0
  632. procesio_cli/webplatform/aat_mcp/protocol.py +175 -0
  633. procesio_cli/webplatform/aat_mcp/server.py +306 -0
  634. procesio_cli/webplatform/aat_mcp/tests/conftest.py +8 -0
  635. procesio_cli/webplatform/aat_mcp/tests/test_aat_mcp.py +255 -0
  636. procesio_cli/webplatform/aat_mcp/tests/test_chat_surface.py +247 -0
  637. procesio_cli/webplatform/aat_mcp/tests/test_discovery.py +489 -0
  638. procesio_cli/webplatform/aat_mcp/tests/test_multiuser.py +86 -0
  639. procesio_cli/webplatform/aat_mcp/tests/test_progressive_discovery.py +89 -0
  640. procesio_cli/webplatform/aat_mcp/tests/test_protocol.py +180 -0
  641. procesio_cli/webplatform/aat_mcp/tests/test_skill_discovery.py +23 -0
  642. procesio_cli-0.1.1.dist-info/METADATA +467 -0
  643. procesio_cli-0.1.1.dist-info/RECORD +647 -0
  644. procesio_cli-0.1.1.dist-info/WHEEL +4 -0
  645. procesio_cli-0.1.1.dist-info/entry_points.txt +7 -0
  646. procesio_cli-0.1.1.dist-info/licenses/LICENSE +201 -0
  647. procesio_cli-0.1.1.dist-info/licenses/NOTICE +5 -0
@@ -0,0 +1,1100 @@
1
+ # PROCESIO resource model — the `.procesio` export DTO (reference)
2
+
3
+ What a `.procesio` export bundle actually contains, field by field, across every
4
+ resource type. Derived from analysis of live production `.procesio` exports
5
+ (`docs_info/Exports/`). This is **additive reference** for the tool and agent: it
6
+ documents the shapes the builders read/merge onto. It does not change any builder.
7
+ Auth/export mechanics stay in [PROCESIO-API-NOTES.md](PROCESIO-API-NOTES.md); casing
8
+ rule (live=camelCase, export=PascalCase) applies to everything here.
9
+
10
+ Top-level keys (every bundle): `DataTypes, Credentials, Webhooks, DocumentTemplates,
11
+ Flows, Forms, DataStores, TimeStamp`. `Webhooks` is empty at top level — webhooks live
12
+ inside `Flows[].Webhooks`.
13
+
14
+ ⚠ **CORRECTED 2026-09-03: `DataStores` is NOT always empty.** It fills whenever the export
15
+ request NAMES a store (`--data-stores` → `dataStoreIds`), and the store's backing data model
16
+ is exported into `DataTypes` alongside it. Each entry carries **schema only** —
17
+ `Id, Name, Description, Columns[{ColumnId, Name, DataTypeId, IsList, IsPrimaryKey, IsRequired,
18
+ IsSystemColumn}], DataTypeId` — with **no rows key of any kind**, so a store always arrives in
19
+ the target EMPTY. The system columns are omitted from the exported `Columns` and re-appended by
20
+ the target on create. Full measurement in
21
+ [PROCESIO-API-NOTES.md](PROCESIO-API-NOTES.md) (dated Data Store section).
22
+
23
+ ---
24
+
25
+ ## 1. The canvas / flow graph (load-bearing for any layout work)
26
+
27
+ A flow's diagram is a **nested directed graph**. Nodes = `Flows[].Actions[]`; edges =
28
+ each action's `Ports[]`. There is no separate node/edge table — geometry and topology
29
+ live on the actions.
30
+
31
+ ### Node geometry — `Action.CustomData`
32
+ ```
33
+ CustomData = {
34
+ type: "square" | "diamond" | "circle" | "area",
35
+ name, description, icon,
36
+ position: { x, y }, // canvas coords, top-left of the node
37
+ areaSize: { width, height, x, y }, // 48x48 for normal nodes; real WxH for "area"
38
+ inputPorts: int, outputPorts: int, // port counts (drive validation 390/391)
39
+ configuration: [ { id, label, settings:[...] } ], // designer mirror of Parameters
40
+ wasDropped: bool
41
+ }
42
+ ```
43
+ - **`type` is dictated by the action, not chosen at placement.** Each action template
44
+ carries its shape: `square` = standard action, `diamond` = a branching action
45
+ (Decisional), `circle` = a terminal (Start/Stop), `area` = a container frame. A
46
+ builder or layout tool **inherits this from the action config and must never change
47
+ the shape** when it places or moves a node — only the `position` (and an area's
48
+ `areaSize`) is the tool's to set.
49
+ - Terminals: **Start** = `in0/out1` (the single source); **Stop** = `in1/out0`. A flow
50
+ has one Start and may have several Stops. Normal nodes are 48x48.
51
+
52
+ ### Edges — `Action.Ports[]`
53
+ ```
54
+ Port = { Id, FlowId, SourceId, DestinationId, Type, State, Data:{}, Errors:{}, Config:{} }
55
+ ```
56
+ - `SourceId` / `DestinationId` are **action Ids** (not port ids). A port stored on an
57
+ action represents an edge leaving/entering the flow graph; iterate every action's
58
+ `Ports[]` to get the full edge set.
59
+ - **`Type`**: `0` = normal flow edge, `1` = **error-port** edge. The error port is how
60
+ "on failure go here" is wired (best-practices §5 global try/catch).
61
+ - A **Decisional** (diamond) fans out **any number of normal (Type 0) edges**, one per
62
+ case — there is no branch limit. Branches do **not** have to re-converge: a branch can
63
+ run to a `Stop`, or several branches can merge at one or more `Join` actions at
64
+ different points. A `Join` is the explicit merge point when paths should reunite.
65
+
66
+ ### Containment / nesting — `Action.ParentId` + `area` nodes
67
+ An `area` node is a **frame that contains other actions**; the contained actions carry a
68
+ `ParentId` pointing at that area, and are drawn inside its `areaSize` rectangle.
69
+ - The container in active use is the **`For Each` loop body**: a `For Each` action
70
+ renders as an area frame (`CustomData.name == "For Each"`, icon `loop`) and the actions
71
+ it iterates over are parented to it.
72
+ - **A `For Each` cannot contain another `For Each`.** To nest a loop, put a
73
+ `Call Subprocess` (or `Trigger Subprocess`) inside the `For Each` and place the inner
74
+ `For Each` in that subprocess.
75
+ - The Process Designer currently supports **only the `For Each` area** as a container —
76
+ it does not yet work with other/free-form areas. Treat `area` containment as For-Each
77
+ containment for now.
78
+
79
+ So layout is **hierarchical**: lay out a container's children within the container's
80
+ box, size the box to fit, then place the box at the parent level. `areaSize` on an
81
+ area node is the frame rectangle.
82
+
83
+ ### Viewport — `Flow.CanvasData`
84
+ `{x, y, zoom}` (e.g. `{0,0,1}`) — the pan/zoom of the designer viewport. **Not** node
85
+ positions. A re-layout tool rewrites each `Action.CustomData.position` and may reset
86
+ `CanvasData` to `{0,0,1}`; it never needs anything else for geometry.
87
+
88
+ ### Cross-process edges (the Resource Map graph)
89
+ A process calls another via a **`Call Subprocess`** (synchronous) or
90
+ **`Trigger Subprocess`** (async) action. The target flow id is the action's config
91
+ setting `type:"flow-list"` `value` (a flow GUID). Input mapping is a
92
+ `type:"process-inputs"` setting holding `[{ id, subprocess:<subVarId>, process:<parentVarId> }]`.
93
+ Build the process→process dependency graph by scanning these settings — that is the
94
+ input to the Resource-Map (LVL-1/LVL0/LVL+n) layout.
95
+
96
+ ---
97
+
98
+ ## 2. Action catalog — common actions by family
99
+
100
+ `Action.Category` is `"cat1"` (a UI bucket, not a useful classifier). The actions a
101
+ builder reaches for most, grouped by what they do (the full per-action config schema is
102
+ the golden `docs_info/Procesio Platform Actions.json`):
103
+
104
+ - **Control / flow:** `Start`, `Stop`, `Decisional` (branch), `Join` (merge),
105
+ `For Each` (loop), `Delay`, `Throw` (raise a controlled error into the error port).
106
+ - **Subprocess:** `Call Subprocess` (run another process synchronously, map vars),
107
+ `Trigger Subprocess` (async fire-and-forget).
108
+ - **Scripting:** `Node` (NodeJS — raw output; needs Timeout>0), `Javascript` (JS — wraps
109
+ output `{result:v}`), `Python`. (Output-wrapping differences: see PROCESIO-API-NOTES.)
110
+ - **Data shaping:** `Map Data`, `Extract Objects`, `Extract List`, `Extract Object`,
111
+ `Extract Text`, `String To JSON`, `Object To String`, `Object To File`, `Get Element`,
112
+ `Has Elements`, `List Keys`, `Length`, `Trim`, `Concatenate`, `Replace`,
113
+ `RegEx Replace`, `Add`.
114
+ - **Integration:** `Call API` (REST), `Execute Query` (SQL SELECT; V1/V2 variants),
115
+ `Execute Command` (SQL non-query; V1 variant), `Send Email` (SMTP — see
116
+ PROCESIO-SEND-EMAIL-NOTES).
117
+ - **Date/time & id:** `UTC Now`, `Today`, `Format DateTime`, `Update DateTime`,
118
+ `Generate GUID`.
119
+ - **Files / Excel:** `Upload File`, `Get File Data`, `Move File v1`, `Create Folder`,
120
+ `Read Range`, `Get Last Used Row`, `Export To XLSX`.
121
+ - **Documents:** `Generate Document` (renders a document template — §5).
122
+
123
+ Icons (for renderers): `icon-stop, icon-start, call_split` (decisional), `icon-join,
124
+ icon-nodejs, icon-call_api, icon-mssql` (SQL), `icon-json, icon-workflow` (subprocess),
125
+ `loop` (For Each), `find_replace, send` (email), `code` (JS), `settings_applications`
126
+ (Map Data).
127
+
128
+ ---
129
+
130
+ ## 3. Credentials — `Credentials[]`
131
+
132
+ ```
133
+ Credential = { name, tname, type, gid, gtid, gtpid, status,
134
+ properties: [ { id, value }, ... ] }
135
+ ```
136
+ - **`gid`** = the credential **instance** id. **`gtid`/`gtpid`** = the credential
137
+ **template** id (the *type*: SQL Server, SMTP, REST API key, Azure GPT, …).
138
+ - `properties[]` = the filled fields, each `{id, value}` where `id` is the template's
139
+ property-definition id. Different credential types have different property sets. A
140
+ property value can itself be a GUID pointing at **another credential** (chained
141
+ credentials — e.g. an LLM credential referencing a key credential).
142
+ - `status` (bool) = tested/enabled state. Secrets are excluded when exported with
143
+ `exportSensitiveData:false`.
144
+
145
+ **How an action uses a credential.** The action's `CustomData.configuration[].settings[]`
146
+ contains a setting of `type:"credentials"` with `credentialsTemplateId` = the template
147
+ (matches the credential's `gtid`) and `value` = the credential **instance `gid`**. Real
148
+ shape — an `Execute Query` action: `{label:"Select Database Server", type:"credentials",
149
+ credentialsTemplateId:"<template guid>", value:"<credential instance gid>"}`. So: pick a
150
+ credential by template, bind the instance gid into the action setting. Never put the
151
+ secret in the process — only the gid reference (best-practices §2 holds).
152
+
153
+ ---
154
+
155
+ ## 4. Webhooks — `Flows[].Webhooks[]` (embedded, not a top-level section)
156
+
157
+ ```
158
+ Webhook = { Id, WebhookId, IsObsoleted,
159
+ WebhookVariables: [ { VariableId, VariableType } ],
160
+ FilterRules: { Value:[], Parameters:[] } }
161
+ ```
162
+ A webhook is **attached to a flow** and binds the flow's **process variables** to
163
+ incoming request parts via `WebhookVariables` (`VariableType` enumerates where the value
164
+ comes from — body/header/query). `FilterRules` gates which requests trigger the run
165
+ (empty = accept all). The webhook URL/secret is referenced by `WebhookId` (the standalone
166
+ webhook resource), not stored inline. Webhook-launched runs are async (launch returns
167
+ empty 200 — playbook §D). The whole body binds to a model-typed variable, not per-field
168
+ (PHASE4-E2E 1).
169
+
170
+ ---
171
+
172
+ ## 5. Document templates — `DocumentTemplates[]`
173
+
174
+ ```
175
+ DocumentTemplate = { id, name, description,
176
+ body: "<html… escaped, may embed base64 images>",
177
+ placeholderDelimiterStart: "<%", placeholderDelimiterStop: "%>",
178
+ documentPageSize: "A4", documentPageOrientation: 0,
179
+ variables: [ { id, dataType, type, name, defaultValue, isList, isInput, isOutput } ] }
180
+ ```
181
+ A document is an **HTML body with `<% %>` placeholders** bound to **typed `variables`**.
182
+ `isList:true` variables drive **repeating tables**; `dataType` is the data-model id, so a
183
+ placeholder can be a typed scalar or a typed list. Page size + orientation are
184
+ configurable. This is what the `Generate Document` action renders: it has a file output
185
+ (**Document Output**) AND an **HTML string** output (direction OUTPUT) — see
186
+ PROCESIO-API-NOTES "Generate Document HTML string". Bind doc variables by **name** in
187
+ docMap (builder resolves name→id), and build the backing data model via the
188
+ attribute-endpoint create flow so it COMPILES (else placeholders render "Unknown").
189
+
190
+ ---
191
+
192
+ ## 6. Forms — `Forms[]` (the forms-designer model)
193
+
194
+ ```
195
+ Form (outer) = { Id, Name, IsPrivate, Type, Status, State, Assignees:[], Data,
196
+ CustomUrl, WorkspaceName, workspaceId, created*/updated* }
197
+ ```
198
+ - **`Type`**: `1` = standard form; `0` = the other variant. **`Status`**: `1` =
199
+ active/published, `0` = draft (per API notes). **`Assignees`** non-empty = a **task /
200
+ approval form** routed to specific users.
201
+ - A single `.procesio` export can be just one form or a form alongside its flows.
202
+
203
+ ### `Form.Data` (stringified JSON in the export; object once parsed)
204
+ ```
205
+ Data = { browserTitle, elements:[…], valueMap:{}, code:"<encrypted>", images:{},
206
+ hideBranding, messages:[…], dataModel:{…}, variables:[…], events:[…],
207
+ theme:[…], dataModelAttributeFormatVersion, notification, chainConfig:{…} }
208
+ ```
209
+
210
+ **Controls — `Data.elements[]`** (flat list, nested via `parentId`). Each element =
211
+ `{ id, category, type, configs:[…], parentId, section, hidden }`. The control catalog
212
+ seen in production, by family:
213
+
214
+ - Layout: `section`, `columns`/`column`, `tabs`/`tab`, `divider`, `side-panel`.
215
+ - Display: `paragraph`, `heading`, `image`, `icon`, `file-viewer`, `chart`.
216
+ - Input: `input`, `textarea`, `number-input`, `datetime-input`, `select`, `dropdown`,
217
+ `checkbox`, `radiobox`, `file-upload`.
218
+ - Data: `table` with `dynamic-table-row` / `static-table-row`, `list`.
219
+ - Action: `button`, **`approval`**.
220
+
221
+ Containment uses `parentId` exactly like the canvas (section/column/tab hold children).
222
+
223
+ **Per-control config — `configs[]`.** Each entry =
224
+ `{ id, key, label, type, value, category, subCategory, exposed, events? }`. The control's
225
+ behavior is spread across these settings. Common keys: `name, id, visible, label,
226
+ tooltip, style, info-text, readonly, value, required, defaultValue, placeholder, regex,
227
+ disabled, sourceType, sourceValue, submit, disabledIfFormIsInvalid, multiple, searchable,
228
+ clearable, rows`, table keys (`hasHeader, hasIndexColumn, tableColumnsSourceType/Value,
229
+ canAdd, canRemove, hasPagination, countPerPage, totalItemsCount`), and the **event keys**
230
+ below.
231
+
232
+ **Data-source binding — `sourceType` / `sourceValue`** (for select/table/list options):
233
+ `JSON` (a static JSON array literal in `sourceValue`), `static-list`, `URL` (fetch options
234
+ from a URL), or `None`. Process-driven option data arrives instead by a RUN_PROCESS event
235
+ writing into the field (below).
236
+
237
+ **Validation / conditional**: `required`, `regex` (e.g. email pattern), `readonly`,
238
+ `disabledIfFormIsInvalid` (enable only when all required are valid — the AAT_ "email me"
239
+ pattern in best-practices), and **`visibility`** rules like
240
+ `{type:"task-assignees", users:null}` (show a field only to the task's assignees — part
241
+ of the approval mechanism).
242
+
243
+ ### Events — three layers
244
+ 1. **Form lifecycle — `Data.events[]`**: `FORM_LOAD` and `FORM_SUBMIT`. Fire on render /
245
+ on submit.
246
+ 2. **Per-control runtime events** live in config keys: `onClickEvents`, `onInputEvents`,
247
+ `onFocusEvents`, `onBlurEvents`, `onTableRowAddedEvents`, `onTableRowDeletedEvents`,
248
+ `onPaginationChangedEvents`. Each holds a list of event objects:
249
+ ```
250
+ { id, type: "CLICK"|"INPUT"|"TABLE_PAGINATED"|"TABLE_ROW_ADDED"|…, // trigger
251
+ action: "RUN_PROCESS" | "RUN_JAVASCRIPT", // what runs
252
+ config: {…} }
253
+ ```
254
+ 3. **Designer-time events** (e.g. `"EMIT_ELEMENTS_UPDATE"` on a `name` config) — editor
255
+ plumbing, not runtime behavior.
256
+
257
+ **`action:"RUN_PROCESS"` config** (how a form calls a process — extends the memory note):
258
+ ```
259
+ config = { syncRun: bool, processId: "<flow guid>",
260
+ inputMap: [ { id, left:"<PROCESS var GUID>", right:"<form value-path>" } ],
261
+ outputMap: [ { id, left:"<form field path>", right:"<process out var path>" } ] }
262
+ ```
263
+ - **`syncRun`** chooses synchronous (wait, fill outputs) vs async fire-and-forget.
264
+ - The **process variable GUID is on the LEFT** in BOTH maps; the form value-path on the
265
+ RIGHT (4-part `elementId.dataModelId.attrId.valueConfigId`). A name or form-path on
266
+ the left = the designer can't render it and the launch 400s. The builder resolves this
267
+ (prepare_ctx); guarded by `test_form_parity.py`.
268
+ - Triggers that can drive RUN_PROCESS: `CLICK` (button), `INPUT` (live field change),
269
+ `TABLE_PAGINATED` (server-side paging), `TABLE_ROW_ADDED`, etc.
270
+
271
+ **`action:"RUN_JAVASCRIPT"` config**: `{ code:"<js>" }`. Runs in the form's
272
+ `iframe.trigger-sandbox`; reach the page via `window.parent.document` (e.g. toggling a
273
+ custom loader's `display`). The custom CSS/JS authored in the designer lives in
274
+ **`Data.code`, AES-encrypted** (the value starts with `U2FsdGVkX1…` = OpenSSL/CryptoJS
275
+ `Salted__` — key in Credential Manager; see `dto/form/FORM-STYLING-NOTES.md`). Inline
276
+ event `code` (above) is stored in clear inside the event config.
277
+
278
+ ### Styling — `Data.theme[]`
279
+ A list of **CSS-variable sections**: each `{label, properties:[{label, value, cssVariable,
280
+ type}]}`. E.g. section "Colors" → `--c-primary`, `--c-primary-variant`, `--c-on-primary`,
281
+ `--c-error`, `--c-on-error`, … `type:"css-color-preview"`. The sections cover the theme
282
+ (Colors, Typography, etc.). So form styling = setting CSS-variable values per section;
283
+ custom overrides go in `Data.code`. Never force a font on `#app *` (breaks Material-Icons
284
+ glyphs).
285
+
286
+ ### Approval — the `approval` control + task routing
287
+ The `approval` control's `configs` carry the approval model:
288
+ - **`approver`** (`type:"user-select"`, value = a user GUID) — the designated approver.
289
+ - **`approverReplacement`** (`type:"approver-replacement"`, value = list of user GUIDs)
290
+ — fallback approvers.
291
+ - Captured outcome: **`response`**, **`comment`**, **`responseDate`**, **`responseUser`**,
292
+ **`approved`** (hidden, filled at runtime).
293
+ - Multi-step: a table-level **`approvalSteps`** key sequences several approvals.
294
+ - Task routing at form level: outer `Assignees` + `Type`, plus per-field
295
+ `assignee`/`assigneeReplacement` configs and the `visibility:{type:"task-assignees"}`
296
+ rule. So an approval flow = assign the form/step to users, show the right fields to
297
+ assignees, capture approve/reject + comment, and branch the downstream process on the
298
+ result.
299
+
300
+ ### Multi-form "apps" — `Data.chainConfig`
301
+ `{columns:[…]}` describes a **dashboard of linked forms/tiles** arranged in columns — the
302
+ "App" concept (e.g. an Overview / Production-Dashboard / Marketplace landing page). This
303
+ is how several forms are composed into one navigable application.
304
+
305
+ ---
306
+
307
+ ## What is genuinely new here (vs prior notes)
308
+
309
+ Prior notes already cover: export/import mechanics, casing, validation oracles,
310
+ scripting-action output wrapping, Send Email, scheduler, Generate Document HTML string,
311
+ RUN_PROCESS map orientation, form CSS/JS encryption. **New in this file:** the canvas
312
+ geometry/edge/containment model (positions, action-dictated node shape, `area` =
313
+ For-Each container, `ParentId`, `Type` 0/1 edges, unlimited decisional fan-out), the
314
+ action catalog by family, the credential template-vs-instance model + action binding, the
315
+ webhook embed shape, the document-template DTO, the forms control catalog + config-key
316
+ inventory, the three-layer event model with the RUN_PROCESS/RUN_JAVASCRIPT config shapes,
317
+ `sourceType` data binding, the theme CSS-variable structure, the approval-control model,
318
+ and `chainConfig` apps. None of this changes a builder; it documents what the builders
319
+ read.
320
+
321
+ ---
322
+ ## These capabilities are CURATED ACTIONS — do not reimplement
323
+ Everything described above is reachable through a runnable action (run `python scripts/run-tool.py procesio`
324
+ to list all ~330). In particular:
325
+ - Parse a flow/export into a node/edge graph → **`read-flow-graph`**; structural summary/smells → **`inspect-flow`**.
326
+ - Auto-layout a canvas (legacy or `LAYOUT_ENGINE=elk`) → **`layout-flow`**; verify positions → **`verify-layout`**;
327
+ cross-process map → **`layout-resource-map`**; re-lay-out a LIVE process in place → **`relayout-process`**.
328
+ - Duplicate a process → **`duplicate-process`**; validate one → **`process-validate`**; delete → **`process-delete`**.
329
+ Reach for the action first; only drop to a raw `<method>-<path>` wrapper or `request` if no curated action fits.
330
+
331
+ ---
332
+
333
+ ## Editing a flow via API: runtime is `parameters[].value`, NOT the setting (CRITICAL)
334
+
335
+ `PUT /api/Projects` persists what you send but does NOT recompile
336
+ `action.parameters[]` from `action.customData.configuration[].settings[]`. The
337
+ **runtime executes `parameters[].value`**; the setting is only the designer/UI copy.
338
+ Edit one without the other and the canvas shows new logic while the flow runs the old
339
+ one (validation passes regardless). Linkage: `parameters[i].tabPropertyId == settings[j].id`.
340
+
341
+ - Code nodes (`code-editor`): setting.value == param.value (same raw-JS form) → copy
342
+ setting→param for the matching tabPropertyId.
343
+ - SQL nodes (`Execute Query`/`Command`): SQL is a nested `side-pannel` → `Query`
344
+ sub-setting. **Setting uses GUID var refs `N'<guid>'`; param uses positional `<%N%>`.**
345
+ Different syntaxes — edit the param's positional SQL directly; never copy setting→param.
346
+ - datatype/credentials/variable-binding params legitimately hold `<%N%>` while the setting
347
+ holds a 36-char GUID — never sync those.
348
+
349
+ Helper `work/networky/migrations/flowpatch.py`: `setscript` now updates the linked param;
350
+ `syncparams` bulk-syncs code-editor params. (Discovered 2026-07 during the Networky
351
+ overhaul: a swapped router prompt kept emitting the old canned refusal because the old
352
+ code was still in `parameters[].value`.)
353
+
354
+ ---
355
+
356
+ ## Hand-building a multi-node flow: node-shape recipes (from the Send Contact Card build, 2026-07)
357
+
358
+ Building a new subprocess by **duplicate + surgically transform the DTO in Python** is the
359
+ safest path (every id/port/param stays internally consistent). Duplicating keeps the source's
360
+ variable + action ids — fine, they're only referenced within the one flow. Recipes proven live:
361
+
362
+ - **Graph = `ports[]`.** Each `action.ports[i]` = `{sourceId, destinationId, type:0, state:1,
363
+ data:{}}`. A `Start` node also has an *entry* port `{sourceId:"0000…", destinationId:StartId}` —
364
+ keep it. A **Decisional** emits one port per case + one default port
365
+ (`data:{"isDefault":"default"}`). A **`Stop` takes exactly ONE input port** — if two branches
366
+ both end, you need **two Stop nodes** (validate error 383 "Action has too many input ports").
367
+
368
+ - **Decisional operators:** only `EQUALS / IS_EMPTY / IS_NOT_EMPTY / IS_TRUE / IS_FALSE` are
369
+ reliable in this workspace. Numeric `LESS_THAN` / `GREATER_THAN_OR_EQUAL` **throw at validate**
370
+ ("Decisional action: … Exception was thrown"). For an HTTP-status gate use `Status EQUALS "200"`
371
+ (WhatsApp/most Cloud APIs return 200 on success) as the success case, default → error branch.
372
+ Dual form: `parameters[].value` = `[{id, actionid, condition:[{operator, logicOperator,
373
+ leftOperator:{value:"<%0%>", variable:[…]}, rightOperator:{value:"200", variable:[]}}]}]` and
374
+ a separate default param `{value: <targetActionId>}`; `customData…decisional-case` mirrors it
375
+ with GUID refs in `leftOperator.value` (+ a `decisional-default` setting). Template
376
+ `f5dcbb04-253d-4061-99a1-9b2822c2e6d2`, case tabProperty `11d4044a-…f30`, default `…f45`.
377
+
378
+ - **Execute Query node** (template `a9f851c2-e0ba-4fee-9a06-5445ba000001`): credential setting
379
+ `…bc000011` (DB credential, e.g. `45a1dd18` "Database"), a `side-pannel` `…bc000012` holding
380
+ `Query` (code-editor, `…bc000014`), `Timeout` (`…bc000015`), `Output` (type "any", isList true,
381
+ direction 3, `…bc000013`). SQL setting uses `N'<guid>'` var refs; **param uses `N'<%N%>'`** with
382
+ a `variable[]` map — different syntaxes, never copy setting→param for SQL.
383
+
384
+ - **Node (NodeJS)** setting ids are per-instance: Code `code-editor`, Timeout, Single Result
385
+ (`datatype`, dir 3), List Result (`datatype`, isList, dir 3), Error (`datatype`, dir 3). Output
386
+ goes to whichever var the Single Result value points at (its own type governs; the setting's
387
+ `dataTypeId` is decorative). Injection convention: an **object/list** var → bare `let x = <%0%>;`
388
+ (NOT in backticks — backticks corrupt JSON); a **string** var → `` `<%1%>` `` in backticks.
389
+
390
+ - **Call Subprocess** (template `c37e56fe-d924-4604-a86f-7c93f863fcdf`): `Select Subprocess`
391
+ (`bc93d0be-…`) = child flow id; inputs (`process-inputs`, `62cdd318-…`) = list of
392
+ `{subprocess:<child input var id>, process:<parent var id>}` — child var on the LEFT.
393
+ - **The target subprocess MUST be `active:true`** or the parent's `validate` returns
394
+ 504 "Called suprocess is not valid… Target flow is inactive". A working callable
395
+ subprocess is `active:true, status:0` (draft-status is fine; *inactive* is not).
396
+ `process-toggle-activation` (PATCH …/toggle-activation) only arms/disarms triggers —
397
+ it does **NOT** flip `active`. To publish for call, set `active:true` on the flow DTO
398
+ and PUT it (`flowpatch put`), then re-fetch to confirm. (Networky Send Contact Card
399
+ wire-up, 2026-07.)
400
+
401
+ - **Call API whole-body injection:** the RAW body value can be a **single `<%0%>`** bound to a
402
+ **string/Text-typed** var whose content is the full JSON (proven by `Chat Flow/Call LLM Fast`:
403
+ RAW body `<%0%>` bound to a Text `Payload`). So a Node can `JSON.stringify(body)` → a Text var →
404
+ Call API posts it verbatim. If a live run ever shows double-encoding, switch the var to Object
405
+ and `return body` (object) from the Node instead.
406
+
407
+ - **Variable PUT schema:** `{id, contextId:null, dataType, type, name, defaultValue:null, isList,
408
+ isError:false, isRequired}`. `type` 10=input, 20=output/local, 40=ProcessInfo. Common dataTypes:
409
+ `…121214` string, `…121211` number, `…121210` bool, `…121220` text/JSON-string, `…121221`
410
+ object, `…121222` uniqueidentifier.
411
+
412
+ Networky flow ids: `Send Contact Card` = `cd3d9bad-45e9-4f05-99f3-fcb86936c0e8` (native
413
+ WhatsApp `type:"contacts"` card; inputs SessionId+UserPhone). Report:
414
+ `work/networky/migrations/reports/vcard-subprocess.md`.
415
+
416
+ ---
417
+
418
+ ## Node/Call-API/generic-passthrough gotchas (from the Networky DB-wire build, 2026-07)
419
+
420
+ Five non-obvious PROCESIO behaviors that each silently broke a live flow. All cross-cutting.
421
+
422
+ - **`<%N%>` substitutes inside COMMENTS too.** PROCESIO does a blind textual replace of every
423
+ `<%N%>` in a Node's code, including `// comment` lines. If the injected value is a JSON
424
+ list/object, the `:` / quotes inside break the surrounding line and the Node dies with
425
+ "Unexpected token ':'" (returns null). **Never put `<%N%>` in a Node comment.** (The Node body is
426
+ syntactically valid on its own `node --check`, so this only reproduces live — check by
427
+ substituting a REAL value, not a stub.)
428
+
429
+ - **Execute Query always returns a LIST of row-objects**, and **bare-injecting a list whose column
430
+ value is a JSON STRING corrupts the JS** (PROCESIO unescapes the inner `\"` -> invalid literal).
431
+ Fix: return **scalar columns** (`JSON_VALUE(cd,'$.a.b') AS x`) so the injected list has no nested
432
+ quotes, OR strip the JSON into a scalar string bound in backticks. A list of simple scalar
433
+ columns bare-injects cleanly.
434
+
435
+ - **The "generic" datatype `8daff4fc-206c-4151-9bad-1cfdec59bfbd` is NOT free-form — it is a fixed
436
+ 10-column model** (the LLMFastData schema). Any extra column a proc SELECTs (e.g. an appended
437
+ `currentSessionId`, or your own scalar columns) is **silently DROPPED** when coerced into a var of
438
+ that type. For a truly column-preserving passthrough use the **object datatype `…121221`** (as a
439
+ list). This dropping can happen at MULTIPLE hops — fix the var type at every hop (subprocess output
440
+ var AND the parent var it maps into).
441
+
442
+ - **Whole-body RAW `<%0%>` for a dynamic JSON body works only with an OBJECT var + Node `return
443
+ body` (object)** and the Node's **Single Result dataTypeId set to object `…121221`**. A Text var
444
+ holding `JSON.stringify(body)` sends a double-encoded string (WhatsApp: "messaging_product is
445
+ required"); an inline template with an unquoted `"k":<%1%>` fails PROCESIO's pre-substitution
446
+ RAW-JSON validator ("invalid Json format for RAW type value"). Proven working precedent:
447
+ `Chat Flow/Call LLM Fast` (`<%0%>` <- object-return Node).
448
+
449
+ - **A proc that ends with `SELECT …` (e.g. `SELECT @@ROWCOUNT`) adds a result set.** Calling it via
450
+ `EXEC` inside an Execute Query that already returns one set -> "Expected maximum one table but
451
+ received 2 tables!". For a fire-and-forget pointer/side-effect write, inline the `UPDATE` in the
452
+ SQL (no `SELECT`) instead of `EXEC`-ing the proc.
453
+
454
+ - **Required subprocess inputs abort the caller.** A `Call Subprocess` whose target has an
455
+ `isRequired=true` input var will abort the WHOLE parent with "Missing required input for
456
+ variables: <type>" if that input resolves to null/missing at runtime — silently killing everything
457
+ downstream. (This is how the DB-wave `Escape Quotes - search_id` node broke all new-contact
458
+ persistence: a fresh extraction has no search_id.) Guard the value, or don't route an
459
+ optional/empty value through a required-input subprocess.
460
+
461
+ - **A proc-parameter value cannot be a T-SQL expression.** `EXEC sp_X @p = CASE WHEN … END` is a
462
+ syntax error ("Incorrect syntax near the keyword 'CASE'"). Compute into a local first:
463
+ `DECLARE @v bit = CASE …; EXEC sp_X @p = @v`.
464
+
465
+ Networky flow ids added this wave: `Resolve And Patch` = `cd337769-d9e0-4f13-9787-b4a219aeb0ec`
466
+ (sp_FindContact + patch ResponseLLM target + pending write); `Get Pending` =
467
+ `98e3fd5f-a6b9-4da0-87fc-e2d88180818c`. Report:
468
+ `work/networky/migrations/reports/dbwire-e2e.md`.
469
+
470
+ ---
471
+
472
+ ## AI Decisional action + Map Data + subprocess-extraction recipes (Networky AI-router refactor, 2026-07)
473
+
474
+ Building the single-router refactor of `Start` (87→43 nodes) surfaced concrete recipes for three
475
+ constructs. All verified via `flowpatch validate` (POST /api/Projects/validate → 200) on a live clone.
476
+
477
+ ### AI Decisional node (template `772aac51-73f5-471d-bf9f-f5099cb30001`, diamond, in1/out-1)
478
+ - **Credential is NOT a validation blocker.** A probe with the platform-export credential gid
479
+ `adc0e765-…` (template `27272727-0001-0000-0000-aaaaaaaaaaaa`, a system/global instance) validated
480
+ fine even though no AI credential exists in the target workspace. Validation checks node/port SHAPE,
481
+ not credential ownership. For LIVE RUNS you still need a workspace-owned, tested AI credential of that
482
+ template (create + `credential-test`, then rebind param `…30111` + the customData `credentials`
483
+ setting). The `/api/Credentials/types` + `/list/{typeId}` endpoints 403 with the userpass tool token.
484
+ - **Params** (tabPropertyId suffixes off `772aac51-73f5-471d-bf9f-f5099cb3____`): `0111` credential,
485
+ `0112` Model (text), `0113` Endpoint (select 1=Chat Completions/2=Responses), `0114` User Prompt
486
+ (code-editor, `<%N%>`), `0115` Timeout, `0123` LLM Response (datatype dir3 OUTPUT, diagnostic),
487
+ `0116`-`0122` temp/topP/maxtok/presence/freq/seed/store, `0124` Cases (`ai-decisional-case` list),
488
+ `0125` Default (`decisional-default`).
489
+ - **Cases** param value = `[{id, actionid:<targetActionId>, condition:"<free text>"}]`; customData mirror
490
+ = `[{id,name,target,condition,internalId}]`. **First true case (declared order) wins.**
491
+ - **Ports:** one Type-0 port per case target + one Type-0 default port (`data:{"isDefault":"default"}`)
492
+ + one **Type-1 error port** (`data:{"isDefault":"error"}`). Node carries `variableErrorId` = an
493
+ **IsError var whose datatype MUST be `10c6ac59-3929-49e6-99dc-121212121220`** (validate error 395
494
+ "invalid data type for error variable" if you use plain string `…214`).
495
+ - **User Prompt type gotcha:** the User Prompt (`0114`) is a STRING slot — injecting an OBJECT-typed var
496
+ via `<%0%>` fails validate 142 "data type mismatch". Also a `text`-typed var (`…220`) mismatches; it
497
+ wants a **string** var (`…214`). To feed a structured object to the prompt, add a `Node` before it that
498
+ `return JSON.stringify(<%0%>)` into a **string** Single-Result var, and inject THAT.
499
+
500
+ ### Map Data node (template `f183b48e-25b0-4a28-b6ec-310344feaa18`, `icon-variables`)
501
+ - ONE side-pannel setting `a5f92824-…` → child `map-process-data` setting `d7ffa148-…`.
502
+ - **customData** map value = `[{id, destination:<targetVarId[.attr]>, source:<srcVarId[.attr]>}]`
503
+ (destination = LEFT/assigned var, source = RIGHT/value).
504
+ - **Runtime param** `d7ffa148-…` value = `[{id, source:{value:"<%N%>", variable:[{variableId,attribute}]},
505
+ destination:{variableId,attribute}}]`. destination assigned FROM source. Used here as `Map Route A/B`
506
+ to normalize two per-branch router DTOs into one shared `Route` var + a `routeSessionId` guid.
507
+
508
+ ### Extract a duplicated tail into a subprocess (the higher-value merge)
509
+ - **POST /api/Projects 403s** with the tool token — you cannot create a process from scratch. Instead:
510
+ **`duplicate-process` any small flow → get a fresh flow id → overwrite its actions/variables with your
511
+ DTO (keep the duplicate's id, propagate it to every action.flowId + port.flowId) → validate → PUT.**
512
+ - A **Call Subprocess input/output map** entry (in `parameters[].value`) is
513
+ `{id, source:{value:"<%N%>", variable:[{variableId, attribute}]}, destination:{id, variableId, attribute}}`.
514
+ For an INPUT map: source=PARENT var, destination=CHILD input var. For an OUTPUT map: source=CHILD output
515
+ var, destination=PARENT var. When re-homing onto a new subprocess you must remap BOTH
516
+ `source.variable[].variableId` AND `destination.variableId` when they are parent vars (missing the
517
+ destination side leaves stale refs). customData mirror uses `{subprocess:<child id>, process:<parent
518
+ id[.attr]>}`.
519
+ - **A called subprocess needs `active:true` AND `isValid:true`** or the parent validate returns 504
520
+ "Called suprocess is not valid". After building, `flowpatch validate` the subprocess, set `isValid:true`,
521
+ and PUT it BEFORE validating the parent that calls it.
522
+
523
+ ### Networky ids added this wave (AI-router refactor, on a CLONE — not live)
524
+ - Refactored clone `Start [AI-Decisional REFACTOR - not live]` = `5dabbaaa-5de0-44cc-ab89-9a307fb90c5e`
525
+ (43 nodes, active:false, isValid). Report: `work/networky/migrations/reports/ai-decisional-refactor.md`.
526
+ - New subprocess `Chat Flow/Handle Complex Turn` = `68a0c9d1-f55b-44bc-ba36-748397093f25` (single copy of
527
+ the complex/enrichment/persist tail; input `session_id` guid required + conversation/User/userMsg/route).
528
+ - The live `fastLLM` router DTO (`be898da6-5e1a-42a0-a6c4-6ad653e8f7e9`) has exactly 7 fields:
529
+ response_text(17522760) send_message(723db89f) need_LLM_complex(0b778bf1) language(77c01d02)
530
+ new_session(4b0fc63b) target_session_id(926d5723) intent(7f56f2b4). `need_contact_search` is NOT a typed
531
+ field — it lives only in the RAW router JSON text var `fastLLMRaw`, read by the `Check Search` Node.
532
+ - The WTB webhook `db92281a-17b5-451f-b9f4-2a7f6a70f1fc` stays bound to LIVE `Start`
533
+ `c6754fc4-…`; a duplicated process carries the same webhookId reference but is inert while `active:false`.
534
+
535
+
536
+ ## Creating nodes/actions in a flow via API (WORKS - userpass; verified 2026-07-05)
537
+
538
+ Adding a NEW node to a flow via `PUT /api/Projects` works with the default userpass `account` profile.
539
+ No designer UI needed. The one trap: do NOT copy an existing node to make a new one - a copied node
540
+ carries the source flow's stale variable bindings in `customData.configuration`, and the validator then
541
+ throws a diagnostic-free `HTTP 500 "Object reference not set to an instance of an object"` (changed in v3.0.0, 2026-10: an unhandled 500 no longer carries the exception text; the body is now the generic `[{"statusCode":100,"value":"An unexpected error occurred.","target":null}]`, so treat that body on a flow PUT as the same symptom). Instead:
542
+
543
+ 1. Fetch the CLEAN action template: `GET /api/Actions?getFullAction=true`, find by `actionId`/`name`.
544
+ `copy.deepcopy(template.configuration)` and fill only your setting values. A clean config makes the
545
+ validator return REAL errors (141/142) instead of the 500.
546
+ 2. A JS "Node" (templateId e0fc20c3): settings are Code (1e6a5523) = JS + input `<%N%>` bound in the
547
+ Code param's `variable` list; **Single Result** (51b9fcdc) = the JS `return` output (NOT Error
548
+ 9e0110c9); List Result (60237e88); Error (9e0110c9) = error port; Timeout (d3e52aab).
549
+ 3. New variables use key **`dataType`** (a type id) + `type` (10 input / 20 process / 30 output / 40 sys).
550
+ Missing type -> `statusCode 141 "type cannot be empty"`. Param/var type must MATCH the setting's
551
+ expected `dataTypeId` -> else `statusCode 142 data type mismatch`. Primitive ids: `...1211` number,
552
+ `...1214` string, `...1221` any/object; file `10c6ac59-...-121212121219` = FileDataModel
553
+ `{name,mimeType,path,size,id,hash}` (blob reference, NOT inline content).
554
+ 4. Files from text: a Node cannot return a FileModel (no inline content). Use **`Base64 To File`**
555
+ (actionId 6fad5599; settings Base64 String, File Name -> File output) to turn a base64 string into a
556
+ stored FileModel; then Call API `/media` upload (FORM_DATA `file`=FILE type, `type`=mime).
557
+ 5. Validate via `POST /api/Projects/validate` (empty body = valid) before PUT.
558
+
559
+ Best path: `tools/procesio/dto/process/builder.py` already builds correct DTOs data-driven from the live
560
+ catalog (`_action_node` = clean template config + values; `_edit` rebuilds a whole flow and PUTs). Prefer
561
+ it for full-flow builds; replicate `_action_node`'s recipe for a surgical single-node insert into a
562
+ fetched (lowercase-keyed) flow. Worked example: added Build VCF (Node) + Base64 To File to Chat Flow/Send
563
+ Export to produce a `.vcf` bulk export (scratchpad build_vcf_flow3.py pattern).
564
+
565
+
566
+ ## Field learnings 2026-07-05 (designer review + real-corpus E2E) — AUTHORITATIVE
567
+
568
+ 1. **Decisional icon**: when creating a Decisional via API set `customData.icon = "call_split"` (the
569
+ designer standard). `icon-decisional` or anything else renders NO icon in the run view. Same rule
570
+ class: give decisional CASES meaningful `name`s — the designer shows them as chips on the canvas.
571
+ 2. **Scripting (Node) actions FAIL SILENTLY**: a JS code error does NOT raise / does NOT take the
572
+ type-1 error port. The action reports SUCCESS and writes the error text to its **Error output**
573
+ setting (9e0110c9). ALWAYS: (a) bind Error output to a string var on every Node; (b) on hot paths,
574
+ follow the Node with a Decisional `errVar IS_NOT_EMPTY -> error join`; (c) compose the user-facing
575
+ error message from the error vars in one downstream Node (values from the FAILED action are unset —
576
+ never map them into the notice directly). Proven: 6 silently-dying turns became 6 successes.
577
+ 3. **Huge values (base64 media) are NOT an engine limit** (corrected 2026-09-21). A controlled probe through
578
+ `/run` carried a 4 MB string intact through a Node `<%N%>` injection, a Decisional `IS_NOT_EMPTY`, the native
579
+ **Length** action (template 057d754a: Input String, Result), Map Data, subprocess maps and SQL parameters, on a
580
+ plain variable and on a data-model attribute alike. The earlier note that 100 KB+ values "materialize as EMPTY"
581
+ in a Node or Decisional was an artefact of the middleware that fed the flow (it capped its own payload), not of
582
+ PROCESIO. Length is still the cheapest way to test a big value's presence; it is not a workaround.
583
+ 4. **For Each via API** (template dbef0804-66a9-4f8f-872c-ece1b89b8fdb, shape `area`): params =
584
+ For Each Item (57b5b1eb…, the per-iteration var) + In List (2d5f230f…) + timeout + 2 ignore
585
+ literals. The FE node has TWO type-0 ports: first -> loop-body entry child, second -> the after-loop
586
+ node. Children carry `parentId` = FE id; the LAST child ports BACK to the FE (loop-back edge).
587
+ 5. **Workspace header is mandatory on run/launch**: `ProcesioClient(workspace_id=…)`. Without it,
588
+ `/api/Webhooks/launch` and `run-process` bind LARGE attribute values as EMPTY (looks like an engine
589
+ bug, is actually your missing header), and admin flows 400/502.
590
+ 6. **WhatsApp bulk contact export**: document sends get the MIME's extension APPENDED by WhatsApp
591
+ (filename `Contacts.vcf` + text/plain ⇒ phone receives `Contacts.vcf.txt`, unimportable). No allowed
592
+ /media MIME maps to .vcf. Correct approach: native `type:"contacts"` messages (array, chunk ~15 per
593
+ message, For Each over chunks) — renders as a tappable contact stack ("X and N other contacts").
594
+
595
+
596
+ ## Layout engine upgrade 2026-07-05 (brief: "fix the tool so it layouts right")
597
+
598
+ Four engine changes in `tools/procesio/layout/engine.py` (all covered by the existing test suite):
599
+ 1. **Error-region detection stops AT error targets**: main-flow reach may point INTO an error-port
600
+ target (e.g. a script-gate's normal case edge into the shared error Join) but never THROUGH it.
601
+ Before, one normal edge into the sink pulled the whole notify subtree into the main graph.
602
+ 2. **ALAP layer pull with branch-head anchoring**: single-successor chains get pulled RIGHT adjacent
603
+ to their join (kills 8-layer join edges), but a branch HEAD (predecessor fans out) keeps its ASAP
604
+ layer so it stays next to its decisional.
605
+ 3. **Error components anchor independently, laid VERTICALLY**: each weakly-connected component of the
606
+ error region becomes a vertical rail below the canvas, anchored at the median x of ITS OWN feeders.
607
+ Vertical rails cannot cross the horizontal main flow.
608
+ 4. **MINIMIZE_CROSSINGS defaults ON** (strictly better in every measurement).
609
+
610
+ FLOW-side pattern that finishes the job: with feeders spread across a wide canvas, a SINGLE shared
611
+ error Join forces ~feeder-span-length edges (geometry, not layout). Split into ONE ERROR RAIL PER
612
+ CANVAS REGION (Networky v2: rails A/left/right - Join+Compose+notice+Stop each, cloned). Result on
613
+ the 78-node v2: crossings 10 -> 0 (proper-intersection; report's endpoint-touch counter: 2),
614
+ max edge 3729 -> ~1500, all four Networky flows re-laid clean.
615
+ LEGACY engine remains the default (ELK is opt-in backup via LAYOUT_ENGINE=elk).
616
+
617
+
618
+ ### Layout round 2 (same day, after the designer re-review)
619
+ 5. **At most ONE folded dead-end chain per branch node** (`_deadend_chains` cap): several chains
620
+ folding onto the same column stacked into overlapping piles (the AI Router 5-way fan). The
621
+ shortest chain folds; the rest lay out normally. Result incl. label-aware collision check:
622
+ v2 = 0 crossings, 0 label collisions; ALL FOUR flows 0/0 by layout-report.
623
+ 6. **Designer code sync for authored Nodes**: when creating a Node via API, the customData Code
624
+ setting must hold the GUID-reference form (variableId[.attributeId]), NOT the runtime `<%N%>`
625
+ form — the designer renders `<%N%>` literally and the variable picker breaks. Convert with
626
+ the param's variable map (flowpatch `_config_value_from_param` pattern). Synced on 9 authored
627
+ Nodes across v2/HCT/SendExport/PEC.
628
+
629
+
630
+ ### TWO validation layers — runtime vs designer (2026-07-06, corrected)
631
+ **A 2026-07-05 note here WRONGLY called the designer's Process Errors "stale phantoms." They were
632
+ REAL and blocked designer SAVE. Corrected below — trust the designer's Process Errors.**
633
+
634
+ - There are TWO validation layers and they disagree:
635
+ - **Runtime layer** — `POST /api/Projects/validate` (the only server validate endpoint; by-id
636
+ variants 403). It checks `parameters[]` (the runtime maps). Returns an EMPTY body when valid.
637
+ It does NOT look at the designer layer, so a flow can pass /validate yet be UNSAVABLE.
638
+ - **Designer layer** — a CLIENT-SIDE check in the Angular designer that populates the "Process
639
+ Errors" panel and BLOCKS SAVE. It reads `customData` (the designer maps + code chips). There is
640
+ NO server endpoint for it — you must replicate it.
641
+ - **Replicate the designer layer with `procesio flow-lint --id <flow> --workspace-id <ws>`** (rewritten
642
+ 2026-07-06 to check the DESIGNER layer, not the runtime one): Node code binding an error-scope
643
+ (`isError=true`) or undeclared variable; and every REQUIRED subprocess input having a `customData`
644
+ process-inputs row (`subprocess`==input id) whose source resolves (GUID source must be a declared
645
+ var; a non-GUID source is a literal and is fine). Call inputs pid 62cdd318…, Trigger 9823b4d9-7a0c….
646
+ - **Root cause we hit twice:** cloning a Call/Trigger-Subprocess node copies its `customData`
647
+ process-inputs verbatim, so the clone keeps the SOURCE node's subprocess-input ids. The real target
648
+ requires DIFFERENT ids → those read as "mapping missing," and the panel shows the stale ids as raw
649
+ GUIDs (they don't resolve in the new target). Fix: regenerate `customData` process-inputs from the
650
+ (correct) runtime map — row `{id, subprocess: <dest varId>, process: <srcVarId>[.<attrId>]}`;
651
+ literals go in `process` verbatim.
652
+ - **Runtime map ≠ designer map.** A node's runtime `parameters` map can be correct while its
653
+ `customData` designer map is stale (Networky v2: Resolve And Patch). The designer validates the
654
+ DESIGNER map. Always sync both when authoring/cloning subprocess nodes and Node code.
655
+ - Error-scope variables (`isError=true`, usually FileDataModel) are an action's error-PORT output;
656
+ they live in the designer's "Error" tab, never appear in the Process tab, and are never
657
+ free-initialized. NEVER bind one as a Node code INPUT — the designer flags it (tooltip shows type
658
+ "Error"). Networky v2's Compose Error wrongly swept 9 of them in via a `name.startswith("err_")`
659
+ filter; rebuilt to bind only the real String gate-error vars.
660
+ - Overhaul-era subprocesses do use VANITY input ids (11111111-0000-4000-…, a1b2c3d4-0000-4000-…) that
661
+ are REAL — but that does not make a mapping to them automatically correct; check that the id belongs
662
+ to THIS node's target, not a sibling's.
663
+ - `flowpatch.py validate` USED to print `validated:True` unconditionally without reading the response
664
+ (fixed 2026-07-06 to inspect the body + fail loudly). Any pre-fix "validate: True" was meaningless.
665
+
666
+ ### Designer-layer validation: the concrete rules (2026-07-06, hard-won)
667
+ `procesio flow-lint --id <flow>` now checks ALL of these (the designer's client-side "Process Errors"
668
+ that block SAVE; POST /Projects/validate does NOT catch them). Root causes found by comparing a
669
+ FLAGGED node against a HEALTHY node of the same template against the CLEAN /api/Actions template — that
670
+ comparison is the reliable method; reverse-engineering from a single node's data is not.
671
+
672
+ 1. **Stale subprocess side-pannel id (THE big one).** A Call/Trigger Subprocess node's
673
+ "Subprocess Configuration" side-pannel setting has an `id` that MUST equal the CURRENT template's
674
+ side-pannel id: Call = `5456caf0-be8e-4a04-86cb-dbae203af978`, Trigger =
675
+ `8540605e-2f4d-1746-9059-8bb7b944e0d3`. PROCESIO bumped this id across template versions; nodes
676
+ built/cloned against an older template keep the OLD id (seen: `1da555da…`, `15af8b81…`). The
677
+ designer looks the mapping up BY this id — wrong id ⇒ it finds nothing ⇒ reports EVERY required
678
+ input as "Mapping of required subprocess variable (X) is missing." The mapping CONTENT is fine;
679
+ only the wrapper id is stale. Fix: set the side-pannel `id` to the current template's; leave the
680
+ nested process-inputs/outputs untouched. This is safe and mechanical.
681
+ 2. **Required subprocess input unmapped / dead source.** Every target input (type 10, isRequired)
682
+ needs a process-inputs row `{id, subprocess:<targetInputId>, process:<srcVarId>[.<attrId>]|literal}`;
683
+ a GUID source must resolve to a declared var (a non-GUID is a literal, fine).
684
+ 3. **Subprocess output must map a type-30 var.** A subprocess only EXPOSES `type==30` variables as
685
+ outputs. Mapping a `type==20` internal var (e.g. an Execute Query `dbout`) as a process-output ⇒
686
+ designer "Check data mapping." To read a value out of a subprocess, that var must be type 30 there.
687
+ 4. **Execute Query requires a non-null Output.** The Output subsection (label "Output", pid
688
+ `8aa02e91-1a1e-a44a-b09b-6965d67da04d`) must be set to a variable, both as a runtime param and in
689
+ customData. Null ⇒ "make sure the action is defined/configured properly."
690
+ 5. **Node code must not bind an isError variable** (an action's error-port output; type shown as
691
+ "Error", never free-initialized) nor an undeclared var.
692
+
693
+ ### Designer-layer rule #6 — Execute Query Output must be a list<Object> (2026-07-06)
694
+ Another client-side "data type mismatch" the server /validate does NOT catch. An `Execute Query`
695
+ returns a SQL result SET, so its **Output** variable must be a `list<Object>`: `isList = true` AND the
696
+ element type an Object - the generic `0317bfee-b2f5-4bde-bfe8-121212121221` (any/Object) OR any custom
697
+ DataModel (whose GUID does not share the `0317bfee-b2f5-4bde-bfe8-1212…` scalar prefix). A scalar
698
+ (e.g. a `number` output `…1211`, isList=false) or a `list<primitive>` (e.g. `list<text>` `…1220`)
699
+ trips "Error: data type mismatch" and blocks SAVE. Typed-DM lists (`list<UserDM>`, `list<MessageDM>`)
700
+ are fine - a DataModel IS an Object. `procesio flow-lint` now checks this (EXECQUERY_OUTPUT_TYPE).
701
+ Fix: set the Output var's dataType to the generic Object (or a DM) and isList=true; the var id on the
702
+ Output setting is unchanged. Found live on `Persist search_id` (SalesOMMO, scalar number) and
703
+ `sp_MessageAdd` (Add Message, list<text>) - both corrected to list<Object>.
704
+
705
+ ## Shapes (canvas-engine PRC-4391, 2026-08)
706
+
707
+ Decorative canvas Shapes (rectangle / ellipse / diamond) are NOT flow nodes: no ports, no
708
+ config, no execution. They persist as `ICanvasShape[]` under the flow DTO's opaque
709
+ **`canvasData.shapes`** blob (live API camelCase `canvasData`; PUT DTO PascalCase
710
+ `CanvasData`) — NOT a top-level `Flow.shapes` field. Z-order sits below the connectors.
711
+ Designer save/load of shapes is platform Phase 2. **Tool rule: never drop `canvasData` on
712
+ a round-trip.** `process-edit` now carries the live `canvasData` into the rebuilt DTO
713
+ (build() resets it to None); the layout/relayout engine already leaves `canvasData`
714
+ byte-identical. A hand-authored PUT must preserve it or every shape is wiped.
715
+
716
+ ## DataStore form event (2026-08)
717
+
718
+ `EventAction.RUN_DATA_STORE_OPERATION`; config `EventRunDataStoreOperationConfig
719
+ {dataStoreId, operation, inputMap, outputMap, filters?, areFiltersConfigured?}`.
720
+ `operation` = DataStoreOperation READ/ADD/UPDATE/DELETE; filters apply to every op except
721
+ ADD (`dataStoreOperationSupportsFilters`). Map items are `{id, left, right}`
722
+ (DataStoreMapItem). Wired via `form-set-element-event --action RUN_DATA_STORE_OPERATION`
723
+ (surgical) or the builder `do: datastore`. Operation wire encoding (string vs numeric) +
724
+ map orientation confirmed live post-launch.
725
+
726
+ ## A platform-seeded data model cannot be deleted
727
+
728
+ `DELETE /api/DataTypes/{id}` on a model the platform seeded into the workspace returns HTTP 400 with
729
+
730
+ {"statusCode": 452, "value": "Unauthorized data type change!", "target": "data_type"}
731
+
732
+ Seeded models are not distinguishable by guid prefix the way the primitive types are, and one of them
733
+ can even carry a real user as `createdBy` (the account that first opened the workspace), so neither
734
+ the id nor the author tells you. Treat 452 as the answer: **an unreferenced model that refuses to
735
+ delete is the platform's, not a leftover of yours.** A tidy-the-workspace pass should report those
736
+ rather than keep retrying them.
737
+
738
+ ## ProcessInfo is a declared variable, not an ambient object
739
+
740
+ The platform's per-run facts are reached through a flow variable, so a flow that never
741
+ references them does not have it:
742
+
743
+ {"Id": "<per-flow guid>", "ContextId": null,
744
+ "DataType": "10c6ac59-3929-49e6-99dc-121212121221", // SystemDataModel
745
+ "Type": 40, "Name": "ProcessInfo",
746
+ "DefaultValue": null, "IsList": false, "IsError": false, "IsRequired": false}
747
+
748
+ Its fields are attributes of `SystemDataModel`:
749
+
750
+ | field | attribute id |
751
+ |---|---|
752
+ | `workspaceId` | `75044bb4-7c70-40f2-8756-21c03de655c8` |
753
+ | `instanceId` | `d3c32b47-df62-4376-8a82-f8f9aff7d6d0` |
754
+ | `templateId` | `3a36d9ea-0fd8-4d4a-a89b-8f297c5d0098` |
755
+ | `name` | `0a5d51bc-2550-4291-a56c-cb027f8db966` |
756
+ | `startedOn` | `75044bb4-7c70-40f2-8756-21c03de655c9` |
757
+
758
+ Bind a field like any model-typed variable: a `<%N%>` placeholder whose `variable[]` entry is
759
+ `{variableId: <the flow's ProcessInfo id>, attribute: <the attribute id>}`.
760
+
761
+ **The designer adds it the first time something references it, which makes it easy to lose.**
762
+ A flow rebuilt from a compact config keeps only the variables the config lists, so a rebuild
763
+ drops ProcessInfo from every flow that had it, silently, because nothing references it
764
+ afterwards. Declare it with `direction: "processinfo"`.
765
+
766
+ **Prefer `ProcessInfo.workspaceId` over any stored copy of the workspace id.** It is always
767
+ right, needs no configuration, and survives an import into another workspace - where a literal
768
+ in an action parameter does not, since an import rewrites a resource's own `workspaceId`
769
+ metadata but never a value inside a parameter.
770
+
771
+ ## What a Transport export/import actually carries (verified cross-tenant, 2026-09)
772
+
773
+ Measured by exporting a whole working product out of one workspace and importing it into a
774
+ workspace under a **different master**, then diffing both sides resource by resource. The summary
775
+ a packager needs: **the graph travels, the environment does not.** Everything a flow points at
776
+ that lives outside the flow is either absent from the pack or present only as an id the new
777
+ workspace cannot resolve.
778
+
779
+ ### Travels, intact
780
+
781
+ | Carried | Detail |
782
+ | --- | --- |
783
+ | Flows, forms, data stores, data models | every one, **with its original id** |
784
+ | Cross-references between them | consequently survive: a form's RUN_PROCESS map binds a process by id, so it still resolves |
785
+ | A form's encrypted `Data.code` | global CSS + JS decrypt byte-identically in the new workspace, so the key is not per-workspace |
786
+ | A form's `IsPrivate` | privacy is part of the form, not of the environment |
787
+ | Action names and canvas coordinates | the readability work is in `customData`, which is part of the flow |
788
+ | `Active` and `IsValid` | **carried verbatim, not recomputed** — see the trap below |
789
+
790
+ A platform-seeded data model is skipped rather than duplicated: those ids are identical in every
791
+ workspace (the `…12121212121x` family plus a few named ones), so the importer finds them already
792
+ present. Only genuinely custom models are created.
793
+
794
+ ### Does NOT travel
795
+
796
+ | Absent | Consequence in the new workspace |
797
+ | --- | --- |
798
+ | **Data store ROWS** | every imported store arrives with the right columns and zero rows |
799
+ | **Credentials**, when the export names none | excluded, and the ids stay baked into the nodes (below). Name them and the records DO travel, secrets withheld - see "Ship the credentials in the pack" |
800
+ | **Schedules** | anything time-driven simply never fires, with no error anywhere |
801
+ | **API keys** | a key is per workspace; a setting that holds one arrives empty |
802
+ | **CustomUrl / tinyUrl**, at workspace AND entity level | **the forms are not reachable at any address** |
803
+
804
+ ### The trap: a credential is a node parameter, so an export WITHOUT them leaves dangling references
805
+
806
+ A credential slot is `CustomData.configuration[].settings[].type == "credentials"`, and the chosen
807
+ credential lands in the node's `Parameters[].Value` as the credential INSTANCE gid. That gid is
808
+ part of the flow, so it travels — pointing at a credential that does not exist in the new
809
+ workspace. Nothing rewrites it, and nothing warns: the flows arrive with `IsValid: true` because
810
+ validity is client-set and simply carried over, so the workspace looks healthy while every
811
+ credential-bearing node is broken.
812
+
813
+ The failure is at least precise, and it happens while building the input model, i.e. before the
814
+ node does anything:
815
+
816
+ ```
817
+ Error while building input model: Error while getting credential <gid> for workspace <new-ws-id>.
818
+ ```
819
+
820
+ The run ends at status 40 and names the action. Two consequences worth designing around:
821
+
822
+ - **A flow fails whole, not partially.** Nodes before the credential node DO commit their work
823
+ (a seeding branch ahead of an API call still seeds), so a failed bootstrap can still leave the
824
+ stores correctly populated. Check state before concluding that nothing ran.
825
+ - **A form whose FORM_LOAD launches such a flow cannot populate.** If that form is the product's
826
+ own setup screen, the new user cannot reach the screen they need in order to fix the problem.
827
+ Keep the nodes that need a credential OFF the load path of the screen that configures them, or
828
+ let them tolerate a missing credential, or the product is unreachable until someone edits it in
829
+ the designer.
830
+
831
+ Re-pointing is scriptable rather than manual: the process config accepts a `{credential: <gid>}`
832
+ binding for that parameter, so a whole workspace can be remapped from one credential to another in
833
+ one pass per flow.
834
+
835
+ ### Rows not travelling is also a safety property
836
+
837
+ It is the reason a pack cannot leak the exporter's configuration or their customers' records: a
838
+ key/value settings store carrying an API key, a logo, an owner's address, and an operational table
839
+ of real bookings all stay behind. So a "publishable" pack is not achieved by scrubbing an export,
840
+ it is achieved by **keeping every environment-specific value in a data store row and never in a
841
+ node** — then the export is publishable by construction. Verified on a real pack: the stored API
842
+ key value is absent from the bundle, and only placeholder/help text carries a hostname.
843
+
844
+ What DOES travel as author metadata: every resource keeps `createdBy` / `createdById` and the
845
+ source `workspaceId` in its metadata. Harmless for logic, but it means a published pack names its
846
+ author.
847
+
848
+ ### Therefore: what a product must ship to be installable
849
+
850
+ Seeding cannot be left to the export. A pack needs a bootstrap flow that creates its own reference
851
+ data and its own settings KEYS with neutral defaults, because it will always land in empty stores.
852
+ Two rules learned from doing it wrong:
853
+
854
+ - **Seed identity and branding BLANK, seed only mechanics.** Buffers, notice periods and a UTC
855
+ default timezone are safe; a name, an address, a logo or a timezone taken from the author's own
856
+ install is a value the new owner has to discover and undo. A seed that hardcodes one region's
857
+ timezone into a weekly-availability table while the global default seeds as UTC is the
858
+ inconsistency this produces.
859
+ - **Seed what is deterministic.** Because form ids survive a transport, a setting that stores a
860
+ form id can be seeded as a literal instead of being left for the installer to look up. A
861
+ setting that a flow READS but that no screen can WRITE, left blank by the seed, is a silent
862
+ defect — the classic case is a cancellation link built from such a setting, which then goes out
863
+ in customer email pointing nowhere.
864
+
865
+ ### Ship the credentials in the pack, and never hand-edit their secret fields
866
+
867
+ Naming credentials in the export changes the install from "rebuild and rewire" to "fill in the
868
+ blanks", and it does it without shipping a single secret:
869
+
870
+ - The credential RECORDS travel **with their original gids**, so every node parameter that points
871
+ at one resolves in the new workspace. The 11-node rewiring job disappears.
872
+ - With sensitive data OFF (the default), every designated secret field arrives **empty** while all
873
+ the non-secret configuration is kept: base URL, port, encryption mode, username, scopes, the
874
+ auth method, the header name, the test endpoint. The installer opens each credential and fills
875
+ only what is blank.
876
+ - Verified on a real pack: the stored API key value is absent from the bundle even though the
877
+ credential that holds it is in it.
878
+
879
+ **Do not write a placeholder into those empty fields.** It is the obvious instinct - make the
880
+ blanks visible as `[Replace_me]` - and it silently breaks the pack: on import the credential is
881
+ **dropped with no error and no mention**, the nodes that referenced it dangle again, and the only
882
+ symptom is a credential missing from a list nobody thought to re-count. Measured: the untouched
883
+ export imported 3 of 3; the same export with a placeholder typed into two secret fields imported 1
884
+ of 3. A valid-base64 value was dropped too, so it is not a formatting problem - treat the field as
885
+ an opaque slot that only the platform may fill. Ship the file exactly as exported.
886
+
887
+ What still needs cleaning by hand, at the SOURCE, before publishing: a credential's non-secret
888
+ fields are plaintext and are author-written, so anything personal typed into one travels. A test
889
+ endpoint naming a real mailbox is the usual case. Fix it in the credential, not in the export.
890
+
891
+ ### An error port cannot go just anywhere: only a Join converges
892
+
893
+ Routing a fault away from the flow's failure path needs a target, and the platform rejects almost
894
+ every obvious one. Both refusals come from the runtime validator, so they are caught before a PUT
895
+ rather than discovered later:
896
+
897
+ | Target | Verdict |
898
+ | --- | --- |
899
+ | the node's own successor | `Duplicate connection port! SourceId:… DestinationId:…` - source+destination is the unique key, and the normal edge already occupies it |
900
+ | any node that already has one inbound edge | `statusCode 383, Action has too many input ports` - this includes Stop |
901
+ | a **Join** inserted after the successor | accepted |
902
+
903
+ So the working shape is: insert a Join between the successor and whatever came next, then point the
904
+ error port at the Join. The node that would have consumed the response simply does not run on the
905
+ fault path, its output variable stays null, and the flow reaches Stop at status 50. That converts
906
+ "the whole run failed" into "one section is empty", which is the difference between a page that
907
+ cannot open and a page that opens with one dropdown blank.
908
+
909
+ Worth designing for deliberately: the consumer node should already tolerate a null input (`const
910
+ res = <%0%> || {}`), because on the error path that is exactly what it gets - and if it also checks
911
+ the HTTP status it keeps working for the OTHER failure mode, an API that answers 401 rather than
912
+ faulting. A Call API that returns an error STATUS does not take the error port at all; only a fault
913
+ while building the input model does (a missing or unresolvable credential). Both need handling and
914
+ they are not the same path.
915
+
916
+ ### A form has no address until a CustomUrl exists
917
+
918
+ Measured against the live forms host, which serves an SPA shell (and therefore HTTP 200) for every
919
+ path - so a status code proves nothing here and the rendered page has to be read:
920
+
921
+ | URL form | Result |
922
+ | --- | --- |
923
+ | `{forms_base}/{masterSlug}/{workspaceSlug}/{entitySlug}` | the form renders |
924
+ | `{forms_base}/{tinyUrl}` | the form renders |
925
+ | `{forms_base}/forms/{formId}` | **"Form not found"** |
926
+ | `{forms_base}/{formId}` | **"Form not found"** |
927
+
928
+ A CustomUrl is what mints the tinyUrl, it is per workspace and per entity, and it does not travel
929
+ in a transport. So an imported form is reachable at no address at all until someone creates one -
930
+ and any link the product BUILDS from a form id is dead. A fallback chain that ends in
931
+ `{base}/forms/{id}` looks like a safety net and is a dead link in a customer's email; leave the
932
+ link out instead.
933
+
934
+ ### A seeded setting defeats COALESCE, because '' is not NULL
935
+
936
+ A bootstrap that creates its settings rows with empty defaults will quietly disable every
937
+ SQL fallback chain written over them: `COALESCE(a, b, c)` returns the first NON-NULL value, the
938
+ seeded row exists with `''`, so the chain stops at the first branch and the two working fallbacks
939
+ behind it are unreachable. The symptom is a blank link in generated mail while the query looks
940
+ correct and the settings look present.
941
+
942
+ Write the chain as `COALESCE(NULLIF(a,''), …)`, and for a composed branch put the `NULLIF` on the
943
+ part that can be blank - `CONCAT` returns NULL when any argument is NULL, so
944
+ `CONCAT(base, '/', NULLIF(slug,''))` makes the whole branch fall through by itself. The same bug
945
+ does not exist in the JavaScript equivalent, because `||` treats `''` as falsy: a chain ported from
946
+ a script to SQL acquires it on the way.
947
+
948
+ ### One `<%N%>` sequence spans ALL of a node's parameters, outputs included
949
+
950
+ A node does not number each parameter separately. A Query Store node's statement chips, its
951
+ parameters-map sources and its output bindings all draw from ONE sequence - in a live release step
952
+ the statement held 0-4, the map 5-7 and the Result-Count output 8. So adding two parameters to the
953
+ map moves the output binding to 10, and an output left reading `<%8%>` is written back to whatever
954
+ now holds that index: a flow that validates clean and quietly files its result in the wrong
955
+ variable.
956
+
957
+ Anything that edits one parameter of a node therefore has to renumber the whole node, in a defined
958
+ order (statement, then map, then the remaining placeholder-bearing parameters in their existing
959
+ index order). Two traps when writing that:
960
+
961
+ - **An `id` is only a placeholder index inside a `variable` list.** A parameters-map ROW carries
962
+ its own ordinal `id` (0, 1, 2 …) which collides with the low indices; renumbering those turns
963
+ the map into nonsense.
964
+ - **A source's resolved ATTRIBUTE path cannot be re-derived from a name** without the model the
965
+ editor never loaded, so a source being carried over should be copied verbatim rather than
966
+ rebuilt. That is why `node-set-query --params` takes `{"keep": true}`.
967
+
968
+ ### A timezone belongs to the reader, availability belongs to the owner
969
+
970
+ Worth stating because the natural implementation gets it half right. In a booking product the
971
+ window maths MUST run in the owner's zone - that is where "available 09:00 to 17:00" is defined -
972
+ while every label a visitor reads must be the visitor's. Those are different answers from the same
973
+ instant, and the instant is the only thing both sides agree on.
974
+
975
+ So a slot is correctly described by its UTC instant plus BOTH renderings, and a consumer picks.
976
+ The failure mode to avoid is an output that carries the owner's wall clock under a field name that
977
+ sounds like the reader's, because a page which trusts that name then shows a visitor in another
978
+ country times that are off by the offset while labelling them as local. Keep the owner's fields,
979
+ add the reader's, and name both for whose clock they are.
980
+
981
+ ### Declaring a calendar event in the invitee's own zone
982
+
983
+ A timed event carries both an instant and a zone NAME, and they answer different questions. With
984
+ `dateTime` written as UTC the instant is already unambiguous, so adding
985
+ `start.timeZone` / `end.timeZone` moves nothing: it changes only which zone the calendar NAMES
986
+ when it renders the event. That matters because an invitation mail is rendered in the event's
987
+ zone, so an invitee who picked a time in their own zone otherwise reads it back in the host's and
988
+ concludes the booking went in wrong.
989
+
990
+ Two things to get right, both learned by breaking them:
991
+
992
+ - **Never let that field reach the API empty.** An empty zone is rejected, and the column feeding
993
+ it is exactly the one a record written by something other than the form may not have. Make the
994
+ query yield a valid zone unconditionally (`COALESCE(NULLIF(col,''), 'UTC')`); UTC is always a
995
+ real zone and leaves the instant alone.
996
+ - **A value only reaches the body if the loop item's MODEL declares it.** A query can select a
997
+ column, and the row will still not carry it unless the item's data model has an attribute of
998
+ that name - so the model attribute comes first, then the column, then the body reference. Adding
999
+ an attribute is additive and leaves existing attribute ids alone (verified), which is what makes
1000
+ this safe to do to a model a live flow is iterating.
1001
+
1002
+ The host's own calendar view is unaffected either way, because a calendar renders events in the
1003
+ VIEWER's zone; only the stored zone name and the mails generated from it change.
1004
+
1005
+ ### A form publishes only TOP-LEVEL variables: a data-model input never arrives
1006
+
1007
+ The hardest fault to see in a form-driven panel, because every layer reports success and the
1008
+ mapping looks right in the designer.
1009
+
1010
+ A `RUN_PROCESS` event can map a form field onto an **attribute of a data-model variable**
1011
+ (`Row.Name`, `Row.DurationMinutes`, ...). The designer accepts it, the left side resolves to a real
1012
+ attribute, the flow validates, and the map reads correctly when you print it back. At runtime the
1013
+ browser does not send it. The publish call carries only the process's **top-level** variables; every
1014
+ attribute-path entry is dropped, so the whole model variable arrives NULL.
1015
+
1016
+ Why it is so hard to attribute:
1017
+
1018
+ - The process looks healthy. Called with a JSON payload through the run endpoint, the same model
1019
+ input works perfectly, so every API-side test passes. Only the browser path fails, which is why
1020
+ "the process runs but the page does not work" is the honest description of the symptom - and the
1021
+ reason to insist on testing through the page rather than through the process.
1022
+ - A guard hides it. A sane statement checks its inputs, so the write is skipped rather than
1023
+ failing: the run ends successfully with an affected-row count of 0 and no error. The page then
1024
+ reports its own generic failure, which points at the page.
1025
+ - Removing the guard diagnostically finally speaks, but it accuses the wrong parameter: the write
1026
+ fails on whichever NOT NULL column it reaches first, so the fault reads as "one field does not
1027
+ arrive" when in fact none of them do. Do not conclude from one named column; check every
1028
+ parameter sourced from the same variable.
1029
+ - Nothing in the attribute's own type matters. The attributes that appear to work are the ones a
1030
+ guard does not test, not the ones with a convenient type. Retyping an attribute is a dead end.
1031
+
1032
+ **How to see it in one step.** Install a tiny interceptor over `fetch` and `XMLHttpRequest.send`
1033
+ in the page before clicking, recording the body of every request whose URL looks like a publish or
1034
+ launch, then click the control for real and read what was recorded. The publish body is a flat list
1035
+ of `{id, value}` pairs, one per variable the form actually sends. Compare those ids against the
1036
+ process's variables: whatever is missing is what never left the browser. This is worth reaching for
1037
+ early on any "the page says it failed but the process is fine" report, because it replaces a chain
1038
+ of inference with one measurement.
1039
+
1040
+ **The fix, and the convention.** Give the process **flat, top-level** input variables - one per
1041
+ field - and bind the statement's parameters to those. Declare them String and coerce in SQL, since
1042
+ a panel's values are text on the wire anyway (and the id of an existing record arrives as a JSON
1043
+ number, not a string, so a cast is needed regardless). Rebuild the event map to the flat variables
1044
+ and the same click starts working with no other change.
1045
+
1046
+ A data model is still the right shape for a value passed **between processes**, or for a list a
1047
+ process returns. The restriction is specific to what a form publishes inward.
1048
+
1049
+ **Worth checking as a one-pass audit** rather than a click at a time: list every process the form
1050
+ calls, expand each input variable, and flag any that is a data model rather than a primitive. If
1051
+ one panel is broken and the others are not, this difference alone usually explains it - and the
1052
+ working panels show the convention the broken one should follow.
1053
+
1054
+ A related restriction has the same flavour and the same cause: a control added to a table row
1055
+ through the API is not part of that row's own field model, so its value path reads nothing and has
1056
+ to be carried to a plain field before a save can see it.
1057
+
1058
+ ### A date picker hands back an INSTANT, not a day
1059
+
1060
+ The control the visitor clicks a day on does not store that day. It stores local midnight of
1061
+ it, and what travels to the server is that moment in UTC. For every zone east of Greenwich
1062
+ the UTC calendar day is then the day BEFORE the one that was clicked, so the oldest trick in
1063
+ the book - taking the first ten characters of the ISO string - silently reads the wrong day.
1064
+
1065
+ It hides well because it is not a crash and not even always visible. The page moves one day
1066
+ back; when that earlier day happens to be a valid choice it is simply shown instead, and the
1067
+ report that comes back is "my selection resets when I change something else" rather than
1068
+ "the date is off by one". West of Greenwich the same code is correct, so whoever wrote it
1069
+ may never have seen it fail.
1070
+
1071
+ Convert the instant in the zone the picker used, which is the VISITOR's zone, not the
1072
+ organisation's:
1073
+
1074
+ ```js
1075
+ new Intl.DateTimeFormat('en-GB', { timeZone: zone, year: 'numeric', month: '2-digit', day: '2-digit' })
1076
+ .formatToParts(new Date(Date.parse(value)))
1077
+ ```
1078
+
1079
+ A page that shows times to a visitor already detects that zone for its own labels, so it is
1080
+ usually available to send alongside the date rather than something new to obtain. Keep the
1081
+ bare-day case first (a value that is already `YYYY-MM-DD` carries no instant and must not be
1082
+ shifted), and fall back to the raw slice inside a `catch` so a missing timezone database
1083
+ degrades to the old behaviour instead of dropping the choice entirely.
1084
+
1085
+ Worth testing explicitly in both directions, because one direction passes by luck: the same
1086
+ instant must resolve to different days under an eastern and a western zone. If it does not,
1087
+ the zone argument is being ignored and the conversion only appeared to work.
1088
+
1089
+ ### Adding a binding to a node: take the next FREE index, not the next one
1090
+
1091
+ A consequence of one `<%N%>` sequence spanning all of a node's parameters, outputs included,
1092
+ and the thing that makes adding a binding look harder than it is. A script using `<%0%>`
1093
+ through `<%2%>` whose node also has two output bindings is already using indices 3 and 4, so
1094
+ introducing `<%3%>` collides with an output and the edit has to renumber the whole node.
1095
+
1096
+ It does not have to. The indices identify bindings, they are not required to be contiguous
1097
+ per parameter or to follow reading order, so the new placeholder can simply take the next
1098
+ index free across the WHOLE node. The script then reads `<%0%>`, `<%1%>`, `<%2%>`, `<%5%>`,
1099
+ which looks odd and is correct, and no existing binding moves. Read every parameter of the
1100
+ node before choosing the number; the outputs are the ones that get forgotten.