ecoportal-api-graphql 1.3.13 → 1.3.16

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 (275) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -0
  3. data/lib/ecoportal/api/common/graphql/client.rb +0 -2
  4. data/lib/ecoportal/api/common/graphql/http_client.rb +6 -0
  5. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -17
  6. data/lib/ecoportal/api/graphql/base/file_container.rb +14 -14
  7. data/lib/ecoportal/api/graphql/base/location_classification_type.rb +15 -15
  8. data/lib/ecoportal/api/graphql/base/location_structure.rb +27 -27
  9. data/lib/ecoportal/api/graphql/base/organization.rb +15 -15
  10. data/lib/ecoportal/api/graphql/base/page/task.rb +24 -0
  11. data/lib/ecoportal/api/graphql/base/page.rb +17 -16
  12. data/lib/ecoportal/api/graphql/base/template.rb +35 -0
  13. data/lib/ecoportal/api/graphql/base.rb +37 -35
  14. data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +41 -41
  15. data/lib/ecoportal/api/graphql/compat/response.rb +35 -35
  16. data/lib/ecoportal/api/graphql/connection/action.rb +11 -11
  17. data/lib/ecoportal/api/graphql/connection/action_category.rb +11 -11
  18. data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +11 -11
  19. data/lib/ecoportal/api/graphql/connection/page.rb +11 -11
  20. data/lib/ecoportal/api/graphql/connection/person_member.rb +11 -11
  21. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -11
  22. data/lib/ecoportal/api/graphql/connection.rb +17 -17
  23. data/lib/ecoportal/api/graphql/file_upload/client.rb +21 -10
  24. data/lib/ecoportal/api/graphql/fragment/field_configuration.rb +38 -0
  25. data/lib/ecoportal/api/graphql/fragment/force.rb +14 -10
  26. data/lib/ecoportal/api/graphql/fragment/location_node.rb +26 -26
  27. data/lib/ecoportal/api/graphql/fragment/page_task.rb +39 -0
  28. data/lib/ecoportal/api/graphql/fragment/template.rb +51 -0
  29. data/lib/ecoportal/api/graphql/fragment.rb +3 -0
  30. data/lib/ecoportal/api/graphql/input/contractor_entity.rb +14 -14
  31. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -16
  32. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -18
  33. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -21
  34. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -28
  35. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -18
  36. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -23
  37. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -20
  38. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -20
  39. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -24
  40. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -20
  41. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -20
  42. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -20
  43. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -18
  44. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -18
  45. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -18
  46. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -251
  47. data/lib/ecoportal/api/graphql/input.rb +24 -24
  48. data/lib/ecoportal/api/graphql/logic/connection.rb +34 -34
  49. data/lib/ecoportal/api/graphql/model/page/task.rb +16 -0
  50. data/lib/ecoportal/api/graphql/model/page.rb +16 -15
  51. data/lib/ecoportal/api/graphql/model/person_member.rb +15 -15
  52. data/lib/ecoportal/api/graphql/model/template.rb +15 -0
  53. data/lib/ecoportal/api/graphql/model.rb +32 -30
  54. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -45
  55. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -40
  56. data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +14 -14
  57. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -40
  58. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +38 -40
  59. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +38 -40
  60. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +38 -40
  61. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +7 -7
  62. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +8 -7
  63. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +38 -40
  64. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -40
  65. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -40
  66. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -40
  67. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +34 -35
  68. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -35
  69. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -36
  70. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -40
  71. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -13
  72. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -46
  73. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +1 -1
  74. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +1 -1
  75. data/lib/ecoportal/api/graphql/mutation.rb +20 -20
  76. data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +14 -14
  77. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +36 -21
  78. data/lib/ecoportal/api/graphql/payload/page/draft.rb +26 -13
  79. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -13
  80. data/lib/ecoportal/api/graphql/payload/page/review_task_batch.rb +23 -0
  81. data/lib/ecoportal/api/graphql/payload/page.rb +20 -19
  82. data/lib/ecoportal/api/graphql/payload/preset_view.rb +15 -11
  83. data/lib/ecoportal/api/graphql/payload/register.rb +15 -11
  84. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +1 -1
  85. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +1 -1
  86. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +1 -1
  87. data/lib/ecoportal/api/graphql/payload.rb +23 -23
  88. data/lib/ecoportal/api/graphql/query/location_structures.rb +57 -57
  89. data/lib/ecoportal/api/graphql/query/page.rb +45 -45
  90. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +9 -3
  91. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +9 -3
  92. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -78
  93. data/lib/ecoportal/api/graphql/query.rb +27 -27
  94. data/lib/ecoportal/api/graphql.rb +9 -5
  95. data/lib/ecoportal/api/graphql_version.rb +1 -1
  96. metadata +9 -180
  97. data/.ai-assistance/bridge/CLAUDE.md +0 -338
  98. data/.ai-assistance/bridge/archive/.gitkeep +0 -0
  99. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +0 -29
  100. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +0 -18
  101. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +0 -42
  102. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +0 -115
  103. data/.ai-assistance/bridge/context/gemini-review-prompt.txt +0 -48
  104. data/.ai-assistance/bridge/context/gemini-review-response.md +0 -104
  105. data/.ai-assistance/bridge/context/project.md +0 -42
  106. data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
  107. data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
  108. data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +0 -48
  109. data/.ai-assistance/bridge/queue/.gitkeep +0 -1
  110. data/.ai-assistance/capabilities/CLAUDE.md +0 -27
  111. data/.ai-assistance/capabilities/assumptions-log.md +0 -80
  112. data/.ai-assistance/capabilities/code.md +0 -47
  113. data/.ai-assistance/capabilities/connectors.md +0 -37
  114. data/.ai-assistance/capabilities/cowork.md +0 -55
  115. data/.ai-assistance/code/OVERVIEW.md +0 -155
  116. data/.ai-assistance/code/data_fields.md +0 -242
  117. data/.ai-assistance/code/dependencies.md +0 -151
  118. data/.ai-assistance/code/diff_as_input.md +0 -234
  119. data/.ai-assistance/code/diff_pairing_engine.md +0 -243
  120. data/.ai-assistance/code/diff_service_deep_dive.md +0 -192
  121. data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +0 -55
  122. data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +0 -181
  123. data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +0 -192
  124. data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +0 -147
  125. data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +0 -277
  126. data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +0 -200
  127. data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +0 -228
  128. data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +0 -197
  129. data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +0 -243
  130. data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +0 -196
  131. data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +0 -132
  132. data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +0 -187
  133. data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +0 -236
  134. data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +0 -183
  135. data/.ai-assistance/code/ecoportal_schema_reference.md +0 -240
  136. data/.ai-assistance/code/filter_contract_matrix.md +0 -177
  137. data/.ai-assistance/code/graphql_domain_knowledge.md +0 -240
  138. data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +0 -71
  139. data/.ai-assistance/code/refactoring/opportunities.md +0 -251
  140. data/.ai-assistance/code/schema_analysis.md +0 -321
  141. data/.ai-assistance/code/search_filters.md +0 -868
  142. data/.ai-assistance/code/spec_coverage.md +0 -73
  143. data/.ai-assistance/code/template_diff_pairing_domain.md +0 -175
  144. data/.ai-assistance/code/workflow-command-guide.md +0 -466
  145. data/.ai-assistance/code/workflow-space.md +0 -353
  146. data/.ai-assistance/conventions/CLAUDE.md +0 -30
  147. data/.ai-assistance/conventions/code-working-tree-protocol.md +0 -199
  148. data/.ai-assistance/conventions/gitignore-rules.md +0 -42
  149. data/.ai-assistance/conventions/permission-guidance.md +0 -120
  150. data/.ai-assistance/integrations/README.md +0 -70
  151. data/.ai-assistance/integrations/gitkraken-mcp.md +0 -107
  152. data/.ai-assistance/integrations/gitlab-mcp.md +0 -123
  153. data/.ai-assistance/integrations/local-git.md +0 -60
  154. data/.ai-assistance/local_paths.example.md +0 -17
  155. data/.ai-assistance/projects/TODO.md +0 -118
  156. data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +0 -168
  157. data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +0 -60
  158. data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +0 -267
  159. data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +0 -53
  160. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +0 -102
  161. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +0 -139
  162. data/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +0 -244
  163. data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +0 -93
  164. data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +0 -244
  165. data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +0 -266
  166. data/.ai-assistance/projects/eco-helpers-compat/TODO.md +0 -86
  167. data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +0 -101
  168. data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +0 -177
  169. data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +0 -161
  170. data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +0 -125
  171. data/.ai-assistance/projects/ooze-graphql-native-migration/INVENTORY.md +0 -136
  172. data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +0 -126
  173. data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +0 -261
  174. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +0 -122
  175. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +0 -148
  176. data/.ai-assistance/projects/page-model/DECISIONS.md +0 -245
  177. data/.ai-assistance/projects/page-model/TODO.md +0 -190
  178. data/.ai-assistance/projects/qa-services-delivery/DECISIONS.md +0 -93
  179. data/.ai-assistance/projects/qa-services-delivery/INTENT.md +0 -76
  180. data/.ai-assistance/projects/qa-services-delivery/PHASE3-SCOPE.md +0 -115
  181. data/.ai-assistance/projects/qa-services-delivery/ROADMAP.md +0 -99
  182. data/.ai-assistance/projects/qa-services-delivery/TODO.md +0 -81
  183. data/.ai-assistance/projects/search-filter-builder/INTENT.md +0 -107
  184. data/.ai-assistance/projects/search-filter-builder/TODO.md +0 -131
  185. data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +0 -87
  186. data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +0 -108
  187. data/.ai-assistance/projects/template-diff-deploy/INTENT.md +0 -12
  188. data/.ai-assistance/projects/template-diff-deploy/TODO.md +0 -9
  189. data/.ai-assistance/projects/template-maintenance/DESIGN.md +0 -134
  190. data/.ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md +0 -93
  191. data/.ai-assistance/projects/template-maintenance/README.md +0 -14
  192. data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +0 -236
  193. data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +0 -243
  194. data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +0 -154
  195. data/.ai-assistance/projects/workflow-space/TODO.md +0 -213
  196. data/.ai-assistance/reinstall-claude-desktop-windows.md +0 -136
  197. data/.ai-assistance/scripts/CLAUDE.md +0 -150
  198. data/.ai-assistance/scripts/bridge-inbox-check.sh +0 -75
  199. data/.ai-assistance/scripts/bridge-init.sh +0 -86
  200. data/.ai-assistance/scripts/bridge-status.sh +0 -44
  201. data/.ai-assistance/scripts/capabilities-check.ts +0 -104
  202. data/.ai-assistance/scripts/check-outbox.sh +0 -43
  203. data/.ai-assistance/scripts/dep_graph.rb +0 -91
  204. data/.ai-assistance/scripts/lock-acquire.sh +0 -103
  205. data/.ai-assistance/scripts/lock-multi.sh +0 -124
  206. data/.ai-assistance/scripts/lock-queue.sh +0 -94
  207. data/.ai-assistance/scripts/setup-mcps.test.ts +0 -188
  208. data/.ai-assistance/scripts/setup-mcps.ts +0 -234
  209. data/.ai-assistance/scripts/task-complete.ts +0 -74
  210. data/.ai-assistance/scripts/task-create.ts +0 -75
  211. data/.ai-assistance/scripts/task-read.ts +0 -125
  212. data/.ai-assistance/scripts/token-logger.js +0 -220
  213. data/.ai-assistance/scripts/token-report.ts +0 -158
  214. data/.ai-assistance/scripts/token-session-start.js +0 -66
  215. data/.ai-assistance/skills/ai-instructions/SKILL.md +0 -48
  216. data/.ai-assistance/skills/code-specs/SKILL.md +0 -69
  217. data/.ai-assistance/skills/corporate-policies/SKILL.md +0 -201
  218. data/.ai-assistance/skills/dep-graph/SKILL.md +0 -139
  219. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +0 -417
  220. data/.ai-assistance/skills/gemini-assist/SKILL.md +0 -63
  221. data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +0 -205
  222. data/.ai-assistance/skills/gemini-assist/gemini_ask.py +0 -1
  223. data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +0 -240
  224. data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +0 -25
  225. data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +0 -261
  226. data/.ai-assistance/skills/procedural-memory/SKILL.md +0 -319
  227. data/.ai-assistance/skills/project-cycle/SKILL.md +0 -177
  228. data/.ai-assistance/skills/project-self-docs/SKILL.md +0 -181
  229. data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +0 -378
  230. data/.ai-assistance/skills/refactor/SKILL.md +0 -62
  231. data/.ai-assistance/skills/rubocop/SKILL.md +0 -93
  232. data/.ai-assistance/skills/ruby-scripting/SKILL.md +0 -215
  233. data/.ai-assistance/skills/spec-generation/SKILL.md +0 -72
  234. data/.ai-assistance/standards-version.json +0 -22
  235. data/.ai-assistance/token-budget.json +0 -32
  236. data/.ai-assistance/version.json +0 -39
  237. data/.claude/settings.json +0 -150
  238. data/.env.example +0 -18
  239. data/.gitattributes +0 -15
  240. data/.gitignore +0 -37
  241. data/.gitlab-ci.yml +0 -45
  242. data/.markdownlint.json +0 -4
  243. data/.rspec +0 -3
  244. data/.rubocop.yml +0 -121
  245. data/.ruby-version +0 -1
  246. data/.yardopts +0 -10
  247. data/CLAUDE.md +0 -243
  248. data/Gemfile +0 -30
  249. data/Rakefile +0 -90
  250. data/bin/console +0 -14
  251. data/bin/setup +0 -8
  252. data/bin/setup.ps1 +0 -6
  253. data/docs/self-docs/ARCHITECTURE.md +0 -88
  254. data/docs/self-docs/CHANGES.jsonl +0 -12
  255. data/docs/self-docs/COMPLIANCE.md +0 -79
  256. data/docs/self-docs/CONVENTIONS.md +0 -74
  257. data/docs/self-docs/INTEGRATIONS.md +0 -65
  258. data/docs/self-docs/OPERATIONS.md +0 -76
  259. data/docs/self-docs/OVERVIEW.md +0 -64
  260. data/docs/self-docs/STATUS.md +0 -73
  261. data/docs/self-docs/self-docs-index.json +0 -51
  262. data/docs/worklog.md +0 -977
  263. data/ecoportal-api-graphql.gemspec +0 -40
  264. data/scripts/auto-worker-scheduler.sh +0 -386
  265. data/tests/actions_get.rb +0 -7
  266. data/tests/contractor_entities_get.rb +0 -20
  267. data/tests/contractor_entity_create.rb +0 -19
  268. data/tests/contractor_entity_udpate.rb +0 -20
  269. data/tests/dump_page_model.rb +0 -74
  270. data/tests/dump_template_model.rb +0 -90
  271. data/tests/loc_structure_get.rb +0 -10
  272. data/tests/loc_structure_update.rb +0 -51
  273. data/tests/loc_structures_get.rb +0 -15
  274. data/tests/local_libs.rb +0 -20
  275. data/tests/validate_queries.rb +0 -127
