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,228 @@
1
+ # EcoPortal Search and Filters
2
+
3
+ ---
4
+
5
+ ## Two Search Modalities
6
+
7
+ ### 1. Org Search — `currentOrganization { pages(...) }`
8
+
9
+ **Data source:** PostgreSQL/MongoDB — full DB render
10
+ **Returns:** `PageUnion` (BasicPage | PhasedPage) — complete data
11
+ **Performance:** ~3 seconds per page (API-layer assembly)
12
+ **Supports archived:** Yes (via `searchConf` state filter)
13
+ **Has field IDs:** Yes — use for pre-update fetch
14
+ **patchVer:** Yes — required for mutations
15
+
16
+ Use when you need full field data to build mutation inputs.
17
+
18
+ **Arguments:**
19
+ - `searchConf: Search` — filter/sort/query
20
+ - `templatesOnly: Boolean` — restrict to templates
21
+ - `showHiddenData: Boolean` — include hidden fields
22
+ - `first/last/after/before` — cursor pagination
23
+
24
+ **Key `searchConf` filter for register-scoping within org search:**
25
+ ```json
26
+ { "operation": "register_filter", "params": { "ids": ["REGISTER_ID"] } }
27
+ ```
28
+ This scopes the org search to a register without switching to `previewPages`.
29
+
30
+ ### 2. Register Search — `currentOrganization { register(id:) { previewPages(...) } }`
31
+
32
+ **Data source:** Elasticsearch — search index snapshot
33
+ **Returns:** `PreviewPage` — metadata only (id, name, state, locations, dataFields as previews)
34
+ **Performance:** Sub-second (ES-powered)
35
+ **Supports archived:** Yes — `includeArchived: Boolean` parameter (default false)
36
+ **Has field IDs:** NO — cannot build mutation inputs from this
37
+ **patchVer:** NO
38
+
39
+ Use for fast metadata lookup (find page IDs, check existence, list pages).
40
+
41
+ **Additional arguments:**
42
+ - `presetViewId: ID` — apply a register PageView preset configuration
43
+ - `searchConf: Search`
44
+ - `includeArchived: Boolean`
45
+
46
+ **Field name:** `previewPages` (not `pages`) — common confusion point.
47
+
48
+ ---
49
+
50
+ ## SearchConf Structure
51
+
52
+ `searchConf` is a `Hash` type (`Search` in the schema) — NOT a typed GraphQL input.
53
+ Engineering has backend classes but they are not exposed as GraphQL types.
54
+
55
+ ```json
56
+ {
57
+ "filters": [ <filter_object>, ... ],
58
+ "sorters": { "key": "created_at", "direction": "asc" },
59
+ "query": "optional full-text search string"
60
+ }
61
+ ```
62
+
63
+ **Note on sorters:** Can be a single object OR an array. Key field is `key` (not `fieldName`).
64
+
65
+ ---
66
+
67
+ ## Filter Operations
68
+
69
+ **ALL operation names are snake_case strings.** If CamelCase is used (e.g. `ExactFilter`),
70
+ the server returns NO error AND NO results — completely silent failure. This is one of
71
+ the hardest bugs for customers to diagnose.
72
+
73
+ **`filters` should always be an Array.** The server normalises a single object to an
74
+ array internally, but always use array form for consistency.
75
+
76
+ ### `exact_filter`
77
+
78
+ Equality match on a field.
79
+
80
+ ```json
81
+ { "operation": "exact_filter", "params": { "key": "external_id", "value": "EXT-001" } }
82
+ ```
83
+
84
+ Common keys: `external_id`, `state`, `creator_id`, `template_id`
85
+
86
+ ### `date_filter`
87
+
88
+ Date range filter.
89
+
90
+ ```json
91
+ {
92
+ "operation": "date_filter",
93
+ "params": {
94
+ "key": "updated_at",
95
+ "gte": "2025-01-01T00:00:00Z",
96
+ "lte": "2025-12-31T23:59:59Z",
97
+ "time_zone": "UTC"
98
+ }
99
+ }
100
+ ```
101
+
102
+ `gte` = lower bound, `lte` = upper bound. Both optional.
103
+
104
+ **v2 format note:** APIv2 used `lbound`/`ubound` instead of `gte`/`lte`. The
105
+ eco-helpers compat layer translates these automatically.
106
+
107
+ ### `register_filter`
108
+
109
+ Scope org search to specific registers (org search only — not applicable to `previewPages`).
110
+
111
+ ```json
112
+ { "operation": "register_filter", "params": { "ids": ["REGISTER_ID"] } }
113
+ ```
114
+
115
+ ### `and_filter`
116
+
117
+ Explicit AND group (top-level `filters` array is already implicit AND).
118
+
119
+ ```json
120
+ {
121
+ "operation": "and_filter",
122
+ "params": {
123
+ "filters": [
124
+ { "operation": "exact_filter", "params": { "key": "external_id", "value": "X" } },
125
+ { "operation": "date_filter", "params": { "key": "updated_at", "gte": "..." } }
126
+ ]
127
+ }
128
+ }
129
+ ```
130
+
131
+ ### `or_filter`
132
+
133
+ OR group.
134
+
135
+ ```json
136
+ {
137
+ "operation": "or_filter",
138
+ "params": {
139
+ "filters": [
140
+ { "operation": "exact_filter", "params": { "key": "external_id", "value": "A" } },
141
+ { "operation": "exact_filter", "params": { "key": "external_id", "value": "B" } }
142
+ ]
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### `tag_filter` (v2 pass-through)
148
+
149
+ Filter by location tags. Used by APIv2 eco-helpers scripts. Passes through to the ES
150
+ backend unchanged. Same ES backend as GraphQL register search.
151
+
152
+ ```json
153
+ {
154
+ "type": "tag_filter",
155
+ "tags": ["Auckland", "Wellington"],
156
+ "mode": "any",
157
+ "negate": false,
158
+ "key": "tags"
159
+ }
160
+ ```
161
+
162
+ ---
163
+
164
+ ## SearchConf Builder (Gem)
165
+
166
+ The gem provides `Input::SearchConf` for building filters programmatically:
167
+
168
+ ```ruby
169
+ # Find by externalId
170
+ conf = SearchConf.new.filter(SearchConf::Exact.new(:external_id, 'EXT-001'))
171
+
172
+ # Register-scoped date range
173
+ conf = SearchConf.new
174
+ .filter(SearchConf::Register.new('REG_ID'))
175
+ .filter(SearchConf::DateRange.new(:updated_at, gte: '2025-01-01', time_zone: 'UTC'))
176
+ .sort(:created_at, :asc)
177
+
178
+ # OR filter (multiple externalIds)
179
+ conf = SearchConf.new.filter(
180
+ SearchConf::Or.new(
181
+ SearchConf::Exact.new(:external_id, 'EXT-001'),
182
+ SearchConf::Exact.new(:external_id, 'EXT-002')
183
+ )
184
+ )
185
+
186
+ # Parametrize (same search, different value)
187
+ base = SearchConf.new.filter(SearchConf::Exact.new(:external_id, 'X'))
188
+ copy = base.with(external_id: 'Y') # original unchanged
189
+
190
+ # Use with queries
191
+ org.pages(searchConf: conf.to_h, first: 10)
192
+ ```
193
+
194
+ **Filter classes:** `SearchConf::Exact`, `SearchConf::DateRange`, `SearchConf::Register`,
195
+ `SearchConf::And`, `SearchConf::Or`
196
+
197
+ ---
198
+
199
+ ## Known Filterable Field Keys (confirmed from production queries)
200
+
201
+ | key | Type | Operations |
202
+ |-----|------|------------|
203
+ | `external_id` | string | `exact_filter` |
204
+ | `updated_at` | datetime | `date_filter` |
205
+ | `created_at` | datetime | `date_filter` (likely) |
206
+ | `state` | enum | `exact_filter` (key: active/archived/draft) |
207
+ | `location_ids` | ID array | likely `in_filter` or similar |
208
+ | `creator_id` | ID | `exact_filter` |
209
+ | `template_id` | ID | `exact_filter` |
210
+
211
+ **Note:** The `fieldName` convention in some Confluence docs uses this same key concept.
212
+ The keys are snake_case corresponding to indexed ES document fields — NOT necessarily
213
+ the GraphQL field names.
214
+
215
+ ---
216
+
217
+ ## Future: Fluent DSL (Planned)
218
+
219
+ A more ergonomic API using Ruby's `&` (AND) and `|` (OR) operators is planned:
220
+
221
+ ```ruby
222
+ Search[:external_id].eq('EXT-001') & Search.in_register('REG_ID')
223
+ Search[:state].is(:active) | Search[:state].is(:draft)
224
+ (Search[:state].is(:active) | Search[:state].is(:draft)) & Search.in_register('REG_ID')
225
+ ```
226
+
227
+ This is operator-based (familiar to Arel/Sequel users), avoids ambiguity in chaining,
228
+ and will support AI-assisted filter generation. See `search-filter-builder/` project.
@@ -0,0 +1,197 @@
1
+ # EcoPortal Data Fields
2
+
3
+ ---
4
+
5
+ ## Overview
6
+
7
+ Data fields are the form inputs within a page. Each field is an INSTANCE of a field
8
+ definition from the template. Field instances have:
9
+ - A MongoDB ObjectId (`id`) assigned on page creation — NOT predictable from template
10
+ - The field type (`__typename` in GraphQL)
11
+ - Label (the question/field name shown to users)
12
+ - Value(s) — the user's response
13
+ - `ref` — the ES index key (Field ID / System ID future)
14
+ - `deindex: Boolean` — if true, field is excluded from Elasticsearch index
15
+ - Linked field configuration (cross-reference from another register)
16
+
17
+ ---
18
+
19
+ ## All Field Types (`DataFieldUnion`)
20
+
21
+ The GraphQL `DataFieldUnion` has 20+ concrete types:
22
+
23
+ | GraphQL type | eco-helpers type | Description |
24
+ |---|---|---|
25
+ | `PlainText` | `:plain_text` | Single or multi-line text |
26
+ | `RichText` | `:rich_text` | HTML rich text editor |
27
+ | `Date` | `:date` | Date/datetime picker |
28
+ | `Number` | `:number` | Numeric input |
29
+ | `Gauge` | `:gauge` | Numeric with min/max (risk matrix output) |
30
+ | `Select` | `:select` | Single or multi-select dropdown |
31
+ | `Checklist` | `:checklist` | Checkbox list (each item: label + checked) |
32
+ | `Signature` | `:signature` | Digital signature capture |
33
+ | `TagField` | `:tag_field` | Location tag selector |
34
+ | `People` | `:people` | Person selector (linked to PersonMember) |
35
+ | `ContractorEntities` | `:contractor_entities` | Contractor selector |
36
+ | `CrossReference` | `:cross_reference` | Reference to pages in another register |
37
+ | `Geo` | `:geo` | Geographic coordinates + address |
38
+ | `ImageGallery` | `:image_gallery` | Image upload gallery |
39
+ | `File` | `:file` | File attachment |
40
+ | `Mailbox` | `:mailbox` | Email inbox linked to the field |
41
+ | `ActionsList` | `:actions_list` | Embedded actions list |
42
+ | `Law` | `:law` | Legal obligation snippets |
43
+ | `AiSummary` | `:ai_summary` | AI-generated summary field |
44
+ | `EmbeddedStructure` | `:embedded_structure` | Embedded reporting structure |
45
+
46
+ ---
47
+
48
+ ## DataFieldInput Structure
49
+
50
+ When updating or adding field values, each field uses a typed input within `DataFieldInput`:
51
+
52
+ ```json
53
+ {
54
+ "plainText": { "id": "FIELD_ID", "value": "Job description text" },
55
+ "richText": { "id": "FIELD_ID", "content": "<p>HTML content</p>" },
56
+ "date": { "id": "FIELD_ID", "value": "2024-11-25T08:30:00+13:00" },
57
+ "number": { "id": "FIELD_ID", "value": 42.5 },
58
+ "gauge": { "id": "FIELD_ID", "value": 14.0 },
59
+ "select": { "id": "FIELD_ID", "options": [{ "id": "OPT_ID", "selected": true }] },
60
+ "people": { "id": "FIELD_ID", "peopleIds": ["PERSON_ID_1", "PERSON_ID_2"] },
61
+ "geo": { "id": "FIELD_ID", "address": "...", "coordinates": { "lat": -36.86, "lon": 174.76 } },
62
+ "checklist": { "id": "FIELD_ID", "items": [{ "id": "ITEM_ID", "checked": true }] },
63
+ "locationField": { "id": "FIELD_ID", "locationIds": ["LOC_ID"] }
64
+ }
65
+ ```
66
+
67
+ Only ONE field type key is used per `DataFieldInput` object.
68
+
69
+ ---
70
+
71
+ ## DataFieldOneToManyInput
72
+
73
+ Used in both `createPageFromTemplate` and `updatePage`:
74
+
75
+ ```json
76
+ {
77
+ "additions": [<DataFieldInput>, ...],
78
+ "updates": [<DataFieldInput>, ...],
79
+ "deletions": ["FIELD_ID_TO_DELETE", ...]
80
+ }
81
+ ```
82
+
83
+ - **`updates`** — change values of existing fields (require their current `id`)
84
+ - **`additions`** — add new field data (for fields that exist in template but have no instance data yet)
85
+ - **`deletions`** — remove field instances
86
+
87
+ **For `createFromTemplate`:** Use `updates` — the `buildFromTemplate` response gives
88
+ instance field IDs, and `updates` sets their values on creation.
89
+
90
+ ---
91
+
92
+ ## Field Access in the Gem
93
+
94
+ ### Getting fields from a page
95
+
96
+ ```ruby
97
+ page = graphql.pages.get(page_id) # fetches CommonPageUnion with $fields: true
98
+
99
+ # As DataField::Collection
100
+ fields = page.components # or page.field_collection
101
+
102
+ # Filter by type
103
+ plain_texts = fields.get_by_type(:plain_text)
104
+ selects = fields.get_by_type('Select')
105
+
106
+ # Find by label
107
+ field = fields.get_by_name('Summary')
108
+
109
+ # Access raw docs (v2 compat)
110
+ fields.doc # → [raw_hash, ...]
111
+ ```
112
+
113
+ ### Modifying fields
114
+
115
+ ```ruby
116
+ # PlainText
117
+ field = page.components.get_by_name('Summary')
118
+ field.value = 'New summary text'
119
+
120
+ # Select — by option name, value, or id
121
+ field = page.components.get_by_name('Status')
122
+ field.select_option('Active') # deselects all others
123
+
124
+ # People
125
+ field = page.components.get_by_name('Assigned To')
126
+ field.people_ids = ['person_id_1', 'person_id_2']
127
+
128
+ # Date (ISO8601 string)
129
+ field = page.components.get_by_name('Due Date')
130
+ field.value = '2025-06-30T09:00:00+12:00'
131
+
132
+ # Checklist
133
+ field = page.components.get_by_name('Checklist')
134
+ field.check('Step A')
135
+ field.check('Step B', checked: false)
136
+
137
+ # Rich text (HTML)
138
+ field = page.components.get_by_name('Description')
139
+ field.content = '<p>Updated description</p>'
140
+ ```
141
+
142
+ ### Adding a new field (additions)
143
+
144
+ ```ruby
145
+ # When a field exists in the template but has no data in this instance:
146
+ draft = graphql.pages.get_new(template_id)
147
+ draft.components.add(id: 'f1_from_draft', type: 'PlainText') { |f| f.value = 'X' }
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Stage-Level Field Access
153
+
154
+ For PhasedPage, fields are within stages → sections → fields:
155
+
156
+ ```ruby
157
+ # Get stage by name
158
+ stage = page.stages['Risk Assessment']
159
+
160
+ # Stage sections
161
+ stage.sections.each do |section|
162
+ puts "Section: #{section.heading}"
163
+ section.components.each { |f| puts " Field: #{f.label}" }
164
+ end
165
+
166
+ # All fields in stage (flat)
167
+ stage.components.get_by_name('Hazard Description').value = 'Exposure to...'
168
+ ```
169
+
170
+ ---
171
+
172
+ ## Field Visibility: deindex and Hidden
173
+
174
+ - `deindex: Boolean` — if true, field value is NOT sent to Elasticsearch. Used for
175
+ sensitive data. Affects: search, filters, analytics, exports.
176
+ - Fields can be hidden (template configuration). `showHiddenData: true` in queries
177
+ includes hidden fields in the response.
178
+
179
+ ---
180
+
181
+ ## Field Reference (ref) — ES Index Key
182
+
183
+ The `ref` property identifies a field in the search index (ES). It is:
184
+ - Composed of: `<type_shorthand>.<hash_of_label>`
185
+ - Used in: sorters (`key:` field), filters, chart configurations, linked field config
186
+ - Different from the MongoDB instance `id`
187
+ - Breaks if the field label changes (hash of label changes)
188
+
189
+ **Formula:** `Ecoportal::API::Common::Content::StringDigest` in `ecoportal-api-v2`
190
+
191
+ **Examples:**
192
+ - Plain text field "Job Number": `plain_text.job_nu` (approximate hash)
193
+ - Date field "Due Date": `date.due_da`
194
+
195
+ **Known issue:** If a field was added via a tech script without copying from the template,
196
+ its `ref` may be unique per instance rather than consistent across all instances of the
197
+ same template. This causes analytics issues (missing data, duplicate columns in exports).
@@ -0,0 +1,243 @@
1
+ # EcoPortal Stages and Sections
2
+
3
+ ---
4
+
5
+ ## Stage Lifecycle
6
+
7
+ ### Stage States (`StageStateEnum`)
8
+
9
+ ```
10
+ pending → inprogress → complete
11
+ ```
12
+
13
+ - **`pending`** — Stage not yet started. No fill-in task exists yet.
14
+ - **`inprogress`** — Stage is active. A fill-in task (`CompletePage`) exists and is
15
+ assigned to someone (per workflow rules). The assignee can enter data.
16
+ - **`complete`** — All required tasks are done. Stage is locked.
17
+
18
+ **Transition triggers:**
19
+ - `pending → inprogress`: Previous stage completes (its task is done/signed off)
20
+ - `inprogress → complete`: Fill-in task completed, OR review task signed off (if review configured)
21
+
22
+ ### Active vs Started
23
+
24
+ - `active: Boolean` — Stage is currently active for input (the "current" stage)
25
+ - `started: Boolean` — Stage has had at least some input (includes all past stages)
26
+
27
+ For a PhasedPage: typically ONE stage is `active` at a time. Previous stages are
28
+ `started` but not `active`. Future stages are neither.
29
+
30
+ ### Stage-Specific Submit
31
+
32
+ When submitting a stage via GraphQL, ALWAYS include `stageId`:
33
+
34
+ ```graphql
35
+ mutation SubmitStage($input: UpdatePageInput!) {
36
+ updatePage(input: $input) {
37
+ item { ...PhasedPageFields }
38
+ errors { details fullMessages }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ```json
44
+ {
45
+ "input": {
46
+ "id": "PAGE_ID",
47
+ "patchVer": 5,
48
+ "stageId": "STAGE_ID",
49
+ "submit": true,
50
+ "dataFields": {
51
+ "updates": [
52
+ { "plainText": { "id": "FIELD_ID", "value": "Final value" } }
53
+ ]
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ Without `stageId`, the submit may not correctly associate with the right stage and
60
+ may not trigger the expected task assignments in the next stage.
61
+
62
+ **Why a stage is mandatory (verified — Oscar, 2026-06-30):** stage tasks, and the
63
+ permissions granted through them, are **stage-scoped**. A people field can grant the user
64
+ attached to it permission to *only the stage where they were attached* — so a save / submit /
65
+ sign-off MUST be executed against that specific stage as the target. The web/mobile front-ends
66
+ always operate within a stage for this reason. Inferring a "current/active" stage from the
67
+ page is therefore not just unreliable, it is semantically wrong: on a multi-stage page it may
68
+ name a different stage than the one the work was done on. The submit must carry the stage the
69
+ caller actually worked on. ⇒ **submit / sign_off without a target stage is invalid.**
70
+
71
+ ### Gem enforcement & stageId resolution (compat layer)
72
+
73
+ The gem treats "a submit must name its stage" as a hard invariant:
74
+
75
+ - `Input::Page::Update.from_model` **raises** (`ArgumentError`) when `submit: true` or a stage
76
+ `task` is requested with no `stage_id`. This replaced a silent failure: the operation hash's
77
+ nil-guard used to *drop* a nil `stageId`, so a stage-less submit went out unnoticed (the
78
+ act-gov TOOCS bug, 2026-06-30). A plain field update / publish needs no stage and is exempt.
79
+ - `Compat::Pages#compat_stage_id` resolves the target most-explicit-first:
80
+ 1. **pinned** — `submit!(stage_id:)` / `sign_off!(stage_id:)` from the script;
81
+ 2. **active** — `page._compat_active_stage_id`, recorded when a `Compat::StageView` is taken
82
+ over the page (`pages.get(id, stage_id:)` or eco-helpers `OozeRedirect#stage`). This is the
83
+ reliable source on the OozeSamples path, where the base loop enqueues and updates the FULL
84
+ page (first-wins by id — the StageView is never the queued object), so the chosen stage must
85
+ live on the page itself;
86
+ 3. **current** — `Phased#current_stage_id` server-state inference (`state=='inprogress'` /
87
+ `active==true`). Last resort only, and guarded: if it yields nil the `from_model` guard fires.
88
+
89
+ So the canonical way to submit a specific stage from a script is to work *through a stage view*
90
+ (`with_stage` → `stage()` → `StageView`), which records the stage; the later `target.submit!`
91
+ then resolves to it automatically.
92
+
93
+ ---
94
+
95
+ ## Stage Permissions
96
+
97
+ Each stage can have restricted access:
98
+ - **User group restrictions** — only members of specified groups can edit the stage
99
+ - **People field access** — if a stage has a people field configured to "grant stage access",
100
+ the people assigned to that field get editing access to that stage only
101
+
102
+ **Key insight:** The configuration enum on a people field determines access scope:
103
+ - `stage_only` — grant access only to the stage where the people field is embedded
104
+ - `page_wide` — grant access to the entire page
105
+
106
+ **GraphQL behaviour:** If a user doesn't have access to a stage, the stage is simply
107
+ ABSENT from the response (`stages`, `stagesIndex`, `activeStages`). No error is thrown.
108
+ This is different from APIv2 which returned explicit access errors per stage.
109
+
110
+ **Scripting implication:** Scripts should check whether an expected stage is present
111
+ in the response rather than catching errors. If a stage is missing, the authenticated
112
+ user may not have access to it.
113
+
114
+ ---
115
+
116
+ ## Section Types (HasSectionsInterface)
117
+
118
+ Both BasicPage (directly) and Stage (within PhasedPage) implement `HasSectionsInterface`.
119
+
120
+ ### `ContentSection`
121
+
122
+ Standard section — one column of fields:
123
+ ```graphql
124
+ fragment on ContentSection {
125
+ id
126
+ heading
127
+ dataFields { ...dataField }
128
+ }
129
+ ```
130
+
131
+ ### `SplitSection`
132
+
133
+ Two-column section (left + right panels):
134
+ ```graphql
135
+ fragment on SplitSection {
136
+ id
137
+ heading
138
+ leftDataFields { ...dataField }
139
+ rightDataFields { ...dataField }
140
+ }
141
+ ```
142
+
143
+ When accessing a split section's fields, always merge left + right:
144
+ ```ruby
145
+ section.components # → DataField::Collection merging left + right
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Stage-Level Field Access (PhasedPage)
151
+
152
+ In GraphQL, all stage data is returned in one request via `CommonPageUnion`.
153
+ The response has stages → sections → fields.
154
+
155
+ ```ruby
156
+ # Gem access pattern
157
+ page = graphql.pages.get(page_id)
158
+
159
+ # All stages
160
+ page.stages # → StageCollection
161
+ page.stages['Risk Assessment'] # → Stage (by name or ID)
162
+ page.stages.ordered # → [Stage, ...] sorted by ordering
163
+
164
+ # Stage contents
165
+ stage = page.stages['Risk Assessment']
166
+ stage.sections # → SectionCollection
167
+ stage.components # → DataField::Collection (all fields in stage)
168
+ stage.sections.get_by_type(:content_section)
169
+ stage.sections.get_by_heading('Details')
170
+
171
+ # Field access in a stage
172
+ stage.components.get_by_name('Hazard Description').value = 'Exposure to...'
173
+ stage.components.get_by_type(:select).find { |f| f.label == 'Risk Level' }.select_option('High')
174
+ ```
175
+
176
+ ---
177
+
178
+ ## APIv2 vs GraphQL Stage Rendering Comparison
179
+
180
+ | Concern | APIv2 | GraphQL |
181
+ |---------|-------|---------|
182
+ | Request count for PhasedPage | One per stage | One for entire page |
183
+ | Stage data isolation | Strict — each request scoped to one stage | Full page returned, client filters |
184
+ | Stage-specific mutations | Via stage_id on URL | Via `stageId:` in `UpdatePageInput` |
185
+ | Permission handling | Access error per forbidden stage | Forbidden stages absent from response |
186
+ | Template stages | Full render | Full render |
187
+
188
+ **Compat layer approach:**
189
+ `graphql.pages.get(page_id, stage_id: 's1')` returns a `StageView` — a client-side
190
+ wrapper that shows only the specified stage's sections and components, while delegating
191
+ all other page accessors (id, name, patchVer, as_update, etc.) to the full page.
192
+ This matches what v2 scripts expect from a per-stage fetch.
193
+
194
+ ---
195
+
196
+ ## StageIndex (Lightweight Stage Summary)
197
+
198
+ For displaying workflow progress without loading full stage content:
199
+
200
+ ```graphql
201
+ fragment PhasedPageFields on PhasedPage {
202
+ stagesIndex {
203
+ id
204
+ name
205
+ ordering
206
+ state
207
+ started
208
+ hasCompleteTask
209
+ hasReviewTask
210
+ }
211
+ }
212
+ ```
213
+
214
+ `hasCompleteTask` — whether a fill-in task is configured for this stage
215
+ `hasReviewTask` — whether a review/sign-off task is configured for this stage
216
+
217
+ ---
218
+
219
+ ## Task Types on Stages
220
+
221
+ | Task type | GraphQL type | Trigger | When complete |
222
+ |-----------|-------------|---------|---------------|
223
+ | Fill-in | `CompletePage` | Stage becomes active | Moves to review (if configured) or completes stage |
224
+ | Review/sign-off | `ReviewPage` | Fill-in task completed | Completes stage → activates next stage |
225
+
226
+ Task fields: `id`, `complete`, `completedAt`, `completedBy`, `due`, `overdue`, `users`
227
+
228
+ **Stage close-out sequence:**
229
+ 1. Fill-in task assigned to user(s) (per workflow rules)
230
+ 2. User submits: `updatePage(stageId:, submit: true)` triggers `CompletePage` completion
231
+ 3. If review configured: `ReviewPage` task created and assigned
232
+ 4. Review approved: stage state → `complete`, next stage activated
233
+ 5. Next fill-in task created per workflow rules
234
+
235
+ **Scripting a stage submit:**
236
+ ```ruby
237
+ input = Input::Page::Update.from_model(page,
238
+ stage_id: 'STAGE_ID',
239
+ submit: true
240
+ )
241
+ # Input will include stageId + submit even if no page/field changes
242
+ Builder::Page.new(client).update(input: input)
243
+ ```