ecoportal-api-graphql 1.3.5 → 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 (499) 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 -477
  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/client.rb +1 -1
  132. data/lib/ecoportal/api/common/graphql/http_client.rb +35 -3
  133. data/lib/ecoportal/api/common/graphql/model/CLAUDE.md +28 -0
  134. data/lib/ecoportal/api/common/graphql/model/as_input.rb +8 -5
  135. data/lib/ecoportal/api/common/graphql/model/diffable/classic_diff_service.rb +3 -1
  136. data/lib/ecoportal/api/common/graphql/model/diffable/diff_service.rb +31 -23
  137. data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff_nesting.rb +54 -1
  138. data/lib/ecoportal/api/graphql/CLAUDE.md +37 -0
  139. data/lib/ecoportal/api/graphql/base/CLAUDE.md +50 -0
  140. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -0
  141. data/lib/ecoportal/api/graphql/base/delta_result.rb +16 -0
  142. data/lib/ecoportal/api/graphql/base/force/binding.rb +19 -0
  143. data/lib/ecoportal/api/graphql/base/force/binding_collection.rb +62 -0
  144. data/lib/ecoportal/api/graphql/base/force/collection.rb +47 -0
  145. data/lib/ecoportal/api/graphql/base/force.rb +65 -0
  146. data/lib/ecoportal/api/graphql/base/kickstand/job.rb +17 -0
  147. data/lib/ecoportal/api/graphql/base/kickstand/workflow.rb +18 -0
  148. data/lib/ecoportal/api/graphql/base/kickstand.rb +13 -0
  149. data/lib/ecoportal/api/graphql/base/page/basic.rb +38 -0
  150. data/lib/ecoportal/api/graphql/base/page/data_field/actions_list.rb +9 -0
  151. data/lib/ecoportal/api/graphql/base/page/data_field/ai_summary.rb +18 -0
  152. data/lib/ecoportal/api/graphql/base/page/data_field/checklist.rb +29 -0
  153. data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +112 -0
  154. data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -0
  155. data/lib/ecoportal/api/graphql/base/page/data_field/cross_reference.rb +54 -0
  156. data/lib/ecoportal/api/graphql/base/page/data_field/date_field.rb +23 -0
  157. data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -0
  158. data/lib/ecoportal/api/graphql/base/page/data_field/gauge.rb +19 -0
  159. data/lib/ecoportal/api/graphql/base/page/data_field/geo.rb +24 -0
  160. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -0
  161. data/lib/ecoportal/api/graphql/base/page/data_field/law.rb +8 -0
  162. data/lib/ecoportal/api/graphql/base/page/data_field/mailbox.rb +9 -0
  163. data/lib/ecoportal/api/graphql/base/page/data_field/number.rb +20 -0
  164. data/lib/ecoportal/api/graphql/base/page/data_field/people.rb +35 -0
  165. data/lib/ecoportal/api/graphql/base/page/data_field/plain_text.rb +17 -0
  166. data/lib/ecoportal/api/graphql/base/page/data_field/rich_text.rb +26 -0
  167. data/lib/ecoportal/api/graphql/base/page/data_field/select.rb +41 -0
  168. data/lib/ecoportal/api/graphql/base/page/data_field/signature.rb +9 -0
  169. data/lib/ecoportal/api/graphql/base/page/data_field/smart_fill.rb +17 -0
  170. data/lib/ecoportal/api/graphql/base/page/data_field/table.rb +9 -0
  171. data/lib/ecoportal/api/graphql/base/page/data_field/tag_field.rb +21 -0
  172. data/lib/ecoportal/api/graphql/base/page/data_field.rb +137 -12
  173. data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +68 -14
  174. data/lib/ecoportal/api/graphql/base/page/phased.rb +1 -0
  175. data/lib/ecoportal/api/graphql/base/page/section.rb +36 -0
  176. data/lib/ecoportal/api/graphql/base/page/section_collection.rb +79 -0
  177. data/lib/ecoportal/api/graphql/base/page.rb +2 -0
  178. data/lib/ecoportal/api/graphql/base/pages_workflow/action_type_selection.rb +17 -0
  179. data/lib/ecoportal/api/graphql/base/pages_workflow/callback_type.rb +28 -0
  180. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change.rb +17 -0
  181. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change_message.rb +15 -0
  182. data/lib/ecoportal/api/graphql/base/pages_workflow/command_es_change.rb +17 -0
  183. data/lib/ecoportal/api/graphql/base/pages_workflow/command_interface.rb +36 -0
  184. data/lib/ecoportal/api/graphql/base/pages_workflow/email_config.rb +18 -0
  185. data/lib/ecoportal/api/graphql/base/pages_workflow/escalation_level.rb +19 -0
  186. data/lib/ecoportal/api/graphql/base/pages_workflow/in_system_config.rb +16 -0
  187. data/lib/ecoportal/api/graphql/base/pages_workflow/mailbox_field_selection.rb +17 -0
  188. data/lib/ecoportal/api/graphql/base/pages_workflow/operation_interface.rb +39 -0
  189. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/assign_to.rb +27 -0
  190. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/create_page.rb +21 -0
  191. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/send_notification.rb +30 -0
  192. data/lib/ecoportal/api/graphql/base/pages_workflow/people_field_selection.rb +17 -0
  193. data/lib/ecoportal/api/graphql/base/pages_workflow/recipient_config.rb +34 -0
  194. data/lib/ecoportal/api/graphql/base/pages_workflow/register_field.rb +17 -0
  195. data/lib/ecoportal/api/graphql/base/pages_workflow/task_config_selection.rb +17 -0
  196. data/lib/ecoportal/api/graphql/base/pages_workflow/time_delay_config.rb +16 -0
  197. data/lib/ecoportal/api/graphql/base/pages_workflow/trigger_interface.rb +26 -0
  198. data/lib/ecoportal/api/graphql/base/pages_workflow/triggers/conditional_logic.rb +17 -0
  199. data/lib/ecoportal/api/graphql/base/pages_workflow/user_selection.rb +16 -0
  200. data/lib/ecoportal/api/graphql/base/pages_workflow.rb +35 -0
  201. data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -0
  202. data/lib/ecoportal/api/graphql/base/preview_page.rb +23 -0
  203. data/lib/ecoportal/api/graphql/base/register.rb +18 -0
  204. data/lib/ecoportal/api/graphql/base.rb +11 -0
  205. data/lib/ecoportal/api/graphql/builder/CLAUDE.md +65 -0
  206. data/lib/ecoportal/api/graphql/builder/kickstand.rb +73 -0
  207. data/lib/ecoportal/api/graphql/builder/page.rb +210 -41
  208. data/lib/ecoportal/api/graphql/builder/register/preset_view.rb +84 -0
  209. data/lib/ecoportal/api/graphql/builder/register.rb +27 -19
  210. data/lib/ecoportal/api/graphql/builder/template.rb +80 -0
  211. data/lib/ecoportal/api/graphql/builder.rb +2 -0
  212. data/lib/ecoportal/api/graphql/compat/filter_translator.rb +107 -0
  213. data/lib/ecoportal/api/graphql/compat/page_reference.rb +23 -0
  214. data/lib/ecoportal/api/graphql/compat/pages.rb +212 -0
  215. data/lib/ecoportal/api/graphql/compat/registers.rb +84 -0
  216. data/lib/ecoportal/api/graphql/compat/response.rb +35 -0
  217. data/lib/ecoportal/api/graphql/compat/search_results.rb +33 -0
  218. data/lib/ecoportal/api/graphql/compat/stage_collection.rb +70 -0
  219. data/lib/ecoportal/api/graphql/compat/stage_view.rb +76 -0
  220. data/lib/ecoportal/api/graphql/compat.rb +17 -0
  221. data/lib/ecoportal/api/graphql/concerns/data_field_access.rb +71 -0
  222. data/lib/ecoportal/api/graphql/concerns/deprecation.rb +20 -0
  223. data/lib/ecoportal/api/graphql/concerns/fragment_definitions.rb +21 -28
  224. data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -0
  225. data/lib/ecoportal/api/graphql/concerns/snake_camel_access.rb +60 -0
  226. data/lib/ecoportal/api/graphql/concerns.rb +4 -0
  227. data/lib/ecoportal/api/graphql/connection/page.rb +11 -0
  228. data/lib/ecoportal/api/graphql/connection/pages_workflow_command.rb +13 -0
  229. data/lib/ecoportal/api/graphql/connection/preset_view.rb +11 -0
  230. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -0
  231. data/lib/ecoportal/api/graphql/connection.rb +4 -0
  232. data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -0
  233. data/lib/ecoportal/api/graphql/file_upload.rb +10 -0
  234. data/lib/ecoportal/api/graphql/fragment/action.rb +1 -1
  235. data/lib/ecoportal/api/graphql/fragment/action_category.rb +1 -1
  236. data/lib/ecoportal/api/graphql/fragment/contractor_entity.rb +1 -1
  237. data/lib/ecoportal/api/graphql/fragment/force.rb +30 -0
  238. data/lib/ecoportal/api/graphql/fragment/location_draft.rb +2 -2
  239. data/lib/ecoportal/api/graphql/fragment/location_node.rb +1 -1
  240. data/lib/ecoportal/api/graphql/fragment/locations_error.rb +1 -1
  241. data/lib/ecoportal/api/graphql/fragment/page.rb +85 -0
  242. data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +395 -0
  243. data/lib/ecoportal/api/graphql/fragment/pages.rb +15 -0
  244. data/lib/ecoportal/api/graphql/fragment/pages_workflow.rb +172 -0
  245. data/lib/ecoportal/api/graphql/fragment/pagination.rb +1 -1
  246. data/lib/ecoportal/api/graphql/fragment.rb +37 -27
  247. data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +25 -0
  248. data/lib/ecoportal/api/graphql/input/delta_input.rb +16 -0
  249. data/lib/ecoportal/api/graphql/input/page/archive.rb +14 -0
  250. data/lib/ecoportal/api/graphql/input/page/build_from_template.rb +13 -0
  251. data/lib/ecoportal/api/graphql/input/page/create_draft.rb +13 -0
  252. data/lib/ecoportal/api/graphql/input/page/create_from_template.rb +18 -0
  253. data/lib/ecoportal/api/graphql/input/page/delete_draft.rb +13 -0
  254. data/lib/ecoportal/api/graphql/input/page/publish_draft.rb +13 -0
  255. data/lib/ecoportal/api/graphql/input/page/review_task.rb +14 -0
  256. data/lib/ecoportal/api/graphql/input/page/unarchive.rb +14 -0
  257. data/lib/ecoportal/api/graphql/input/page/update.rb +140 -0
  258. data/lib/ecoportal/api/graphql/input/page.rb +26 -0
  259. data/lib/ecoportal/api/graphql/input/preset_view/create.rb +18 -0
  260. data/lib/ecoportal/api/graphql/input/preset_view/permission.rb +16 -0
  261. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -0
  262. data/lib/ecoportal/api/graphql/input/preset_view.rb +14 -0
  263. data/lib/ecoportal/api/graphql/input/register/create.rb +18 -0
  264. data/lib/ecoportal/api/graphql/input/register/update.rb +15 -0
  265. data/lib/ecoportal/api/graphql/input/register.rb +13 -0
  266. data/lib/ecoportal/api/graphql/input/search_conf/ai_generator.rb +234 -0
  267. data/lib/ecoportal/api/graphql/input/search_conf.rb +367 -0
  268. data/lib/ecoportal/api/graphql/input/variable_binding.rb +20 -0
  269. data/lib/ecoportal/api/graphql/input/workflow_command/add_action_tag.rb +18 -0
  270. data/lib/ecoportal/api/graphql/input/workflow_command/add_binding.rb +18 -0
  271. data/lib/ecoportal/api/graphql/input/workflow_command/add_comment_tagging_user_group.rb +18 -0
  272. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_direct_strategy_user.rb +18 -0
  273. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_strategy.rb +18 -0
  274. data/lib/ecoportal/api/graphql/input/workflow_command/add_direct_strategy_user.rb +18 -0
  275. data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +18 -0
  276. data/lib/ecoportal/api/graphql/input/workflow_command/add_force.rb +18 -0
  277. data/lib/ecoportal/api/graphql/input/workflow_command/add_gauge_field_stop.rb +19 -0
  278. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_field_config.rb +23 -0
  279. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_helper.rb +18 -0
  280. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation.rb +18 -0
  281. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_direct_strategy_user.rb +18 -0
  282. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_strategy.rb +18 -0
  283. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_action_type.rb +18 -0
  284. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_filter.rb +18 -0
  285. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_people_field.rb +18 -0
  286. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_task_config.rb +18 -0
  287. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_user.rb +18 -0
  288. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback.rb +23 -0
  289. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback_action.rb +18 -0
  290. data/lib/ecoportal/api/graphql/input/workflow_command/add_section.rb +18 -0
  291. data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +19 -0
  292. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -0
  293. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_section.rb +18 -0
  294. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_tag.rb +18 -0
  295. data/lib/ecoportal/api/graphql/input/workflow_command/add_strategy.rb +18 -0
  296. data/lib/ecoportal/api/graphql/input/workflow_command/add_task.rb +18 -0
  297. data/lib/ecoportal/api/graphql/input/workflow_command/add_task_assignment_user_group.rb +18 -0
  298. data/lib/ecoportal/api/graphql/input/workflow_command/add_workflow_callback.rb +18 -0
  299. data/lib/ecoportal/api/graphql/input/workflow_command/collapse_section.rb +18 -0
  300. data/lib/ecoportal/api/graphql/input/workflow_command/edit_binding.rb +18 -0
  301. data/lib/ecoportal/api/graphql/input/workflow_command/edit_creator_permissions.rb +18 -0
  302. data/lib/ecoportal/api/graphql/input/workflow_command/edit_default_strategy.rb +18 -0
  303. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -0
  304. data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +18 -0
  305. data/lib/ecoportal/api/graphql/input/workflow_command/edit_gauge_field_stop.rb +19 -0
  306. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_field_config.rb +19 -0
  307. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_helper.rb +18 -0
  308. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation.rb +18 -0
  309. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation_strategy.rb +18 -0
  310. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -0
  311. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -0
  312. data/lib/ecoportal/api/graphql/input/workflow_command/edit_reminder.rb +18 -0
  313. data/lib/ecoportal/api/graphql/input/workflow_command/edit_required_sign_offs.rb +18 -0
  314. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_comment_tagging.rb +18 -0
  315. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_task_assignment.rb +18 -0
  316. data/lib/ecoportal/api/graphql/input/workflow_command/edit_scheduled_callback.rb +22 -0
  317. data/lib/ecoportal/api/graphql/input/workflow_command/edit_section_header.rb +18 -0
  318. data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +19 -0
  319. data/lib/ecoportal/api/graphql/input/workflow_command/edit_stage.rb +18 -0
  320. data/lib/ecoportal/api/graphql/input/workflow_command/edit_strategy.rb +18 -0
  321. data/lib/ecoportal/api/graphql/input/workflow_command/edit_task_due.rb +18 -0
  322. data/lib/ecoportal/api/graphql/input/workflow_command/edit_trigger.rb +18 -0
  323. data/lib/ecoportal/api/graphql/input/workflow_command/expand_section.rb +18 -0
  324. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +24 -0
  325. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -0
  326. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -0
  327. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/gauge.rb +20 -0
  328. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -0
  329. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/location_field.rb +24 -0
  330. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -0
  331. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -0
  332. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -0
  333. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -0
  334. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/signature.rb +20 -0
  335. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/table.rb +25 -0
  336. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -0
  337. data/lib/ecoportal/api/graphql/input/workflow_command/move_stage.rb +18 -0
  338. data/lib/ecoportal/api/graphql/input/workflow_command/remove_action_tag.rb +18 -0
  339. data/lib/ecoportal/api/graphql/input/workflow_command/remove_binding.rb +18 -0
  340. data/lib/ecoportal/api/graphql/input/workflow_command/remove_callback.rb +18 -0
  341. data/lib/ecoportal/api/graphql/input/workflow_command/remove_comment_tagging_user_group.rb +18 -0
  342. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_direct_strategy_user.rb +18 -0
  343. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_strategy.rb +18 -0
  344. data/lib/ecoportal/api/graphql/input/workflow_command/remove_direct_strategy_user.rb +18 -0
  345. data/lib/ecoportal/api/graphql/input/workflow_command/remove_field.rb +18 -0
  346. data/lib/ecoportal/api/graphql/input/workflow_command/remove_force.rb +18 -0
  347. data/lib/ecoportal/api/graphql/input/workflow_command/remove_gauge_field_stop.rb +17 -0
  348. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_field_config.rb +17 -0
  349. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_helper.rb +18 -0
  350. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation.rb +18 -0
  351. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_direct_strategy_user.rb +18 -0
  352. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_strategy.rb +18 -0
  353. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_action_type.rb +18 -0
  354. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_filter.rb +18 -0
  355. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_people_field.rb +18 -0
  356. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_task_config.rb +18 -0
  357. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_user.rb +18 -0
  358. data/lib/ecoportal/api/graphql/input/workflow_command/remove_scheduled_callback.rb +18 -0
  359. data/lib/ecoportal/api/graphql/input/workflow_command/remove_section.rb +18 -0
  360. data/lib/ecoportal/api/graphql/input/workflow_command/remove_select_field_option.rb +17 -0
  361. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage.rb +18 -0
  362. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_section.rb +18 -0
  363. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_tag.rb +18 -0
  364. data/lib/ecoportal/api/graphql/input/workflow_command/remove_strategy.rb +18 -0
  365. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task.rb +18 -0
  366. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_assignment_user_group.rb +18 -0
  367. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_due.rb +18 -0
  368. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_priority_level.rb +18 -0
  369. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -0
  370. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -0
  371. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -0
  372. data/lib/ecoportal/api/graphql/input.rb +8 -0
  373. data/lib/ecoportal/api/graphql/interface/base_page.rb +100 -58
  374. data/lib/ecoportal/api/graphql/interface/location_structure/nodes.rb +27 -28
  375. data/lib/ecoportal/api/graphql/logic/base_model.rb +2 -0
  376. data/lib/ecoportal/api/graphql/logic/base_query.rb +45 -2
  377. data/lib/ecoportal/api/graphql/logic/input.rb +15 -0
  378. data/lib/ecoportal/api/graphql/model/ai_summary_version.rb +10 -0
  379. data/lib/ecoportal/api/graphql/model/organization.rb +65 -55
  380. data/lib/ecoportal/api/graphql/model/page/basic.rb +14 -0
  381. data/lib/ecoportal/api/graphql/model/page/phased.rb +82 -20
  382. data/lib/ecoportal/api/graphql/model/page.rb +1 -0
  383. data/lib/ecoportal/api/graphql/model/page_union.rb +21 -0
  384. data/lib/ecoportal/api/graphql/model/pages_workflow.rb +20 -0
  385. data/lib/ecoportal/api/graphql/model/preset_view.rb +10 -0
  386. data/lib/ecoportal/api/graphql/model/preview_page.rb +10 -0
  387. data/lib/ecoportal/api/graphql/model/register.rb +10 -0
  388. data/lib/ecoportal/api/graphql/model.rb +8 -0
  389. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -0
  390. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -0
  391. data/lib/ecoportal/api/graphql/mutation/ai_summary.rb +13 -0
  392. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_jobs.rb +43 -0
  393. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_workflows.rb +43 -0
  394. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_job.rb +39 -0
  395. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_workflow.rb +39 -0
  396. data/lib/ecoportal/api/graphql/mutation/kickstand/start_job.rb +39 -0
  397. data/lib/ecoportal/api/graphql/mutation/kickstand/start_workflow.rb +39 -0
  398. data/lib/ecoportal/api/graphql/mutation/kickstand/stop_workflow.rb +39 -0
  399. data/lib/ecoportal/api/graphql/mutation/kickstand.rb +18 -0
  400. data/lib/ecoportal/api/graphql/mutation/location_structure/apply_commands.rb +3 -3
  401. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/add_commands.rb +2 -2
  402. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/create.rb +2 -2
  403. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/drop_bad_commands.rb +3 -3
  404. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/publish.rb +3 -3
  405. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -0
  406. data/lib/ecoportal/api/graphql/mutation/page/archive.rb +40 -0
  407. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +40 -0
  408. data/lib/ecoportal/api/graphql/mutation/page/build_from_template.rb +50 -0
  409. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +40 -0
  410. data/lib/ecoportal/api/graphql/mutation/page/create_from_template.rb +43 -0
  411. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +40 -0
  412. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +69 -0
  413. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +51 -0
  414. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +40 -0
  415. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -0
  416. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -0
  417. data/lib/ecoportal/api/graphql/mutation/page/unarchive.rb +40 -0
  418. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -0
  419. data/lib/ecoportal/api/graphql/mutation/page/update.rb +40 -0
  420. data/lib/ecoportal/api/graphql/mutation/page/update_variable_bindings.rb +44 -0
  421. data/lib/ecoportal/api/graphql/mutation/page.rb +28 -0
  422. data/lib/ecoportal/api/graphql/mutation/preset_view/create.rb +35 -0
  423. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +35 -0
  424. data/lib/ecoportal/api/graphql/mutation/preset_view/permission.rb +37 -0
  425. data/lib/ecoportal/api/graphql/mutation/preset_view/update.rb +35 -0
  426. data/lib/ecoportal/api/graphql/mutation/preset_view.rb +15 -0
  427. data/lib/ecoportal/api/graphql/mutation/register/create.rb +35 -0
  428. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -0
  429. data/lib/ecoportal/api/graphql/mutation/register/update.rb +35 -0
  430. data/lib/ecoportal/api/graphql/mutation/register.rb +14 -0
  431. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -0
  432. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -0
  433. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -0
  434. data/lib/ecoportal/api/graphql/mutation/template/create.rb +39 -0
  435. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -0
  436. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +43 -0
  437. data/lib/ecoportal/api/graphql/mutation/template/publish.rb +39 -0
  438. data/lib/ecoportal/api/graphql/mutation/template/unpublish.rb +39 -0
  439. data/lib/ecoportal/api/graphql/mutation/template/update.rb +43 -0
  440. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +43 -0
  441. data/lib/ecoportal/api/graphql/mutation/template.rb +18 -0
  442. data/lib/ecoportal/api/graphql/mutation.rb +8 -0
  443. data/lib/ecoportal/api/graphql/payload/ai_summary_generate.rb +12 -0
  444. data/lib/ecoportal/api/graphql/payload/execute_workflow_commands.rb +36 -0
  445. data/lib/ecoportal/api/graphql/payload/force_commands.rb +31 -0
  446. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +36 -0
  447. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +36 -0
  448. data/lib/ecoportal/api/graphql/payload/kickstand/job.rb +13 -0
  449. data/lib/ecoportal/api/graphql/payload/kickstand/workflow.rb +13 -0
  450. data/lib/ecoportal/api/graphql/payload/kickstand.rb +15 -0
  451. data/lib/ecoportal/api/graphql/payload/location_structure/draft/create.rb +33 -34
  452. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +21 -0
  453. data/lib/ecoportal/api/graphql/payload/page/archive.rb +13 -0
  454. data/lib/ecoportal/api/graphql/payload/page/build_from_template.rb +13 -0
  455. data/lib/ecoportal/api/graphql/payload/page/create_from_template.rb +13 -0
  456. data/lib/ecoportal/api/graphql/payload/page/draft.rb +13 -0
  457. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -0
  458. data/lib/ecoportal/api/graphql/payload/page/unarchive.rb +13 -0
  459. data/lib/ecoportal/api/graphql/payload/page/update.rb +13 -0
  460. data/lib/ecoportal/api/graphql/payload/page/update_variable_bindings.rb +13 -0
  461. data/lib/ecoportal/api/graphql/payload/page.rb +19 -0
  462. data/lib/ecoportal/api/graphql/payload/preset_view.rb +11 -0
  463. data/lib/ecoportal/api/graphql/payload/register.rb +11 -0
  464. data/lib/ecoportal/api/graphql/payload/template/create.rb +13 -0
  465. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +13 -0
  466. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +13 -0
  467. data/lib/ecoportal/api/graphql/payload/template/publish.rb +13 -0
  468. data/lib/ecoportal/api/graphql/payload/template/unpublish.rb +13 -0
  469. data/lib/ecoportal/api/graphql/payload/template/update.rb +13 -0
  470. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +13 -0
  471. data/lib/ecoportal/api/graphql/payload/template.rb +18 -0
  472. data/lib/ecoportal/api/graphql/payload.rb +11 -0
  473. data/lib/ecoportal/api/graphql/query/action.rb +1 -1
  474. data/lib/ecoportal/api/graphql/query/action_categories.rb +1 -1
  475. data/lib/ecoportal/api/graphql/query/actions.rb +2 -2
  476. data/lib/ecoportal/api/graphql/query/contractor_entities.rb +1 -1
  477. data/lib/ecoportal/api/graphql/query/file_upload_signature.rb +76 -0
  478. data/lib/ecoportal/api/graphql/query/location_structure/draft.rb +2 -2
  479. data/lib/ecoportal/api/graphql/query/location_structure.rb +1 -1
  480. data/lib/ecoportal/api/graphql/query/location_structures.rb +1 -1
  481. data/lib/ecoportal/api/graphql/query/page.rb +45 -0
  482. data/lib/ecoportal/api/graphql/query/page_delta.rb +47 -0
  483. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +43 -0
  484. data/lib/ecoportal/api/graphql/query/pages.rb +59 -0
  485. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +59 -0
  486. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -0
  487. data/lib/ecoportal/api/graphql/query/register_preview_pages.rb +83 -0
  488. data/lib/ecoportal/api/graphql/query/templates.rb +53 -0
  489. data/lib/ecoportal/api/graphql/query.rb +11 -0
  490. data/lib/ecoportal/api/graphql.rb +60 -2
  491. data/lib/ecoportal/api/graphql_version.rb +5 -5
  492. data/scripts/auto-worker-scheduler.sh +386 -0
  493. data/tests/contractor_entity_create.rb +19 -19
  494. data/tests/contractor_entity_udpate.rb +20 -20
  495. data/tests/dump_page_model.rb +74 -0
  496. data/tests/loc_structure_get.rb +1 -2
  497. data/tests/loc_structure_update.rb +51 -51
  498. data/tests/loc_structures_get.rb +15 -15
  499. metadata +436 -5
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Gemini Assist — packs files + prompt and queries the Gemini API.
4
+ #
5
+ # Usage:
6
+ # ruby .ai-assistance/skills/gemini-assist/gemini_ask.rb \
7
+ # --prompt "Your question here" \
8
+ # --files lib/foo.rb lib/bar.rb
9
+ #
10
+ # ruby .ai-assistance/skills/gemini-assist/gemini_ask.rb \
11
+ # --prompt-file .ai-assistance/skills/gemini-assist/prompts/review.txt \
12
+ # --files "lib/**/*.rb"
13
+ #
14
+ # Requires: GEMINI_API_KEY environment variable (or set in .env)
15
+ # Optional: GEMINI_MODEL environment variable (default: gemini-1.5-pro)
16
+
17
+ require "json"
18
+ require "net/http"
19
+ require "openssl"
20
+ require "optparse"
21
+ require "rbconfig"
22
+ require "uri"
23
+
24
+ # Returns an SSL certificate store that works cross-platform.
25
+ # Priority: SSL_CERT_FILE env var → RubyInstaller bundled cacert.pem →
26
+ # OpenSSL compiled-in default → system paths.
27
+ # VERIFY_PEER is always on — verification is never disabled.
28
+ def ssl_cert_store
29
+ store = OpenSSL::X509::Store.new
30
+
31
+ candidates = [
32
+ ENV["FALLBACK_SSL_CERT_FILE"],
33
+ ENV["SSL_CERT_FILE"],
34
+ File.join(RbConfig::CONFIG["prefix"], "ssl", "cacert.pem"),
35
+ OpenSSL::X509::DEFAULT_CERT_FILE
36
+ ].compact
37
+
38
+ cert_file = candidates.find { |f| File.file?(f) }
39
+
40
+ if cert_file
41
+ store.add_file(cert_file)
42
+ else
43
+ store.set_default_paths
44
+ end
45
+
46
+ store
47
+ end
48
+
49
+ DEFAULT_MODEL = "gemini-2.5-pro"
50
+ API_BASE = "https://generativelanguage.googleapis.com/v1beta/models"
51
+
52
+ SYSTEM_PREAMBLE = <<~PREAMBLE.strip
53
+ You are an expert Ruby developer reviewing code for the ecoportal-api-graphql gem.
54
+ This gem provides a GraphQL client layer. It uses graphql-ruby, graphlient, and graphql-client upstream,
55
+ and is consumed by eco-helpers downstream. Backwards compatibility matters.
56
+
57
+ When you identify issues, be specific: include file name, approximate line reference, and a concrete suggestion.
58
+ Flag hallucination risk: if you're unsure about something, say so rather than guessing.
59
+ PREAMBLE
60
+
61
+ # Loads key=value pairs from a .env file into ENV (skips already-set keys).
62
+ def load_dotenv(path = ".env")
63
+ return unless File.file?(path)
64
+
65
+ File.foreach(path) do |raw|
66
+ line = raw.strip
67
+ next if line.empty? || line.start_with?("#") || !line.include?("=")
68
+
69
+ key, value = line.split("=", 2)
70
+ key = key.strip
71
+ value = value.to_s.strip
72
+ .delete_prefix('"').delete_suffix('"')
73
+ .delete_prefix("'").delete_suffix("'")
74
+ ENV[key] ||= value if key
75
+ end
76
+ end
77
+
78
+ # Expands glob patterns and returns formatted file blocks.
79
+ def load_files(patterns)
80
+ patterns.flat_map do |pattern|
81
+ paths = Dir.glob(pattern, File::FNM_DOTMATCH).select { |p| File.file?(p) }.sort
82
+ paths = [pattern] if paths.empty?
83
+
84
+ paths.filter_map do |path|
85
+ unless File.file?(path)
86
+ warn "[warn] skipping non-file: #{path}"
87
+ next
88
+ end
89
+
90
+ text = File.read(path, encoding: "utf-8:utf-8", invalid: :replace, undef: :replace)
91
+ "### FILE: #{path}\n```ruby\n#{text}\n```"
92
+ rescue => e
93
+ warn "[warn] could not read #{path}: #{e.message}"
94
+ nil
95
+ end
96
+ end
97
+ end
98
+
99
+ def build_prompt(user_prompt, file_contents)
100
+ parts = [SYSTEM_PREAMBLE, ""]
101
+
102
+ if file_contents.any?
103
+ parts << "## Files provided for context\n"
104
+ parts.concat(file_contents)
105
+ parts << ""
106
+ end
107
+
108
+ parts << "## Task\n"
109
+ parts << user_prompt.strip
110
+ parts.join("\n")
111
+ end
112
+
113
+
114
+ def list_models(api_key)
115
+ url = URI("#{API_BASE.sub('/models', '')}/models?key=#{api_key}&pageSize=50")
116
+ http = Net::HTTP.new(url.host, url.port)
117
+ http.use_ssl = true
118
+ http.cert_store = ssl_cert_store
119
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
120
+
121
+ response = http.get(url)
122
+ abort "[error] HTTP #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
123
+
124
+ models = JSON.parse(response.body).fetch("models", [])
125
+ models
126
+ .select { |m| m["name"]&.include?("gemini") }
127
+ .sort_by { |m| m["name"] }
128
+ .each do |m|
129
+ methods = Array(m["supportedGenerationMethods"]).join(", ")
130
+ puts " #{m['name'].sub('models/', '').ljust(40)} #{m.fetch('displayName', '')} [#{methods}]"
131
+ end
132
+ end
133
+
134
+ def call_gemini(prompt, model:, api_key:)
135
+ url = URI("#{API_BASE}/#{model}:generateContent?key=#{api_key}")
136
+
137
+ payload = {
138
+ contents: [{ parts: [{ text: prompt }] }],
139
+ generationConfig: {
140
+ temperature: 0.2,
141
+ maxOutputTokens: 8192
142
+ }
143
+ }
144
+
145
+ http = Net::HTTP.new(url.host, url.port)
146
+ http.use_ssl = true
147
+ http.cert_store = ssl_cert_store
148
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
149
+ http.read_timeout = 120
150
+ http.write_timeout = 120
151
+
152
+ request = Net::HTTP::Post.new(url)
153
+ request["Content-Type"] = "application/json"
154
+ request.body = payload.to_json
155
+
156
+ response = http.request(request)
157
+
158
+ unless response.is_a?(Net::HTTPSuccess)
159
+ abort "[error] HTTP #{response.code}: #{response.body}"
160
+ end
161
+
162
+ result = JSON.parse(response.body)
163
+ result.dig("candidates", 0, "content", "parts", 0, "text") or
164
+ abort "[error] unexpected response shape:\n#{JSON.pretty_generate(result)}"
165
+ rescue JSON::ParserError => e
166
+ abort "[error] could not parse Gemini response: #{e.message}"
167
+ end
168
+
169
+ def parse_options
170
+ options = { files: [], model: nil, output: nil }
171
+
172
+ parser = OptionParser.new do |opts|
173
+ opts.banner = "Usage: gemini_ask.rb --prompt TEXT [--files GLOB...] [options]"
174
+
175
+ opts.on("--prompt TEXT", "Prompt text inline") { |v| options[:prompt] = v }
176
+ opts.on("--prompt-file PATH", "Path to a file with prompt") { |v| options[:prompt_file] = v }
177
+ opts.on("--files GLOBS", "Files or glob patterns (repeatable)") { |v| options[:files] << v }
178
+ opts.on("--model MODEL", "Gemini model override") { |v| options[:model] = v }
179
+ opts.on("--output PATH", "Write response to file") { |v| options[:output] = v }
180
+ opts.on("--list-models", "List available Gemini models") { |_| options[:list_models] = true }
181
+ opts.on("-h", "--help", "Show this help") { puts opts; exit }
182
+ end
183
+
184
+ parser.parse!
185
+ options
186
+ end
187
+
188
+ def resolve_prompt(options)
189
+ if options[:prompt]
190
+ options[:prompt]
191
+ elsif options[:prompt_file]
192
+ File.read(options[:prompt_file], encoding: "utf-8")
193
+ else
194
+ abort "[error] must supply --prompt or --prompt-file"
195
+ end
196
+ end
197
+
198
+ #--
199
+ # Entry point
200
+ #++
201
+ load_dotenv
202
+
203
+ options = parse_options
204
+
205
+ if options[:list_models]
206
+ load_dotenv
207
+ api_key = ENV.fetch("GEMINI_API_KEY") { abort "[error] GEMINI_API_KEY not set" }
208
+ puts "\nAvailable Gemini models:\n"
209
+ list_models(api_key)
210
+ exit
211
+ end
212
+
213
+ api_key = ENV.fetch("GEMINI_API_KEY") { abort "[error] GEMINI_API_KEY is not set (check .env)" }
214
+ model = options[:model] || ENV.fetch("GEMINI_MODEL", DEFAULT_MODEL)
215
+
216
+ user_prompt = resolve_prompt(options)
217
+ file_contents = load_files(options[:files])
218
+
219
+ warn "[info] Loaded #{file_contents.size} file(s) into context." if file_contents.any?
220
+
221
+ full_prompt = build_prompt(user_prompt, file_contents)
222
+ est_tokens = full_prompt.length / 4
223
+ warn "[info] Model: #{model} | Estimated input tokens: ~#{est_tokens.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse}"
224
+ warn "[info] Querying Gemini..."
225
+
226
+ response_text = call_gemini(full_prompt, model: model, api_key: api_key)
227
+
228
+ if options[:output]
229
+ File.write(options[:output], response_text, encoding: "utf-8")
230
+ warn "[info] Response written to #{options[:output]}"
231
+ else
232
+ puts
233
+ puts "=" * 60
234
+ puts "GEMINI RESPONSE"
235
+ puts "=" * 60
236
+ puts response_text
237
+ puts "=" * 60
238
+ puts
239
+ puts "[reminder] Always verify Gemini's claims against actual code before acting on them."
240
+ end
@@ -0,0 +1,25 @@
1
+ You are doing a cycle-end code review for the ecoportal-api-graphql Ruby gem.
2
+
3
+ Review the provided files for:
4
+ 1. Correctness — logic errors, edge cases not handled, incorrect assumptions.
5
+ 2. Backwards compatibility — anything that could break eco-helpers (the downstream consumer).
6
+ 3. Consistency — does the code follow the patterns established elsewhere in this gem?
7
+ 4. Completeness — are there obvious gaps (missing specs, missing error handling, etc.)?
8
+
9
+ Respond in this format:
10
+
11
+ ## Summary
12
+ One paragraph: overall assessment.
13
+
14
+ ## Issues Found
15
+ For each issue:
16
+ - **Severity:** [critical | major | minor]
17
+ - **File:** `path/to/file.rb`
18
+ - **Concern:** What the problem is.
19
+ - **Suggestion:** What to do about it.
20
+
21
+ ## No Issues Found In
22
+ List areas that look good.
23
+
24
+ ## Uncertain / Needs Human Verification
25
+ Anything you're not confident about — flag it here rather than guessing.
@@ -0,0 +1,261 @@
1
+ # SKILL: GraphQL Schema Analysis
2
+
3
+ **Purpose:** Systematically analyse a GraphQL schema (introspection JSON) to understand
4
+ type coverage, identify mutation/query patterns, compare against a Ruby gem implementation,
5
+ and produce structured documentation for AI agents.
6
+
7
+ **When to use:** Before implementing new gem coverage for any GraphQL API area, or when
8
+ auditing existing coverage against a live schema.
9
+
10
+ ---
11
+
12
+ ## Prerequisites
13
+
14
+ 1. An introspection JSON file from the live API — standard `__schema` introspection format.
15
+ - Fetch it from a live or staging instance.
16
+ - Store it in `.ai-assistance/tmp/` (git-ignored).
17
+ - File naming convention: `YYYYMMDDTHHMMSS_<env>_graphql_schema.json`
18
+
19
+ 2. The existing gem code for context (read `lib/` and `.ai-assistance/code/`).
20
+
21
+ ---
22
+
23
+ ## Step 1 — Orientate on Scale
24
+
25
+ Before diving in, understand the size:
26
+
27
+ ```bash
28
+ # File size
29
+ (Get-Item schema.json).Length / 1KB # PowerShell
30
+ wc -c schema.json # Unix
31
+
32
+ # Count types and mutations
33
+ ruby -rjson -e '
34
+ s = JSON.parse(File.read("schema.json")).dig("data","__schema")
35
+ types = s["types"].reject { |t| t["name"].start_with?("__") }
36
+ puts "Types: #{types.count}"
37
+ puts "Objects: #{types.count { |t| t["kind"] == "OBJECT" }}"
38
+ puts "Inputs: #{types.count { |t| t["kind"] == "INPUT_OBJECT" }}"
39
+ puts "Enums: #{types.count { |t| t["kind"] == "ENUM" }}"
40
+ puts "Unions: #{types.count { |t| t["kind"] == "UNION" }}"
41
+ puts "Interfaces:#{types.count { |t| t["kind"] == "INTERFACE" }}"
42
+ mut = types.find { |t| t["name"] == "Mutation" }
43
+ puts "Mutations: #{mut ? mut["fields"].count : 0}"
44
+ q = types.find { |t| t["name"] == "Query" }
45
+ puts "Queries: #{q ? q["fields"].count : 0}"
46
+ '
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Step 2 — Map Top-Level Structure
52
+
53
+ Extract the root Query and Mutation fields. These are the entry points for all API calls:
54
+
55
+ ```ruby
56
+ ruby -rjson -e '
57
+ s = JSON.parse(File.read("schema.json")).dig("data","__schema","types")
58
+ type_map = s.each_with_object({}) { |t, h| h[t["name"]] = t }
59
+
60
+ def type_str(t)
61
+ return "?" unless t
62
+ case t["kind"]
63
+ when "NON_NULL" then "#{type_str(t["ofType"])}!"
64
+ when "LIST" then "[#{type_str(t["ofType"])}]"
65
+ else t["name"]
66
+ end
67
+ end
68
+
69
+ puts "=== QUERY ==="
70
+ type_map["Query"]["fields"].each { |f| puts " #{f["name"]}: #{type_str(f["type"])}" }
71
+
72
+ puts "\n=== MUTATIONS ==="
73
+ type_map["Mutation"]["fields"].each { |f| puts " #{f["name"]}" }
74
+ '
75
+ ```
76
+
77
+ **Key questions to answer:**
78
+ - Is there a primary gateway object? (e.g. `currentOrganization`) Under which everything nests?
79
+ - Which areas have the most mutations? (Group by prefix: `create*/update*/destroy*`)
80
+ - Are mutations consistent? (All use `input: SomeType!` or mixed styles?)
81
+
82
+ ---
83
+
84
+ ## Step 3 — Identify Core Patterns
85
+
86
+ Look for schema-wide patterns before diving into individual types. These inform how the
87
+ Ruby gem should be designed:
88
+
89
+ ```ruby
90
+ ruby -rjson -e '
91
+ s = JSON.parse(File.read("schema.json")).dig("data","__schema","types")
92
+ type_map = s.each_with_object({}) { |t, h| h[t["name"]] = t }
93
+
94
+ # Check mutation argument patterns
95
+ muts = type_map["Mutation"]["fields"]
96
+ single_input = muts.count { |f| f["args"].length == 1 && f["args"][0]["name"] == "input" }
97
+ puts "Mutations with single input: #{single_input}/#{muts.count}"
98
+
99
+ # Find shared patterns
100
+ puts "\nTypes containing patchVer:"
101
+ s.select { |t| (t["fields"] || []).any? { |f| f["name"] == "patchVer" } }.each { |t| puts " #{t["name"]}" }
102
+
103
+ puts "\nInput types using IdDiffInput:"
104
+ s.select { |t| t["kind"] == "INPUT_OBJECT" }.each do |t|
105
+ t["inputFields"].each do |f|
106
+ next unless ["name","ofType"].all? { |k| f["type"].to_s.include?("IdDiff") } ||
107
+ f["type"]["name"] == "IdDiffInput" ||
108
+ (f["type"]["ofType"] || {})["name"] == "IdDiffInput"
109
+ puts " #{t["name"]}.#{f["name"]}"
110
+ end
111
+ end
112
+ '
113
+ ```
114
+
115
+ **Patterns to look for:**
116
+ - **Concurrency control:** Any type with `patchVer` (optimistic locking — must query before update)
117
+ - **Delta sync:** `*Delta` query fields returning `[DeltaResult]` — incremental sync support
118
+ - **IdDiff pattern:** `IdDiffInput { additions, removals }` for array relationship updates
119
+ - **OneToMany inputs:** `*OneToManyInput { additions, updates, deletions }` for complex nested updates
120
+ - **Payload pattern:** Do mutations return `{ item, errors }` or `{ payload, errors }` or other?
121
+ - **Connection pattern:** Paginated lists use Relay-style `*Connection { edges { node }, pageInfo }`
122
+
123
+ ---
124
+
125
+ ## Step 4 — Extract a Specific Type
126
+
127
+ For each area you're implementing, extract the full type definition:
128
+
129
+ ```ruby
130
+ ruby -rjson -e '
131
+ s = JSON.parse(File.read("schema.json")).dig("data","__schema","types")
132
+ type_map = s.each_with_object({}) { |t, h| h[t["name"]] = t }
133
+
134
+ def type_str(t); return "?" unless t; case t["kind"]; when "NON_NULL" then "#{type_str(t["ofType"])}!"; when "LIST" then "[#{type_str(t["ofType"])}]"; else t["name"]; end; end
135
+
136
+ def print_type(type_map, name)
137
+ t = type_map[name]; return puts "NOT FOUND: #{name}" unless t
138
+ puts "\n=== #{name} (#{t["kind"]}) ==="
139
+ if t["kind"] == "ENUM"
140
+ (t["enumValues"] || []).each { |v| puts " #{v["name"]}" }
141
+ return
142
+ end
143
+ (t["fields"] || t["inputFields"] || []).each do |f|
144
+ args = (f["args"] || []).map { |a| "#{a["name"]}: #{type_str(a["type"])}" }
145
+ arg_str = args.empty? ? "" : "(#{args.join(", ")})"
146
+ puts " #{f["name"]}#{arg_str}: #{type_str(f["type"])}"
147
+ end
148
+ end
149
+
150
+ # Replace with the type names you need:
151
+ %w[ TypeNameHere InputTypeHere ].each { |n| print_type(type_map, n) }
152
+ '
153
+ ```
154
+
155
+ **For each area, extract:**
156
+ 1. The output type(s) — what the query/mutation returns
157
+ 2. The input type(s) — what the mutation accepts
158
+ 3. The mutation args — confirm `input: InputType!` single-wrapper vs separate args
159
+ 4. Related enums — field options, state values
160
+
161
+ ---
162
+
163
+ ## Step 5 — Compare Against Gem Implementation
164
+
165
+ For each implemented area, systematically check coverage:
166
+
167
+ ```
168
+ Schema field | Ruby gem mapping | Status
169
+ ----------------------|-------------------------------|--------
170
+ id: ID! | passkey :id | ✓
171
+ name: String! | passthrough :name | ✓
172
+ patchVer: Int! | passthrough :patchVer | ✓ (queried)
173
+ | (not in mutation input yet) | ✗ (not injected)
174
+ recurrence: Recurrence| — | ✗ MISSING
175
+ ```
176
+
177
+ **Gem mapping patterns (ecoportal-api-graphql):**
178
+ - `passkey :fieldName` — ID primary key
179
+ - `passthrough :fieldName` — scalar, string, enum
180
+ - `passboolean :fieldName` — boolean field
181
+ - `passdate :fieldName` — DateTime (read-only dates)
182
+ - `passarray :fieldName` — array of scalars
183
+ - `embeds_one :fieldName, klass: SomeClass` — nested object
184
+ - `embeds_many :fieldName, klass: SomeClass` — array of objects
185
+ - `read_only!` modifier — field cannot be written
186
+ - `root!` on class — excluded from diff (lookup/reference, not owned)
187
+
188
+ ---
189
+
190
+ ## Step 6 — Document Findings
191
+
192
+ Write to `.ai-assistance/code/<area>.md` covering:
193
+ 1. Schema type inventory (output types, input types, enums)
194
+ 2. Mutation/query signatures
195
+ 3. Coverage gap table
196
+ 4. Key patterns and invariants (patchVer, IdDiff, etc.)
197
+ 5. Decisions made (deferred features, simplifications)
198
+
199
+ Reference: see `.ai-assistance/code/schema_analysis.md` and `diff_as_input.md` for examples.
200
+
201
+ ---
202
+
203
+ ## Key Extraction Scripts (Quick Reference)
204
+
205
+ ```ruby
206
+ # Find all types mentioning a keyword
207
+ type_map.keys.select { |k| k.match?(/keyword/i) }.sort
208
+
209
+ # Find all mutations for an area
210
+ type_map["Mutation"]["fields"].select { |f| f["name"].match?(/area/i) }.map { |f| f["name"] }
211
+
212
+ # Find which types have a specific field
213
+ type_map.values.select { |t| (t["fields"] || []).any? { |f| f["name"] == "fieldName" } }.map { |t| t["name"] }
214
+
215
+ # Get all enums with their values
216
+ type_map.values.select { |t| t["kind"] == "ENUM" }.each do |t|
217
+ puts "#{t["name"]}: #{t["enumValues"].map { |v| v["name"] }.join(" | ")}"
218
+ end
219
+
220
+ # Check mutation argument pattern for one mutation
221
+ mut = type_map["Mutation"]["fields"].find { |f| f["name"] == "mutationName" }
222
+ puts mut["args"].map { |a| "#{a["name"]}: #{type_str(a["type"])}" }
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Common Gotchas
228
+
229
+ 1. **Schema vs source code divergence.** The introspection JSON is from the live API.
230
+ Source code may be a different version. Always trust the introspection JSON.
231
+
232
+ 2. **Non-null vs nullable.** `field: Type!` means non-null; `field: Type` is nullable.
233
+ For mutations, non-null input fields are required. Always check nullability before
234
+ setting `required: true` in a Ruby input class.
235
+
236
+ 3. **Input type vs output type names.** `UpdateActionInput` ≠ `Action`. Always check
237
+ the specific input type, not the output type, when building mutation inputs.
238
+
239
+ 4. **Relay Connection types.** `FooConnection { edges { node: Foo }, pageInfo }` —
240
+ the actual data is under `edges.node` or (with the `nodes` shortcut) directly `nodes`.
241
+
242
+ 5. **Union types.** `PageUnion = BasicPage | PhasedPage` — always include `__typename`
243
+ in queries on union types so you can dispatch to the right Ruby class.
244
+
245
+ 6. **`patchVer` = must read before write.** Any type with `patchVer` in the schema
246
+ requires you to fetch the current value before updating. You cannot construct a
247
+ valid update input without it.
248
+
249
+ 7. **`IdDiffInput` in Update vs `[ID!]` in Create.** Create mutations often take flat
250
+ ID arrays; update mutations use `IdDiffInput`. Build separate Create and Update
251
+ input classes rather than sharing one.
252
+
253
+ ---
254
+
255
+ ## Output: What to Write
256
+
257
+ After analysis, produce:
258
+ - `.ai-assistance/code/<area>.md` — technical spec for the area
259
+ - `.ai-assistance/projects/<area>/TODO.md` — phased implementation plan
260
+ - `.ai-assistance/projects/<area>/DECISIONS.md` — rationale for scope choices
261
+ - Update `.ai-assistance/code/schema_analysis.md` coverage table with findings