ecoportal-api-graphql 1.3.4 → 1.3.9

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 (513) hide show
  1. checksums.yaml +4 -4
  2. data/.ai-assistance/bridge/CLAUDE.md +338 -0
  3. data/.ai-assistance/bridge/archive/.gitkeep +0 -0
  4. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +29 -0
  5. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +18 -0
  6. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +42 -0
  7. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +115 -0
  8. data/.ai-assistance/bridge/context/gemini-review-prompt.txt +48 -0
  9. data/.ai-assistance/bridge/context/gemini-review-response.md +104 -0
  10. data/.ai-assistance/bridge/context/project.md +42 -0
  11. data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
  12. data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
  13. data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +48 -0
  14. data/.ai-assistance/bridge/queue/.gitkeep +1 -0
  15. data/.ai-assistance/capabilities/CLAUDE.md +27 -0
  16. data/.ai-assistance/capabilities/assumptions-log.md +80 -0
  17. data/.ai-assistance/capabilities/code.md +47 -0
  18. data/.ai-assistance/capabilities/connectors.md +37 -0
  19. data/.ai-assistance/capabilities/cowork.md +55 -0
  20. data/.ai-assistance/code/OVERVIEW.md +155 -0
  21. data/.ai-assistance/code/data_fields.md +242 -0
  22. data/.ai-assistance/code/dependencies.md +151 -0
  23. data/.ai-assistance/code/diff_as_input.md +234 -0
  24. data/.ai-assistance/code/diff_service_deep_dive.md +192 -0
  25. data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +55 -0
  26. data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +181 -0
  27. data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +192 -0
  28. data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +147 -0
  29. data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +277 -0
  30. data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +200 -0
  31. data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +228 -0
  32. data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +197 -0
  33. data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +243 -0
  34. data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +196 -0
  35. data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +132 -0
  36. data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +187 -0
  37. data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +236 -0
  38. data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +183 -0
  39. data/.ai-assistance/code/ecoportal_schema_reference.md +240 -0
  40. data/.ai-assistance/code/graphql_domain_knowledge.md +230 -0
  41. data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +71 -0
  42. data/.ai-assistance/code/refactoring/opportunities.md +251 -0
  43. data/.ai-assistance/code/schema_analysis.md +321 -0
  44. data/.ai-assistance/code/search_filters.md +868 -0
  45. data/.ai-assistance/code/spec_coverage.md +73 -0
  46. data/.ai-assistance/code/workflow-command-guide.md +438 -0
  47. data/.ai-assistance/code/workflow-space.md +353 -0
  48. data/.ai-assistance/conventions/CLAUDE.md +30 -0
  49. data/.ai-assistance/conventions/code-working-tree-protocol.md +199 -0
  50. data/.ai-assistance/conventions/gitignore-rules.md +42 -0
  51. data/.ai-assistance/conventions/permission-guidance.md +120 -0
  52. data/.ai-assistance/integrations/README.md +70 -0
  53. data/.ai-assistance/integrations/gitkraken-mcp.md +107 -0
  54. data/.ai-assistance/integrations/gitlab-mcp.md +123 -0
  55. data/.ai-assistance/integrations/local-git.md +60 -0
  56. data/.ai-assistance/local_paths.example.md +17 -0
  57. data/.ai-assistance/projects/TODO.md +97 -0
  58. data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +168 -0
  59. data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +60 -0
  60. data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +267 -0
  61. data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +53 -0
  62. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +102 -0
  63. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +139 -0
  64. data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +93 -0
  65. data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +244 -0
  66. data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +266 -0
  67. data/.ai-assistance/projects/eco-helpers-compat/TODO.md +86 -0
  68. data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +101 -0
  69. data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +177 -0
  70. data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +161 -0
  71. data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +125 -0
  72. data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +126 -0
  73. data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +256 -0
  74. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +122 -0
  75. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +148 -0
  76. data/.ai-assistance/projects/page-model/DECISIONS.md +245 -0
  77. data/.ai-assistance/projects/page-model/TODO.md +190 -0
  78. data/.ai-assistance/projects/search-filter-builder/INTENT.md +107 -0
  79. data/.ai-assistance/projects/search-filter-builder/TODO.md +131 -0
  80. data/.ai-assistance/projects/template-maintenance/DESIGN.md +134 -0
  81. data/.ai-assistance/projects/workflow-space/TODO.md +213 -0
  82. data/.ai-assistance/reinstall-claude-desktop-windows.md +136 -0
  83. data/.ai-assistance/scripts/CLAUDE.md +150 -0
  84. data/.ai-assistance/scripts/bridge-init.sh +86 -0
  85. data/.ai-assistance/scripts/bridge-status.sh +44 -0
  86. data/.ai-assistance/scripts/capabilities-check.ts +104 -0
  87. data/.ai-assistance/scripts/check-outbox.sh +43 -0
  88. data/.ai-assistance/scripts/dep_graph.rb +91 -0
  89. data/.ai-assistance/scripts/lock-acquire.sh +103 -0
  90. data/.ai-assistance/scripts/lock-multi.sh +124 -0
  91. data/.ai-assistance/scripts/lock-queue.sh +94 -0
  92. data/.ai-assistance/scripts/setup-mcps.test.ts +188 -0
  93. data/.ai-assistance/scripts/setup-mcps.ts +234 -0
  94. data/.ai-assistance/scripts/task-complete.ts +74 -0
  95. data/.ai-assistance/scripts/task-create.ts +75 -0
  96. data/.ai-assistance/scripts/task-read.ts +125 -0
  97. data/.ai-assistance/scripts/token-logger.js +220 -0
  98. data/.ai-assistance/scripts/token-report.ts +158 -0
  99. data/.ai-assistance/scripts/token-session-start.js +66 -0
  100. data/.ai-assistance/skills/ai-instructions/SKILL.md +48 -0
  101. data/.ai-assistance/skills/code-specs/SKILL.md +69 -0
  102. data/.ai-assistance/skills/corporate-policies/SKILL.md +201 -0
  103. data/.ai-assistance/skills/dep-graph/SKILL.md +139 -0
  104. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +417 -0
  105. data/.ai-assistance/skills/gemini-assist/SKILL.md +63 -0
  106. data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +205 -0
  107. data/.ai-assistance/skills/gemini-assist/gemini_ask.py +1 -0
  108. data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +240 -0
  109. data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +25 -0
  110. data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +261 -0
  111. data/.ai-assistance/skills/project-cycle/SKILL.md +177 -0
  112. data/.ai-assistance/skills/refactor/SKILL.md +62 -0
  113. data/.ai-assistance/skills/rubocop/SKILL.md +93 -0
  114. data/.ai-assistance/skills/ruby-scripting/SKILL.md +215 -0
  115. data/.ai-assistance/skills/spec-generation/SKILL.md +72 -0
  116. data/.ai-assistance/standards-version.json +21 -0
  117. data/.ai-assistance/token-budget.json +32 -0
  118. data/.ai-assistance/version.json +39 -0
  119. data/.claude/settings.json +146 -0
  120. data/.env.example +18 -0
  121. data/.gitattributes +15 -0
  122. data/.gitignore +13 -0
  123. data/.rubocop.yml +121 -97
  124. data/CHANGELOG.md +673 -470
  125. data/CLAUDE.md +232 -0
  126. data/Gemfile +30 -6
  127. data/Rakefile +90 -38
  128. data/docs/worklog.md +574 -0
  129. data/ecoportal-api-graphql.gemspec +40 -40
  130. data/lib/ecoportal/api/common/graphql/CLAUDE.md +36 -0
  131. data/lib/ecoportal/api/common/graphql/auth_service.rb +12 -14
  132. data/lib/ecoportal/api/common/graphql/client.rb +11 -3
  133. data/lib/ecoportal/api/common/graphql/http_client.rb +117 -29
  134. data/lib/ecoportal/api/common/graphql/model/CLAUDE.md +28 -0
  135. data/lib/ecoportal/api/common/graphql/model/as_input.rb +8 -5
  136. data/lib/ecoportal/api/common/graphql/model/diffable/classic_diff_service.rb +3 -1
  137. data/lib/ecoportal/api/common/graphql/model/diffable/diff_service.rb +31 -23
  138. data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff_nesting.rb +54 -1
  139. data/lib/ecoportal/api/common/graphql.rb +1 -1
  140. data/lib/ecoportal/api/graphql/CLAUDE.md +37 -0
  141. data/lib/ecoportal/api/graphql/base/CLAUDE.md +50 -0
  142. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -0
  143. data/lib/ecoportal/api/graphql/base/delta_result.rb +16 -0
  144. data/lib/ecoportal/api/graphql/base/force/binding.rb +19 -0
  145. data/lib/ecoportal/api/graphql/base/force/binding_collection.rb +62 -0
  146. data/lib/ecoportal/api/graphql/base/force/collection.rb +47 -0
  147. data/lib/ecoportal/api/graphql/base/force.rb +65 -0
  148. data/lib/ecoportal/api/graphql/base/kickstand/job.rb +17 -0
  149. data/lib/ecoportal/api/graphql/base/kickstand/workflow.rb +18 -0
  150. data/lib/ecoportal/api/graphql/base/kickstand.rb +13 -0
  151. data/lib/ecoportal/api/graphql/base/page/basic.rb +38 -0
  152. data/lib/ecoportal/api/graphql/base/page/data_field/actions_list.rb +9 -0
  153. data/lib/ecoportal/api/graphql/base/page/data_field/ai_summary.rb +18 -0
  154. data/lib/ecoportal/api/graphql/base/page/data_field/checklist.rb +29 -0
  155. data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +112 -0
  156. data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -0
  157. data/lib/ecoportal/api/graphql/base/page/data_field/cross_reference.rb +54 -0
  158. data/lib/ecoportal/api/graphql/base/page/data_field/date_field.rb +23 -0
  159. data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -0
  160. data/lib/ecoportal/api/graphql/base/page/data_field/gauge.rb +19 -0
  161. data/lib/ecoportal/api/graphql/base/page/data_field/geo.rb +24 -0
  162. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -0
  163. data/lib/ecoportal/api/graphql/base/page/data_field/law.rb +8 -0
  164. data/lib/ecoportal/api/graphql/base/page/data_field/mailbox.rb +9 -0
  165. data/lib/ecoportal/api/graphql/base/page/data_field/number.rb +20 -0
  166. data/lib/ecoportal/api/graphql/base/page/data_field/people.rb +35 -0
  167. data/lib/ecoportal/api/graphql/base/page/data_field/plain_text.rb +17 -0
  168. data/lib/ecoportal/api/graphql/base/page/data_field/rich_text.rb +26 -0
  169. data/lib/ecoportal/api/graphql/base/page/data_field/select.rb +41 -0
  170. data/lib/ecoportal/api/graphql/base/page/data_field/signature.rb +9 -0
  171. data/lib/ecoportal/api/graphql/base/page/data_field/smart_fill.rb +17 -0
  172. data/lib/ecoportal/api/graphql/base/page/data_field/table.rb +9 -0
  173. data/lib/ecoportal/api/graphql/base/page/data_field/tag_field.rb +21 -0
  174. data/lib/ecoportal/api/graphql/base/page/data_field.rb +137 -0
  175. data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +68 -0
  176. data/lib/ecoportal/api/graphql/base/page/phased.rb +15 -0
  177. data/lib/ecoportal/api/graphql/base/page/section.rb +48 -0
  178. data/lib/ecoportal/api/graphql/base/page/section_collection.rb +79 -0
  179. data/lib/ecoportal/api/graphql/base/page.rb +7 -1
  180. data/lib/ecoportal/api/graphql/base/pages_workflow/action_type_selection.rb +17 -0
  181. data/lib/ecoportal/api/graphql/base/pages_workflow/callback_type.rb +28 -0
  182. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change.rb +17 -0
  183. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change_message.rb +15 -0
  184. data/lib/ecoportal/api/graphql/base/pages_workflow/command_es_change.rb +17 -0
  185. data/lib/ecoportal/api/graphql/base/pages_workflow/command_interface.rb +36 -0
  186. data/lib/ecoportal/api/graphql/base/pages_workflow/email_config.rb +18 -0
  187. data/lib/ecoportal/api/graphql/base/pages_workflow/escalation_level.rb +19 -0
  188. data/lib/ecoportal/api/graphql/base/pages_workflow/in_system_config.rb +16 -0
  189. data/lib/ecoportal/api/graphql/base/pages_workflow/mailbox_field_selection.rb +17 -0
  190. data/lib/ecoportal/api/graphql/base/pages_workflow/operation_interface.rb +39 -0
  191. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/assign_to.rb +27 -0
  192. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/create_page.rb +21 -0
  193. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/send_notification.rb +30 -0
  194. data/lib/ecoportal/api/graphql/base/pages_workflow/people_field_selection.rb +17 -0
  195. data/lib/ecoportal/api/graphql/base/pages_workflow/recipient_config.rb +34 -0
  196. data/lib/ecoportal/api/graphql/base/pages_workflow/register_field.rb +17 -0
  197. data/lib/ecoportal/api/graphql/base/pages_workflow/task_config_selection.rb +17 -0
  198. data/lib/ecoportal/api/graphql/base/pages_workflow/time_delay_config.rb +16 -0
  199. data/lib/ecoportal/api/graphql/base/pages_workflow/trigger_interface.rb +26 -0
  200. data/lib/ecoportal/api/graphql/base/pages_workflow/triggers/conditional_logic.rb +17 -0
  201. data/lib/ecoportal/api/graphql/base/pages_workflow/user_selection.rb +16 -0
  202. data/lib/ecoportal/api/graphql/base/pages_workflow.rb +35 -0
  203. data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -0
  204. data/lib/ecoportal/api/graphql/base/preview_page.rb +23 -0
  205. data/lib/ecoportal/api/graphql/base/register.rb +18 -0
  206. data/lib/ecoportal/api/graphql/base.rb +26 -15
  207. data/lib/ecoportal/api/graphql/builder/CLAUDE.md +65 -0
  208. data/lib/ecoportal/api/graphql/builder/kickstand.rb +73 -0
  209. data/lib/ecoportal/api/graphql/builder/page.rb +210 -0
  210. data/lib/ecoportal/api/graphql/builder/register/preset_view.rb +84 -0
  211. data/lib/ecoportal/api/graphql/builder/register.rb +49 -0
  212. data/lib/ecoportal/api/graphql/builder/template.rb +80 -0
  213. data/lib/ecoportal/api/graphql/builder.rb +7 -3
  214. data/lib/ecoportal/api/graphql/compat/filter_translator.rb +107 -0
  215. data/lib/ecoportal/api/graphql/compat/page_reference.rb +23 -0
  216. data/lib/ecoportal/api/graphql/compat/pages.rb +212 -0
  217. data/lib/ecoportal/api/graphql/compat/registers.rb +84 -0
  218. data/lib/ecoportal/api/graphql/compat/response.rb +35 -0
  219. data/lib/ecoportal/api/graphql/compat/search_results.rb +33 -0
  220. data/lib/ecoportal/api/graphql/compat/stage_collection.rb +70 -0
  221. data/lib/ecoportal/api/graphql/compat/stage_view.rb +76 -0
  222. data/lib/ecoportal/api/graphql/compat.rb +17 -0
  223. data/lib/ecoportal/api/graphql/concerns/data_field_access.rb +71 -0
  224. data/lib/ecoportal/api/graphql/concerns/deprecation.rb +20 -0
  225. data/lib/ecoportal/api/graphql/concerns/fragment_definitions.rb +68 -0
  226. data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -0
  227. data/lib/ecoportal/api/graphql/concerns/snake_camel_access.rb +60 -0
  228. data/lib/ecoportal/api/graphql/concerns.rb +14 -0
  229. data/lib/ecoportal/api/graphql/connection/page.rb +11 -0
  230. data/lib/ecoportal/api/graphql/connection/pages_workflow_command.rb +13 -0
  231. data/lib/ecoportal/api/graphql/connection/preset_view.rb +11 -0
  232. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -0
  233. data/lib/ecoportal/api/graphql/connection.rb +8 -4
  234. data/lib/ecoportal/api/graphql/error.rb +3 -3
  235. data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -0
  236. data/lib/ecoportal/api/graphql/file_upload.rb +10 -0
  237. data/lib/ecoportal/api/graphql/fragment/action.rb +1 -1
  238. data/lib/ecoportal/api/graphql/fragment/action_category.rb +1 -1
  239. data/lib/ecoportal/api/graphql/fragment/contractor_entity.rb +1 -1
  240. data/lib/ecoportal/api/graphql/fragment/force.rb +30 -0
  241. data/lib/ecoportal/api/graphql/fragment/location_draft.rb +2 -2
  242. data/lib/ecoportal/api/graphql/fragment/location_node.rb +1 -1
  243. data/lib/ecoportal/api/graphql/fragment/locations_error.rb +1 -1
  244. data/lib/ecoportal/api/graphql/fragment/page.rb +85 -0
  245. data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +395 -0
  246. data/lib/ecoportal/api/graphql/fragment/pages.rb +15 -0
  247. data/lib/ecoportal/api/graphql/fragment/pages_workflow.rb +172 -0
  248. data/lib/ecoportal/api/graphql/fragment/pagination.rb +1 -1
  249. data/lib/ecoportal/api/graphql/fragment.rb +37 -71
  250. data/lib/ecoportal/api/graphql/helpers/locations_tree.rb +1 -0
  251. data/lib/ecoportal/api/graphql/helpers.rb +1 -1
  252. data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +25 -0
  253. data/lib/ecoportal/api/graphql/input/delta_input.rb +16 -0
  254. data/lib/ecoportal/api/graphql/input/page/archive.rb +14 -0
  255. data/lib/ecoportal/api/graphql/input/page/build_from_template.rb +13 -0
  256. data/lib/ecoportal/api/graphql/input/page/create_draft.rb +13 -0
  257. data/lib/ecoportal/api/graphql/input/page/create_from_template.rb +18 -0
  258. data/lib/ecoportal/api/graphql/input/page/delete_draft.rb +13 -0
  259. data/lib/ecoportal/api/graphql/input/page/publish_draft.rb +13 -0
  260. data/lib/ecoportal/api/graphql/input/page/review_task.rb +14 -0
  261. data/lib/ecoportal/api/graphql/input/page/unarchive.rb +14 -0
  262. data/lib/ecoportal/api/graphql/input/page/update.rb +140 -0
  263. data/lib/ecoportal/api/graphql/input/page.rb +26 -0
  264. data/lib/ecoportal/api/graphql/input/preset_view/create.rb +18 -0
  265. data/lib/ecoportal/api/graphql/input/preset_view/permission.rb +16 -0
  266. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -0
  267. data/lib/ecoportal/api/graphql/input/preset_view.rb +14 -0
  268. data/lib/ecoportal/api/graphql/input/register/create.rb +18 -0
  269. data/lib/ecoportal/api/graphql/input/register/update.rb +15 -0
  270. data/lib/ecoportal/api/graphql/input/register.rb +13 -0
  271. data/lib/ecoportal/api/graphql/input/search_conf/ai_generator.rb +234 -0
  272. data/lib/ecoportal/api/graphql/input/search_conf.rb +367 -0
  273. data/lib/ecoportal/api/graphql/input/variable_binding.rb +20 -0
  274. data/lib/ecoportal/api/graphql/input/workflow_command/add_action_tag.rb +18 -0
  275. data/lib/ecoportal/api/graphql/input/workflow_command/add_binding.rb +18 -0
  276. data/lib/ecoportal/api/graphql/input/workflow_command/add_comment_tagging_user_group.rb +18 -0
  277. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_direct_strategy_user.rb +18 -0
  278. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_strategy.rb +18 -0
  279. data/lib/ecoportal/api/graphql/input/workflow_command/add_direct_strategy_user.rb +18 -0
  280. data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +18 -0
  281. data/lib/ecoportal/api/graphql/input/workflow_command/add_force.rb +18 -0
  282. data/lib/ecoportal/api/graphql/input/workflow_command/add_gauge_field_stop.rb +19 -0
  283. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_field_config.rb +23 -0
  284. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_helper.rb +18 -0
  285. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation.rb +18 -0
  286. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_direct_strategy_user.rb +18 -0
  287. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_strategy.rb +18 -0
  288. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_action_type.rb +18 -0
  289. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_filter.rb +18 -0
  290. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_people_field.rb +18 -0
  291. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_task_config.rb +18 -0
  292. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_user.rb +18 -0
  293. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback.rb +23 -0
  294. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback_action.rb +18 -0
  295. data/lib/ecoportal/api/graphql/input/workflow_command/add_section.rb +18 -0
  296. data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +19 -0
  297. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -0
  298. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_section.rb +18 -0
  299. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_tag.rb +18 -0
  300. data/lib/ecoportal/api/graphql/input/workflow_command/add_strategy.rb +18 -0
  301. data/lib/ecoportal/api/graphql/input/workflow_command/add_task.rb +18 -0
  302. data/lib/ecoportal/api/graphql/input/workflow_command/add_task_assignment_user_group.rb +18 -0
  303. data/lib/ecoportal/api/graphql/input/workflow_command/add_workflow_callback.rb +18 -0
  304. data/lib/ecoportal/api/graphql/input/workflow_command/collapse_section.rb +18 -0
  305. data/lib/ecoportal/api/graphql/input/workflow_command/edit_binding.rb +18 -0
  306. data/lib/ecoportal/api/graphql/input/workflow_command/edit_creator_permissions.rb +18 -0
  307. data/lib/ecoportal/api/graphql/input/workflow_command/edit_default_strategy.rb +18 -0
  308. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -0
  309. data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +18 -0
  310. data/lib/ecoportal/api/graphql/input/workflow_command/edit_gauge_field_stop.rb +19 -0
  311. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_field_config.rb +19 -0
  312. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_helper.rb +18 -0
  313. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation.rb +18 -0
  314. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation_strategy.rb +18 -0
  315. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -0
  316. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -0
  317. data/lib/ecoportal/api/graphql/input/workflow_command/edit_reminder.rb +18 -0
  318. data/lib/ecoportal/api/graphql/input/workflow_command/edit_required_sign_offs.rb +18 -0
  319. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_comment_tagging.rb +18 -0
  320. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_task_assignment.rb +18 -0
  321. data/lib/ecoportal/api/graphql/input/workflow_command/edit_scheduled_callback.rb +22 -0
  322. data/lib/ecoportal/api/graphql/input/workflow_command/edit_section_header.rb +18 -0
  323. data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +19 -0
  324. data/lib/ecoportal/api/graphql/input/workflow_command/edit_stage.rb +18 -0
  325. data/lib/ecoportal/api/graphql/input/workflow_command/edit_strategy.rb +18 -0
  326. data/lib/ecoportal/api/graphql/input/workflow_command/edit_task_due.rb +18 -0
  327. data/lib/ecoportal/api/graphql/input/workflow_command/edit_trigger.rb +18 -0
  328. data/lib/ecoportal/api/graphql/input/workflow_command/expand_section.rb +18 -0
  329. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +24 -0
  330. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -0
  331. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -0
  332. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/gauge.rb +20 -0
  333. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -0
  334. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/location_field.rb +24 -0
  335. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -0
  336. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -0
  337. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -0
  338. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -0
  339. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/signature.rb +20 -0
  340. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/table.rb +25 -0
  341. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -0
  342. data/lib/ecoportal/api/graphql/input/workflow_command/move_stage.rb +18 -0
  343. data/lib/ecoportal/api/graphql/input/workflow_command/remove_action_tag.rb +18 -0
  344. data/lib/ecoportal/api/graphql/input/workflow_command/remove_binding.rb +18 -0
  345. data/lib/ecoportal/api/graphql/input/workflow_command/remove_callback.rb +18 -0
  346. data/lib/ecoportal/api/graphql/input/workflow_command/remove_comment_tagging_user_group.rb +18 -0
  347. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_direct_strategy_user.rb +18 -0
  348. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_strategy.rb +18 -0
  349. data/lib/ecoportal/api/graphql/input/workflow_command/remove_direct_strategy_user.rb +18 -0
  350. data/lib/ecoportal/api/graphql/input/workflow_command/remove_field.rb +18 -0
  351. data/lib/ecoportal/api/graphql/input/workflow_command/remove_force.rb +18 -0
  352. data/lib/ecoportal/api/graphql/input/workflow_command/remove_gauge_field_stop.rb +17 -0
  353. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_field_config.rb +17 -0
  354. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_helper.rb +18 -0
  355. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation.rb +18 -0
  356. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_direct_strategy_user.rb +18 -0
  357. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_strategy.rb +18 -0
  358. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_action_type.rb +18 -0
  359. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_filter.rb +18 -0
  360. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_people_field.rb +18 -0
  361. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_task_config.rb +18 -0
  362. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_user.rb +18 -0
  363. data/lib/ecoportal/api/graphql/input/workflow_command/remove_scheduled_callback.rb +18 -0
  364. data/lib/ecoportal/api/graphql/input/workflow_command/remove_section.rb +18 -0
  365. data/lib/ecoportal/api/graphql/input/workflow_command/remove_select_field_option.rb +17 -0
  366. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage.rb +18 -0
  367. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_section.rb +18 -0
  368. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_tag.rb +18 -0
  369. data/lib/ecoportal/api/graphql/input/workflow_command/remove_strategy.rb +18 -0
  370. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task.rb +18 -0
  371. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_assignment_user_group.rb +18 -0
  372. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_due.rb +18 -0
  373. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_priority_level.rb +18 -0
  374. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -0
  375. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -0
  376. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -0
  377. data/lib/ecoportal/api/graphql/input.rb +15 -7
  378. data/lib/ecoportal/api/graphql/interface/base_page.rb +100 -58
  379. data/lib/ecoportal/api/graphql/interface/location_structure/nodes.rb +27 -28
  380. data/lib/ecoportal/api/graphql/logic/base_model.rb +2 -0
  381. data/lib/ecoportal/api/graphql/logic/base_query.rb +45 -2
  382. data/lib/ecoportal/api/graphql/logic/input.rb +15 -0
  383. data/lib/ecoportal/api/graphql/logic/mutation.rb +6 -1
  384. data/lib/ecoportal/api/graphql/logic/query_array.rb +1 -0
  385. data/lib/ecoportal/api/graphql/logic/query_connection.rb +2 -0
  386. data/lib/ecoportal/api/graphql/logic.rb +9 -9
  387. data/lib/ecoportal/api/graphql/model/ai_summary_version.rb +10 -0
  388. data/lib/ecoportal/api/graphql/model/organization.rb +65 -52
  389. data/lib/ecoportal/api/graphql/model/page/basic.rb +14 -0
  390. data/lib/ecoportal/api/graphql/model/page/data_field.rb +12 -0
  391. data/lib/ecoportal/api/graphql/model/page/phased/stage.rb +14 -0
  392. data/lib/ecoportal/api/graphql/model/page/phased.rb +82 -0
  393. data/lib/ecoportal/api/graphql/model/page/section.rb +12 -0
  394. data/lib/ecoportal/api/graphql/model/page.rb +6 -7
  395. data/lib/ecoportal/api/graphql/model/page_union.rb +21 -0
  396. data/lib/ecoportal/api/graphql/model/pages_workflow.rb +20 -0
  397. data/lib/ecoportal/api/graphql/model/preset_view.rb +10 -0
  398. data/lib/ecoportal/api/graphql/model/preview_page.rb +10 -0
  399. data/lib/ecoportal/api/graphql/model/register.rb +10 -0
  400. data/lib/ecoportal/api/graphql/model/resource.rb +1 -1
  401. data/lib/ecoportal/api/graphql/model.rb +21 -13
  402. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -0
  403. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -0
  404. data/lib/ecoportal/api/graphql/mutation/ai_summary.rb +13 -0
  405. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_jobs.rb +43 -0
  406. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_workflows.rb +43 -0
  407. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_job.rb +39 -0
  408. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_workflow.rb +39 -0
  409. data/lib/ecoportal/api/graphql/mutation/kickstand/start_job.rb +39 -0
  410. data/lib/ecoportal/api/graphql/mutation/kickstand/start_workflow.rb +39 -0
  411. data/lib/ecoportal/api/graphql/mutation/kickstand/stop_workflow.rb +39 -0
  412. data/lib/ecoportal/api/graphql/mutation/kickstand.rb +18 -0
  413. data/lib/ecoportal/api/graphql/mutation/location_structure/apply_commands.rb +3 -3
  414. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/add_commands.rb +2 -2
  415. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/create.rb +2 -2
  416. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/drop_bad_commands.rb +3 -3
  417. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/publish.rb +3 -3
  418. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -0
  419. data/lib/ecoportal/api/graphql/mutation/page/archive.rb +40 -0
  420. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +40 -0
  421. data/lib/ecoportal/api/graphql/mutation/page/build_from_template.rb +50 -0
  422. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +40 -0
  423. data/lib/ecoportal/api/graphql/mutation/page/create_from_template.rb +43 -0
  424. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +40 -0
  425. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +69 -0
  426. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +51 -0
  427. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +40 -0
  428. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -0
  429. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -0
  430. data/lib/ecoportal/api/graphql/mutation/page/unarchive.rb +40 -0
  431. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -0
  432. data/lib/ecoportal/api/graphql/mutation/page/update.rb +40 -0
  433. data/lib/ecoportal/api/graphql/mutation/page/update_variable_bindings.rb +44 -0
  434. data/lib/ecoportal/api/graphql/mutation/page.rb +28 -0
  435. data/lib/ecoportal/api/graphql/mutation/preset_view/create.rb +35 -0
  436. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +35 -0
  437. data/lib/ecoportal/api/graphql/mutation/preset_view/permission.rb +37 -0
  438. data/lib/ecoportal/api/graphql/mutation/preset_view/update.rb +35 -0
  439. data/lib/ecoportal/api/graphql/mutation/preset_view.rb +15 -0
  440. data/lib/ecoportal/api/graphql/mutation/register/create.rb +35 -0
  441. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -0
  442. data/lib/ecoportal/api/graphql/mutation/register/update.rb +35 -0
  443. data/lib/ecoportal/api/graphql/mutation/register.rb +14 -0
  444. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -0
  445. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -0
  446. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -0
  447. data/lib/ecoportal/api/graphql/mutation/template/create.rb +39 -0
  448. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -0
  449. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +43 -0
  450. data/lib/ecoportal/api/graphql/mutation/template/publish.rb +39 -0
  451. data/lib/ecoportal/api/graphql/mutation/template/unpublish.rb +39 -0
  452. data/lib/ecoportal/api/graphql/mutation/template/update.rb +43 -0
  453. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +43 -0
  454. data/lib/ecoportal/api/graphql/mutation/template.rb +18 -0
  455. data/lib/ecoportal/api/graphql/mutation.rb +11 -3
  456. data/lib/ecoportal/api/graphql/payload/ai_summary_generate.rb +12 -0
  457. data/lib/ecoportal/api/graphql/payload/execute_workflow_commands.rb +36 -0
  458. data/lib/ecoportal/api/graphql/payload/force_commands.rb +31 -0
  459. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +36 -0
  460. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +36 -0
  461. data/lib/ecoportal/api/graphql/payload/kickstand/job.rb +13 -0
  462. data/lib/ecoportal/api/graphql/payload/kickstand/workflow.rb +13 -0
  463. data/lib/ecoportal/api/graphql/payload/kickstand.rb +15 -0
  464. data/lib/ecoportal/api/graphql/payload/location_structure/draft/create.rb +33 -34
  465. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +21 -0
  466. data/lib/ecoportal/api/graphql/payload/page/archive.rb +13 -0
  467. data/lib/ecoportal/api/graphql/payload/page/build_from_template.rb +13 -0
  468. data/lib/ecoportal/api/graphql/payload/page/create_from_template.rb +13 -0
  469. data/lib/ecoportal/api/graphql/payload/page/draft.rb +13 -0
  470. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -0
  471. data/lib/ecoportal/api/graphql/payload/page/unarchive.rb +13 -0
  472. data/lib/ecoportal/api/graphql/payload/page/update.rb +13 -0
  473. data/lib/ecoportal/api/graphql/payload/page/update_variable_bindings.rb +13 -0
  474. data/lib/ecoportal/api/graphql/payload/page.rb +19 -0
  475. data/lib/ecoportal/api/graphql/payload/preset_view.rb +11 -0
  476. data/lib/ecoportal/api/graphql/payload/register.rb +11 -0
  477. data/lib/ecoportal/api/graphql/payload/template/create.rb +13 -0
  478. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +13 -0
  479. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +13 -0
  480. data/lib/ecoportal/api/graphql/payload/template/publish.rb +13 -0
  481. data/lib/ecoportal/api/graphql/payload/template/unpublish.rb +13 -0
  482. data/lib/ecoportal/api/graphql/payload/template/update.rb +13 -0
  483. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +13 -0
  484. data/lib/ecoportal/api/graphql/payload/template.rb +18 -0
  485. data/lib/ecoportal/api/graphql/payload.rb +14 -3
  486. data/lib/ecoportal/api/graphql/query/action.rb +1 -1
  487. data/lib/ecoportal/api/graphql/query/action_categories.rb +2 -1
  488. data/lib/ecoportal/api/graphql/query/actions.rb +2 -2
  489. data/lib/ecoportal/api/graphql/query/contractor_entities.rb +2 -1
  490. data/lib/ecoportal/api/graphql/query/file_upload_signature.rb +76 -0
  491. data/lib/ecoportal/api/graphql/query/location_structure/draft.rb +2 -2
  492. data/lib/ecoportal/api/graphql/query/location_structure.rb +1 -1
  493. data/lib/ecoportal/api/graphql/query/location_structures.rb +1 -1
  494. data/lib/ecoportal/api/graphql/query/page.rb +45 -0
  495. data/lib/ecoportal/api/graphql/query/page_delta.rb +47 -0
  496. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +43 -0
  497. data/lib/ecoportal/api/graphql/query/pages.rb +59 -0
  498. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +59 -0
  499. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -0
  500. data/lib/ecoportal/api/graphql/query/register_preview_pages.rb +83 -0
  501. data/lib/ecoportal/api/graphql/query/templates.rb +53 -0
  502. data/lib/ecoportal/api/graphql/query.rb +18 -7
  503. data/lib/ecoportal/api/graphql.rb +70 -2
  504. data/lib/ecoportal/api/graphql_version.rb +5 -5
  505. data/scripts/auto-worker-scheduler.sh +386 -0
  506. data/tests/contractor_entity_create.rb +19 -19
  507. data/tests/contractor_entity_udpate.rb +20 -20
  508. data/tests/dump_page_model.rb +74 -0
  509. data/tests/loc_structure_get.rb +1 -2
  510. data/tests/loc_structure_update.rb +51 -51
  511. data/tests/loc_structures_get.rb +15 -15
  512. data/tests/local_libs.rb +7 -7
  513. metadata +450 -11
