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,168 @@
1
+ # Decisions — APIv2 → GraphQL Migration
2
+
3
+ ## 2026-06-04 — Bypass `graphql-client` for HTTP dispatch, retain DSL
4
+
5
+ **Context:** `graphql-client` requires fragments to be parsed into Ruby constants, making
6
+ fragment reuse across query helpers impossible for large models like Page.
7
+
8
+ **Options considered:**
9
+ 1. Patch `graphql-client` to relax the constant requirement.
10
+ 2. Fork `graphql-client` fully and rewrite fragment handling.
11
+ 3. Bypass `graphql-client` for HTTP dispatch only; keep `graphlient` DSL for query building.
12
+
13
+ **Decision:** Option 3 — bypass dispatch, retain DSL.
14
+
15
+ **Reason:** Least invasive. The DSL value is in query construction, not request execution.
16
+ A plain HTTP client posting `{ query, operationName, variables }` is sufficient and
17
+ well-understood (matches Insomnia/Postman behaviour). Avoids a full fork maintenance burden.
18
+
19
+ **Consequences:** Need to contribute a PR to `graphlient` fork allowing DSL-only mode
20
+ (returns String without firing request). Fragment strings assembled manually before dispatch.
21
+
22
+ ---
23
+
24
+ ## 2026-06-04 — Fragment strategy: plain strings assembled at query time
25
+
26
+ **Context:** `graphql-client` parses fragments into anonymous Ruby constants at load time,
27
+ preventing reuse across multiple query helpers.
28
+
29
+ **Options considered:**
30
+ 1. Define one mega-fragment constant per query (duplication, unmaintainable for large Page model).
31
+ 2. Use `graphql-client` anonymous fragments (current blocker).
32
+ 3. Define fragments as plain Ruby strings; append to query string before dispatch.
33
+
34
+ **Decision:** Option 3.
35
+
36
+ **Reason:** Simple, no gem dependency for fragment definition. Aligns with how the GraphQL
37
+ server (graphql-ruby pro, persistent queries) already handles this server-side.
38
+
39
+ **Consequences:** Fragment strings need to be validated once against schema. No dynamic
40
+ introspection of fragment fields from client code (acceptable for now).
41
+
42
+ ---
43
+
44
+ ## 2026-06-05 — Fragment spread in query blocks: preserve custom block capability
45
+
46
+ **Context:** The `___ConstantName` convention (graphql-client) is used inside both the gem's
47
+ own `default_block` procs and any caller-supplied blocks (from end-scripts, `eco-helpers`,
48
+ integration repos). Dropping graphql-client breaks this convention everywhere it is used.
49
+
50
+ **Options considered:**
51
+ 1. Remove custom block capability entirely; provide a utility/DSL for custom queries instead.
52
+ 2. Preserve custom block capability; change the fragment-spread syntax and document the new convention.
53
+ 3. Preserve for now; accept a major version bump + clean break if the migration causes a
54
+ development loop.
55
+
56
+ **Decision:** Option 2 as the starting point, with Option 3 as an explicit fallback.
57
+
58
+ **Reason:** Custom blocks are rare in practice and were introduced before the author was
59
+ fully fluent with GraphQL. However, removing them without a replacement utility would break
60
+ the handful of integration repos that do use them. Preserving the capability with a new
61
+ syntax is lower friction. If the `___Const` → new-syntax migration causes repeated
62
+ back-and-forth complexity, a major version bump with a clean break (plus a utility/DSL
63
+ replacement) is explicitly on the table.
64
+
65
+ **Consequences:** The fragment-spread syntax inside all blocks (default and custom) changes.
66
+ The new convention must be documented clearly and the change tagged as a major version bump.
67
+ Custom block authors need to migrate; the number of affected sites is small.
68
+
69
+ ---
70
+
71
+ ## 2026-06-05 — `as_input` conversion ownership: input class, not model
72
+
73
+ **Context:** When building a GraphQL mutation input from a model diff, something must
74
+ own the logic of converting `as_update` output into the specific shape required by a
75
+ given mutation input type. Two natural homes: the model (via `as_update(target_class:)`)
76
+ or the input class (via `InputClass.from_model(model)`).
77
+
78
+ **Options considered:**
79
+ 1. Model owns it: `model.as_update(target_class: SomeInputClass)` — model knows about
80
+ its input types.
81
+ 2. Input class owns it: `SomeInputClass.from_model(model)` — input class knows how to
82
+ interpret a model diff into its own shape.
83
+
84
+ **Decision:** Option 2 — input class owns the conversion.
85
+
86
+ **Reason:** The model should not need to know which mutation it is feeding. The cascade
87
+ (`as_update` → nested `as_update`) still runs on the model side; the input class wraps
88
+ it at the top and applies any structural reshaping needed for that specific mutation.
89
+ Cleaner separation of concerns; easier to add new input types without touching model code.
90
+
91
+ **Consequences:** Each mutation input class needs a `from_model(model)` (or equivalent)
92
+ class method. The `as_input` instance method on the model becomes a convenience delegator
93
+ to the resolved input class.
94
+
95
+ ---
96
+
97
+ ## 2026-06-04 — `as_update` diff: changed props only, no patch operations
98
+
99
+ **Context:** APIv2 `as_update` builds a JSON Patch diff across nested models. GraphQL
100
+ mutations target one model at a time and only need the final changed-property values,
101
+ not patch operations. `patch_ver` is only required at the top level of a Page record.
102
+
103
+ **Options considered:**
104
+ 1. Reuse APIv2 `as_update` as-is (produces wrong format for GraphQL).
105
+ 2. Add a `graphql:` mode flag to APIv2 `as_update`.
106
+ 3. Implement a separate `as_update` adapted for GraphQL in `ecoportal-api-graphql`.
107
+
108
+ **Decision:** Option 3.
109
+
110
+ **Reason:** Keeps GraphQL concerns in the GraphQL gem. Avoids coupling APIv2 model
111
+ definitions to GraphQL-specific behaviour. Per-model override support (`as_update(target_class:)`)
112
+ keeps it flexible for custom mutation input types.
113
+
114
+ **Consequences:** Some duplication of diff logic vs APIv2, but the logic is simpler (no
115
+ patch ops). Must maintain in parallel with APIv2 `as_update` for now.
116
+
117
+ ---
118
+
119
+ ## 2026-06-05 — Fragment spread syntax in DSL blocks: `spread :Name`
120
+
121
+ **Context:** The `___Const` convention (graphql-client) is no longer viable after removing
122
+ `graphql-client` from fragment parsing. A new syntax is needed for fragment spreads in
123
+ both default and custom query/mutation blocks.
124
+
125
+ **Options considered:**
126
+ A. Add a `spread(name)` helper to `Graphlient::Query` (in the fork). Usage: `spread :Action`
127
+ emits `...Action` in the query string. DSL blocks remain fully functional.
128
+ B. Drop the DSL block for selection bodies that contain fragment spreads; use heredoc strings
129
+ for those sections. More invasive; harder for custom blocks.
130
+
131
+ **Decision:** Option A — add `Graphlient::Query#spread(fragment_name)`.
132
+
133
+ **Reason:** Preserves the DSL convention. `spread :FragmentName` is readable and
134
+ aligns with the existing `:Symbol` argument style throughout the query DSL. Custom
135
+ blocks (from end-scripts, eco-helpers) only need the method name replaced — a mechanical
136
+ change. Requires one small graphlient fork addition (already committed on
137
+ `feature/dsl-only-mode` in the graphlient repo).
138
+
139
+ **Consequences:**
140
+ - All `___Ecoportal__API__GraphQL__Fragment__X` references in query/mutation blocks
141
+ replaced with `spread :X` (items 3.4).
142
+ - Fragment heredoc strings must be named: `fragment X on Type { ... }` (item 3.5).
143
+ - Fragment name = the Ruby symbol key (e.g., `:Action` → `...Action` → `fragment Action on ...`).
144
+ - End-script and eco-helpers authors using custom blocks must migrate `___Const` → `spread :Name`.
145
+ - Major version bump required when shipping (existing API decision from 2026-06-05).
146
+
147
+ ---
148
+
149
+ ## 2026-06-05 — HTTP dispatch client ownership: stored on Common::GraphQL::Client
150
+
151
+ **Context:** BaseQuery needs both a DSL client (`to_query_string`) and an HTTP client
152
+ (`execute`). Two options: pass both separately, or carry both on one object.
153
+
154
+ **Options considered:**
155
+ 1. Pass `http_client:` as a separate argument to BaseQuery (changes constructor signature
156
+ and all callsites).
157
+ 2. Store `http_client` as an attribute on `Common::GraphQL::Client` (single entry point,
158
+ no callsite changes needed).
159
+
160
+ **Decision:** Option 2 — add `attr_accessor :http_client` to `Common::GraphQL::Client`.
161
+ Set from `Ecoportal::API::GraphQL#initialize` after constructing the graphql client.
162
+
163
+ **Reason:** Minimal callsite changes. BaseQuery already receives `client`; adding
164
+ `client.http_client` requires zero changes to how query/mutation objects are instantiated.
165
+
166
+ **Consequences:** `Common::GraphQL::Client` holds a reference to `Common::GraphQL::HttpClient`.
167
+ If the auth context changes, both clients need updating. Acceptable for now; can be
168
+ refactored to a unified facade later.
@@ -0,0 +1,60 @@
1
+ # Project: APIv2 → GraphQL Migration
2
+
3
+ **Created:** 2026-06-04
4
+ **Status:** active
5
+ **Branch:** ai_dev_process
6
+ **Target branch:** main
7
+
8
+ ## Goal
9
+
10
+ Migrate ecoPortal integrations off the deprecated REST `/api/v2/pages` endpoint onto the
11
+ GraphQL endpoint, by completing the `ecoportal-api-graphql` gem so it can serve as the
12
+ primary client for page record queries and mutations — without requiring full rewrites of
13
+ existing end scripts.
14
+
15
+ ## Motivation
16
+
17
+ The `/api/v2/pages` endpoint has been deprecated and will be removed. All page-record
18
+ integrations must move to GraphQL. The `ecoportal-api-graphql` gem exists for this purpose
19
+ but has gaps: fragment reuse is blocked by `graphql-client` limitations, the HTTP client
20
+ layer is incomplete, and the mutation input / diff model hasn't been fully designed.
21
+
22
+ ## Scope
23
+
24
+ **In scope:**
25
+ - Implement a standalone HTTP client in `ecoportal-api-graphql` that bypasses `graphql-client`
26
+ for request dispatch, while retaining the `graphlient` DSL for query/mutation building.
27
+ - Solve the fragment reuse problem: define page model fragments as plain strings that are
28
+ assembled/appended at query time, not parsed into Ruby constants.
29
+ - Design and implement the mutation diff model: adapted `as_update` that produces the final
30
+ changed-properties body (no patch operations, no nested diffs), with per-model override support.
31
+ - Provide a DSL compatibility layer in `eco-helpers` so existing end scripts can swap to
32
+ GraphQL with minimal changes.
33
+ - Contribute a PR to the `graphlient` fork to allow DSL usage without triggering a request
34
+ (returns query string only).
35
+
36
+ **Out of scope:**
37
+ - Migrating the actual end scripts themselves (that's downstream work).
38
+ - Dynamic response model generation (desirable future state, not this project).
39
+ - Full replacement of `graphql-client` — only bypass its request/fragment constraints.
40
+
41
+ ## Dependencies
42
+
43
+ | Gem | Role | Location |
44
+ |-----|------|----------|
45
+ | `ecoportal-api` | HTTP client base | `c:\ruby_scripts\git\ecoportal-api\` |
46
+ | `ecoportal-api-v2` | Model definitions (`DoubleModel`, `as_update`) | `c:\ruby_scripts\git\ecoportal-api-v2\` |
47
+ | `graphlient` | GraphQL DSL (fork) | https://github.com/rellampec/graphlient |
48
+ | `graphql-client` | GraphQL base (fork, to be bypassed for requests) | https://github.com/rellampec/graphql-client |
49
+ | `eco-helpers` | **Downstream** consumer (uses this gem); DSL compat layer target | `c:\ruby_scripts\git\eco-helpers\` |
50
+
51
+ Open upstream issue: https://github.com/ashkan18/graphlient/issues/114
52
+
53
+ ## Success Criteria
54
+
55
+ - Page queries and mutations can be executed via `ecoportal-api-graphql` without
56
+ `graphql-client` handling the HTTP dispatch.
57
+ - Fragment strings for the page model can be defined once and reused across queries.
58
+ - `as_update` produces correct GraphQL mutation input bodies (changed props only, no patch ops).
59
+ - Existing `eco-helpers`-based scripts require only minor adjustments to target GraphQL.
60
+ - CI green, rubocop clean.
@@ -0,0 +1,267 @@
1
+ # TODOs — APIv2 → GraphQL Migration
2
+
3
+ > Granulated during session 2026-06-04. Items are numbered for reference in discussion.
4
+ > Natural implementation order: 1.1 → 2.1 → 2.2 → 3.1 → 3.2 → 3.3 decision → 3.4+3.5 → 2.3 → 4.x → 5.x → 6.x
5
+ > Items 1.2, 6.1, and 6.4 are continuous throughout.
6
+
7
+ ---
8
+
9
+ ## Group 1 — `graphlient` fork: DSL-only mode
10
+
11
+ - [x] **1.1** Add `to_query_string(**kargs, &block)` to `Graphlient::Client` (fork): builds the full
12
+ query document from the DSL block and returns a `String` — without touching Faraday/HTTP.
13
+ *Done 2026-06-05 — `graphlient/lib/graphlient/client.rb`, branch `feature/dsl-only-mode`*
14
+
15
+ - [x] **1.2** Open a PR from the fork to upstream `ashkan18/graphlient` for the DSL-only mode change
16
+ (ref issue #114). *Done — PR #115 "Add DSL-only mode: to_query_string + spread" MERGED into
17
+ `ashkan18/graphlient` master (verified via `git ls-remote upstream` 2026-06-13). NOT yet released,
18
+ so still open to coordinated changes; we are the authors. See memory `project-fork-revival-strategy`.*
19
+
20
+ ---
21
+
22
+ ## Group 2 — HTTP dispatch layer in `HttpClient`
23
+
24
+ - [x] **2.1** Add `execute(query_string, variables: {}, operation_name: nil)` to `HttpClient`:
25
+ POSTs `{ query:, variables:, operationName: }` as JSON to `/api/:org_id/graphql`, returns
26
+ the parsed response hash.
27
+ *Done 2026-06-05 — `lib/ecoportal/api/common/graphql/http_client.rb`*
28
+
29
+ - [x] **2.2** Add response error handling in `execute`: surface GraphQL `errors` from the response
30
+ body as typed exceptions consistent with the existing `Ecoportal::API::GraphQL::Error` hierarchy.
31
+ *Done 2026-06-05 — raises `Common::GraphQL::ResponseError` for GraphQL errors, `Client::Error::UnexpectedServerError` for HTTP errors*
32
+
33
+ - [x] **2.3** Wire dispatch end-to-end in `BaseQuery#graphql_query`:
34
+ (a) `client.to_query_string(&block)` builds query string,
35
+ (b) `assemble_fragments` scans for `...Name` and appends named fragment strings,
36
+ (c) `client.http_client.execute(full_query, variables: query_params)` dispatches,
37
+ (d) raw JSON hash returned → `wrap_response` navigates `data.*path`.
38
+ Decision: `http_client` stored as attr on `Common::GraphQL::Client` (see DECISIONS.md).
39
+ *Done 2026-06-05 — base_query.rb, common/graphql/client.rb, graphql.rb*
40
+
41
+ ---
42
+
43
+ ## Group 3 — Fragment plain-string strategy
44
+
45
+ - [x] **3.1** Strip `client.parse` and Ruby constant assignment from `FragmentDefinitions#define`.
46
+ Fragment strings are already stored via `fragment :Sym, heredoc` — keep the storage, drop the parse step.
47
+ *Done 2026-06-05 — `lib/ecoportal/api/graphql/concerns/fragment_definitions.rb`*
48
+
49
+ - [x] **3.2** Add `Fragment.assemble(*syms)`: returns concatenated fragment definition strings for the
50
+ given symbols, ready to append to a query string before dispatch.
51
+ *Done 2026-06-05 — `ClassMethods#assemble` in `fragment_definitions.rb`*
52
+
53
+ - [x] **3.3** **Decision item** — resolved: Option A — `spread :Name` DSL helper in `Graphlient::Query`.
54
+ *Done 2026-06-05 — documented in `DECISIONS.md`; `spread` committed to graphlient `feature/dsl-only-mode`*
55
+
56
+ - [x] **3.4** Update all existing query `default_block` procs to use `spread :Name`:
57
+ Action, Actions, ActionCategories, ContractorEntities, LocationStructure, LocationDraft, etc.
58
+ *Done 2026-06-05 — 12 query/mutation files updated*
59
+
60
+ - [x] **3.5** Update all `Fragment::*` class definitions to use named fragment syntax.
61
+ *Done 2026-06-05 — 7 fragment files updated (e.g., `fragment Action on Action { ... }`)*
62
+
63
+ ---
64
+
65
+ ## Group 4 — `as_update` / `as_input` pipeline
66
+
67
+ - [x] **4.1** Verify `DiffService` output for a realistic Page model: confirm no patch-op keys,
68
+ only changed prop values. Write/update the code-spec doc for this area.
69
+ *Done 2026-06-05 — `.ai-assistance/code/diff_as_input.md` written.*
70
+ *Finding: DiffService DOES produce patch-op keys (`api_operation`, `change_data`) at the top
71
+ level — this is wrong for GraphQL. Fix is part of 4.2/4.3 work. `patch_ver` is tracked on
72
+ `BasePage` via `passthrough :patchVer` but not yet injected in mutation input.*
73
+
74
+ - [x] **4.2** Inject `patch_ver` at the top level in `AsInput#as_input`: read from the subject
75
+ model and include in the mutation input body. Currently not handled.
76
+ *Done 2026-06-05 — also fixed `DiffService#classic_diff` to return flat changed-prop hash
77
+ (no `api_operation`/`change_data` wrapper). `as_input` now returns nil when no changes.*
78
+
79
+ - [x] **4.3** Implement `as_input(target_class:)` support: map diff output through a specified
80
+ `Input` subclass for key renaming / reshaping per mutation type (the "per-model override" pattern
81
+ from `DECISIONS.md`).
82
+ *Done 2026-06-05 — Logic::Input.from_model + AsInput#as_input(target_class:) +
83
+ ContractorEntity::Update override for IdDiff reshaping.*
84
+
85
+ - [x] **4.4** RSpec specs for `DiffService` and `AsInput` covering: no-change → nil/empty,
86
+ single-prop change, nested model change, `root?` objects excluded, `patch_ver` present at top level.
87
+ *Done 2026-06-06 — 33 examples, 0 failures. Fixed: dig_path? Hash#key? check,
88
+ ContractorEntity::Update.from_model nil-diff + doc[] comparison, as_input_spec
89
+ Base::ContractorEntity + array field mutation via doc[].*
90
+
91
+ ---
92
+
93
+ ## Group 5 — `eco-helpers` DSL compat layer
94
+
95
+ > Blocked on Groups 1–3 being functional.
96
+
97
+ - [ ] **5.1** Audit the existing end-script API surface in `eco-helpers`: list the exact methods
98
+ scripts currently call against `ecoportal-api-v2` Page objects (fetch, update, create, etc.),
99
+ so the compat interface can be defined precisely.
100
+
101
+ - [ ] **5.2** Design and implement a compat module in `eco-helpers` that proxies those methods
102
+ to the new GraphQL gem.
103
+
104
+ - [ ] **5.3** Write a short migration guide for end-script authors.
105
+
106
+ ---
107
+
108
+ ## Group 6 — Validation & cleanup
109
+
110
+ - [x] **6.0** Build a rubocop skill before writing any code: runs `rubocop --format json`
111
+ filtered to in-scope files, surfaces only actionable offences. Wire `rake spec` into
112
+ the same skill. `rubocop:disable` comments are handled by rubocop itself — the skill
113
+ does not need to second-guess them.
114
+ *Done 2026-06-05 — `.ai-assistance/skills/rubocop/SKILL.md`*
115
+
116
+ - [ ] **6.1** Rubocop clean on all new/modified files — run continuously as work progresses,
117
+ not deferred to end.
118
+
119
+ - [ ] **6.2** RSpec coverage for: `HttpClient#execute`, `Fragment.assemble`, and end-to-end
120
+ `BaseQuery#query` with stubs.
121
+
122
+ - [x] **6.3** Manual integration test against `live.ecoportal.com` once dispatch is wired (after 2.3).
123
+ *Done 2026-06-05 — fetching reporting structure + Excel generation verified working.*
124
+ *Fixed during test: `ENDPOINT_PATH` constant in `api_url`, `request ||= HTTP` guard in `base_request`.*
125
+
126
+ - [ ] **6.4** Update `DECISIONS.md` for any choices made during implementation (especially 3.3).
127
+
128
+ - [ ] **6.5** Cycle-end Gemini review (see `project-cycle` skill).
129
+
130
+ ---
131
+
132
+ ## Group 7 — Fork packaging & portable test harness
133
+
134
+ > Added 2026-06-27. Both items unblock day-to-day use of the revived forks and ongoing
135
+ > backwards-compat validation ahead of the APIv2-cutover deadline (1 Jul 2026).
136
+ > See memory `project-fork-revival-strategy`, `session-handoff-graphql-client-revival`,
137
+ > `project-parity-test-plan`.
138
+
139
+ - [x] **7.1** **Merged `graphql-client` revival branch + point the Gemfile at it.**
140
+ *Done 2026-06-27.* Built `integration/revival` on the fork (`rellampec/graphql-client`),
141
+ off `master`, cherry-picking the unique content of all four PRs in clean order (#79 base →
142
+ #80 docs → #81 char-tests → #82 parse fix) + a single `ci: pin i18n` commit (the four
143
+ branches each carried an identical copy). Applying #79 before #80 sidestepped the 1-line
144
+ CHANGELOG conflict. **Pushed; green:** 176 runs, 0 failures/errors, 4 skips; rubocop clean
145
+ (61 files). Wired into this gem via the **Gemfile** (not the gemspec — gemspecs can't carry
146
+ git sources; graphlient is a public gem we can't fork-point, so graphql-client is overridden
147
+ in the highest Gemfile we control). Commit `8827b2f`. **Reversible** — remove once the parse
148
+ fix ships upstream. `UPSTREAM.md` refreshed.
149
+ - ⏳ **Remaining (dev shell):** `bundle install` couldn't complete from the agent sandbox
150
+ (rubygems.org TLS interception — the git source itself fetched fine). Run `bundle install`
151
+ in your own shell to regenerate `Gemfile.lock` and confirm resolution, then a quick
152
+ `bundle exec rspec`.
153
+ - Upstream PRs stay OPEN — this branch is a convenience, not a substitute for merging.
154
+ - Mirror this for `graphlient` #116 only if needed (already fork-pinned separately).
155
+
156
+ - [x] **7.2** **Portable `mini/automated-tests/` case folder (training repo).**
157
+ *Done 2026-06-27 (training repo, uncommitted pending dev).* Built a self-contained suite at
158
+ `mini/automated-tests/`:
159
+ - **Harness** (`lib/`): `asserter.rb` (pass/fail/skip recorder + green/red summary),
160
+ `test.rb` (base class + auto-registry via `inherited`), `fixtures.rb` (loads `fixtures.json`).
161
+ - **Runner** `automated_tests_case.rb` → CLI `-automated-tests` (one PASS/FAIL summary;
162
+ `-list`, `-only <Test>`, `-page-id`, `-fixtures` flags). Auto-loaded by one added line in
163
+ `mini/config/session.rb`.
164
+ - **Seed tests** (`tests/`): `org_read_test.rb` (auth smoke, no fixtures, env-agnostic) +
165
+ `field_dispatch_test.rb` (assertion-based evolution of `gql_read_probe_case.rb` — v2 `===`
166
+ dispatch on real fields).
167
+ - **(a) Portable** — org-specifics live only in `fixtures.json` (org_id pinned by `-mini`,
168
+ never embedded); `fixtures.example.json` + a documented 3-step port flow (copy folder →
169
+ edit fixtures → add one session.rb line). Missing fixture ⇒ test SKIPS, never fails.
170
+ - **(b) Reusable** — run against any gem branch (`pages`, released, fork-pinned per 7.1).
171
+ - **Verified offline**: harness wiring (registry/asserter/fixtures/runner) proven green via a
172
+ throwaway mock-graphql driver. **Live run is the dev's** (sandbox TLS-blocked to
173
+ `live.ecoportal.com`): `ruby main.rb -mini -automated-tests -page-id <all-21-fields ID>`.
174
+ Cross-ref `project-parity-test-plan`. Folder/convention doc: `mini/automated-tests/CLAUDE.md`.
175
+
176
+ ---
177
+
178
+ ## Group 8 — Cutover parity test coverage (live harness + gem specs)
179
+
180
+ > Added 2026-06-28 after the page READ path went green live (8 gem bugs fixed; see
181
+ > worklog 2026-06-28). READ + create-blank are validated; the WRITE/value/submit path
182
+ > and the actual use cases are the remaining cutover risk. Live tests go in
183
+ > `training/mini/automated-tests/tests/`; gem specs under `spec/`.
184
+ > Memory: [[project-apiv2-cutover-usecases]], [[project-parity-test-plan]].
185
+
186
+ - [x] **8.1** **Field value round-trip test** (`compat_v2/fields/values`) — HIGHEST VALUE,
187
+ entirely unexercised. create → set value → `updatePage` → re-read → assert persisted, per
188
+ type: PlainText/Number `value=`, Select `.select`/`.values`, People append, CrossReference
189
+ `.add`/`.clear`/`.reference_ids`. Several of these compat methods are flagged **missing** in
190
+ `project-apiv2-cutover-usecases` — this proves/exposes each and exercises the update path
191
+ (first time). Write-gated, on the seeded page.
192
+ *Done & LIVE-GREEN 2026-06-29 — PlainText/Number/Select/People (`people_ids`)/CrossReference
193
+ (`page_ids`/`reference_ids`) all round-trip. The P2 coded-but-untested array writes (People
194
+ `<<`, CrossRef `add`/`clear`) are now validated live; full mini suite 35/35.*
195
+ - [x] **8.2** **`submit!` / stage completion test** — wires to `updatePage` task input
196
+ (see [[page-stage-submission]]); the cutover scripts call it.
197
+ *Done & LIVE-GREEN 2026-06-29. Root cause of the old stuck-stage failure: the stage has a
198
+ review-task config, so `completePageTask` ALONE enters "reviewing" and never advances. Fix =
199
+ `updatePage(submit: true, task: { completePageTask: { signOff: true } })` (inline sign-off
200
+ auto-approves the review). Gem gained
201
+ `Input::Page::Update.from_model(complete_page_task: { sign_off:, forced_complete:, notes: })`.
202
+ New test `compat_v2/page/submit_signoff_on_create` passes; old `stage_submit` flipped to the
203
+ confirmed call.*
204
+ - [ ] **8.3** **Search test** — `registers.search` with `text_filter`/`options_filter`,
205
+ `get_by_name(name, type:)`, cans preview-dedup (`page_result.membranes` — search preview
206
+ exposes no field values).
207
+ *Partial 2026-06-29: FilterTranslator now translates `text_filter`→`match_filter` and
208
+ `options_filter`→`one_of`/`none_of` (commit 7725f96), and `get_by_name(type:)` landed
209
+ (2026-06-28). REMAINING = LIVE-validate the translated text/options filters against a real
210
+ `registers.search`, and the membranes dedup rework.*
211
+ - [ ] **8.4** **BasicPage read test** — surfaces the known gap: `CommonPageUnion` has no
212
+ `... on BasicPage { sections }`, so basic pages get `corePageData` only.
213
+ *IN PROGRESS 2026-06-29 on branch `cutover/basicpage-sections`.*
214
+ - [ ] **8.5** **Run the 3 prod use cases** (farmers / jamestrong / act-gov) against `mini` —
215
+ the ultimate parity, ideally A/B vs APIv2 per [[project-parity-test-plan]].
216
+ *Gap audit done + refreshed: `notes/cutover-usecase-gap-audit.md` (Update — 2026-06-29).*
217
+ Blockers BEFORE they can run, re-prioritized 2026-06-29:
218
+ **P0** — release/pin `pages` (HARD PREREQUISITE); farmers must include OozeRedirect (hits dead
219
+ v2 server today). ✅ **Phased write-path identity RESOLVED** (`stage.components` writes persist
220
+ via `updatePage`; `compat_v2/page/stage_write`). **P1** — ✅ `get_by_name(type:)` done;
221
+ ✅ `text_filter`+`options_filter` translated (now a LIVE-validation item, 8.3); cans `membranes`
222
+ dedup rework; eco-helpers `submit!` to pass `stageId` + map `force:`→`forced_complete:` (gem
223
+ supports it, draft in progress).
224
+ - [ ] **8.6** **Gem RSpec regression specs** — the page-read path had ZERO coverage (8 bugs
225
+ lurked). Lock in: fragment-name assembly, DateTime sub-selection, `PageUnion` class dispatch,
226
+ `base_path` navigation, phased flatten/dedup, create `dataFields`. Use the `spec-generation`
227
+ skill.
228
+ *IN PROGRESS 2026-06-29 on branch `cutover/regression-specs-8-6`.*
229
+ - [ ] **8.7** **Write/collection-level compat review** — single-element mutations forward via
230
+ shared doc refs; collection ops (add/remove/reorder fields, multi-section identity) need
231
+ review (the piece deferred from the 2026-06-28 phased flatten work). *Still outstanding 2026-06-29.*
232
+
233
+ ---
234
+
235
+ ## Group 9 — Template & field-structure edits (tests + the CSV/diff deliverables)
236
+
237
+ > Added 2026-06-28 (dev). Tests for STRUCTURE edits (not just value writes), plus two
238
+ > deadline-bearing deliverables. Behavioral precedent = the old one-off scripts under
239
+ > `C:\ruby_scripts\implementation\orgs\*/config/ooze_cases` (multi-org-idle, multi_org_api):
240
+ > `OozeSamples::RegisterUpdateCase` helpers — `with_target_stage`/`with_sections`/
241
+ > `with_target_field`/`add_field(…, after:)`/`add_option`/`delete!`/`add_section`
242
+ > (`components.add`/`add_component` ~120×, `add_section` ~23×). Backwards-compat with those
243
+ > old scripts NOT required (nice-to-have). Memory: [[project-template-csv-pipeline]],
244
+ > [[project-template-diff-deploy]], [[project-template-update-architecture]].
245
+
246
+ - [ ] **9.1** **Page field-structure tests** — add / move (reorder) / delete a field on a
247
+ page via the GraphQL path; re-read and assert. Mirrors the most common past ops. Likely
248
+ exposes gem gaps (field add/delete + reposition via `updatePage` / WorkflowCommandInput).
249
+ - [ ] **9.2** **Template edit tests** — create + update a template (add/edit/delete section
250
+ & field, set labels/options) via `Builder::Template#create/#update(commands:)`. Most
251
+ WorkflowCommandInput command types are unwrapped — this maps the gap.
252
+ - [ ] **9.3** **CSV → templates pipeline** *(DEADLINE — see [[project-template-csv-pipeline]])*
253
+ — standard solution (eco-helpers?) to create/maintain ~300 templates from an input CSV
254
+ (sections/fields/labels columns). **Format agreed with customer ~mid-Jul; deliver Sept 2026.**
255
+ Identity: field id ← field internal **description**; section id ← a **hidden+deindex
256
+ PlainText marker field** (section external id) as the first field of each section. Needs
257
+ 9.1/9.2 commands + a CSV→WorkflowCommandInput mapper.
258
+ *Design drafted: `notes/csv-template-pipeline-design.md`.* **First build step (also unblocks
259
+ the 9.2 "maintain" path): add `description` + `hideView` to the dataField read fragment
260
+ (`fragment/pages/common_page_union.rb` `dataFieldInterface`) + `passthrough` on
261
+ `Base::Page::DataField` — the identity scheme cannot pair fields/sections without reading
262
+ them back. Then a template-structure read entry-point (`Builder::Template` has no read).**
263
+ - [ ] **9.4** **UAT→PROD diff-deploy** *(future — see [[project-template-diff-deploy]])* — a
264
+ deploy function applying a **UAT changelog** (not a raw snapshot diff) to PROD, preserving
265
+ PROD-only additions. Capturing UAT edits as the WorkflowCommandInput stream → replay on
266
+ PROD sidesteps the diff→command compiler. Page/template diff = nice-to-have for monitoring /
267
+ draft-from-diff.
@@ -0,0 +1,53 @@
1
+ # Upstream Dependencies — APIv2 → GraphQL Migration
2
+
3
+ ## graphlient (fork)
4
+
5
+ **Remote:** https://github.com/rellampec/graphlient (fork of https://github.com/ashkan18/graphlient)
6
+ **Required change:** DSL-only mode — allow a `graphlient` block to return the query String
7
+ without triggering HTTP dispatch. See open issue: https://github.com/ashkan18/graphlient/issues/114
8
+
9
+ **Approach:** Fork change + upstream PR contribution.
10
+ **Status:** Pending — PR not yet opened.
11
+
12
+ ---
13
+
14
+ ## graphql-client (fork)
15
+
16
+ **Remote:** https://github.com/rellampec/graphql-client (fork of
17
+ `github-community-projects/graphql-client`)
18
+ **Original migration need:** None — we bypass its HTTP dispatch layer and avoid the fragment
19
+ constant requirement via the plain-string fragment strategy.
20
+ **Revival effort (since 2026-06-13):** four upstream PRs are OPEN, awaiting a maintainer to
21
+ approve the first-time-contributor CI run:
22
+ - **#79** hygiene (CHANGELOG/CONTRIBUTING) — base
23
+ - **#80** docs (stacked on #79)
24
+ - **#81** characterization tests
25
+ - **#82** parse-robustness fix (anchored regex; rspec+rubocop clean)
26
+
27
+ **Status:** Revival in progress. Planned: merge all four into one fork branch (`integration/revival`)
28
+ and source this gem's gemspec/Gemfile from it as a stopgap until upstream merges/releases — see
29
+ TODO **7.1**. See memory `project-fork-revival-strategy`, `session-handoff-graphql-client-revival`.
30
+
31
+ ---
32
+
33
+ ## ecoportal-api-v2
34
+
35
+ **Remote:** https://gitlab.ecoportal.co.nz/oscar/ecoportal-api-v2.git
36
+ **Local:** `c:\ruby_scripts\git\ecoportal-api-v2\`
37
+ **Required change:** Per-model `as_update` override support (define behaviour per subclass
38
+ rather than relying solely on the base path-diff). This has been partially added already.
39
+ **Status:** In progress — confirm extent of existing implementation before adding more.
40
+
41
+ ---
42
+
43
+ ## eco-helpers (downstream — not upstream)
44
+
45
+ > Note: `eco-helpers` is a **downstream** consumer of `ecoportal-api-graphql`, not an
46
+ > upstream dependency. Changes here flow *down* to it, not up from it.
47
+ > Tracked here for awareness; full scope documented in INTENT.md.
48
+
49
+ **Remote:** https://gitlab.ecoportal.co.nz/oscar/script_api_helpers.git
50
+ **Local:** `c:\ruby_scripts\git\eco-helpers\`
51
+ **Required change:** DSL compat layer module so existing end scripts can target GraphQL
52
+ with minimal edits.
53
+ **Status:** Not started — design depends on `ecoportal-api-graphql` HTTP client being complete first.
@@ -0,0 +1,102 @@
1
+ # Design — CSV → GraphQL Templates Pipeline (TODO 9.3)
2
+
3
+ > Drafted 2026-06-28 (background design pass). Deliverable: a standard solution to
4
+ > create + maintain ~300 templates for a customer from an input CSV (format agreed with
5
+ > customer ~mid-Jul; delivery Sept 2026). Memory: [[project-template-csv-pipeline]],
6
+ > [[project-template-update-architecture]].
7
+
8
+ ## Headline finding — the #1 gap to fix first (read side)
9
+
10
+ The gem's command-bus foundation is solid: `Builder::Template#create(commands:)` /
11
+ `#update(model, commands:, patch_ver:)` feed `WorkflowCommandInput[]`, and the structural
12
+ section/field/option/stage commands are **already wrapped** (see §3). **But the identity
13
+ scheme depends on reading back data, and the read fragments don't expose it:**
14
+
15
+ - `fragment dataFieldInterface` (`fragment/pages/common_page_union.rb`) exposes only
16
+ `id, label, deindex, linkedFieldConfig` (+ per-type props) — **no `description`, no `hideView`**.
17
+ - `Base::Page::DataField` (`base/page/data_field.rb`) `passthrough`s only `label, deindex, type`.
18
+
19
+ Identity uses `description` (field key) and the hidden marker field (section key) — neither
20
+ is currently readable. **FIRST BUILD STEP:** add `description` + `hideView` (and
21
+ `hiddenOnMobile`) to `dataFieldInterface` and `passthrough :description, :hideView` on the
22
+ base model. (Verify those are valid read fields on `DataFieldsInterface` — a re-run of the
23
+ mini dispatch test will catch it if not, like the earlier fragment bugs.) Also needs a
24
+ **template-structure read entry-point** (`Builder::Template` has no read method today).
25
+
26
+ ## 1. CSV schema (canonical internal model — adapt customer headers to this)
27
+
28
+ One row per **field**; section identity repeated on each row (section implied by its rows).
29
+
30
+ | Column | Purpose |
31
+ |---|---|
32
+ | `template_key` / `template_name` | external id (groups rows into a template) / display name |
33
+ | `stage` | stage name/key (phased; constant default for single-stage) |
34
+ | `section_key` | **external id of section** (→ hidden marker field, §2) |
35
+ | `section_heading` / `section_layout` (`content`/`split`) / `section_order` | heading / layout / ordinal |
36
+ | `field_key` | **external id of field** (→ field `description`, §2) |
37
+ | `field_label` / `field_type` | label / type token (plainText, number, select, date, people, crossReference, …) |
38
+ | `required` / `hidden` / `deindex` | per-field config |
39
+ | `column` (0/1) / `field_order` | split-section side / ordinal (ordering via `moveField`) |
40
+ | `options` (`Name:value|Name:value`) / `multiple` / `flat` | select config |
41
+ | `tooltip` / `config_json` (escape hatch) | extra config |
42
+
43
+ Keep additive/tolerant: unknown columns ignored, missing optionals defaulted. Write a thin
44
+ **adapter** from the customer's headers to this model once their format lands.
45
+
46
+ ## 2. Identity / pairing
47
+
48
+ - **Field id ← `description`.** After `addField` (no description arg), issue
49
+ `editFieldConfiguration(dataFieldId:, description: field_key)`. On maintain, read all fields,
50
+ map `description → dataFieldId`, pair CSV rows by `field_key` (match→update, none→add,
51
+ absent→delete-or-leave per config).
52
+ - **Section id ← hidden marker field.** First field of each section = a hidden+deindex
53
+ PlainText whose `description` carries `section_key`. On maintain, read each section's first
54
+ component → `section_key → sectionId`. (Store key in `description`, not `value`, for uniform
55
+ extraction + safety if hide fails.)
56
+ - **Create vs maintain.** Create: one ordered `commands:` array using `placeholderId`
57
+ cross-refs (addSection→addField(sectionId: placeholder)…), then `create(commands:)`.
58
+ Maintain: read → identity maps → diff desired-vs-actual → delta commands →
59
+ `update(model, commands:, patch_ver:)`. The **diff/keyed-pairing engine is the core**
60
+ (robust version of the v2 `unless get_by_name(...)` guards, keyed by stable ids not labels).
61
+
62
+ ## 3. CSV-row → WorkflowCommandInput mapping (all verified in COMMAND_MAP)
63
+
64
+ | Op | Command(s) |
65
+ |---|---|
66
+ | add stage | `addStage` |
67
+ | add section | `addSection` + `addStageSection` |
68
+ | section heading / reorder / delete | `editSectionHeader` / `reorderSection` / `removeSection`(+`removeStageSection`) |
69
+ | add field | `addField{placeholderId, fieldType, label, stageId, sectionId, column}` |
70
+ | configure field (label, **description=id**, hideView, deindex, tooltip, byType config) | `editFieldConfiguration` |
71
+ | move/reorder field | `moveField{id, sectionId, column}` |
72
+ | delete field | `removeField` |
73
+ | select options | `addSelectFieldOption` / `editSelectFieldOption` / `removeSelectFieldOption` |
74
+ | per-type config | `editFieldConfiguration byType:` via the 12 `field_config/*` builders |
75
+
76
+ ## 4. Wrapped vs MISSING
77
+ - **Wrapped/usable:** all structural commands above + 12 `field_config` builders;
78
+ `editFieldConfiguration` already has `description`/`hideView` as BASE_KEYS;
79
+ `Builder::Template#create/#update/#publish`.
80
+ - **MISSING (build):** (1) **read-side `description`+`hideView`** on fragment + base model
81
+ [headline]; (2) **template-structure read entry-point** (hydrate stages→sections→fields +
82
+ patchVer, with `$fields`/`$content` flags); (3) **the pipeline** (CSV adapter → canonical
83
+ model → identity maps → diff → command builder → create/maintain/publish + retry); (4)
84
+ **verify `placeholderId`-as-reference** resolves server-side in one batch (else two-phase
85
+ create→read→configure); (5) hidden-marker helper/convention; (6) `Builder::Register` is a
86
+ stub — confirm whether templates are standalone or per-register.
87
+
88
+ ## 5. Open questions for the customer + risks
89
+ - Headers + how sections are grouped; are `field_key`/`section_key` present or must we
90
+ synthesise stable keys (label-edit orphan risk if synthesised); phased vs single-stage;
91
+ in-scope field types; **delete semantics on maintain** (data-loss risk); auto-`publish` vs
92
+ leave drafts for review.
93
+ - **Risks:** read-gap (high — de-risk first); `placeholderId` resolution (high — verify early,
94
+ have two-phase fallback); marker fragility (medium — `hideView`+`deindex`+`enableActions:false`,
95
+ treat missing marker as unknown→warn); `description` collides with human-visible descriptions
96
+ (use a sentinel prefix like `__key:`); scale/API volume (batch + patchVer-conflict retry);
97
+ `WorkflowCommandInput` is volatile → keep row→command mapping in ONE isolated translation layer.
98
+
99
+ ## Build order
100
+ 1. Read-side `description`/`hideView` + template-structure read. 2. Verify `placeholderId`
101
+ cross-ref. 3. Canonical model + CSV adapter. 4. Diff + command translator (isolated).
102
+ 5. create/maintain/publish + retry. 6. Pilot on a few templates in `mini` before the 300 run.