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,1098 @@
1
+ """Front-end (designer-layer) process validation — a faithful offline port of the
2
+ Process Designer's client-side "Process Errors" checks.
3
+
4
+ Why this exists: POST /api/Projects/validate validates only the RUNTIME layer
5
+ (parameters) and returns EMPTY even when the designer REFUSES to save. The designer's
6
+ "Process Errors" panel is a separate CLIENT-SIDE check of the DESIGNER layer
7
+ (customData) with NO server endpoint. This module replicates that check so a flow can
8
+ be validated offline / before every save, exactly like the FE does before it lets the
9
+ user save.
10
+
11
+ Source of truth: tools/procesio/docs_info/process-validation-reference-1.md — the FE
12
+ team's verbatim dump of Validation.ts + ControlValidators/*. Every check here maps to a
13
+ method/validator there; section refs are noted inline (e.g. "ref §2 noUnconnectedNodes").
14
+
15
+ Two severities:
16
+ * error — deterministic, high-signal, structural/required/placeholder/value-format
17
+ checks + subprocess mapping. These BLOCK a save (the designer would too).
18
+ * warning — the data-type MISMATCH layer, which depends on the FE helper
19
+ getValueDataTypes whose exact source is NOT in the reference. Ported
20
+ best-effort and kept NON-blocking so an imperfect reconstruction can never
21
+ wrongly block a legitimate save. Tunable against real flows.
22
+
23
+ PURE + offline + case-insensitive: exports are PascalCase (Actions, CustomData), the
24
+ live Web-API is camelCase (actions, customData). Every field access goes through `_g`.
25
+ Never mutates the input. No network — subprocess data and the datatype catalog are
26
+ INJECTED (target_vars_of / datatypes) so the whole thing is unit-testable.
27
+
28
+ Born 2026-07-06 from process-validation-reference-1.md.
29
+ """
30
+ from __future__ import annotations
31
+
32
+ import json
33
+ import re
34
+ from typing import Any, Callable
35
+
36
+ # -- constant reference (ref §6) ----------------------------------------------
37
+
38
+ START_TID = "c0e32108-6e3e-4ab8-96bd-cd61be6edb33"
39
+ STOP_TID = "c0e32108-6e3e-4ab8-96bd-cd61be6edb34"
40
+ FOREACH_TID = "dbef0804-66a9-4f8f-872c-ece1b89b8fdb"
41
+ CALL_SUB_TID = "c37e56fe-d924-4604-a86f-7c93f863fcdf"
42
+ TRIGGER_SUB_TID = "615365f9-9ccb-dd46-85a6-af824b7be897"
43
+ DECISIONAL_TID = "f5dcbb04-253d-4061-99a1-9b2822c2e6d2"
44
+ AI_DECISIONAL_TID = "772aac51-73f5-471d-bf9f-f5099cb30001"
45
+
46
+ DECISIONAL_CASE_SETTING = "11d4044a-8586-47f6-b3ce-1cae5da40f30"
47
+ DELAY_TYPE_SETTING = "f1293589-6ede-4cc6-a04b-cc70e7084cb0"
48
+
49
+ # Data-type GUIDs (ref §6.3)
50
+ BOOLEAN = "0317bfee-b2f5-4bde-bfe8-121212121210"
51
+ INTEGER = "0317bfee-b2f5-4bde-bfe8-121212121211"
52
+ FLOAT = "0317bfee-b2f5-4bde-bfe8-121212121212"
53
+ DOUBLE = "0317bfee-b2f5-4bde-bfe8-121212121213"
54
+ STRING = "0317bfee-b2f5-4bde-bfe8-121212121214"
55
+ DATE = "0317bfee-b2f5-4bde-bfe8-121212121215"
56
+ RELATIONSHIP = "0317bfee-b2f5-4bde-bfe8-121212121216"
57
+ TIME = "0317bfee-b2f5-4bde-bfe8-121212121217"
58
+ DATETIME = "0317bfee-b2f5-4bde-bfe8-121212121218"
59
+ GUID = "0317bfee-b2f5-4bde-bfe8-121212121222"
60
+ NUMBER = "NUMBER"
61
+ FILE = "10c6ac59-3929-49e6-99dc-121212121219"
62
+ JSON_T = "0317bfee-b2f5-4bde-bfe8-121212121220"
63
+ OBJECT = "0317bfee-b2f5-4bde-bfe8-121212121221"
64
+
65
+ _PRIMITIVE_IDS = {BOOLEAN, INTEGER, FLOAT, DOUBLE, STRING, DATE, RELATIONSHIP,
66
+ TIME, DATETIME, GUID, NUMBER}
67
+ # variable-name collision map (ref checkVariables + mapGUIDtoType/typeArray)
68
+ _PRIMITIVE_NAME = {
69
+ BOOLEAN: "boolean", INTEGER: "integer", FLOAT: "float", DOUBLE: "double",
70
+ STRING: "string", DATE: "date", RELATIONSHIP: "relationship", TIME: "time",
71
+ DATETIME: "datetime", GUID: "guid", NUMBER: "number",
72
+ }
73
+ _TYPE_NAMES = set(_PRIMITIVE_NAME.values())
74
+
75
+ # SettingType values (ref §6.4) -> validator key
76
+ AI_DECISIONAL_CASE = "ai-decisional-case"
77
+ _SETTING_TYPE_TO_VALIDATOR = {
78
+ "tabs-payload": "tabs_payload_old",
79
+ "tabs-payload-v2": "tabs_payload",
80
+ "decisional-case": "conditional",
81
+ "ai-decisional-case": "ai_decisional",
82
+ "process-inputs": "process_io",
83
+ "process-outputs": "process_io",
84
+ "column-definition": "column_definition",
85
+ "delay-definition": "delay",
86
+ "map-process-data": "map_process_data",
87
+ "document-mapper": "document_mapper",
88
+ "data-store-mapper": "data_store_mapper",
89
+ "data-store-decisional": "data_store_decisional",
90
+ "map-parameters": "map_parameters",
91
+ # "number" and everything else -> default
92
+ }
93
+
94
+ # GUID / dotted-GUID reference regex (ref §2 variableRegex)
95
+ _VAR_RE = re.compile(
96
+ r"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
97
+ r"(\.[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})*)\b(?!\.)")
98
+ # leftover-placeholder regex (ref AbstractControlValidator.hasPlaceholder)
99
+ _PLACEHOLDER_RE = re.compile(
100
+ r"([x]{8}-[x]{4}-[x]{4}-[x]{4}-[a-zA-Z]{11}(\.[x]{8}-[x]{4}-[x]{4}-[x]{4}-[a-zA-Z]{11})*)\b(?!\.)")
101
+
102
+
103
+ # -- case-insensitive access ---------------------------------------------------
104
+
105
+ def _g(obj: Any, *names: str, default=None):
106
+ if not isinstance(obj, dict):
107
+ return default
108
+ low = {str(k).lower(): v for k, v in obj.items()}
109
+ for n in names:
110
+ if n.lower() in low:
111
+ return low[n.lower()]
112
+ return default
113
+
114
+
115
+ # -- primitive helpers (ref utils/type/*) -------------------------------------
116
+
117
+ def _is_empty(value) -> bool:
118
+ return value is None or value == "" or (isinstance(value, list) and len(value) == 0)
119
+
120
+
121
+ def _is_valid_number(x) -> bool:
122
+ if isinstance(x, bool):
123
+ return False
124
+ if isinstance(x, (int, float)):
125
+ return True
126
+ if isinstance(x, str):
127
+ s = x.strip()
128
+ if s == "":
129
+ return False
130
+ try:
131
+ float(s)
132
+ return True
133
+ except ValueError:
134
+ return False
135
+ return False
136
+
137
+
138
+ def _is_valid_integer(x) -> bool:
139
+ if isinstance(x, bool):
140
+ return False
141
+ if isinstance(x, int):
142
+ return True
143
+ if isinstance(x, float):
144
+ return x.is_integer()
145
+ if isinstance(x, str):
146
+ s = x.strip()
147
+ if not re.fullmatch(r"[+-]?\d+", s or ""):
148
+ return False
149
+ return True
150
+ return False
151
+
152
+
153
+ def _is_boolean(x) -> bool:
154
+ if isinstance(x, bool):
155
+ return True
156
+ if isinstance(x, str):
157
+ return x.strip().lower() in ("true", "false")
158
+ return False
159
+
160
+
161
+ def _has_variable(value) -> bool:
162
+ """ref Variables/Utils.hasVariable — the value string contains a variable ref."""
163
+ if not isinstance(value, str):
164
+ return False
165
+ return bool(_VAR_RE.search(value))
166
+
167
+
168
+ def _has_placeholder(value: str) -> bool:
169
+ return bool(_PLACEHOLDER_RE.search(value))
170
+
171
+
172
+ # -- node model helpers --------------------------------------------------------
173
+
174
+ def _template_id(action: dict) -> str | None:
175
+ return _g(action, "ActionTemplateId", "templateId")
176
+
177
+
178
+ def _template_name(action: dict) -> str | None:
179
+ return _g(action, "ActionTemplateName", "actionTemplateName", "ActionName", "actionName")
180
+
181
+
182
+ def _cd(action: dict) -> dict:
183
+ return _g(action, "CustomData", "customData", default={}) or {}
184
+
185
+
186
+ def _shape(action: dict) -> str | None:
187
+ return _g(_cd(action), "type")
188
+
189
+
190
+ def _cd_name(action: dict):
191
+ """The canvas node label = customData.name (the FE model's node.name). Returns a
192
+ sentinel `_ABSENT` when customData has no `name` key at all (older/fixture DTOs that
193
+ keep the name on actionName instead), so callers can fall back correctly."""
194
+ cd = _cd(action)
195
+ if isinstance(cd, dict) and any(str(k).lower() == "name" for k in cd):
196
+ return _g(cd, "name") or ""
197
+ return _ABSENT
198
+
199
+
200
+ _ABSENT = object()
201
+
202
+
203
+ def _kind(action: dict) -> str:
204
+ """Classify an action into a template family, by GUID first then name/shape."""
205
+ tid = _template_id(action)
206
+ if tid == START_TID:
207
+ return "start"
208
+ if tid == STOP_TID:
209
+ return "stop"
210
+ if tid == FOREACH_TID:
211
+ return "foreach"
212
+ if tid == CALL_SUB_TID:
213
+ return "call_sub"
214
+ if tid == TRIGGER_SUB_TID:
215
+ return "trigger_sub"
216
+ if tid == DECISIONAL_TID:
217
+ return "decisional"
218
+ if tid == AI_DECISIONAL_TID:
219
+ return "ai_decisional"
220
+ name = (_template_name(action) or "").strip().lower()
221
+ by_name = {
222
+ "start": "start", "stop": "stop", "for each": "foreach",
223
+ "call subprocess": "call_sub", "trigger subprocess": "trigger_sub",
224
+ "decisional": "decisional", "decision": "decisional",
225
+ "ai decisional": "ai_decisional",
226
+ }
227
+ if name in by_name:
228
+ return by_name[name]
229
+ # legacy DTOs may carry no template id/name — fall back to the canvas label. Only
230
+ # structural templates default their label from the template, so this stays reliable
231
+ # for Start/Stop/For Each/(Trigger) Subprocess; a renamed node just reads as "other".
232
+ cdn = _cd_name(action)
233
+ if cdn is not _ABSENT and (cdn or "").strip().lower() in by_name:
234
+ return by_name[(cdn or "").strip().lower()]
235
+ if _shape(action) == "diamond":
236
+ return "decisional"
237
+ return "other"
238
+
239
+
240
+ def _action_id(action: dict) -> str | None:
241
+ return _g(action, "Id", "id")
242
+
243
+
244
+ def _action_name(action: dict) -> str:
245
+ # The FE's node.name IS customData.name; use it when present (even when ""), else fall
246
+ # back to actionName for DTOs that keep the label there (fixtures / some exports).
247
+ cdn = _cd_name(action)
248
+ if cdn is not _ABSENT:
249
+ return cdn
250
+ return _g(action, "ActionName", "actionName", "name", default="") or ""
251
+
252
+
253
+ def _badges(action: dict, suffix: str = "") -> list[str]:
254
+ """ref AbstractControlValidator.getBadges (offline approximation: no store lookup)."""
255
+ name = _action_name(action)
256
+ tmpl = (_template_name(action) or "").strip()
257
+ badges: list[str] = []
258
+ if name and tmpl and tmpl != name.strip():
259
+ badges.append(name)
260
+ label = (tmpl or name or "action") + suffix
261
+ badges.append(label)
262
+ return badges
263
+
264
+
265
+ def _settings_of(action: dict):
266
+ """Yield (config_setting) for each top-level setting in the action's configuration."""
267
+ cd = _g(action, "CustomData", default={}) or {}
268
+ for tab in _g(cd, "configuration", default=[]) or []:
269
+ for s in _g(tab, "settings", default=[]) or []:
270
+ if isinstance(s, dict):
271
+ yield s
272
+
273
+
274
+ def _walk_settings_loose(action: dict):
275
+ """Yield every setting in the configuration, recursing one level into side-panel
276
+ list values (which hold sub-settings that may carry no `id`). Mirrors the reader's
277
+ _walk_settings; used to locate id-less settings like `flow-list`."""
278
+ for s in _settings_of(action):
279
+ yield s
280
+ val = _g(s, "value")
281
+ if isinstance(val, list):
282
+ for sub in val:
283
+ if isinstance(sub, dict) and (_g(sub, "label") or _g(sub, "type")):
284
+ yield sub
285
+
286
+
287
+ def _all_settings_recursive(action: dict) -> list[dict]:
288
+ """ref AbstractControlValidator.getAllSettings — flatten nested side-panel settings."""
289
+ out: list[dict] = []
290
+
291
+ def walk(s: dict):
292
+ if _g(s, "id") is not None:
293
+ out.append(s)
294
+ val = _g(s, "value")
295
+ if isinstance(val, list):
296
+ for sub in val:
297
+ if isinstance(sub, dict):
298
+ walk(sub)
299
+ for s in _settings_of(action):
300
+ walk(s)
301
+ return out
302
+
303
+
304
+ # -- ports -> line model (ref §2 lineArray) -----------------------------------
305
+
306
+ def _build_lines(actions: list[dict]) -> dict[str, list[dict]]:
307
+ """Reconstruct each node's `lineArray` from all actions' Ports (edges live on the
308
+ source action). A line touches a node if it is that node's source OR destination.
309
+ error_path is true for the Error output path (Type==1 or Data.isDefault=='error')."""
310
+ lines: list[dict] = []
311
+ for a in actions:
312
+ src = _action_id(a)
313
+ for p in _g(a, "Ports", "ports", default=[]) or []:
314
+ s = _g(p, "SourceId", "sourceId", default=src)
315
+ d = _g(p, "DestinationId", "destinationId")
316
+ if not s or not d:
317
+ continue
318
+ pdata = _g(p, "Data", "data", default={}) or {}
319
+ is_err = (int(_g(p, "Type", "type", default=0) or 0) == 1) or \
320
+ (_g(pdata, "isDefault") == "error")
321
+ lines.append({"source": s, "dest": d, "error_path": is_err})
322
+ by_node: dict[str, list[dict]] = {}
323
+ for ln in lines:
324
+ by_node.setdefault(ln["source"], []).append(ln)
325
+ if ln["dest"] != ln["source"]:
326
+ by_node.setdefault(ln["dest"], []).append(ln)
327
+ return by_node
328
+
329
+
330
+ # =============================================================================
331
+ # Warning helper
332
+ # =============================================================================
333
+
334
+ def _w(severity: str, code: str, text: str, action: dict | None = None,
335
+ suffix: str = "", badges: list[str] | None = None) -> dict:
336
+ return {
337
+ "severity": severity,
338
+ "code": code,
339
+ "text": text,
340
+ "badges": badges if badges is not None else (_badges(action, suffix) if action else []),
341
+ "actionId": _action_id(action) if action else None,
342
+ "action": _action_name(action) if action else None,
343
+ }
344
+
345
+
346
+ # =============================================================================
347
+ # getValueDataTypes + validateDataType (the WARNING type layer, ref §3 / glossary)
348
+ # =============================================================================
349
+
350
+ def _resolve_ref_type(ref: str, var_by_id: dict, dt_by_id: dict) -> tuple[str | None, bool] | None:
351
+ """Resolve a dotted variable reference (v.attr.attr) to (dataTypeId, isList).
352
+ Returns None if the root variable is unknown."""
353
+ parts = ref.split(".")
354
+ root = var_by_id.get(parts[0])
355
+ if not root:
356
+ return None
357
+ dt = _g(root, "DataType", "dataType")
358
+ is_list = bool(_g(root, "IsList", "isList", default=False))
359
+ for attr_id in parts[1:]:
360
+ model = dt_by_id.get(dt)
361
+ if not model:
362
+ return (None, is_list)
363
+ attr = next((at for at in (_g(model, "attributes", "Attributes", default=[]) or [])
364
+ if _g(at, "id", "Id") == attr_id), None)
365
+ if not attr:
366
+ return (None, is_list)
367
+ dt = _g(attr, "dataTypeId", "DataTypeId", "dataType", "DataType")
368
+ is_list = bool(_g(attr, "isList", "IsList", default=False))
369
+ return (dt, is_list)
370
+
371
+
372
+ def get_value_data_types(value, var_by_id: dict, dt_by_id: dict) -> dict:
373
+ """ref Values/ValueDataTypesHelper.getValueDataTypes — parse variable refs in a raw
374
+ value and return the datatypes they resolve to, split scalar vs list.
375
+ A plain constant (no variable refs) yields empty lists ({} => "valid, skip")."""
376
+ scalar: list[dict] = []
377
+ listed: list[dict] = []
378
+ if not isinstance(value, str):
379
+ return {"scalar": scalar, "list": listed}
380
+ for m in _VAR_RE.finditer(value):
381
+ resolved = _resolve_ref_type(m.group(1), var_by_id, dt_by_id)
382
+ if resolved is None:
383
+ continue
384
+ dt_id, is_list = resolved
385
+ if dt_id is None:
386
+ continue
387
+ model = dt_by_id.get(dt_id) or {"id": dt_id}
388
+ (listed if is_list else scalar).append(model)
389
+ return {"scalar": scalar, "list": listed}
390
+
391
+
392
+ def _is_primitive(dt_id: str) -> bool:
393
+ return dt_id in _PRIMITIVE_IDS
394
+
395
+
396
+ def _is_custom_type_allowed(dt_id: str) -> bool:
397
+ return dt_id in (JSON_T, OBJECT)
398
+
399
+
400
+ def validate_data_type(types: dict, setting: dict, excluded: list[str] | None = None) -> bool:
401
+ """ref §3 SettingValidation.validateDataType — true if the value's actual types are
402
+ assignable to what the setting expects (dataTypeId / isList)."""
403
+ excluded = excluded or []
404
+ scalar = types.get("scalar", [])
405
+ listed = types.get("list", [])
406
+ if len(scalar) == 0 and len(listed) == 0:
407
+ return True
408
+ is_list = bool(_g(setting, "isList", "IsList", default=False))
409
+ if (is_list and (len(scalar) != 0 or len(listed) != 1)) or \
410
+ (not is_list and (len(scalar) == 0 or len(listed) != 0)):
411
+ return False
412
+ setting_dt = _g(setting, "dataTypeId", "DataTypeId")
413
+ for dt in [*scalar, *listed]:
414
+ if not dt:
415
+ continue
416
+ dt_id = _g(dt, "id", "Id")
417
+ if dt_id in excluded:
418
+ flag = False
419
+ elif setting_dt == OBJECT:
420
+ flag = True
421
+ elif _is_primitive(dt_id):
422
+ flag = True if setting_dt == STRING else (dt_id == setting_dt)
423
+ else:
424
+ flag = _is_custom_type_allowed(setting_dt) or (dt_id == setting_dt)
425
+ if not flag:
426
+ return False
427
+ return True
428
+
429
+
430
+ # =============================================================================
431
+ # Field-level validators (ref §4) — dispatched per setting.type
432
+ # =============================================================================
433
+
434
+ def _placeholders_check(action: dict, setting: dict) -> list[dict]:
435
+ """ref AbstractControlValidator.doPlaceholdersCheck — runs for EVERY setting."""
436
+ value = _g(setting, "value")
437
+ if not value:
438
+ return []
439
+ if isinstance(value, (dict, list)):
440
+ value = json.dumps(value)
441
+ if isinstance(value, str) and (_has_placeholder(value) or "<%%>" in value):
442
+ return [_w("error", "PLACEHOLDER",
443
+ "Please replace all placeholders in the template.",
444
+ action, f" - {_g(setting, 'label', default='')}")]
445
+ return []
446
+
447
+
448
+ def _label(setting: dict) -> str:
449
+ return _g(setting, "label", "Label", default="") or ""
450
+
451
+
452
+ def _default_required(action, setting) -> list[dict]:
453
+ if _g(setting, "isRequired", "IsRequired") and _is_empty(_g(setting, "value")):
454
+ return [_w("error", "REQUIRED",
455
+ "Please make sure that the action is defined/configured properly.",
456
+ action, f" - {_label(setting)}")]
457
+ return []
458
+
459
+
460
+ def _default_value_check(action, setting) -> list[dict]:
461
+ """ref DefaultControlValidator.doValueCheck — number/integer literal well-formedness."""
462
+ value = _g(setting, "value")
463
+ if value is None or _has_variable(value):
464
+ return []
465
+ is_list = bool(_g(setting, "isList", "IsList", default=False))
466
+ values = value if (is_list and isinstance(value, list)) else [value]
467
+ if not isinstance(values, list):
468
+ # try to parse a JSON list literal
469
+ try:
470
+ parsed = json.loads(values.replace("'", '"')) if isinstance(values, str) else values
471
+ if isinstance(parsed, list):
472
+ values = parsed
473
+ else:
474
+ values = [value]
475
+ except Exception: # noqa: BLE001
476
+ values = [value]
477
+ # An empty / unset value is NOT a format error — the required check owns emptiness.
478
+ # (ref: the FE's isValidNumber/isValidInteger treat "" as valid, so an optional numeric
479
+ # field left blank — e.g. a Delay's unused "Runtime Amount" — must not be flagged.)
480
+ values = [v for v in values if not (v is None or (isinstance(v, str) and v.strip() == ""))]
481
+ if not values:
482
+ return []
483
+ dt = _g(setting, "dataTypeId", "DataTypeId")
484
+ stype = _g(setting, "type")
485
+ if dt in (FLOAT, DOUBLE, NUMBER):
486
+ if not all(_is_valid_number(v) for v in values):
487
+ return [_w("error", "VALUE_NUMBER", "Please make sure that the value is a number.",
488
+ action, f" - {_label(setting)}")]
489
+ elif dt == INTEGER:
490
+ if not all(_is_valid_integer(v) for v in values):
491
+ return [_w("error", "VALUE_INTEGER", "Please make sure that the value is an integer.",
492
+ action, f" - {_label(setting)}")]
493
+ elif dt == OBJECT and stype == "number":
494
+ if not all(_is_valid_number(v) for v in values):
495
+ return [_w("error", "VALUE_NUMBER", "Please make sure that the value is a number.",
496
+ action, f" - {_label(setting)}")]
497
+ return []
498
+
499
+
500
+ def _dt_label(dt_by_id: dict, dt_id, is_list: bool = False) -> str:
501
+ name = _g(dt_by_id.get(dt_id) or {}, "name", "Name") or (dt_id or "?")
502
+ return f"list<{name}>" if is_list else str(name)
503
+
504
+
505
+ def _type_hint(setting, types: dict, dt_by_id: dict) -> str:
506
+ """Name the expected vs actual type. "Error: data type mismatch." alone does not
507
+ say WHAT to change; a real case (2026-07-30) was Call API's `Response Status`,
508
+ which needs an **integer** variable — binding a `number` failed with only that
509
+ opaque text, once as an FE warning and again as a BE error."""
510
+ expected = _dt_label(dt_by_id, _g(setting, "dataTypeId", "DataTypeId"),
511
+ bool(_g(setting, "isList", "IsList", default=False)))
512
+ actual = [_dt_label(dt_by_id, _g(dt, "id", "Id")) for dt in (types.get("scalar") or [])]
513
+ actual += [_dt_label(dt_by_id, _g(dt, "id", "Id"), True) for dt in (types.get("list") or [])]
514
+ got = ", ".join(a for a in actual if a) or "nothing"
515
+ return (f"binds {got}; this property expects {expected}. Retype the variable (or "
516
+ f"rebind) to match — the BE validator rejects the same mismatch on save.")
517
+
518
+
519
+ def _default_datatype_check(action, setting, var_by_id, dt_by_id) -> list[dict]:
520
+ """ref DefaultControlValidator.doDataTypeCheck — WARNING layer."""
521
+ value = _g(setting, "value") or ""
522
+ if not isinstance(value, str):
523
+ return []
524
+ types = get_value_data_types(value, var_by_id, dt_by_id)
525
+ if not validate_data_type(types, setting):
526
+ w = _w("warning", "TYPE_MISMATCH", "Error: data type mismatch.",
527
+ action, f" - {_label(setting)}")
528
+ w["hint"] = _type_hint(setting, types, dt_by_id) # additive; text kept for FE parity
529
+ return [w]
530
+ return []
531
+
532
+
533
+ def _ai_decisional_required(action, setting) -> list[dict]:
534
+ """ref §4.4 AiDecisionalCaseValidator."""
535
+ value = _g(setting, "value")
536
+ if not isinstance(value, list) or len(value) == 0:
537
+ return []
538
+ incomplete = any(
539
+ not (_g(c, "name") or "").strip() or not (_g(c, "condition") or "").strip()
540
+ or not _g(c, "target")
541
+ for c in value if isinstance(c, dict))
542
+ if incomplete:
543
+ return [_w("error", "AI_CASE_INCOMPLETE",
544
+ "Action definition incomplete. Please make sure all AI cases have a "
545
+ "name, condition, and target configured.", action, f" - {_label(setting)}")]
546
+ return []
547
+
548
+
549
+ def _tabs_payload_required(action, setting) -> list[dict]:
550
+ """ref §4.5 TabsPayloadControlValidator (v2): row has value but no key."""
551
+ value = _g(setting, "value") or {}
552
+ errors: list[dict] = []
553
+ body = _g(value, "body", default={}) or {}
554
+ btype = _g(body, "type")
555
+ bval = _g(body, "value", default={}) or {}
556
+ active = _g(bval, btype) if btype else None
557
+ label = _label(setting)
558
+
559
+ def row_missing_key(item):
560
+ return _g(item, "value") and len(str(_g(item, "value"))) > 0 and \
561
+ (not _g(item, "key") or len(str(_g(item, "key")).strip()) == 0)
562
+
563
+ if isinstance(active, list):
564
+ for i, item in enumerate(active):
565
+ if isinstance(item, dict) and row_missing_key(item):
566
+ errors.append(_w("error", "PAYLOAD_ROW_NO_KEY",
567
+ "Please make sure that the action is defined/configured properly.",
568
+ action, f" - {label} (Body, row {i + 1})"))
569
+ for key, subname in (("queryParams", "Query Params"), ("headers", "Headers")):
570
+ for i, item in enumerate(_g(value, key, default=[]) or []):
571
+ if isinstance(item, dict) and row_missing_key(item):
572
+ errors.append(_w("error", "PAYLOAD_ROW_NO_KEY",
573
+ "Please make sure that the action is defined/configured properly.",
574
+ action, f" - {label} ({subname}, row {i + 1})"))
575
+ return errors
576
+
577
+
578
+ def _tabs_payload_old_required(action, setting) -> list[dict]:
579
+ """ref §4.6 TabsPayloadOldControlValidator: query/header row value w/o key."""
580
+ value = _g(setting, "value") or {}
581
+ errors: list[dict] = []
582
+ label = _label(setting)
583
+ for key, subname in (("queryParams", "Query Params"), ("headers", "Headers")):
584
+ for item in _g(value, key, default=[]) or []:
585
+ if isinstance(item, dict) and _g(item, "value") and \
586
+ len(str(_g(item, "value"))) > 0 and \
587
+ (not _g(item, "key") or len(str(_g(item, "key")).strip()) == 0):
588
+ errors.append(_w("error", "PAYLOAD_ROW_NO_KEY",
589
+ "Please make sure that the action is defined/configured properly.",
590
+ action, f" - {label} ({subname})"))
591
+ return errors
592
+
593
+
594
+ def _process_io_required(action, setting) -> list[dict]:
595
+ """ref §4.7 ProcessInputOutputValidator."""
596
+ value = _g(setting, "value")
597
+ label = _label(setting)
598
+ if value is None:
599
+ return [_w("error", "REQUIRED",
600
+ "Please make sure that the action is defined/configured properly.",
601
+ action, f" - {label}")]
602
+ errors: list[dict] = []
603
+ if isinstance(value, list):
604
+ if len(value) == 0:
605
+ return []
606
+ for i, row in enumerate(value):
607
+ sub = _g(row, "subprocess")
608
+ if not sub or len(str(sub).strip()) == 0:
609
+ errors.append(_w("error", "REQUIRED",
610
+ "Please make sure that the action is defined/configured properly.",
611
+ action, f" - {label} (row {i + 1})"))
612
+ return errors
613
+
614
+
615
+ def _conditional_required(action, setting) -> list[dict]:
616
+ """ref §4.8 ConditionalValidator + §5 DecisionalCardValidation."""
617
+ value = _g(setting, "value")
618
+ if not value or (isinstance(value, list) and len(value) == 0):
619
+ return []
620
+ has_errors = False
621
+ if isinstance(value, list):
622
+ for case in value:
623
+ if _decisional_card_invalid(case):
624
+ has_errors = True
625
+ if has_errors:
626
+ return [_w("error", "CASE_INCOMPLETE",
627
+ "Action definition incomplete. Please make sure all cases are configured.",
628
+ action, f" - {_label(setting)}")]
629
+ return []
630
+
631
+
632
+ def _decisional_card_invalid(case: dict) -> bool:
633
+ """ref §5 DecisionalCardValidation — name required, target required, >=1 condition,
634
+ and each condition's operator+operands filled."""
635
+ if not isinstance(case, dict):
636
+ return True
637
+ if not (_g(case, "name") or "").strip():
638
+ return True
639
+ if not _g(case, "target"):
640
+ return True
641
+ conditions = _g(case, "condition", default=[]) or []
642
+ if len(conditions) < 1:
643
+ return True
644
+ return _conditions_incomplete(conditions)
645
+
646
+
647
+ def _conditions_incomplete(conditions: list) -> bool:
648
+ for cond in conditions:
649
+ if not isinstance(cond, dict):
650
+ return True
651
+ val = _g(cond, "value")
652
+ if val is None:
653
+ # row condition: operator + left operand required (right unless unary — unknown
654
+ # offline, so require left+operator which the FE always requires)
655
+ if not _g(cond, "operator"):
656
+ return True
657
+ left = _g(cond, "leftOperator", default={}) or {}
658
+ if _is_empty(_g(left, "value")):
659
+ return True
660
+ elif isinstance(val, list):
661
+ if len(val) == 0:
662
+ return True
663
+ if _conditions_incomplete(val):
664
+ return True
665
+ return False
666
+
667
+
668
+ def _data_store_decisional_required(action, setting) -> list[dict]:
669
+ """ref §4.9 DataStoreDecisionalValidator — Where conditions configured."""
670
+ value = _g(setting, "value")
671
+ if not isinstance(value, list) or len(value) == 0:
672
+ return []
673
+ conditions = _g(value[0], "condition", default=[]) or []
674
+ if len(conditions) == 0:
675
+ return []
676
+ if _conditions_incomplete(conditions):
677
+ return [_w("error", "WHERE_INCOMPLETE",
678
+ "Action definition incomplete. Please make sure the Where conditions "
679
+ "are configured.", action, f" - {_label(setting)}")]
680
+ return []
681
+
682
+
683
+ def _delay_required(action, setting) -> list[dict]:
684
+ """ref §4.10 DelayDefinitionValidator."""
685
+ value = _g(setting, "value")
686
+ errors: list[dict] = []
687
+ label = _label(setting)
688
+ delay_type = None
689
+ for s in _all_settings_recursive(action):
690
+ if _g(s, "id") == DELAY_TYPE_SETTING and _g(s, "value"):
691
+ delay_type = _g(s, "value")
692
+ break
693
+ if not delay_type:
694
+ return []
695
+ dval = _g(value, "value") if isinstance(value, dict) else None
696
+ interval = _g(value, "interval") if isinstance(value, dict) else None
697
+ is_wait_for = str(delay_type).lower().replace("_", " ") in ("wait for", "waitfor")
698
+ if not value or dval is None or dval == "" or (is_wait_for and not interval):
699
+ errors.append(_w("error", "REQUIRED",
700
+ "Please make sure that the action is defined/configured properly.",
701
+ action, f" - {label}"))
702
+ if isinstance(value, dict) and dval not in (None, ""):
703
+ if is_wait_for and _is_valid_number(dval) and float(dval) < 0:
704
+ errors.append(_w("error", "DELAY_INVALID", "Invalid delay value.",
705
+ action, f" - {label}"))
706
+ return errors
707
+
708
+
709
+ def _data_store_mapper_required(action, setting) -> list[dict]:
710
+ """Data Store `Set Values` (data-store-mapper) -- each designer row {id, left, right}
711
+ needs a column (`left`) and a value (`right`). It does NOT reuse the document field, so
712
+ it must not be validated by _document_mapper_required."""
713
+ value = _g(setting, "value")
714
+ if not value or (isinstance(value, list) and len(value) == 0):
715
+ return []
716
+ errors: list[dict] = []
717
+ label = _label(setting)
718
+ idx = 0
719
+ for row in (r for r in value if isinstance(r, dict)):
720
+ if _is_empty(_g(row, "left")) or _is_empty(_g(row, "right")):
721
+ errors.append(_w("error", "REQUIRED",
722
+ "Please make sure that the action is defined/configured properly.",
723
+ action, f" - {label} (row {idx + 1})"))
724
+ idx += 1
725
+ return errors
726
+
727
+
728
+ def _document_mapper_required(action, setting) -> list[dict]:
729
+ """ref §4.11 DocumentMapperValidator."""
730
+ value = _g(setting, "value")
731
+ if not value or (isinstance(value, list) and len(value) == 0):
732
+ return []
733
+ errors: list[dict] = []
734
+ label = _label(setting)
735
+ rows = [r for r in value if isinstance(r, dict)]
736
+ idx = 0
737
+ for row in rows:
738
+ doc = _g(row, "document")
739
+ if (not doc) or (doc and _has_variable(doc)):
740
+ if not doc or len(str(doc).strip()) == 0:
741
+ errors.append(_w("error", "REQUIRED",
742
+ "Please make sure that the action is defined/configured properly.",
743
+ action, f" - {label} (row {idx + 1})"))
744
+ idx += 1
745
+ return errors
746
+
747
+
748
+ def _column_definition_required(action, setting) -> list[dict]:
749
+ """ref §4.12 ColumnDefinitionControlValidator."""
750
+ value = _g(setting, "value")
751
+ rows = _g(value, "rows") if isinstance(value, dict) else None
752
+ label = _label(setting)
753
+ if not rows or (isinstance(rows, list) and len(rows) == 0):
754
+ return [_w("error", "REQUIRED",
755
+ "Please make sure that the action is defined/configured properly.",
756
+ action, " - column definition")]
757
+ errors: list[dict] = []
758
+ for i, row in enumerate(rows):
759
+ cn = _g(row, "columnName")
760
+ at = _g(row, "attribute")
761
+ if not cn or len(str(cn).strip()) == 0 or not at or len(str(at).strip()) == 0:
762
+ errors.append(_w("error", "REQUIRED",
763
+ "Please make sure that the action is defined/configured properly.",
764
+ action, f" - {label} (row {i + 1})"))
765
+ return errors
766
+
767
+
768
+ def _column_definition_datatype(action, setting, var_by_id, dt_by_id) -> list[dict]:
769
+ """ref §4.12 ColumnDefinitionControlValidator.doDataTypeCheck — WARNING layer."""
770
+ value = _g(setting, "value")
771
+ rows = _g(value, "rows") if isinstance(value, dict) else None
772
+ if not rows:
773
+ return []
774
+ probe = dict(setting)
775
+ probe["dataTypeId"] = STRING
776
+ errors: list[dict] = []
777
+ for i, row in enumerate(rows):
778
+ attr = (_g(row, "attribute") or "").strip()
779
+ if not validate_data_type(get_value_data_types(attr, var_by_id, dt_by_id), probe):
780
+ errors.append(_w("warning", "TYPE_MISMATCH", "Error: data type mismatch.",
781
+ action, f" - {_label(setting)} attribute (row {i + 1})"))
782
+ return errors
783
+
784
+
785
+ def _map_row_required(action, setting) -> list[dict]:
786
+ """ref §4.13 MapParametersValidator / §4.14 MapProcessDataValidator (identical required)."""
787
+ value = _g(setting, "value")
788
+ label = _label(setting)
789
+ if isinstance(value, list) and len(value) == 0:
790
+ return []
791
+ if not value:
792
+ return [_w("error", "REQUIRED",
793
+ "Please make sure that the action is defined/configured properly.",
794
+ action, f" - {label} (row 1)")]
795
+ errors: list[dict] = []
796
+ for i, row in enumerate(value):
797
+ dest = _g(row, "destination")
798
+ src = _g(row, "source")
799
+ if not dest or (src is not None and len(str(src).strip()) == 0):
800
+ errors.append(_w("error", "REQUIRED",
801
+ "Please make sure that the action is defined/configured properly.",
802
+ action, f" - {label} (row {i + 1})"))
803
+ return errors
804
+
805
+
806
+ _REQUIRED_VALIDATORS: dict[str, Callable] = {
807
+ "ai_decisional": _ai_decisional_required,
808
+ "tabs_payload": _tabs_payload_required,
809
+ "tabs_payload_old": _tabs_payload_old_required,
810
+ "process_io": _process_io_required,
811
+ "conditional": _conditional_required,
812
+ "data_store_decisional": _data_store_decisional_required,
813
+ "delay": _delay_required,
814
+ "document_mapper": _document_mapper_required,
815
+ "data_store_mapper": _data_store_mapper_required,
816
+ "column_definition": _column_definition_required,
817
+ "map_parameters": _map_row_required,
818
+ "map_process_data": _map_row_required,
819
+ }
820
+
821
+
822
+ def _validator_key(setting_type: str | None) -> str:
823
+ return _SETTING_TYPE_TO_VALIDATOR.get(setting_type or "", "default")
824
+
825
+
826
+ # =============================================================================
827
+ # Graph-level checks (ref §2)
828
+ # =============================================================================
829
+
830
+ def _check_field_validation(actions, var_by_id, dt_by_id, include_types) -> list[dict]:
831
+ """ref §2 checkFieldValidation — dispatch every setting to its validator."""
832
+ errors: list[dict] = []
833
+ for a in actions:
834
+ for setting in _settings_of(a):
835
+ value = _g(setting, "value")
836
+ stype = _g(setting, "type")
837
+ sid = _g(setting, "id")
838
+ # ref: array-valued settings are expanded to their elements, EXCEPT decisional /
839
+ # AI-decisional cases (validated as a whole setting).
840
+ if isinstance(value, list) and sid != DECISIONAL_CASE_SETTING and \
841
+ stype != AI_DECISIONAL_CASE:
842
+ items = [v for v in value if isinstance(v, dict)]
843
+ else:
844
+ items = [setting]
845
+ for val in items:
846
+ key = _validator_key(_g(val, "type"))
847
+ if key in _REQUIRED_VALIDATORS:
848
+ errors.extend(_REQUIRED_VALIDATORS[key](a, val))
849
+ else: # default
850
+ errors.extend(_default_required(a, val))
851
+ errors.extend(_default_value_check(a, val))
852
+ if include_types:
853
+ errors.extend(_default_datatype_check(a, val, var_by_id, dt_by_id))
854
+ # placeholders check runs for every setting (base class)
855
+ errors.extend(_placeholders_check(a, val))
856
+ # type layer for the two ported composite type-checks
857
+ if include_types and key == "column_definition":
858
+ errors.extend(_column_definition_datatype(a, val, var_by_id, dt_by_id))
859
+ return errors
860
+
861
+
862
+ def _check_unconnected(actions, lines_by_node, children_by_parent) -> list[dict]:
863
+ """ref §2 noUnconnectedNodes."""
864
+ UNCONNECTED = "Please make sure that all actions are connected."
865
+ MISSING_END = ("Process definition incomplete. Please make sure the action is "
866
+ "connected to Stop action.")
867
+ MISSING_END_ERR = ("Process definition incomplete. Please make sure the action has any "
868
+ "connection besides Error path connection.")
869
+ kind_by_id = {_action_id(a): _kind(a) for a in actions}
870
+ errors: list[dict] = []
871
+ for a in actions:
872
+ nid = _action_id(a)
873
+ kind = kind_by_id.get(nid)
874
+ original = lines_by_node.get(nid, [])
875
+ # filterErrorPortLines: keep incoming lines always; drop outgoing error-path lines
876
+ filtered = [ln for ln in original if ln["dest"] == nid or not ln["error_path"]]
877
+ if kind in ("start", "stop"):
878
+ if not filtered:
879
+ errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
880
+ continue
881
+ if not filtered:
882
+ errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
883
+ elif len(filtered) == 1:
884
+ ln = filtered[0]
885
+ other_is_stop = kind_by_id.get(ln["source"]) == "stop" or \
886
+ kind_by_id.get(ln["dest"]) == "stop"
887
+ if other_is_stop:
888
+ errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
889
+ elif not _g(a, "ParentId", "parentId"):
890
+ text = MISSING_END_ERR if len(filtered) != len(original) else MISSING_END
891
+ errors.append(_w("error", "MISSING_STOP", text, a))
892
+ elif kind in ("decisional", "ai_decisional") and len(filtered) >= 2:
893
+ has_entry = any(ln["dest"] == nid for ln in filtered)
894
+ if not has_entry:
895
+ errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
896
+ elif kind == "foreach":
897
+ child_ids = children_by_parent.get(nid, [])
898
+ inner_start = next((c for c in child_ids
899
+ if kind_by_id.get(c) == "start"), None)
900
+ if inner_start and not lines_by_node.get(inner_start):
901
+ errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
902
+ return errors
903
+
904
+
905
+ def _check_node_names(actions) -> list[dict]:
906
+ """ref §2 checkNodeNameValidation."""
907
+ errors: list[dict] = []
908
+ for a in actions:
909
+ if _action_name(a) == "":
910
+ errors.append(_w("error", "NODE_NAME",
911
+ "Please make sure that all actions have a name.", a, " - node name"))
912
+ return errors
913
+
914
+
915
+ def _check_points(actions) -> list[dict]:
916
+ """ref §2 checkPointsValidation — exactly 1 Start, >=1 Stop."""
917
+ errors: list[dict] = []
918
+ starts = [a for a in actions if _kind(a) == "start"]
919
+ stops = [a for a in actions if _kind(a) == "stop"]
920
+ if len(starts) != 1:
921
+ errors.append(_w("error", "START_COUNT",
922
+ "Please make sure that the process have 1 start action",
923
+ badges=["Start"]))
924
+ if len(stops) < 1:
925
+ errors.append(_w("error", "STOP_COUNT",
926
+ "Please make sure that the process have at least 1 stop action",
927
+ badges=["Stop"]))
928
+ return errors
929
+
930
+
931
+ def _check_limits(actions) -> list[dict]:
932
+ """ref §2 checkLimits — numeric settings with limits within [min, max]."""
933
+ errors: list[dict] = []
934
+ for a in actions:
935
+ for setting in _settings_of(a):
936
+ value = _g(setting, "value")
937
+ candidates = value if isinstance(value, list) else [setting]
938
+ for s in (candidates if isinstance(value, list) else [setting]):
939
+ lim = _g(s, "limits") if isinstance(s, dict) else None
940
+ sval = _g(s, "value") if isinstance(s, dict) else None
941
+ if not lim:
942
+ continue
943
+ lo, hi = _g(lim, "min"), _g(lim, "max")
944
+ if sval is None or (isinstance(sval, str) and sval.strip() == ""):
945
+ continue # unset value: nothing to range-check (ref: FE Number("")===0 path)
946
+ if not _is_valid_number(sval):
947
+ errors.append(_w("error", "LIMIT_NAN",
948
+ "Please make sure that the value is a number.",
949
+ a, f" - {_label(s)}"))
950
+ errors.append(_w("error", "LIMIT_RANGE",
951
+ f"Please make sure that the value is between {lo} and {hi}.",
952
+ a, f" - {_label(s)}"))
953
+ continue
954
+ n = float(sval)
955
+ if (lo is not None and n < lo) or (hi is not None and n > hi):
956
+ errors.append(_w("error", "LIMIT_RANGE",
957
+ f"Please make sure that the value is between {lo} and {hi}.",
958
+ a, f" - {_label(s)}"))
959
+ return errors
960
+
961
+
962
+ def _check_variables(variables) -> list[dict]:
963
+ """ref §2 checkVariables — unique names + primitive-name collision."""
964
+ errors: list[dict] = []
965
+ dups: set[str] = set()
966
+ for v in variables:
967
+ name = _g(v, "Name", "name")
968
+ vid = _g(v, "Id", "id")
969
+ if name is None:
970
+ continue
971
+ clash = any(_g(o, "Name", "name") == name and _g(o, "Id", "id") != vid
972
+ for o in variables)
973
+ if clash and name not in dups:
974
+ errors.append(_w("error", "VAR_UNIQUE", "Variable name should be unique.",
975
+ badges=[f"Variable: {name}"]))
976
+ dups.add(name)
977
+ dt = _g(v, "DataType", "dataType")
978
+ type_name = _PRIMITIVE_NAME.get(dt, "custom")
979
+ low = str(name).lower()
980
+ if low in _TYPE_NAMES and type_name != "custom" and type_name != low:
981
+ errors.append(_w("error", "VAR_PRIMITIVE_NAME",
982
+ "Cannot name variables as Primitives with a different type.",
983
+ badges=[f"Variable: {name}"]))
984
+ return errors
985
+
986
+
987
+ def _check_subprocess(actions, target_vars_of) -> list[dict]:
988
+ """ref §2 checkSubprocess — subprocess validity + required-input mapping + dead refs.
989
+ target_vars_of(flow_id) -> {var_id: var_dict}; returns {} when unavailable (skips)."""
990
+ if target_vars_of is None:
991
+ return []
992
+ errors: list[dict] = []
993
+ for a in actions:
994
+ if _kind(a) not in ("call_sub", "trigger_sub"):
995
+ continue
996
+ # locate the side-panel + selected subprocess id
997
+ sidepanel = None
998
+ for s in _settings_of(a):
999
+ if _g(s, "type") == "side-pannel":
1000
+ sidepanel = s
1001
+ break
1002
+ target = None
1003
+ for s in _walk_settings_loose(a):
1004
+ if _g(s, "type") == "flow-list":
1005
+ target = _g(s, "value")
1006
+ break
1007
+ if not target:
1008
+ # fall back to a GUID parameter value
1009
+ for p in _g(a, "parameters", "Parameters", default=[]) or []:
1010
+ pv = _g(p, "value", "Value")
1011
+ if isinstance(pv, str) and _VAR_RE.fullmatch(pv or "") and \
1012
+ pv != "00000000-0000-0000-0000-000000000000":
1013
+ target = pv
1014
+ break
1015
+ if not target:
1016
+ continue
1017
+ tvars = target_vars_of(target) or {}
1018
+ # process-inputs / outputs subsections
1019
+ inputs, outputs = [], []
1020
+ for sub in (_g(sidepanel, "value", default=[]) or []) if sidepanel else []:
1021
+ if _g(sub, "type") == "process-inputs":
1022
+ inputs = _g(sub, "value", default=[]) or []
1023
+ elif _g(sub, "type") == "process-outputs":
1024
+ outputs = _g(sub, "value", default=[]) or []
1025
+ mapped_in = {_g(r, "subprocess"): _g(r, "process") for r in inputs}
1026
+ for vid, v in tvars.items():
1027
+ if _g(v, "type", "Type") == 10 and _g(v, "isRequired", "IsRequired"):
1028
+ src = mapped_in.get(vid)
1029
+ if src is None or src == "":
1030
+ errors.append(_w("error", "SUB_REQ_UNMAPPED",
1031
+ f"Mapping of required subprocess variable "
1032
+ f"(<b>{_g(v, 'name', 'Name')}</b>) is missing.", a))
1033
+ input_ids = set(tvars.keys())
1034
+ for r in inputs:
1035
+ sub = _g(r, "subprocess") or ""
1036
+ for m in _VAR_RE.finditer(str(sub)):
1037
+ root = m.group(1).split(".")[0]
1038
+ if root not in input_ids:
1039
+ errors.append(_w("error", "SUB_CHECK_MAPPING", "Check data mapping.", a))
1040
+ output_ids = {vid for vid, v in tvars.items()
1041
+ if _g(v, "type", "Type") == 30}
1042
+ for r in outputs:
1043
+ sub = _g(r, "subprocess") or ""
1044
+ for m in _VAR_RE.finditer(str(sub)):
1045
+ root = m.group(1).split(".")[0]
1046
+ if root in tvars and root not in output_ids:
1047
+ errors.append(_w("error", "SUB_CHECK_MAPPING", "Check data mapping.", a))
1048
+ return errors
1049
+
1050
+
1051
+ # =============================================================================
1052
+ # Public entrypoint
1053
+ # =============================================================================
1054
+
1055
+ def validate_flow(flow: dict, *, datatypes: list[dict] | None = None,
1056
+ target_vars_of: Callable[[str], dict] | None = None,
1057
+ include_types: bool = True) -> list[dict]:
1058
+ """Pure FE (designer-layer) validation over a flow DTO. Returns a list of warning
1059
+ dicts {severity, code, text, badges, action, actionId}. severity 'error' entries
1060
+ block a save; 'warning' entries are the advisory type-mismatch layer.
1061
+
1062
+ - datatypes: DataModel[] catalog (for the type layer; type checks skipped if None).
1063
+ - target_vars_of(flow_id) -> {var_id: var}: resolver for subprocess checks (offline
1064
+ injectable); when None, subprocess mapping checks are skipped.
1065
+ - include_types: master switch for the WARNING type-mismatch layer.
1066
+ """
1067
+ flow = _g(flow, "flow") or flow
1068
+ actions = [a for a in (_g(flow, "Actions", "actions", default=[]) or [])
1069
+ if not _g(a, "IsDisabled", "isDisabled", default=False)]
1070
+ variables = _g(flow, "Variables", "variables", default=[]) or []
1071
+
1072
+ var_by_id = {_g(v, "Id", "id"): v for v in variables}
1073
+ dt_by_id = {_g(d, "id", "Id"): d for d in (datatypes or [])}
1074
+ have_types = include_types and bool(datatypes)
1075
+
1076
+ lines_by_node = _build_lines(actions)
1077
+ children_by_parent: dict[str, list[str]] = {}
1078
+ for a in actions:
1079
+ pid = _g(a, "ParentId", "parentId")
1080
+ if pid:
1081
+ children_by_parent.setdefault(pid, []).append(_action_id(a))
1082
+
1083
+ warnings: list[dict] = []
1084
+ warnings.extend(_check_unconnected(actions, lines_by_node, children_by_parent))
1085
+ warnings.extend(_check_field_validation(actions, var_by_id, dt_by_id, have_types))
1086
+ warnings.extend(_check_node_names(actions))
1087
+ warnings.extend(_check_points(actions))
1088
+ warnings.extend(_check_limits(actions))
1089
+ warnings.extend(_check_subprocess(actions, target_vars_of))
1090
+ warnings.extend(_check_variables(variables))
1091
+ return warnings
1092
+
1093
+
1094
+ def split_severity(warnings: list[dict]) -> tuple[list[dict], list[dict]]:
1095
+ """(errors, warnings) — errors block a save."""
1096
+ errs = [w for w in warnings if w.get("severity") == "error"]
1097
+ warns = [w for w in warnings if w.get("severity") != "error"]
1098
+ return errs, warns