@@ -1,240 +0,0 @@
1
- # GraphQL Domain Knowledge — Agent Reference
2
-
3
- *Accumulated from developer context, backend source code, and production Insomnia queries.*
4
- *This document is for AI agents. Read it before analysing or generating any GraphQL query or mutation.*
5
-
6
- ---
7
-
8
- ## GraphQL is a Language — Not a Fixed REST Schema
9
-
10
- **Critical for agents:** GraphQL queries are client-defined. The same server resource can be
11
- queried in infinitely many ways — with different field selections, variable names, fragment
12
- compositions, and directive combinations. Variability between queries is **intentional and
13
- expected**, not an error. Do not assume a query "should look like" any specific previous example.
14
-
15
- Specific implications:
16
- - **Multiple operations per request** are valid GraphQL. A single HTTP request can contain
17
- multiple `query` or `mutation` definitions, separated by blank lines. The server executes
18
- them in declaration order. The `operationName` field in the JSON payload identifies which
19
- named definition is the primary one. This is used when a stateless client middleware cannot
20
- sequence separate requests (e.g. `ArchivePage` blanks `externalId` then archives, in one call).
21
- - **Fragment composition varies** — one team member's query may inline all fields; another's
22
- uses the shared `CommonPageUnion` fragment. Both are correct.
23
- - **Variable names are arbitrary** — `$search`, `$searchConf`, `$conf` all refer to the
24
- same `Search` argument type.
25
- - **Field selection is partial** — a query that omits `patchVer` is not wrong; it just
26
- means the caller doesn't need it for that use case.
27
-
28
- ---
29
-
30
- ## Backend Naming Conventions — Rosetta Stone
31
-
32
- The same concept appears under different names depending on which layer you're looking at.
33
- This causes confusion when reading source code alongside API output.
34
-
35
- | Concept | Backend Enzyme (old) | Backend NewEp (current) | APIv2 endpoint | GraphQL endpoint |
36
- |---|---|---|---|---|
37
- | Workflow phases | `stages` | `stages` | `stages` | `stages` |
38
- | Content containers within a stage | `flow_nodes` | `sections` | `sections` | `sections` |
39
- | Content fields within a section | `membranes` | `dataFields` | `components` | `dataFields` |
40
-
41
- **Note:** Enzyme is the original Rails backend namespace. NewEp is the revamp that the team
42
- is progressively migrating to. React front-end components that have been migrated to React
43
- use GraphQL; anything still on AngularJS uses the old APIv2. A model being available via
44
- GraphQL implies it has a NewEp backend class.
45
-
46
- ---
47
-
48
- ## The Sorter `key` Field — aka `ref` — aka the Future Field ID
49
-
50
- The `key` field in searchConf sorters (and similarly in many filter params) is NOT the
51
- MongoDB `id` of the field. It is the **field's ES index key**, also called the `ref` property
52
- in the backend model (`Enzyme::Membrane` / `NewEp::DataField`).
53
-
54
- **Composition of `ref`:**
55
- ```
56
- <type_shorthand>.<truncated_hash_of_label>
57
- ```
58
-
59
- - `type_shorthand`: e.g. `date`, `gauge`, `files`, `select_str`, `select_num`,
60
- `rich_text`, `plain_text`, `actions_list`
61
- - `.` separator
62
- - Hash of the label name (truncated when label > 3 characters)
63
- - Formula: `Ecoportal::API::Common::Content::StringDigest` in `ecoportal-api-v2`
64
-
65
- **Why this matters:**
66
- - This `ref` is how Elasticsearch identifies and indexes the field
67
- - It is used in: dashboard charts, filter configs, cross-register configs (linked fields,
68
- visitor management), exports
69
- - A field's `ref` is stable as long as its label doesn't change
70
- - The team refers to this as the "Field ID" — a future project aims to formalise it as a
71
- proper System ID (as opposed to the internal MongoDB `id`)
72
-
73
- **Known problem:** When tech scripts added fields to existing pages without copying from
74
- the template (e.g. bulk backfills), fields were created with random generation-time IDs
75
- rather than inheriting the template's `ref`. This creates duplicate/conflicting refs and
76
- causes issues in exports (missing CSV values) and some chart types.
77
-
78
- ---
79
-
80
- ## The Genome Signature — ⚠️ IMPORTANT — Will Be Revisited
81
-
82
- **Record this carefully. This topic will be picked up again.**
83
-
84
- The genome signature is an identifier designed to pair fields across pages and their
85
- source templates. It enables knowing that "this field in page X is the same field as
86
- this field in template Y", even after the page was created, cloned, or restored.
87
-
88
- **Original use:** The `Migrator` Rails Service (under the Rails Services loadable path)
89
- had a full solution using the genome to pair fields during template changes and page
90
- restorations (backup to YAML + restore).
91
-
92
- **Current state (CORRECTED 2026-07-03 against the live schema — supersedes earlier claims):**
93
- - ✅ **IT IS EXPOSED IN GraphQL.** The live NewEp schema exposes **`genomeSignature: String`** on the
94
- **`DataFieldsInterface`** and on **every concrete data-field type** (PlainText, Select, CrossReference,
95
- People, Date, Number, Gauge, Checklist, TagField, Geo, Signature, ContractorEntities, ImageGallery,
96
- File, Mailbox, ActionsList, Law, RichText, AiSummary, TableField, Chart, FrequencyRateChart,
97
- EmbeddedStructure, PageAction). Verified in `.ai-assistance/tmp/20260605T101224_live_ep_graphql_schema.graphql.json`.
98
- APIv2 also exposed it. **This corrects the previous "NOT currently exploitable" claim, which was wrong.**
99
- - It is on **data fields only** (per the introspection) — not on sections/stages/pages.
100
- - **Our gem does not yet REQUEST it** — no fragment/model field. To use it: add `genomeSignature` to the
101
- data-field fragment + `Base::Page::DataField`. (This is the real gap, not schema availability.)
102
- - **Not indexed** — the genome is not sent to Elasticsearch.
103
- - **Unreliable as a sole key** — see the failure modes (field re-use/re-purpose/revamp keep the same
104
- genome for a different meaning; wrong for select options; etc.). Treat as a **strong-but-fallible
105
- signal** in equivalence matching, never an unarguable key.
106
- - The Migrator solution (`app/services/migration/*`, `Migration::GenomeKindship`) was abandoned for
107
- maintenance/coverage reasons, but its founding principle (data lives in data-fields) is valid.
108
-
109
- **Full treatment:** see `.ai-assistance/code/template_diff_pairing_domain.md` — the authoritative
110
- capture of the template diff/pairing/deploy problem space (lifecycle, failure modes, equivalence
111
- reframe, diff modalities, layering, pipeline, Product context).
112
-
113
- **Why it matters for the future:** The genome was the intended foundation for:
114
- - Automatically applying template changes to existing pages
115
- - Correctly pairing fields across page versions for cross-register configurations
116
- - The Field ID project (see above) may supersede or build on this concept
117
-
118
- ---
119
-
120
- ## DataField Input — The `dataFields.updates` Structure
121
-
122
- When creating or updating page data fields via `createFromTemplate` or `updatePage`,
123
- field updates use a typed union pattern in `dataFields.updates`:
124
-
125
- ```json
126
- {
127
- "dataFields": {
128
- "updates": [
129
- { "date": { "id": "FIELD_ID", "value": "2024-11-25T08:30+13:00" } },
130
- { "people": { "id": "FIELD_ID", "peopleIds": ["PERSON_ID"] } },
131
- { "plainText": { "id": "FIELD_ID", "value": "JOB-0777" } },
132
- { "richText": { "id": "FIELD_ID", "content": "<p>HTML content</p>" } },
133
- { "select": { "id": "FIELD_ID", "options": [{ "id": "OPT_ID", "selected": true }] } },
134
- { "geo": { "id": "FIELD_ID", "address": "...", "coordinates": { "lat": -36.86, "lon": 174.76 } } }
135
- ]
136
- }
137
- }
138
- ```
139
-
140
- The `id` in each field update is the **data field's MongoDB ObjectId** (from the built
141
- draft — obtained via `buildFromTemplate`), not the `ref`/Field ID.
142
-
143
- ---
144
-
145
- ## Filter Rules — Silent Failure Traps
146
-
147
- **snake_case is mandatory for filter operations.** If a CamelCase operation name is used
148
- (e.g. `ExactFilter` instead of `exact_filter`), the server returns no error — it silently
149
- returns zero results. This is one of the hardest bugs to diagnose for customers.
150
-
151
- **`filters` should always be an Array.** The server normalises a single filter object to
152
- an array internally, so both formats work. However, always use the array form to be
153
- consistent and avoid edge cases.
154
-
155
- ---
156
-
157
- ## Compound Mutations — Multiple Operations Per Request
158
-
159
- GraphQL allows combining multiple mutations in a single request. They execute in
160
- declaration order on the server. Use cases:
161
-
162
- 1. **Stateless middleware:** When a client framework cannot maintain session state between
163
- requests, combining dependent mutations into one request guarantees correct sequencing.
164
- Example: `ArchivePage` blanks `externalId` first (to avoid duplicate ID conflicts on
165
- unarchive+recreate), then calls `archivePage`.
166
-
167
- 2. **Atomic-ish operations:** While GraphQL doesn't provide true ACID transactions, running
168
- mutations in one request at least avoids partial state where a network failure between
169
- two separate requests could leave data in an inconsistent state.
170
-
171
- The `operationName` field in the HTTP JSON payload names the primary definition. In a
172
- compound mutation, this is conventionally the name of the encompassing `mutation` block.
173
-
174
- ---
175
-
176
- ## Template vs Instance Pages
177
-
178
- - **Template** (`templatesOnly: true` in org search): Source pages that define field
179
- structure. Used with `buildFromTemplate` / `createFromTemplate` workflows.
180
- - **Instance page**: A page created from a template. Field IDs are assigned by the DB
181
- on creation — they must be fetched (via `buildFromTemplate` result or subsequent org
182
- search) before constructing field update inputs.
183
- - **`showHiddenData: Boolean`**: Controls whether fields marked as hidden in the template
184
- are included in query results. Set to `true` for integration scripts that need all fields.
185
-
186
- ---
187
-
188
- ## Stage Rendering: APIv2 vs GraphQL (critical)
189
-
190
- **APIv2:** Instance pages (pages WITH stages) are rendered and saved ONE STAGE PER REQUEST.
191
- `pages.get(id, stage_id: id)` returns a single-stage view. This was an architectural
192
- limitation. Templates and stageless pages are the only ones rendered in full by v2.
193
-
194
- **GraphQL:** Full page in one request. `page(id:)` returns all stages, all sections,
195
- all data fields the user has access to. Can update all stages at once, OR target a
196
- specific stage via `stageId: ID` in `UpdatePageInput`.
197
-
198
- **Stage-specific operations that REQUIRE `stageId`:**
199
- - Stage close-out / submit (triggers fill-in task creation on next stage)
200
- - Stage review sign-off (task completion triggers stage N+1 activation)
201
- - People fields scoped to a specific stage (permission grants are stage-scoped)
202
- Without `stageId`, these operations may succeed but not trigger the expected workflows.
203
-
204
- **Stage permissions in GraphQL:** No access errors — stages the user cannot access
205
- are simply absent from the response. APIv2 returned explicit errors per stage.
206
- Scripts that relied on stage-access errors need to check for stage presence instead.
207
-
208
- **Template updates:** Engineering explicitly does NOT want templates updated via GraphQL
209
- while the AngularJS template editor is still in use. Block template mutation endpoints.
210
- Read-only template queries (`templates(...)`) are fine.
211
-
212
- ---
213
-
214
- ## Forces — Being Replaced by Workflow Builder
215
-
216
- **Forces** are AngularJS front-end snippets that attach to specific stages via bindings.
217
- They implement conditional field behaviour (show/hide sections, computed values, risk
218
- matrices). Forces are NOT accessible or meaningful via GraphQL. Low scripting priority.
219
-
220
- **Workflow Builder** (`pagesWorkflow`) is the replacement:
221
- - Deployed: notifications, automated page creation, task assignments
222
- - NOT yet deployed: conditional field/section workflows (show this section only when
223
- field X has value Y; compute gauge value from severity × likelihood matrix)
224
- - Recently refactored — active development, high change velocity, quick CI/CD fixes
225
- - Do NOT design scripting against workflow builder internals — API is unstable
226
-
227
- **Implication for scripts:** Workflow triggers (task creation, stage transitions) fire
228
- as server-side side effects of `updatePage`. Scripts should check mutation payloads for
229
- unexpected errors, but should not attempt to directly manage workflow state.
230
-
231
- ---
232
-
233
- ## Two Build Patterns — Legacy vs Standard
234
-
235
- **Build - Model (legacy):** Uses own inline fragment definitions per query. No shared
236
- `CommonPageUnion`. Still in use in some older Insomnia requests. Avoid in new code.
237
-
238
- **Build - Standard (current):** Uses `CommonPageUnion` with `$fields/$content/$only_content`
239
- variables. This is the team-wide standard — designed for customer-facing documentation
240
- and team knowledge sharing. New scripts should use this pattern.
@@ -1,71 +0,0 @@
1
- # Review: DataField read/write shape asymmetry + dirty-detection baseline
2
-
3
- **Status:** OPEN — needs a thorough, dedicated review pass.
4
- **Raised:** 2026-06-30 (after a real bug found + fixed on `CrossReference`).
5
- **Owner cue:** Oscar flagged this for thorough review.
6
-
7
- ---
8
-
9
- ## The bug class
10
-
11
- Several `DataField` subtypes are **READ** from the GraphQL response under one key/shape and
12
- **WRITTEN** (staged for mutation) under a *different* key/shape. This breaks two things:
13
-
14
- 1. **Reading current values** — the typed reader reads the WRITE key, which the read response
15
- never populates → returns `[]` (or stale) on a freshly-fetched page.
16
- 2. **Dirty detection** — the default leaf diff compares `doc` vs `original_doc` at the key
17
- level. A write adds a brand-new key (e.g. `pages`) absent from the read shape, so the field
18
- looks dirty the moment any setter runs — even when the effective value is unchanged
19
- (e.g. `clear` on an already-empty reference → phantom `referenceIds: []`). Under APIv2 the
20
- equivalent no-op was correctly not-dirty; this is a v2-parity regression.
21
-
22
- ## Per-field audit (2026-06-30)
23
-
24
- | Field | READ (fragment `common_page_union.rb`) | WRITE key | Reader reads | Verdict |
25
- |---|---|---|---|---|
26
- | `CrossReference` | `references { nodes { id … } }` | `pages` | refs→pages | **FIXED** (set-based `dirty?` vs `original_doc`; shared `ref_ids_from`) |
27
- | `FileField` | `items { fileContainer { id … } }` | `fileContainers` | `fileContainers` | **BROKEN reader** (reads key the response never has → `[]`) + phantom-dirty |
28
- | `ImageGallery` | `images { downloadUrl, caption … }` (no `id` even fetched) | `fileContainers` | `fileContainers` | **BROKEN reader** + phantom-dirty; read fragment also omits ids |
29
- | `ContractorEntities` | `contractorEntities { id, name … }` | `contractors` | `contractors` | **BROKEN reader** + phantom-dirty |
30
- | `TagField` | `locations { …locationNode }` | `locations` | `locations` | key matches, but write narrows shape (`{id,name,…}`→`{id}`); `as_input` is nil (page-level locations) → low impact, still verify |
31
- | `People`, `Select`, `Checklist`, `PlainText`, `RichText`, `Number`, `Gauge`, `DateField`, `Geo` | read key == write key | same | same | OK (no asymmetry) |
32
-
33
- ## Recommended review/fix direction
34
-
35
- Two layers, decide per field:
36
-
37
- - **Reader correctness:** make `*_ids` read the actual response key (`items[].fileContainer.id`,
38
- `images[].id` (needs the fragment to fetch it), `contractorEntities[].id`) — falling back to
39
- the staged write key when present (so post-write reads reflect the staged value). The shared
40
- `ref_ids_from(doc)` pattern in `cross_reference.rb` is the template.
41
- - **Dirty baseline:** any custom `dirty?`/`as_input` MUST diff the effective value against
42
- **`original_doc`** (the diff `prev_doc` — see `Diffable::ClassicDiffService#prev_doc`), NOT
43
- `doc`. Better still, where shapes can be normalised, defer to the inherited `DiffService`
44
- rather than hand-rolling. Add a `consolidate!`-cycle assertion to every such field's spec
45
- (read → mutate → as_input → `consolidate!` → re-check `dirty? == false`).
46
-
47
- These fields are NOT exercised by the 3 cutover use cases (toocs = CrossReference + People;
48
- cans = PlainText + Number + Select), so this is not a 1-Jul blocker — but it is a real
49
- correctness gap for full parity and any script touching files/images/contractors.
50
-
51
- ## Retro — why the `CrossReference` bug went unnoticed (so we don't repeat it)
52
-
53
- 1. **Hand-rolled the baseline from `doc` instead of `original_doc`.** I wrote a custom `dirty?`
54
- from first principles instead of reading the existing diff contract first. Had I checked
55
- `ClassicDiffService#prev_doc` (returns `original_doc`) before writing it, I'd have used the
56
- right baseline. Root cause: built a new abstraction parallel to an existing one without
57
- consulting it.
58
- 2. **Incidental-correctness masked it in tests AND live.** My reader and my baseline both read
59
- `references.nodes` from `doc`; since the setter only writes `pages`, the two always agreed on
60
- the fetch-then-mutate path — the only path the specs and the live run exercised. The error is
61
- invisible until `consolidate!` puts `pages` into `original_doc`.
62
- 3. **No lifecycle (`consolidate!`) test.** The test matrix covered read→mutate but not
63
- read→mutate→update→consolidate→re-diff, where the bug actually surfaces.
64
- 4. **Conflated "read key isn't mutated" with "doc == baseline".** True premise, wrong
65
- conclusion. Fragile coupling to current setter behaviour.
66
-
67
- **Guardrails going forward:**
68
- - Never compute a dirty baseline from `doc`; use `original_doc` or the `DiffService`.
69
- - Every field with a staged-write setter gets a `consolidate!`-cycle spec.
70
- - When adding a typed field, cross-check the fragment READ key against the setter WRITE key;
71
- if they differ, normalise reads and baseline dirty on `original_doc`.
@@ -1,251 +0,0 @@
1
- # Refactoring Opportunities
2
-
3
- Low-priority items noted for future consideration. Not blocking current work.
4
-
5
- ---
6
-
7
- ## [LOW] Review graphlient DSL for future method chaining support
8
-
9
- **Logged:** 2026-06-13
10
- **Upstream repo:** `C:\ruby_scripts\git\graphlient` — branch `feature/directives-dsl-support`
11
-
12
- **Context:** The current graphlient DSL passes directives as arguments:
13
- ```ruby
14
- spread :InvoiceFields, _skip(if: :skip_invoice)
15
- on(:DraftInvoice, _skip(if: :skip_drafts)) { draft_id }
16
- ```
17
-
18
- A fluent chain-style equivalent (`spread(:InvoiceFields).skip(if: :x)`) would require
19
- DSL methods to return a builder object rather than appending to `@query_str` and returning
20
- `nil`. The current approach (directives as arguments) does **not block** future chaining —
21
- both can coexist as alternative interfaces.
22
-
23
- **Before the next graphlient upstream PR:** verify no API decisions in the current MR
24
- force a hard dependency on the argument-passing style that would make a chain builder
25
- impossible to introduce later without a breaking change.
26
-
27
- **When to revisit:** When MR #116 is merged upstream. Low urgency — chaining is optional,
28
- current syntax is functional.
29
-
30
- ---
31
-
32
- ## [LOW] Fragment definitions as `.graphql` files instead of Ruby HEREDOCs
33
-
34
- **Logged:** 2026-06-05
35
- **Files affected:** `lib/ecoportal/api/graphql/fragment/*.rb`
36
-
37
- **Idea:** Define named fragments in `.graphql` files (e.g.
38
- `lib/ecoportal/api/graphql/fragment/action.graphql`) and load them at startup via
39
- `File.read`, rather than embedding them as Ruby HEREDOCs.
40
-
41
- **Benefits:**
42
- - GraphQL files get syntax highlighting and editor tooling (linting, autocomplete).
43
- - Cleaner separation — `.graphql` is the DSL, `.rb` is the Ruby wrapper.
44
- - Easier to diff fragment changes in git without Ruby noise.
45
-
46
- **Open questions / why it's low priority:**
47
- - HEREDOCs work fine and there is no string interpolation in fragments, so the
48
- only gain is cosmetic/tooling.
49
- - Client scripts may define custom queries as HEREDOCs in their own code. Whether
50
- those should auto-load into the fragment registry (and how) is unresolved.
51
- `FragmentDefinitions.assemble(*syms)` only knows about fragments registered via
52
- `fragment :Sym, string` — any `.graphql` file loader would need to register them
53
- the same way.
54
- - Until the auto-loading story for custom script fragments is clear, standardising
55
- on `.graphql` files for the gem's own fragments may create inconsistency.
56
-
57
- **When to revisit:** Once there is a concrete script use case that benefits from
58
- `.graphql` file loading, or when editor tooling for HEREDOCs becomes a pain point.
59
-
60
- ---
61
-
62
- ## [MEDIUM] Upstream-stack friction points — candidates for future graphql-client / graphlient MRs
63
-
64
- **Logged:** 2026-06-16
65
- **Scope:** `graphql-client` (fork: `rellampec/graphql-client`) and `graphlient` (fork: `rellampec/graphlient`)
66
- **Origin:** Adoption-friction analysis while documenting inline named fragments (graphql-client
67
- issue [#76](https://github.com/github-community-projects/graphql-client/issues/76)).
68
-
69
- These are the genuine reasons a large Ruby shop tends to avoid or rip out the
70
- `graphlient → graphql-client → graphql-ruby → faraday` stack. None is confirmed as the
71
- cause of any specific org's removal (see note below); they are the real ergonomic/architectural
72
- frictions worth addressing if we want the forks to be community-scalable.
73
-
74
- 1. **Layered indirection for an HTTP POST.** Four abstraction layers to send a query string
75
- and parse JSON. *MR angle:* keep graphlient a genuinely thin, optional convenience layer;
76
- don't deepen coupling. Make the "drop graphlient, use graphql-client directly" path easy.
77
-
78
- 2. **Heavy/opinionated dependency footprint.** graphql-client pulls in **ActiveSupport**
79
- (only for `constantize`/Inflector at `lib/graphql/client.rb:161`) and the *full*
80
- **graphql-ruby** gem to act as a client. *MR angle:* scope down the ActiveSupport surface
81
- (the Inflector use is small and replaceable) — a frequent complaint for client-only use.
82
-
83
- 3. **Design-philosophy mismatch — forced component isolation.** The constant-bound,
84
- colocated-fragment model raises `ImplicitlyFetchedFieldError` on reuse-only fragments.
85
- This is exactly issue #76. *MR angle:* the inline-named-fragment convention (documented)
86
- + the planned anchored-regex parse fix together give a sanctioned "fragments for pure
87
- reuse" path. Land both.
88
-
89
- 4. **Client-side schema coupling.** Parse-time validation needs a vendored schema artifact
90
- kept in sync with the server. *MR angle:* document/support a dynamic-query path
91
- (`allow_dynamic_queries`) clearly so teams that build queries at runtime aren't forced
92
- into static constants.
93
-
94
- 5. **Maintenance / bus-factor optics.** graphlient last released 0.8.0 (Jan 2024); graphql-client
95
- moved to the low-staff `github-community-projects/` org. *MR angle (non-code):* CHANGELOG +
96
- CONTRIBUTING hygiene (already in flight on `chore/add-changelog-contributing`) and steady,
97
- well-scoped PRs are the cheapest way to improve adoption confidence.
98
-
99
- 6. **Parse-step robustness.** The unanchored `str.match(/fragment\s*#{const_name}/)` heuristic
100
- at `lib/graphql/client.rb:161` misfires on prefix collisions (`...UserFields` vs
101
- `fragment UserFieldsExtended`) and `\s*` even matches `fragmentUserFields`. *MR angle:*
102
- the planned **Step 5** anchored fix (`fragment\s+Name\s+on\b`), stacked on the
103
- characterization specs. Note upstream tried + reverted a fix here once (`c576c2e` →
104
- `b5a4612`), so characterization-first is the right order.
105
-
106
- **Note on the "GitLab removed it" premise:** a GitLab.com group search for `graphql-client`
107
- MRs (2026-06-16) surfaced **dependency *updates*, not removals** — e.g. triage-ops bumping
108
- graphql-client to `~> 0.26.0` (merged Feb 2026). i.e. GitLab appears to actively maintain the
109
- dep, the opposite of dropping it. Treat the rendered search as indicative only (may be
110
- login-gated/partial); no verified evidence of a major-org removal exists. ecoPortal's own
111
- CI/CD removal was an internal decision, not an industry trend. See memory
112
- `project-fork-revival-strategy`.
113
-
114
- **When to revisit:** Items 3 & 6 are active (the #76 docs MR + Step-5 fix). Items 1, 2, 4 are
115
- future upstream MRs once the fork-revival sequence (hygiene → characterization → parse fix) lands.
116
-
117
- **Update (2026-06-16):** the fork-revival PRs are OPEN upstream — graphql-client #79 (hygiene),
118
- #80 (inline-fragment docs), #81 (characterization tests), #82 (the anchored parse fix). #82
119
- verified RuboCop-clean + tests green locally. See memory `session-handoff-graphql-client-revival`.
120
-
121
- ---
122
-
123
- ## [LOW] graphql-client 1.0 (#15) — aligned contribution candidates
124
-
125
- **Logged:** 2026-06-23
126
- **Upstream:** `github-community-projects/graphql-client` issue
127
- [#15](https://github.com/github-community-projects/graphql-client/issues/15) (1.0.0 roadmap,
128
- rmosolgo, Feb 2024). Triggered by our #76 comment pinging the maintainer.
129
- **Priority:** LOW for us — opportunistic, only as the fork-revival PRs (#79–#82) progress.
130
-
131
- **State of the #15 roadmap (verified 2026-06-23 via issue + comments):** rmosolgo listed three
132
- pre-1.0 items and invited community suggestions ("If anyone has other suggestions for 1.0, please
133
- share them in a comment below").
134
-
135
- 1. **Performance audit** (view helpers + runtime) — rmosolgo reported "performance audit work"
136
- complete (Nov 14 2024). No formal closure; the runtime hot path includes `Client#parse`.
137
- 2. **Subscription support** — **STILL OPEN** (the one unaddressed 1.0 item), and **genuinely out of
138
- our lane.** A brief 2026-06-23 detour considered that the upcoming ecoPortal webhooks feature
139
- might use GraphQL subscriptions, but the Confluence draft settles it: webhooks are **outbound
140
- HTTP POST** (Standard Webhooks / Ed25519), and the "subscription" there is just a
141
- **webhook-registration entity** CRUD'd via ordinary mutations — NOT a GraphQL `subscription` root
142
- type. So webhooks do not pull #15 item 2 into scope. The webhook-registration CRUD *is* in our
143
- lane, but as plain queries/mutations in this gem once it hits the PROD schema — no upstream
144
- graphql-client change needed. See memory `project-webhooks-subscriptions`. Revisit only if some
145
- *other* future need exposes a real `subscription` root type.
146
- 3. **Response metadata in HTTP results** — **DONE** via billybonks' PR #43 (rate-limit headers etc.);
147
- rmosolgo closed the item Nov 14 2024.
148
-
149
- So the literal roadmap is mostly closed except subscriptions. The real opening is the maintainer's
150
- **open invitation for 1.0 suggestions** — a 1.0 is the semver moment to land convention/cleanup
151
- changes that overlap exactly with our aim. Candidate contributions, each already partly in flight:
152
-
153
- - **[in flight] Land the parse-robustness fix (#82) as a 1.0 correctness item.** Anchored
154
- `fragment\s+Name\s+on\b` regex at `client.rb:161`. Touches the "runtime code" the perf audit
155
- named; converts a crash into a clean `ValidationError`. Cheapest, most defensible 1.0 contribution.
156
- - **[in flight] Bless "fragments for pure reuse" as a documented 1.0 convention (#76 + #80/#81).**
157
- Inline named fragments, Apollo-aligned, backwards-compatible (constant-bound isolation kept as the
158
- opt-in boundary). A behaviour/philosophy clarification is best made at a 1.0 boundary — frame it
159
- to the maintainer that way.
160
- - **[new] Scope down the ActiveSupport dependency.** graphql-client pulls full ActiveSupport for
161
- `constantize`/Inflector around `client.rb:161`. A 1.0 is when you'd drop/loosen a heavy dep;
162
- classic adoption-friction win (friction item #2 above). Spec out a small internal constantize or
163
- a narrower require before proposing.
164
- - **[new] Document/support the dynamic-query path (`allow_dynamic_queries`).** Lets teams build
165
- queries at runtime without static constants — pairs with the fragments work and the schema-coupling
166
- friction (item #4 above). Likely docs + a small guard test.
167
- - **[new] Named operations.** Optional operation names for server-side logging / APM / persisted
168
- queries — also the natural identifier subscriptions would need later, so it nudges item #2 forward
169
- without committing us to transports. Couples with the graphlient named-query DSL
170
- (`query(:GetInvoice)`) already noted in memory `project-graphql-client-redesign`.
171
-
172
- **Suggested action (low urgency):** once #79–#82 are merged, post a single comment on #15 offering
173
- the parse-fix + inline-fragment convention + ActiveSupport scope-down as concrete 1.0 suggestions,
174
- and noting subscriptions are out of our scope (we drive query/mutation over HTTP POST; ecoPortal's
175
- webhooks are outbound HTTP, not GraphQL subscriptions — see item 2). Do not post before the revival
176
- PRs land (keeps our credibility — ship the easy ones first).
177
-
178
- **When to revisit:** after graphql-client #79–#82 merge. See memories
179
- `project-fork-revival-strategy`, `session-handoff-graphql-client-revival`.
180
-
181
- ---
182
-
183
- ## [MEDIUM] graphlient masks client-side graphql-client errors (lost `inner_exception`)
184
-
185
- **Logged:** 2026-06-16
186
- **Upstream repo:** `C:\ruby_scripts\git\graphlient` — fork `rellampec/graphlient`, branch `feature/directives-dsl-support`
187
-
188
- **Problem:** `lib/graphlient/client.rb` rescues `GraphQL::Client::Error` in both `parse` (L19-20)
189
- and `execute` (L41-42) and re-raises `ClientError, e.message` — passing only the message string.
190
- graphlient's base `Error#initialize(message, inner_exception = nil)` is built to carry the
191
- original, and `FaradayServerError` uses it correctly (`super(inner_exception.message, inner_exception)`),
192
- but these two rescues leave `inner_exception = nil`. Lost: the original error's class and the
193
- source-location backtrace graphql-client deliberately sets (e.g. `uninitialized constant X`,
194
- static-validation messages). Related inconsistency: `faraday_adapter.rb:73` passes the exception
195
- as the *message* arg, so it also leaves `inner_exception` nil.
196
-
197
- **Fix:** `raise Graphlient::Errors::ClientError.new(e.message, e)` (matches FaradayServerError).
198
- If the symptom is a *blank* message rather than a lost cause, also audit the
199
- `GraphQLError`/`ExecutionError#to_s` path (formats from `errors.details`, can render empty).
200
-
201
- **Note:** user recalls patching BOTH graphql-client and graphlient for this — so there may be a
202
- graphql-client-side aspect too (the #82 anchoring fix already converts one crash into a clean
203
- `ValidationError`). Own graphlient PR, separate from #115/#116. Test-first (repro spec asserting
204
- `inner_exception` populated). Full detail: memory `graphlient-error-masking`.
205
-
206
- **When to revisit:** after the #116 DSL work; or sooner if error opacity bites again during the
207
- template-update / Workflow* work.
208
-
209
- ---
210
-
211
- ## [VERY LOW] Extract `SnakeCamelIndifferentAccess` into its own gem
212
-
213
- **Logged:** 2026-06-05
214
- **Origin:** `eco-helpers` — `lib/eco/data/hashes/sanke_camel_indifferent_access.rb`
215
- (note: filename has a typo — `sanke` instead of `snake`)
216
- **Specs:** already exist in `eco-helpers` spec suite
217
-
218
- **Idea:** Extract `Eco::Data::Hashes::SnakeCamelIndifferentAccess` (and its dependencies
219
- `Eco::Data::Strings::SnakeCase`, `Eco::Data::Strings::CamelCase`) into a standalone gem.
220
- Then use it in `ecoportal-api-graphql`'s base model so that GraphQL response docs (stored
221
- internally in camelCase as the API returns them) are also accessible via snake_case Ruby
222
- methods — without a full key rename on every read.
223
-
224
- **What the module does:**
225
- Hash accessor that treats `fooBar` and `foo_bar` as the same key. Configurable preferred
226
- form (defaults to `:snake_case`). Operations: `get`, `set`, `merge!`, `each`, `slice`,
227
- `camelize_keys`, `snakeize_keys`, all case-indifferent. Uses round-trip conversion via
228
- `snake_case(camelCase) ↔ camelCase(snake_case)`.
229
-
230
- **Why this matters for the GraphQL gem:**
231
- Currently `ecoportal-api-graphql` models map every field explicitly via `passthrough`,
232
- `passboolean`, etc. using camelCase names matching the API (e.g. `passthrough :patchVer`).
233
- Ruby callers must use `model.patchVer`. With indifferent access, `model.patch_ver` and
234
- `model.patchVer` would both work without any change to the field declarations. This
235
- eliminates the Ruby style inconsistency that has been a known pain point for 4+ years.
236
-
237
- **Proposed gem name:** `hash_case_indifferent` or `camel_snake_hash` (TBD)
238
-
239
- **Work involved:**
240
- 1. Extract module + string helpers + specs into new gem repo
241
- 2. Add gem as dependency of `eco-helpers` and `ecoportal-api-graphql`
242
- 3. Include in `Common::GraphQL::Model` (or `Logic::BaseModel`) so all model
243
- instances support both `model.patchVer` and `model.patch_ver`
244
- 4. Verify no regressions in downstream `eco-helpers` scripts that use camelCase access
245
-
246
- **Prerequisite:** `as_update`/`as_input` pipeline (Group 4) should be stable first,
247
- since that pipeline reads field values off models. Once indifferent access is in place,
248
- the diff/input code may need to handle both key forms for lookups.
249
-
250
- **When to revisit:** After Group 4 + Page model Phase A/B are stable. New project
251
- milestone, not a small refactor.