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,891 @@
1
+ #!/usr/bin/env python3
2
+ """Enforce a reproducible, held-out Agent Skill optimization loop.
3
+
4
+ The controller makes no model or network calls. It snapshots packages, confines
5
+ candidate changes, enforces a textual edit budget, verifies evaluation identity,
6
+ promotes only strict paired-validation improvements and records rejected hypotheses.
7
+ Final-test checks do not enforce one-shot custody: finalize permits resubmission
8
+ and overwrites its saved report. An external evidence owner must prevent retries.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import difflib
14
+ import fnmatch
15
+ import hashlib
16
+ import json
17
+ import math
18
+ import os
19
+ import re
20
+ import shutil
21
+ import tempfile
22
+ from dataclasses import dataclass
23
+ from datetime import datetime, timezone
24
+ from pathlib import Path, PurePosixPath
25
+ from typing import Any, Iterable
26
+
27
+ SCHEMA_VERSION = 1
28
+ IGNORED_PARTS = {".git", ".venv", "__pycache__", "node_modules"}
29
+ ALWAYS_FORBIDDEN = ("evals", "evals/*", "evals/**", ".git", ".git/*", ".git/**")
30
+ DIRECTIONS = {"maximize", "minimize"}
31
+ OPS = {"<", "<=", "==", "!=", ">=", ">"}
32
+ CANDIDATE_ID_RE = re.compile(r"^c[0-9]{4,}$")
33
+ IDENTITY_FIELDS = (
34
+ "corpus_fingerprint",
35
+ "rubric_fingerprint",
36
+ "model_fingerprint",
37
+ "harness_fingerprint",
38
+ "pairing_fingerprint",
39
+ )
40
+ OUTCOME_FIELDS = ("repairs", "regressions", "preserved_successes", "unresolved_failures")
41
+
42
+
43
+ class OptimizationError(ValueError):
44
+ """Stable user-correctable experiment failure."""
45
+
46
+ def __init__(self, code: str, message: str, details: dict[str, Any] | None = None):
47
+ super().__init__(message)
48
+ self.code = code
49
+ self.details = details or {}
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class DiffSummary:
54
+ changed_files: int
55
+ added_lines: int
56
+ deleted_lines: int
57
+ total_line_changes: int
58
+ binary_bytes: int
59
+ paths: tuple[str, ...]
60
+
61
+ def as_dict(self) -> dict[str, Any]:
62
+ return {
63
+ "changed_files": self.changed_files,
64
+ "added_lines": self.added_lines,
65
+ "deleted_lines": self.deleted_lines,
66
+ "total_line_changes": self.total_line_changes,
67
+ "binary_bytes": self.binary_bytes,
68
+ "paths": list(self.paths),
69
+ }
70
+
71
+
72
+ def _now() -> str:
73
+ return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
74
+
75
+
76
+ def _finite_number(value: Any) -> bool:
77
+ return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value)
78
+
79
+
80
+ def _json_read(path: Path) -> Any:
81
+ try:
82
+ return json.loads(path.read_text(encoding="utf-8"), parse_constant=lambda value: (_ for _ in ()).throw(ValueError(value)))
83
+ except FileNotFoundError as exc:
84
+ raise OptimizationError("file_not_found", f"file does not exist: {path}") from exc
85
+ except (json.JSONDecodeError, ValueError) as exc:
86
+ raise OptimizationError("invalid_json", f"invalid strict JSON in {path}: {exc}") from exc
87
+
88
+
89
+ def _atomic_json(path: Path, value: Any) -> None:
90
+ path.parent.mkdir(parents=True, exist_ok=True)
91
+ text = json.dumps(value, indent=2, sort_keys=True, ensure_ascii=False, allow_nan=False) + "\n"
92
+ fd, raw = tempfile.mkstemp(prefix=f".{path.name}.", dir=str(path.parent))
93
+ temp = Path(raw)
94
+ try:
95
+ with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as stream:
96
+ stream.write(text)
97
+ stream.flush()
98
+ os.fsync(stream.fileno())
99
+ temp.replace(path)
100
+ finally:
101
+ temp.unlink(missing_ok=True)
102
+
103
+
104
+ def _append_ledger(workspace: Path, event: dict[str, Any]) -> None:
105
+ row = {"recorded_at": _now(), **event}
106
+ path = workspace / "ledger.jsonl"
107
+ with path.open("a", encoding="utf-8", newline="\n") as stream:
108
+ stream.write(json.dumps(row, sort_keys=True, ensure_ascii=False, allow_nan=False) + "\n")
109
+ stream.flush()
110
+ os.fsync(stream.fileno())
111
+
112
+
113
+ def _fingerprint_json(value: Any) -> str:
114
+ payload = json.dumps(
115
+ value, sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False
116
+ ).encode("utf-8")
117
+ return hashlib.sha256(payload).hexdigest()
118
+
119
+
120
+ def _is_ignored(relative: PurePosixPath) -> bool:
121
+ return relative.suffix == ".pyc" or any(part in IGNORED_PARTS for part in relative.parts)
122
+
123
+
124
+ def _files(root: Path) -> dict[str, Path]:
125
+ root = root.expanduser().resolve()
126
+ if not (root / "SKILL.md").is_file():
127
+ raise OptimizationError("invalid_skill_root", f"SKILL.md is missing under {root}")
128
+ result: dict[str, Path] = {}
129
+ for path in root.rglob("*"):
130
+ relative = PurePosixPath(path.relative_to(root).as_posix())
131
+ if _is_ignored(relative):
132
+ continue
133
+ if path.is_symlink():
134
+ raise OptimizationError(
135
+ "symlink_not_allowed",
136
+ f"skill packages under optimization may not contain symlinks: {relative}",
137
+ )
138
+ if not path.is_file():
139
+ continue
140
+ resolved = path.resolve(strict=True)
141
+ try:
142
+ resolved.relative_to(root)
143
+ except ValueError as exc:
144
+ raise OptimizationError("path_escape", f"file resolves outside the skill root: {relative}") from exc
145
+ result[relative.as_posix()] = resolved
146
+ return result
147
+
148
+
149
+ def tree_fingerprint(root: Path) -> str:
150
+ digest = hashlib.sha256()
151
+ for relative, path in sorted(_files(root).items()):
152
+ digest.update(relative.encode("utf-8"))
153
+ digest.update(b"\0")
154
+ digest.update(path.read_bytes())
155
+ digest.update(b"\0")
156
+ return digest.hexdigest()
157
+
158
+
159
+ def _copy_tree(source: Path, destination: Path) -> None:
160
+ if destination.exists():
161
+ raise OptimizationError("destination_exists", f"destination already exists: {destination}")
162
+ files = _files(source)
163
+ destination.mkdir(parents=True)
164
+ try:
165
+ for relative, source_file in sorted(files.items()):
166
+ target = destination.joinpath(*PurePosixPath(relative).parts)
167
+ target.parent.mkdir(parents=True, exist_ok=True)
168
+ shutil.copy2(source_file, target, follow_symlinks=False)
169
+ except Exception:
170
+ shutil.rmtree(destination, ignore_errors=True)
171
+ raise
172
+
173
+
174
+ def _replace_tree(source: Path, destination: Path) -> None:
175
+ destination.parent.mkdir(parents=True, exist_ok=True)
176
+ temp = destination.parent / f".{destination.name}.replacement"
177
+ previous = destination.parent / f".{destination.name}.previous"
178
+ shutil.rmtree(temp, ignore_errors=True)
179
+ shutil.rmtree(previous, ignore_errors=True)
180
+ _copy_tree(source, temp)
181
+ if destination.exists():
182
+ destination.replace(previous)
183
+ try:
184
+ temp.replace(destination)
185
+ except Exception:
186
+ if previous.exists() and not destination.exists():
187
+ previous.replace(destination)
188
+ raise
189
+ else:
190
+ shutil.rmtree(previous, ignore_errors=True)
191
+
192
+
193
+ def _inside(child: Path, parent: Path) -> bool:
194
+ try:
195
+ child.expanduser().resolve().relative_to(parent.expanduser().resolve())
196
+ return True
197
+ except ValueError:
198
+ return False
199
+
200
+
201
+ def _safe_patterns(label: str, value: Any, *, required: bool) -> list[str]:
202
+ if not isinstance(value, list) or (required and not value) or not all(isinstance(item, str) for item in value):
203
+ qualifier = "non-empty " if required else ""
204
+ raise OptimizationError("invalid_objective", f"{label} must be a {qualifier}string list")
205
+ patterns: list[str] = []
206
+ for raw in value:
207
+ pattern = raw.replace("\\", "/").strip()
208
+ posix = PurePosixPath(pattern)
209
+ if not pattern or posix.is_absolute() or ".." in posix.parts:
210
+ raise OptimizationError("invalid_objective", f"unsafe {label} pattern: {raw}")
211
+ patterns.append(pattern)
212
+ return patterns
213
+
214
+
215
+ def _validate_objective(raw: Any) -> dict[str, Any]:
216
+ if not isinstance(raw, dict):
217
+ raise OptimizationError("invalid_objective", "objective must be a JSON object")
218
+ objective = dict(raw)
219
+ if objective.get("schema_version") != SCHEMA_VERSION:
220
+ raise OptimizationError("invalid_objective", f"schema_version must be {SCHEMA_VERSION}")
221
+ if not isinstance(objective.get("primary_metric"), str) or not objective["primary_metric"].strip():
222
+ raise OptimizationError("invalid_objective", "primary_metric must be a non-empty string")
223
+ if objective.get("direction") not in DIRECTIONS:
224
+ raise OptimizationError("invalid_objective", "direction must be maximize or minimize")
225
+ if not _finite_number(objective.get("min_delta")) or objective["min_delta"] < 0:
226
+ raise OptimizationError("invalid_objective", "min_delta must be a finite non-negative number")
227
+
228
+ minimum_pairs = objective.get("minimum_valid_pairs", 1)
229
+ plateau_limit = objective.get("plateau_limit", 3)
230
+ for key, value in (("minimum_valid_pairs", minimum_pairs), ("plateau_limit", plateau_limit)):
231
+ if not isinstance(value, int) or isinstance(value, bool) or value < 1:
232
+ raise OptimizationError("invalid_objective", f"{key} must be an integer >= 1")
233
+
234
+ budget = objective.get("edit_budget")
235
+ budget_keys = (
236
+ "max_changed_files",
237
+ "max_added_lines",
238
+ "max_deleted_lines",
239
+ "max_total_line_changes",
240
+ "max_binary_bytes",
241
+ )
242
+ if not isinstance(budget, dict):
243
+ raise OptimizationError("invalid_objective", "edit_budget must be an object")
244
+ for key in budget_keys:
245
+ value = budget.get(key)
246
+ if not isinstance(value, int) or isinstance(value, bool) or value < 0:
247
+ raise OptimizationError("invalid_objective", f"edit_budget.{key} must be an integer >= 0")
248
+
249
+ constraints = objective.get("hard_constraints", [])
250
+ if not isinstance(constraints, list):
251
+ raise OptimizationError("invalid_objective", "hard_constraints must be a list")
252
+ for index, rule in enumerate(constraints):
253
+ if not isinstance(rule, dict) or not isinstance(rule.get("metric"), str) or rule.get("op") not in OPS:
254
+ raise OptimizationError("invalid_objective", f"hard_constraints[{index}] is invalid")
255
+ if not _finite_number(rule.get("value")):
256
+ raise OptimizationError("invalid_objective", f"hard_constraints[{index}].value must be finite")
257
+
258
+ secondary = objective.get("secondary_metrics", [])
259
+ if not isinstance(secondary, list):
260
+ raise OptimizationError("invalid_objective", "secondary_metrics must be a list")
261
+ for index, rule in enumerate(secondary):
262
+ if not isinstance(rule, dict) or not isinstance(rule.get("metric"), str):
263
+ raise OptimizationError("invalid_objective", f"secondary_metrics[{index}] is invalid")
264
+ if rule.get("direction") not in DIRECTIONS:
265
+ raise OptimizationError("invalid_objective", f"secondary_metrics[{index}].direction is invalid")
266
+ relative = rule.get("max_relative_regression", 0)
267
+ absolute = rule.get("max_absolute_regression", 0)
268
+ if not _finite_number(relative) or relative < 0:
269
+ raise OptimizationError(
270
+ "invalid_objective", f"secondary_metrics[{index}].max_relative_regression is invalid"
271
+ )
272
+ if not _finite_number(absolute) or absolute < 0:
273
+ raise OptimizationError(
274
+ "invalid_objective", f"secondary_metrics[{index}].max_absolute_regression is invalid"
275
+ )
276
+
277
+ objective["minimum_valid_pairs"] = minimum_pairs
278
+ objective["plateau_limit"] = plateau_limit
279
+ objective["allowed_paths"] = _safe_patterns("allowed_paths", objective.get("allowed_paths"), required=True)
280
+ objective["forbidden_paths"] = _safe_patterns(
281
+ "forbidden_paths", objective.get("forbidden_paths", []), required=False
282
+ )
283
+ objective["hard_constraints"] = constraints
284
+ objective["secondary_metrics"] = secondary
285
+ return objective
286
+
287
+
288
+ def _report_identity(report: dict[str, Any]) -> dict[str, str]:
289
+ identity: dict[str, str] = {}
290
+ for field in IDENTITY_FIELDS:
291
+ value = report.get(field)
292
+ if not isinstance(value, str) or not value.strip():
293
+ raise OptimizationError("invalid_report", f"report.{field} must be a non-empty string")
294
+ identity[field] = value
295
+ return identity
296
+
297
+
298
+ def _validate_metrics(raw: Any, objective: dict[str, Any], label: str) -> dict[str, float]:
299
+ if not isinstance(raw, dict):
300
+ raise OptimizationError("invalid_report", f"{label} must be an object")
301
+ required = {objective["primary_metric"]}
302
+ required.update(rule["metric"] for rule in objective["hard_constraints"])
303
+ required.update(rule["metric"] for rule in objective["secondary_metrics"])
304
+ metrics: dict[str, float] = {}
305
+ for key, value in raw.items():
306
+ if not isinstance(key, str) or not _finite_number(value):
307
+ raise OptimizationError("invalid_report", f"{label}.{key} must be a finite number")
308
+ metrics[key] = float(value)
309
+ missing = sorted(required - metrics.keys())
310
+ if missing:
311
+ raise OptimizationError("missing_metric", f"{label} is missing metrics: {', '.join(missing)}")
312
+ return metrics
313
+
314
+
315
+ def _validate_common_report(raw: Any, *, split: str, objective: dict[str, Any]) -> tuple[dict[str, Any], dict[str, str]]:
316
+ if not isinstance(raw, dict):
317
+ raise OptimizationError("invalid_report", "evaluation report must be a JSON object")
318
+ report = dict(raw)
319
+ if report.get("schema_version") != SCHEMA_VERSION:
320
+ raise OptimizationError("invalid_report", f"report schema_version must be {SCHEMA_VERSION}")
321
+ if report.get("split") != split:
322
+ raise OptimizationError("wrong_report_split", f"expected {split!r}, got {report.get('split')!r}")
323
+ valid_pairs = report.get("valid_pairs")
324
+ if not isinstance(valid_pairs, int) or isinstance(valid_pairs, bool) or valid_pairs < objective["minimum_valid_pairs"]:
325
+ raise OptimizationError(
326
+ "insufficient_pairs",
327
+ f"report.valid_pairs must be an integer >= {objective['minimum_valid_pairs']}",
328
+ )
329
+ failures = report.get("infrastructure_failures", 0)
330
+ if not isinstance(failures, int) or isinstance(failures, bool) or failures < 0:
331
+ raise OptimizationError("invalid_report", "infrastructure_failures must be an integer >= 0")
332
+ report["infrastructure_failures"] = failures
333
+ return report, _report_identity(report)
334
+
335
+
336
+ def _validate_baseline_report(
337
+ raw: Any,
338
+ *,
339
+ split: str,
340
+ skill_fingerprint: str,
341
+ objective: dict[str, Any],
342
+ ) -> dict[str, Any]:
343
+ report, _identity = _validate_common_report(raw, split=split, objective=objective)
344
+ if report.get("report_type") != "baseline":
345
+ raise OptimizationError("invalid_report", "baseline report_type must be 'baseline'")
346
+ if report.get("skill_fingerprint") != skill_fingerprint:
347
+ raise OptimizationError(
348
+ "skill_fingerprint_mismatch",
349
+ "baseline report does not belong to the expected package",
350
+ {"expected": skill_fingerprint, "actual": report.get("skill_fingerprint")},
351
+ )
352
+ report["metrics"] = _validate_metrics(report.get("metrics"), objective, "metrics")
353
+ if report["infrastructure_failures"]:
354
+ raise OptimizationError("unstable_baseline", "baseline report contains infrastructure failures")
355
+ return report
356
+
357
+
358
+ def _validate_paired_outcomes(raw: Any, valid_pairs: int) -> dict[str, int]:
359
+ if not isinstance(raw, dict):
360
+ raise OptimizationError("invalid_report", "paired_outcomes must be an object")
361
+ outcomes: dict[str, int] = {}
362
+ for field in OUTCOME_FIELDS:
363
+ value = raw.get(field)
364
+ if not isinstance(value, int) or isinstance(value, bool) or value < 0:
365
+ raise OptimizationError("invalid_report", f"paired_outcomes.{field} must be an integer >= 0")
366
+ outcomes[field] = value
367
+ if sum(outcomes.values()) != valid_pairs:
368
+ raise OptimizationError(
369
+ "invalid_report",
370
+ "paired outcome counts must sum exactly to valid_pairs",
371
+ {"outcomes": outcomes, "valid_pairs": valid_pairs},
372
+ )
373
+ return outcomes
374
+
375
+
376
+ def _validate_pair_report(
377
+ raw: Any,
378
+ *,
379
+ split: str,
380
+ candidate_fingerprint: str,
381
+ parent_fingerprint: str,
382
+ objective: dict[str, Any],
383
+ expected_identity: dict[str, str] | None = None,
384
+ ) -> dict[str, Any]:
385
+ report, identity = _validate_common_report(raw, split=split, objective=objective)
386
+ if report.get("report_type") != "paired":
387
+ raise OptimizationError("invalid_report", "candidate report_type must be 'paired'")
388
+ expected_fingerprints = {
389
+ "candidate_fingerprint": candidate_fingerprint,
390
+ "parent_fingerprint": parent_fingerprint,
391
+ }
392
+ for field, expected in expected_fingerprints.items():
393
+ if report.get(field) != expected:
394
+ raise OptimizationError(
395
+ f"{field}_mismatch",
396
+ f"report {field} does not match the staged comparison",
397
+ {"expected": expected, "actual": report.get(field)},
398
+ )
399
+ if expected_identity is not None and identity != expected_identity:
400
+ raise OptimizationError(
401
+ "evaluation_identity_mismatch",
402
+ "corpus, rubric, model, harness, or pairing differs from the frozen evaluation identity",
403
+ {"expected": expected_identity, "actual": identity},
404
+ )
405
+ report["candidate_metrics"] = _validate_metrics(
406
+ report.get("candidate_metrics"), objective, "candidate_metrics"
407
+ )
408
+ report["parent_metrics"] = _validate_metrics(
409
+ report.get("parent_metrics"), objective, "parent_metrics"
410
+ )
411
+ report["paired_outcomes"] = _validate_paired_outcomes(
412
+ report.get("paired_outcomes"), report["valid_pairs"]
413
+ )
414
+ return report
415
+
416
+
417
+ def _path_matches(path: str, patterns: Iterable[str]) -> bool:
418
+ return any(
419
+ fnmatch.fnmatchcase(path, pattern)
420
+ or PurePosixPath(path).match(pattern)
421
+ for pattern in patterns
422
+ )
423
+
424
+
425
+ def _text_lines(path: Path | None) -> list[str] | None:
426
+ if path is None:
427
+ return []
428
+ try:
429
+ return path.read_text(encoding="utf-8").splitlines()
430
+ except UnicodeDecodeError:
431
+ return None
432
+
433
+
434
+ def diff_summary(parent: Path, candidate: Path) -> DiffSummary:
435
+ before = _files(parent)
436
+ after = _files(candidate)
437
+ changed: list[str] = []
438
+ added = deleted = binary_bytes = 0
439
+ for relative in sorted(set(before) | set(after)):
440
+ old = before.get(relative)
441
+ new = after.get(relative)
442
+ if old is not None and new is not None and old.read_bytes() == new.read_bytes():
443
+ continue
444
+ changed.append(relative)
445
+ old_lines = _text_lines(old)
446
+ new_lines = _text_lines(new)
447
+ if old_lines is None or new_lines is None:
448
+ binary_bytes += new.stat().st_size if new is not None else 0
449
+ continue
450
+ matcher = difflib.SequenceMatcher(a=old_lines, b=new_lines, autojunk=False)
451
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
452
+ if tag in {"replace", "delete"}:
453
+ deleted += i2 - i1
454
+ if tag in {"replace", "insert"}:
455
+ added += j2 - j1
456
+ return DiffSummary(
457
+ changed_files=len(changed),
458
+ added_lines=added,
459
+ deleted_lines=deleted,
460
+ total_line_changes=added + deleted,
461
+ binary_bytes=binary_bytes,
462
+ paths=tuple(changed),
463
+ )
464
+
465
+
466
+ def _enforce_diff(summary: DiffSummary, objective: dict[str, Any]) -> None:
467
+ forbidden = tuple(ALWAYS_FORBIDDEN) + tuple(objective["forbidden_paths"])
468
+ for path in summary.paths:
469
+ if _path_matches(path, forbidden):
470
+ raise OptimizationError("forbidden_path_changed", f"candidate changes protected path: {path}")
471
+ if not _path_matches(path, objective["allowed_paths"]):
472
+ raise OptimizationError("path_not_allowed", f"candidate changes path outside allowed_paths: {path}")
473
+ observed = summary.as_dict()
474
+ budget = objective["edit_budget"]
475
+ keys = {
476
+ "changed_files": "max_changed_files",
477
+ "added_lines": "max_added_lines",
478
+ "deleted_lines": "max_deleted_lines",
479
+ "total_line_changes": "max_total_line_changes",
480
+ "binary_bytes": "max_binary_bytes",
481
+ }
482
+ exceeded = {
483
+ metric: {"observed": observed[metric], "limit": budget[limit]}
484
+ for metric, limit in keys.items()
485
+ if observed[metric] > budget[limit]
486
+ }
487
+ if exceeded:
488
+ raise OptimizationError("edit_budget_exceeded", "candidate exceeds the edit budget", exceeded)
489
+
490
+
491
+ def _compare(actual: float, op: str, target: float) -> bool:
492
+ if op == "<":
493
+ return actual < target
494
+ if op == "<=":
495
+ return actual <= target
496
+ if op == "==":
497
+ return actual == target
498
+ if op == "!=":
499
+ return actual != target
500
+ if op == ">=":
501
+ return actual >= target
502
+ return actual > target
503
+
504
+
505
+ def _eligibility(report: dict[str, Any], objective: dict[str, Any]) -> tuple[bool, list[str], dict[str, Any]]:
506
+ candidate = report["candidate_metrics"]
507
+ parent = report["parent_metrics"]
508
+ reasons: list[str] = []
509
+ checks: dict[str, Any] = {"hard_constraints": [], "secondary_metrics": []}
510
+
511
+ for rule in objective["hard_constraints"]:
512
+ actual = candidate[rule["metric"]]
513
+ target = float(rule["value"])
514
+ passed = _compare(actual, rule["op"], target)
515
+ checks["hard_constraints"].append({**rule, "actual": actual, "passed": passed})
516
+ if not passed:
517
+ reasons.append(
518
+ f"hard constraint failed: {rule['metric']} {rule['op']} {target} (actual {actual})"
519
+ )
520
+
521
+ metric = objective["primary_metric"]
522
+ current, previous = candidate[metric], parent[metric]
523
+ improvement = current - previous if objective["direction"] == "maximize" else previous - current
524
+ primary_passed = improvement >= float(objective["min_delta"])
525
+ checks["primary"] = {
526
+ "metric": metric,
527
+ "direction": objective["direction"],
528
+ "candidate": current,
529
+ "parent": previous,
530
+ "improvement": improvement,
531
+ "minimum": float(objective["min_delta"]),
532
+ "passed": primary_passed,
533
+ }
534
+ if not primary_passed:
535
+ reasons.append(f"primary improvement {improvement} is below required {objective['min_delta']}")
536
+
537
+ for rule in objective["secondary_metrics"]:
538
+ metric = rule["metric"]
539
+ current, previous = candidate[metric], parent[metric]
540
+ relative = float(rule.get("max_relative_regression", 0))
541
+ absolute = float(rule.get("max_absolute_regression", 0))
542
+ if rule["direction"] == "minimize":
543
+ boundary = previous * (1 + relative) + absolute
544
+ passed = current <= boundary
545
+ else:
546
+ boundary = previous * (1 - relative) - absolute
547
+ passed = current >= boundary
548
+ checks["secondary_metrics"].append(
549
+ {**rule, "candidate": current, "parent": previous, "permitted_boundary": boundary, "passed": passed}
550
+ )
551
+ if not passed:
552
+ reasons.append(f"secondary metric regressed beyond tolerance: {metric}")
553
+
554
+ checks["paired_outcomes"] = report["paired_outcomes"]
555
+ checks["infrastructure_failures"] = report["infrastructure_failures"]
556
+ if report["infrastructure_failures"]:
557
+ reasons.append("paired report contains infrastructure failures")
558
+ return not reasons, reasons, checks
559
+
560
+
561
+ def _load_workspace(path: Path) -> tuple[Path, dict[str, Any], dict[str, Any]]:
562
+ workspace = path.expanduser().resolve()
563
+ state = _json_read(workspace / "state.json")
564
+ objective = _validate_objective(_json_read(workspace / "objective.json"))
565
+ if not isinstance(state, dict) or state.get("schema_version") != SCHEMA_VERSION:
566
+ raise OptimizationError("invalid_workspace", f"invalid state file under {workspace}")
567
+ if state.get("objective_fingerprint") != _fingerprint_json(objective):
568
+ raise OptimizationError("objective_drift", "objective.json changed after initialization")
569
+ baseline = workspace / "snapshots" / "baseline"
570
+ best = workspace / "snapshots" / "best"
571
+ if tree_fingerprint(baseline) != state.get("baseline_fingerprint"):
572
+ raise OptimizationError("snapshot_drift", "baseline snapshot changed after initialization")
573
+ if tree_fingerprint(best) != state.get("best_fingerprint"):
574
+ raise OptimizationError("snapshot_drift", "best snapshot changed outside the controller")
575
+ return workspace, state, objective
576
+
577
+
578
+ def command_init(args: argparse.Namespace) -> dict[str, Any]:
579
+ skill_root = args.skill_root.expanduser().resolve()
580
+ workspace = args.workspace.expanduser().resolve()
581
+ if workspace.exists():
582
+ raise OptimizationError("workspace_exists", f"refusing to overwrite workspace: {workspace}")
583
+ if _inside(workspace, skill_root) or _inside(skill_root, workspace):
584
+ raise OptimizationError("overlapping_roots", "workspace and skill root must not contain one another")
585
+ objective = _validate_objective(_json_read(args.objective.expanduser().resolve()))
586
+ baseline_fingerprint = tree_fingerprint(skill_root)
587
+ baseline_report = _validate_baseline_report(
588
+ _json_read(args.baseline_report.expanduser().resolve()),
589
+ split="validation",
590
+ skill_fingerprint=baseline_fingerprint,
591
+ objective=objective,
592
+ )
593
+
594
+ try:
595
+ (workspace / "snapshots").mkdir(parents=True)
596
+ (workspace / "candidates").mkdir()
597
+ (workspace / "reports").mkdir()
598
+ _copy_tree(skill_root, workspace / "snapshots" / "baseline")
599
+ _copy_tree(skill_root, workspace / "snapshots" / "best")
600
+ _atomic_json(workspace / "objective.json", objective)
601
+ _atomic_json(workspace / "reports" / "baseline-validation.json", baseline_report)
602
+ state = {
603
+ "schema_version": SCHEMA_VERSION,
604
+ "status": "optimizing",
605
+ "created_at": _now(),
606
+ "skill_name": skill_root.name,
607
+ "source_root": str(skill_root),
608
+ "objective_fingerprint": _fingerprint_json(objective),
609
+ "validation_identity": _report_identity(baseline_report),
610
+ "baseline_fingerprint": baseline_fingerprint,
611
+ "best_fingerprint": baseline_fingerprint,
612
+ "best_candidate_id": "baseline",
613
+ "next_candidate": 1,
614
+ "accepted_candidates": 0,
615
+ "rejected_candidates": 0,
616
+ "consecutive_rejections": 0,
617
+ }
618
+ _atomic_json(workspace / "state.json", state)
619
+ _append_ledger(
620
+ workspace,
621
+ {
622
+ "event": "initialized",
623
+ "baseline_fingerprint": baseline_fingerprint,
624
+ "objective_fingerprint": state["objective_fingerprint"],
625
+ "validation_identity": state["validation_identity"],
626
+ },
627
+ )
628
+ except Exception:
629
+ shutil.rmtree(workspace, ignore_errors=True)
630
+ raise
631
+ return {
632
+ "ok": True,
633
+ "event": "initialized",
634
+ "workspace": str(workspace),
635
+ "baseline_fingerprint": baseline_fingerprint,
636
+ "objective_fingerprint": _fingerprint_json(objective),
637
+ }
638
+
639
+
640
+ def command_stage(args: argparse.Namespace) -> dict[str, Any]:
641
+ workspace, state, objective = _load_workspace(args.workspace)
642
+ if state.get("status") != "optimizing":
643
+ raise OptimizationError("experiment_not_open", f"experiment status is {state.get('status')!r}")
644
+ hypothesis = " ".join(args.hypothesis.split())
645
+ if not hypothesis:
646
+ raise OptimizationError("missing_hypothesis", "candidate hypothesis must not be empty")
647
+ candidate_root = args.candidate_root.expanduser().resolve()
648
+ if _inside(candidate_root, workspace / "snapshots") or _inside(candidate_root, workspace / "candidates"):
649
+ raise OptimizationError("unsafe_candidate_root", "candidate_root points into controller-owned state")
650
+ summary = diff_summary(workspace / "snapshots" / "best", candidate_root)
651
+ if not summary.changed_files:
652
+ raise OptimizationError("no_candidate_change", "candidate is byte-identical to the current best")
653
+ _enforce_diff(summary, objective)
654
+ candidate_id = f"c{int(state['next_candidate']):04d}"
655
+ destination = workspace / "candidates" / candidate_id
656
+ fingerprint = tree_fingerprint(candidate_root)
657
+ try:
658
+ destination.mkdir()
659
+ _copy_tree(candidate_root, destination / "skill")
660
+ proposal = {
661
+ "schema_version": SCHEMA_VERSION,
662
+ "candidate_id": candidate_id,
663
+ "status": "staged",
664
+ "staged_at": _now(),
665
+ "hypothesis": hypothesis,
666
+ "parent_candidate_id": state["best_candidate_id"],
667
+ "parent_fingerprint": state["best_fingerprint"],
668
+ "candidate_fingerprint": fingerprint,
669
+ "diff": summary.as_dict(),
670
+ }
671
+ _atomic_json(destination / "proposal.json", proposal)
672
+ state["next_candidate"] = int(state["next_candidate"]) + 1
673
+ _atomic_json(workspace / "state.json", state)
674
+ _append_ledger(workspace, {"event": "candidate_staged", **proposal})
675
+ except Exception:
676
+ shutil.rmtree(destination, ignore_errors=True)
677
+ raise
678
+ return {
679
+ "ok": True,
680
+ "event": "candidate_staged",
681
+ "candidate_id": candidate_id,
682
+ "parent_fingerprint": state["best_fingerprint"],
683
+ "candidate_fingerprint": fingerprint,
684
+ "diff": summary.as_dict(),
685
+ }
686
+
687
+
688
+ def _candidate_id(value: str) -> str:
689
+ if not CANDIDATE_ID_RE.fullmatch(value):
690
+ raise OptimizationError("invalid_candidate_id", f"invalid candidate id: {value!r}")
691
+ return value
692
+
693
+
694
+ def command_decide(args: argparse.Namespace) -> dict[str, Any]:
695
+ workspace, state, objective = _load_workspace(args.workspace)
696
+ candidate_id = _candidate_id(args.candidate_id)
697
+ candidate_dir = workspace / "candidates" / candidate_id
698
+ proposal_path = candidate_dir / "proposal.json"
699
+ proposal = _json_read(proposal_path)
700
+ if not isinstance(proposal, dict) or proposal.get("candidate_id") != candidate_id:
701
+ raise OptimizationError("invalid_candidate", f"invalid candidate record: {candidate_id}")
702
+ if proposal.get("status") != "staged":
703
+ raise OptimizationError("candidate_already_decided", f"candidate status is {proposal.get('status')!r}")
704
+ if proposal.get("parent_fingerprint") != state.get("best_fingerprint"):
705
+ raise OptimizationError(
706
+ "stale_candidate", "candidate parent is no longer current best; restage the hypothesis"
707
+ )
708
+ candidate_root = candidate_dir / "skill"
709
+ if tree_fingerprint(candidate_root) != proposal.get("candidate_fingerprint"):
710
+ raise OptimizationError("candidate_drift", "staged candidate changed after staging")
711
+
712
+ report = _validate_pair_report(
713
+ _json_read(args.report.expanduser().resolve()),
714
+ split="validation",
715
+ candidate_fingerprint=proposal["candidate_fingerprint"],
716
+ parent_fingerprint=proposal["parent_fingerprint"],
717
+ objective=objective,
718
+ expected_identity=state["validation_identity"],
719
+ )
720
+ accepted, reasons, checks = _eligibility(report, objective)
721
+ _atomic_json(candidate_dir / "validation.json", report)
722
+ proposal.update(
723
+ {
724
+ "decided_at": _now(),
725
+ "decision_checks": checks,
726
+ "decision_reasons": reasons,
727
+ "status": "accepted" if accepted else "rejected",
728
+ }
729
+ )
730
+
731
+ if accepted:
732
+ _replace_tree(candidate_root, workspace / "snapshots" / "best")
733
+ state["best_fingerprint"] = proposal["candidate_fingerprint"]
734
+ state["best_candidate_id"] = candidate_id
735
+ state["accepted_candidates"] = int(state["accepted_candidates"]) + 1
736
+ state["consecutive_rejections"] = 0
737
+ event = "candidate_accepted"
738
+ else:
739
+ state["rejected_candidates"] = int(state["rejected_candidates"]) + 1
740
+ state["consecutive_rejections"] = int(state["consecutive_rejections"]) + 1
741
+ if state["consecutive_rejections"] >= objective["plateau_limit"]:
742
+ state["status"] = "plateau"
743
+ event = "candidate_rejected"
744
+
745
+ _atomic_json(proposal_path, proposal)
746
+ _atomic_json(workspace / "state.json", state)
747
+ _append_ledger(
748
+ workspace,
749
+ {
750
+ "event": event,
751
+ "candidate_id": candidate_id,
752
+ "hypothesis": proposal["hypothesis"],
753
+ "parent_fingerprint": proposal["parent_fingerprint"],
754
+ "candidate_fingerprint": proposal["candidate_fingerprint"],
755
+ "diff": proposal["diff"],
756
+ "checks": checks,
757
+ "reasons": reasons,
758
+ "best_candidate_id": state["best_candidate_id"],
759
+ "best_fingerprint": state["best_fingerprint"],
760
+ },
761
+ )
762
+ return {
763
+ "ok": True,
764
+ "event": event,
765
+ "accepted": accepted,
766
+ "candidate_id": candidate_id,
767
+ "checks": checks,
768
+ "reasons": reasons,
769
+ "best_candidate_id": state["best_candidate_id"],
770
+ "best_fingerprint": state["best_fingerprint"],
771
+ "experiment_status": state["status"],
772
+ }
773
+
774
+
775
+ def command_status(args: argparse.Namespace) -> dict[str, Any]:
776
+ workspace, state, objective = _load_workspace(args.workspace)
777
+ return {"ok": True, "event": "status", "workspace": str(workspace), "state": state, "objective": objective}
778
+
779
+
780
+ def command_finalize(args: argparse.Namespace) -> dict[str, Any]:
781
+ workspace, state, objective = _load_workspace(args.workspace)
782
+ report = _validate_pair_report(
783
+ _json_read(args.report.expanduser().resolve()),
784
+ split="test",
785
+ candidate_fingerprint=state["best_fingerprint"],
786
+ parent_fingerprint=state["baseline_fingerprint"],
787
+ objective=objective,
788
+ )
789
+ identity = _report_identity(report)
790
+ if identity["corpus_fingerprint"] == state["validation_identity"]["corpus_fingerprint"]:
791
+ raise OptimizationError("test_validation_leakage", "test corpus matches the validation corpus")
792
+ passed, reasons, checks = _eligibility(report, objective)
793
+ result = {
794
+ "schema_version": SCHEMA_VERSION,
795
+ "evaluated_at": _now(),
796
+ "baseline_fingerprint": state["baseline_fingerprint"],
797
+ "candidate_fingerprint": state["best_fingerprint"],
798
+ "best_candidate_id": state["best_candidate_id"],
799
+ "test_identity": identity,
800
+ "passed": passed,
801
+ "checks": checks,
802
+ "reasons": reasons,
803
+ }
804
+ _atomic_json(workspace / "reports" / "final-test.json", {**report, "decision": result})
805
+ _append_ledger(workspace, {"event": "final_test_passed" if passed else "final_test_failed", **result})
806
+ if not passed:
807
+ state["status"] = "test_failed"
808
+ _atomic_json(workspace / "state.json", state)
809
+ return {"ok": True, "event": "final_test_failed", **result}
810
+
811
+ output = args.output.expanduser().resolve()
812
+ if output.exists():
813
+ raise OptimizationError("output_exists", f"refusing to overwrite final output: {output}")
814
+ if _inside(output, workspace):
815
+ raise OptimizationError("unsafe_output", "final output must be outside the optimizer workspace")
816
+ _copy_tree(workspace / "snapshots" / "best", output)
817
+ state.update({"status": "finalized", "finalized_at": _now(), "final_output": str(output)})
818
+ _atomic_json(workspace / "state.json", state)
819
+ return {"ok": True, "event": "finalized", "output": str(output), **result}
820
+
821
+
822
+ def _parser() -> argparse.ArgumentParser:
823
+ parser = argparse.ArgumentParser(description=__doc__)
824
+ sub = parser.add_subparsers(dest="command", required=True)
825
+
826
+ init = sub.add_parser("init", help="freeze the baseline, objective, and validation identity")
827
+ init.add_argument("--skill-root", type=Path, required=True)
828
+ init.add_argument("--workspace", type=Path, required=True)
829
+ init.add_argument("--objective", type=Path, required=True)
830
+ init.add_argument("--baseline-report", type=Path, required=True)
831
+ init.set_defaults(handler=command_init)
832
+
833
+ stage = sub.add_parser("stage", help="stage one bounded candidate against current best")
834
+ stage.add_argument("--workspace", type=Path, required=True)
835
+ stage.add_argument("--candidate-root", type=Path, required=True)
836
+ stage.add_argument("--hypothesis", required=True)
837
+ stage.set_defaults(handler=command_stage)
838
+
839
+ decide = sub.add_parser("decide", help="accept or reject from a paired held-out validation report")
840
+ decide.add_argument("--workspace", type=Path, required=True)
841
+ decide.add_argument("--candidate-id", required=True)
842
+ decide.add_argument("--report", type=Path, required=True)
843
+ decide.set_defaults(handler=command_decide)
844
+
845
+ status = sub.add_parser("status", help="inspect immutable fingerprints and experiment state")
846
+ status.add_argument("--workspace", type=Path, required=True)
847
+ status.set_defaults(handler=command_status)
848
+
849
+ finalize = sub.add_parser("finalize", help="gate and export the best package on untouched paired test data")
850
+ finalize.add_argument("--workspace", type=Path, required=True)
851
+ finalize.add_argument("--report", type=Path, required=True)
852
+ finalize.add_argument("--output", type=Path, required=True)
853
+ finalize.set_defaults(handler=command_finalize)
854
+ return parser
855
+
856
+
857
+ def main(argv: list[str] | None = None) -> int:
858
+ args = _parser().parse_args(argv)
859
+ try:
860
+ result = args.handler(args)
861
+ except OptimizationError as exc:
862
+ print(
863
+ json.dumps(
864
+ {"ok": False, "error": {"code": exc.code, "message": str(exc), "details": exc.details}},
865
+ ensure_ascii=False,
866
+ separators=(",", ":"),
867
+ )
868
+ )
869
+ return 2
870
+ except Exception as exc: # noqa: BLE001 - stable standalone boundary
871
+ print(
872
+ json.dumps(
873
+ {
874
+ "ok": False,
875
+ "error": {
876
+ "code": "internal_error",
877
+ "message": f"{type(exc).__name__}: {exc}",
878
+ "details": {},
879
+ },
880
+ },
881
+ ensure_ascii=False,
882
+ separators=(",", ":"),
883
+ )
884
+ )
885
+ return 1
886
+ print(json.dumps(result, ensure_ascii=False, separators=(",", ":")))
887
+ return 0
888
+
889
+
890
+ if __name__ == "__main__":
891
+ raise SystemExit(main())