@@ -0,0 +1,234 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class SearchConf
6
+ # AI-assisted filter generation — builds a SearchConf from a natural-language
7
+ # description using Claude (Anthropic API) with structured tool use output.
8
+ #
9
+ # Requires the `anthropic` gem to be available in the caller's environment.
10
+ # No hard dependency is added to ecoportal-api-graphql's gemspec.
11
+ #
12
+ # Usage:
13
+ # require 'anthropic'
14
+ #
15
+ # conf = SearchConf.from_description(
16
+ # "find active pages in the Safety register updated in the last month",
17
+ # api_key: ENV['ANTHROPIC_API_KEY'],
18
+ # register_id: 'REG_SAFETY_123'
19
+ # )
20
+ # # => #<SearchConf filters=[...] sorters=...>
21
+ #
22
+ # # Then use it:
23
+ # org.pages(searchConf: conf.to_h, first: 50)
24
+ #
25
+ # Options:
26
+ # api_key: Anthropic API key (default: ENV['ANTHROPIC_API_KEY'])
27
+ # model: Claude model ID (default: 'claude-sonnet-4-6')
28
+ # register_id: Scope results to this register (adds register_filter)
29
+ # context: Hash of extra context passed to the prompt (e.g. available states)
30
+ # validate: Boolean — if true, raises on ambiguous/empty result (default: false)
31
+ module AIGenerator
32
+ TOOL_NAME = 'build_search_conf'.freeze
33
+ DEFAULT_MODEL = 'claude-sonnet-4-6'.freeze
34
+
35
+ SYSTEM_PROMPT = <<~PROMPT.freeze
36
+ You are a search query translator for the EcoPortal GraphQL API.
37
+ Convert natural-language descriptions into structured searchConf filter specifications.
38
+
39
+ ## Available filter operations (snake_case strings — case-sensitive)
40
+
41
+ | operation | params | notes |
42
+ |---------------------|---------------------------------------------|--------------------------------|
43
+ | exact_filter | key, value | equality match |
44
+ | one_of_filter | key, values (array) | any of N values |
45
+ | none_of_filter | key, values (array) | exclude all values |
46
+ | date_filter | key, gte, lte, time_zone (UTC default) | ISO8601 or mode |
47
+ | date_filter (mode) | key, mode, time_zone | modes: last_month, last_week, current_year, past, future, etc. |
48
+ | register_filter | ids (array) | scope to register(s) |
49
+ | has_any_filter | key | field has any non-empty value |
50
+ | match_filter | key, value | partial text match |
51
+ | and_filter | filters (array of filter objects) | explicit AND group |
52
+ | or_filter | filters (array of filter objects) | OR group |
53
+ | boolean_filter | key, value (true/false) | boolean field |
54
+ | numeric_range_filter| key, gte, lte | numeric bounds |
55
+ | isnt_exact_filter | key, value | negated equality |
56
+
57
+ ## Common page field names (key values)
58
+
59
+ | fieldName | type | common values |
60
+ |-----------------|---------|----------------------------------------|
61
+ | state | string | "active", "inactive", "complete" |
62
+ | external_id | string | unique identifier |
63
+ | name | string | page name (use match_filter for partial)|
64
+ | creator_id | string | PersonMember ID |
65
+ | created_at | date | ISO8601 datetime |
66
+ | updated_at | date | ISO8601 datetime |
67
+ | all_location_ids| string | location node ID (includes ancestors) |
68
+ | location_id | string | exact location node ID |
69
+ | other_tags | string | tag string values |
70
+ | archived | boolean | true/false |
71
+ | draft | boolean | true/false |
72
+
73
+ ## Rules
74
+ - Top-level filters array is implicitly AND — no need to wrap in and_filter unless nesting inside or_filter
75
+ - Use register_filter when a specific register is mentioned or provided in context
76
+ - Use date_filter with mode (e.g. "last_month") for relative dates; ISO8601 for absolute dates
77
+ - Default sort is created_at desc unless the user specifies otherwise
78
+ - If the description is too vague to produce a useful filter, produce an empty filters array and explain in the reasoning field
79
+ - snake_case operation names are MANDATORY — CamelCase returns no results silently
80
+
81
+ Call the build_search_conf tool with your result.
82
+ PROMPT
83
+
84
+ module_function
85
+
86
+ # Build a SearchConf from a natural-language description via Claude API.
87
+ def from_description(text, api_key: nil, model: DEFAULT_MODEL,
88
+ register_id: nil, context: {}, validate: false)
89
+ require_anthropic!
90
+
91
+ client = ::Anthropic::Client.new(api_key: api_key || ENV.fetch('ANTHROPIC_API_KEY'))
92
+
93
+ user_message = build_user_message(text, register_id: register_id, context: context)
94
+ response = client.messages.create(
95
+ model: model,
96
+ max_tokens: 1024,
97
+ system: SYSTEM_PROMPT,
98
+ tools: [SEARCH_CONF_TOOL],
99
+ messages: [{ role: 'user', content: user_message }]
100
+ )
101
+
102
+ result = extract_tool_result(response)
103
+ conf = build_search_conf(result, register_id: register_id)
104
+
105
+ if validate && conf.to_h.empty?
106
+ raise ArgumentError, "AI could not produce filters for: #{text.inspect}. " \
107
+ "Reasoning: #{result['reasoning']}"
108
+ end
109
+
110
+ conf
111
+ end
112
+
113
+ def require_anthropic!
114
+ require 'anthropic'
115
+ rescue LoadError
116
+ raise LoadError,
117
+ "SearchConf.from_description requires the 'anthropic' gem. " \
118
+ "Add `gem 'anthropic'` to your Gemfile and run `bundle install`."
119
+ end
120
+
121
+ def build_user_message(text, register_id:, context:)
122
+ parts = ["Convert this description to a searchConf filter: #{text.inspect}"]
123
+ parts << "Register ID to scope results: #{register_id}" if register_id
124
+ context.each { |k, v| parts << "Context — #{k}: #{v}" }
125
+ parts.join("\n")
126
+ end
127
+
128
+ def extract_tool_result(response)
129
+ tool_use = response.content.find { |b| b.type == 'tool_use' && b.name == TOOL_NAME }
130
+ raise "AI did not call #{TOOL_NAME} — response: #{response.content.map(&:type)}" unless tool_use
131
+
132
+ tool_use.input
133
+ end
134
+
135
+ def build_search_conf(result, register_id:)
136
+ conf = SearchConf.new
137
+ conf = apply_register(conf, result, register_id)
138
+ conf = apply_filters(conf, result['filters'])
139
+ conf = apply_sorters(conf, result['sorters'])
140
+ apply_query(conf, result['query'])
141
+ end
142
+
143
+ def apply_register(conf, result, register_id)
144
+ ids = result['register_ids']&.compact&.reject(&:empty?)
145
+ if ids && !ids.empty?
146
+ conf.filter(SearchConf::Register.new(*ids))
147
+ elsif register_id
148
+ conf.filter(SearchConf::Register.new(register_id))
149
+ else
150
+ conf
151
+ end
152
+ end
153
+
154
+ def apply_filters(conf, filters)
155
+ Array(filters).reduce(conf) do |c, f|
156
+ c.filter(SearchConf::RawFilter.new(f['operation'], **symbolize(f['params'] || {})))
157
+ end
158
+ end
159
+
160
+ def apply_sorters(conf, sorters)
161
+ Array(sorters).reduce(conf) do |c, s|
162
+ c.sort(s['key'].to_sym, s['direction']&.to_sym || :desc)
163
+ end
164
+ end
165
+
166
+ def apply_query(conf, query)
167
+ return conf unless query && !query.strip.empty?
168
+
169
+ conf.query(query)
170
+ end
171
+
172
+ def symbolize(hash)
173
+ hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
174
+ end
175
+
176
+ # JSON Schema for the tool — Claude must call this with the filter spec.
177
+ SEARCH_CONF_TOOL = {
178
+ name: TOOL_NAME,
179
+ description: 'Output a structured searchConf filter specification',
180
+ input_schema: {
181
+ type: 'object',
182
+ required: %w[filters],
183
+ properties: {
184
+ reasoning: {
185
+ type: 'string',
186
+ description: 'Brief explanation of the chosen filters'
187
+ },
188
+ register_ids: {
189
+ type: 'array',
190
+ description: 'Register IDs to scope the search (leave empty for org-wide)',
191
+ items: { type: 'string' }
192
+ },
193
+ filters: {
194
+ type: 'array',
195
+ description: 'Array of filter objects (top-level is implicit AND)',
196
+ items: {
197
+ type: 'object',
198
+ required: %w[operation params],
199
+ properties: {
200
+ operation: {
201
+ type: 'string',
202
+ description: 'Filter operation name (snake_case)'
203
+ },
204
+ params: {
205
+ type: 'object',
206
+ description: 'Operation parameters (key, value, ids, filters, gte, lte, etc.)'
207
+ }
208
+ }
209
+ }
210
+ },
211
+ sorters: {
212
+ type: 'array',
213
+ items: {
214
+ type: 'object',
215
+ required: %w[key direction],
216
+ properties: {
217
+ key: { type: 'string' },
218
+ direction: { type: 'string', enum: %w[asc desc] }
219
+ }
220
+ }
221
+ },
222
+ query: {
223
+ type: 'string',
224
+ description: 'Full-text search string (optional)'
225
+ }
226
+ }
227
+ }
228
+ }.freeze
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,367 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ # Builder for the searchConf hash argument used by pages, contractorEntities,
6
+ # personMembers, and other paginated queries.
7
+ #
8
+ # Usage:
9
+ # conf = SearchConf.new
10
+ # .filter(SearchConf::Exact.new(:external_id, 'ABC'))
11
+ # .sort(:created_at, :desc)
12
+ #
13
+ # # OR filter (find by one of several externalIds)
14
+ # conf = SearchConf.new.filter(
15
+ # SearchConf::Or.new(
16
+ # SearchConf::Exact.new(:external_id, '1111'),
17
+ # SearchConf::Exact.new(:external_id, '2222')
18
+ # )
19
+ # )
20
+ #
21
+ # # Parametrize — reuse the same structure with a different value
22
+ # base = SearchConf.new.filter(SearchConf::Exact.new(:external_id, 'X'))
23
+ # copy = base.with(external_id: 'Y')
24
+ #
25
+ # # Pass to any query:
26
+ # org.pages(searchConf: conf.to_h, first: 10)
27
+ #
28
+ # Filter operations (snake_case — CamelCase silently returns no results):
29
+ # SearchConf::Exact — exact_filter
30
+ # SearchConf::DateRange — date_filter
31
+ # SearchConf::Register — register_filter (org search only)
32
+ # SearchConf::And — and_filter (explicit AND group)
33
+ # SearchConf::Or — or_filter
34
+ class SearchConf
35
+ # ---------------------------------------------------------------------------
36
+ # AI-assisted filter generation (optional — requires 'anthropic' gem)
37
+ # ---------------------------------------------------------------------------
38
+ class << self
39
+ # Build a SearchConf from a natural-language description using Claude.
40
+ # See SearchConf::AIGenerator for full documentation and options.
41
+ #
42
+ # Usage:
43
+ # require 'anthropic'
44
+ # conf = SearchConf.from_description(
45
+ # "active pages in Safety register updated this month",
46
+ # register_id: 'REG_123'
47
+ # )
48
+ def from_description(text, **opts)
49
+ require_relative 'search_conf/ai_generator'
50
+ AIGenerator.from_description(text, **opts)
51
+ end
52
+ end
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Composable — adds & and | operators to filter classes so they can be
56
+ # combined using natural Ruby operator syntax:
57
+ # filter1 & filter2 → SearchConf::And.new(filter1, filter2)
58
+ # filter1 | filter2 → SearchConf::Or.new(filter1, filter2)
59
+ # ---------------------------------------------------------------------------
60
+ module Composable
61
+ def &(other)
62
+ SearchConf::And.new(self, other)
63
+ end
64
+
65
+ def |(other)
66
+ SearchConf::Or.new(self, other)
67
+ end
68
+ end
69
+
70
+ # ---------------------------------------------------------------------------
71
+ # Filter: exact_filter
72
+ # ---------------------------------------------------------------------------
73
+ class Exact
74
+ include Composable
75
+
76
+ OPERATION = 'exact_filter'.freeze
77
+
78
+ def initialize(key, value)
79
+ @key = key.to_s
80
+ @value = value
81
+ end
82
+
83
+ def to_h
84
+ { operation: OPERATION, params: { key: @key, value: @value } }
85
+ end
86
+
87
+ def substitute(subs)
88
+ key_sym = @key.to_sym
89
+ return self unless subs.key?(key_sym)
90
+
91
+ self.class.new(@key, subs[key_sym])
92
+ end
93
+ end
94
+
95
+ # ---------------------------------------------------------------------------
96
+ # Filter: date_filter
97
+ # ---------------------------------------------------------------------------
98
+ class DateRange
99
+ include Composable
100
+
101
+ OPERATION = 'date_filter'.freeze
102
+
103
+ def initialize(key, gte: nil, lte: nil, time_zone: 'UTC')
104
+ @key = key.to_s
105
+ @gte = gte
106
+ @lte = lte
107
+ @time_zone = time_zone
108
+ end
109
+
110
+ def to_h
111
+ params = { key: @key, time_zone: @time_zone }
112
+ params[:gte] = @gte if @gte
113
+ params[:lte] = @lte if @lte
114
+ { operation: OPERATION, params: params }
115
+ end
116
+
117
+ def substitute(subs)
118
+ self.class.new(
119
+ @key,
120
+ gte: subs.fetch(:gte, @gte),
121
+ lte: subs.fetch(:lte, @lte),
122
+ time_zone: subs.fetch(:time_zone, @time_zone)
123
+ )
124
+ end
125
+ end
126
+
127
+ # ---------------------------------------------------------------------------
128
+ # Filter: register_filter (org search only — scope to specific registers)
129
+ # ---------------------------------------------------------------------------
130
+ class Register
131
+ include Composable
132
+
133
+ OPERATION = 'register_filter'.freeze
134
+
135
+ def initialize(*ids)
136
+ @ids = ids.flatten
137
+ end
138
+
139
+ def to_h
140
+ { operation: OPERATION, params: { ids: @ids } }
141
+ end
142
+
143
+ def substitute(subs)
144
+ self.class.new(*Array(subs.fetch(:register_ids, @ids)))
145
+ end
146
+ end
147
+
148
+ # ---------------------------------------------------------------------------
149
+ # Filter: and_filter (explicit AND group; top-level filters array is implicit AND)
150
+ # ---------------------------------------------------------------------------
151
+ class And
152
+ include Composable
153
+
154
+ OPERATION = 'and_filter'.freeze
155
+
156
+ def initialize(*filters)
157
+ @filters = filters.flatten
158
+ end
159
+
160
+ def to_h
161
+ { operation: OPERATION, params: { filters: @filters.map(&:to_h) } }
162
+ end
163
+
164
+ def substitute(subs)
165
+ self.class.new(*@filters.map { |f| f.respond_to?(:substitute) ? f.substitute(subs) : f })
166
+ end
167
+ end
168
+
169
+ # ---------------------------------------------------------------------------
170
+ # Filter: or_filter
171
+ # ---------------------------------------------------------------------------
172
+ class Or
173
+ include Composable
174
+
175
+ OPERATION = 'or_filter'.freeze
176
+
177
+ def initialize(*filters)
178
+ @filters = filters.flatten
179
+ end
180
+
181
+ def to_h
182
+ { operation: OPERATION, params: { filters: @filters.map(&:to_h) } }
183
+ end
184
+
185
+ def substitute(subs)
186
+ self.class.new(*@filters.map { |f| f.respond_to?(:substitute) ? f.substitute(subs) : f })
187
+ end
188
+ end
189
+
190
+ # ---------------------------------------------------------------------------
191
+ # RawFilter — escape hatch for operations not yet wrapped as SearchConf classes.
192
+ # Supports & and | composition just like the typed filter classes.
193
+ # Usage:
194
+ # SearchConf.new.filter(SearchConf::RawFilter.new('has_any_filter', key: 'external_id'))
195
+ # ---------------------------------------------------------------------------
196
+ class RawFilter
197
+ include Composable
198
+
199
+ def initialize(operation, **params)
200
+ @operation = operation
201
+ @params = params
202
+ end
203
+
204
+ def to_h
205
+ { 'operation' => @operation, 'params' => @params.transform_keys(&:to_s) }
206
+ end
207
+
208
+ def substitute(_subs)
209
+ self
210
+ end
211
+ end
212
+
213
+ # ---------------------------------------------------------------------------
214
+ # Fluent DSL class methods — factory for filter expressions.
215
+ #
216
+ # SearchConf[:field] → FieldRef (then call .eq, .since, .matches, etc.)
217
+ # SearchConf.in_register(id) → Register filter
218
+ # SearchConf.raw(op, **params) → RawFilter
219
+ #
220
+ # Examples:
221
+ # SearchConf[:external_id].eq('ABC')
222
+ # SearchConf[:state].is(:active)
223
+ # SearchConf[:updated_at].since('2025-01-01T00:00:00Z')
224
+ # SearchConf[:state].is(:active) & SearchConf.in_register('REG')
225
+ # SearchConf[:state].is(:active) | SearchConf[:state].is(:complete)
226
+ # ---------------------------------------------------------------------------
227
+ class << self
228
+ def [](key)
229
+ FieldRef.new(key.to_s)
230
+ end
231
+
232
+ def in_register(*ids)
233
+ Register.new(*ids.flatten)
234
+ end
235
+
236
+ def raw(operation, **params)
237
+ RawFilter.new(operation, **params)
238
+ end
239
+ end
240
+
241
+ # FieldRef — intermediate object produced by SearchConf[:field].
242
+ # Calling an operation method returns a concrete Composable filter object.
243
+ class FieldRef
244
+ def initialize(key)
245
+ @key = key
246
+ end
247
+
248
+ def eq(value)
249
+ Exact.new(@key, value)
250
+ end
251
+
252
+ def is(value)
253
+ eq(value.to_s)
254
+ end
255
+
256
+ def one_of(*values)
257
+ Or.new(*values.flatten.map { |v| eq(v.to_s) })
258
+ end
259
+
260
+ def not_eq(value)
261
+ RawFilter.new('isnt_exact_filter', key: @key, value: value)
262
+ end
263
+
264
+ def matches(text)
265
+ RawFilter.new('match_filter', key: @key, value: text)
266
+ end
267
+
268
+ def since(date, time_zone: 'UTC')
269
+ DateRange.new(@key, gte: date, time_zone: time_zone)
270
+ end
271
+
272
+ def before(date, time_zone: 'UTC')
273
+ DateRange.new(@key, lte: date, time_zone: time_zone)
274
+ end
275
+
276
+ def between(from, til, time_zone: 'UTC')
277
+ DateRange.new(@key, gte: from, lte: til, time_zone: time_zone)
278
+ end
279
+
280
+ # Human date mode — Search[:updated_at].in_period(:last_month)
281
+ # See search_filters.md for full list of mode values.
282
+ def in_period(mode, time_zone: 'Pacific/Auckland', **extra)
283
+ RawFilter.new('date_filter', key: @key, mode: mode.to_s, time_zone: time_zone, **extra)
284
+ end
285
+
286
+ def present
287
+ RawFilter.new('has_any_filter', key: @key)
288
+ end
289
+
290
+ alias_method :exists, :present
291
+
292
+ def blank
293
+ RawFilter.new('doesnt_exist_filter', field: @key)
294
+ end
295
+
296
+ def true?
297
+ RawFilter.new('boolean_filter', key: @key, value: true)
298
+ end
299
+
300
+ def false?
301
+ RawFilter.new('boolean_filter', key: @key, value: false)
302
+ end
303
+
304
+ def between_nums(min, max)
305
+ RawFilter.new('numeric_range_filter', key: @key, gte: min, lte: max)
306
+ end
307
+ end
308
+
309
+ # ---------------------------------------------------------------------------
310
+ # SearchConf builder
311
+ # ---------------------------------------------------------------------------
312
+ def initialize
313
+ @filters = []
314
+ @sorters = []
315
+ @search_query = nil
316
+ end
317
+
318
+ # Add one or more filter objects. Multiple calls accumulate (implicit AND at top level).
319
+ def filter(*filter_objs)
320
+ @filters.concat(filter_objs.flatten)
321
+ self
322
+ end
323
+
324
+ # Add a sorter. direction: :asc or :desc.
325
+ # Multiple sorters are supported (applied in order).
326
+ def sort(key, direction = :desc)
327
+ @sorters << { key: key.to_s, direction: direction.to_s }
328
+ self
329
+ end
330
+
331
+ # Set a full-text search string.
332
+ def query(text)
333
+ @search_query = text
334
+ self
335
+ end
336
+
337
+ # Returns a new SearchConf with filter values substituted.
338
+ # Substitution keys match the filter's field key (e.g. external_id:, gte:, lte:).
339
+ # Filters that don't recognise a key are returned unchanged.
340
+ def with(**substitutions)
341
+ clone.tap do |copy|
342
+ copy.instance_variable_set(
343
+ :@filters,
344
+ @filters.map { |f| f.respond_to?(:substitute) ? f.substitute(substitutions) : f }
345
+ )
346
+ end
347
+ end
348
+
349
+ # Serialises to the Hash structure expected by searchConf arguments.
350
+ # Sorters: single object when one sorter (API convention), array when multiple.
351
+ def to_h
352
+ h = {}
353
+ h[:filters] = @filters.map(&:to_h) unless @filters.empty?
354
+ h[:sorters] = @sorters.length == 1 ? @sorters.first : @sorters unless @sorters.empty?
355
+ h[:query] = @search_query if @search_query
356
+ h
357
+ end
358
+
359
+ # Allow JSON serialisation directly (via json gem as_json protocol).
360
+ def as_json(_ = nil)
361
+ to_h
362
+ end
363
+ end
364
+ end
365
+ end
366
+ end
367
+ end
@@ -0,0 +1,20 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class VariableBinding
6
+ attr_reader :name, :data_field_id
7
+
8
+ def initialize(name:, data_field_id:)
9
+ @name = name
10
+ @data_field_id = data_field_id
11
+ end
12
+
13
+ def to_h
14
+ { name: name, dataFieldId: data_field_id }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ module AddActionTag
7
+ SCHEMA_VERSION = '20260605'.freeze
8
+ VALID_KEYS = %i[stageId taskId scheduledCallbackId actionId tag].freeze
9
+
10
+ def self.build(**kwargs)
11
+ kwargs.slice(*VALID_KEYS).compact
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ module AddBinding
7
+ SCHEMA_VERSION = '20260605'.freeze
8
+ VALID_KEYS = %i[placeholderId forceId name referenceId type].freeze
9
+
10
+ def self.build(**kwargs)
11
+ kwargs.slice(*VALID_KEYS).compact
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ module AddCommentTaggingUserGroup
7
+ SCHEMA_VERSION = '20260605'.freeze
8
+ VALID_KEYS = %i[stageId userGroupId].freeze
9
+
10
+ def self.build(**kwargs)
11
+ kwargs.slice(*VALID_KEYS).compact
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end