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,40 +0,0 @@
1
- # rubocop:disable Gemspec/DevelopmentDependencies
2
- lib = File.expand_path('lib', __dir__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'ecoportal/api/graphql_version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'ecoportal-api-graphql'
8
- spec.version = Ecoportal::API::GRAPQL_VERSION
9
- spec.authors = ['Oscar Segura']
10
- spec.email = ['oscar@ecoportal.co.nz']
11
-
12
- spec.summary = 'A collection of helpers for interacting with the ecoPortal GraphQL API'
13
- spec.homepage = 'https://www.ecoportal.com'
14
- spec.licenses = %w[MIT]
15
-
16
- spec.metadata['rubygems_mfa_required'] = 'true'
17
-
18
- spec.required_ruby_version = '>= 3.2.2'
19
-
20
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
21
- f.match(/^(test|spec|features)\//)
22
- end
23
- spec.bindir = 'exe'
24
- spec.executables = spec.files.grep(/^exe\//) { |f| File.basename(f) }
25
- spec.require_paths = ['lib']
26
-
27
- spec.add_development_dependency 'pry', '>= 0.14'
28
- spec.add_development_dependency 'rake', '>= 13.0.3', '< 14'
29
- spec.add_development_dependency 'redcarpet', '>= 3.6.0', '< 4'
30
- spec.add_development_dependency 'rspec', '>= 3.12.0', '< 4'
31
- spec.add_development_dependency 'rubocop', '~> 1'
32
- spec.add_development_dependency 'rubocop-rake', '~> 0'
33
- spec.add_development_dependency 'yard', '>= 0.9.34', '< 1'
34
-
35
- spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.16'
36
- spec.add_dependency 'ecoportal-api-v2', '~> 3.3', '>= 3.3.3'
37
- spec.add_dependency 'graphlient', '>= 0.8.0', '< 0.9'
38
- end
39
-
40
- # rubocop:enable Gemspec/DevelopmentDependencies
@@ -1,386 +0,0 @@
1
- #!/usr/bin/env bash
2
- # auto-worker-scheduler.sh
3
- # Scheduler for auto-todo-worker. Called by the Claude Code Stop hook.
4
- # Reads local config, checks token budget and conflict guard, then
5
- # launches the worker as a background process if all gates pass.
6
- #
7
- # Log format: TIMESTAMP | ACTION | REASON
8
- # Log file: .ai-assistance/local/auto-worker-changes.log
9
- #
10
- # Usage: bash scripts/auto-worker-scheduler.sh
11
- # (called automatically by Stop hook — no arguments)
12
- #
13
- # Requires: jq (for JSON parsing). Falls back to grep/sed on missing jq.
14
-
15
- set -euo pipefail
16
-
17
- # ---------------------------------------------------------------------------
18
- # Paths (all relative — script must be run from repo root)
19
- # ---------------------------------------------------------------------------
20
- TOKEN_BUDGET_JSON=".ai-assistance/token-budget.json"
21
- LAST_RUN_JSON=".ai-assistance/local/auto-worker-last-run.json"
22
- SESSION_LOCK=".ai-assistance/local/session.lock"
23
- CHANGES_LOG=".ai-assistance/local/auto-worker-changes.log"
24
- STDOUT_LOG=".ai-assistance/local/auto-worker-stdout.log"
25
- STDERR_LOG=".ai-assistance/local/auto-worker-stderr.log"
26
- PROMPT_FILE=".ai-assistance/local/auto-worker-prompt.txt"
27
-
28
- # --dry-run: run every gate and log what WOULD launch, but spawn nothing.
29
- # Use this to smoke-test the gates without burning tokens on a real worker.
30
- DRY_RUN=0
31
- if [ "${1:-}" = "--dry-run" ]; then
32
- DRY_RUN=1
33
- fi
34
-
35
- # ---------------------------------------------------------------------------
36
- # Helpers
37
- # ---------------------------------------------------------------------------
38
-
39
- # ai_log_usage <component> <action> <detail> [session_id]
40
- # Inline copy — scheduler is self-contained; common.sh is not sourced here.
41
- # Never fails fatally; all errors suppressed.
42
- ai_log_usage() {
43
- local component="${1:-unknown}"
44
- local action="${2:-run}"
45
- local detail="${3:-}"
46
- local session_id="${4:-}"
47
- local log_dir=".ai-assistance/local/kpi"
48
- local week
49
- week=$(date +%Y-W%V 2>/dev/null || echo "unknown")
50
- local log_file="${log_dir}/usage-${week}.jsonl"
51
- local ts
52
- ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "unknown")
53
- mkdir -p "$log_dir" 2>/dev/null || true
54
- printf '{"component":"%s","action":"%s","detail":"%s","ts":"%s","session_id":"%s"}\n' \
55
- "$component" "$action" "$detail" "$ts" "$session_id" >> "$log_file" 2>/dev/null || true
56
- }
57
-
58
- log_action() {
59
- local action="$1"
60
- local reason="$2"
61
- local ts
62
- ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date +"%Y-%m-%dT%H:%M:%SZ")
63
- mkdir -p "$(dirname "$CHANGES_LOG")"
64
- printf "%s | %s | %s\n" "$ts" "$action" "$reason" >> "$CHANGES_LOG"
65
- }
66
-
67
- # Read a JSON field using jq if available, otherwise grep+sed fallback.
68
- # Usage: json_field <file> <dotted.key> e.g. json_field config.json auto_worker.enabled
69
- json_field() {
70
- local file="$1"
71
- local key="$2"
72
- if command -v jq >/dev/null 2>&1; then
73
- jq -r ".${key} // empty" "$file" 2>/dev/null
74
- else
75
- # Minimal fallback: grep for the last segment key, extract bare value.
76
- local leaf
77
- leaf=$(printf '%s' "$key" | sed 's/.*\.//')
78
- grep -o "\"${leaf}\"[[:space:]]*:[[:space:]]*[^,}]*" "$file" 2>/dev/null \
79
- | tail -1 \
80
- | sed 's/.*:[[:space:]]*//' \
81
- | tr -d '"' \
82
- | tr -d ' '
83
- fi
84
- }
85
-
86
- # Make silent failures visible. With `set -e`, any unexpected non-zero exit
87
- # fires this trap and records an ERROR line before the script dies. The Stop
88
- # hook swallows stderr (`2>/dev/null || true`), so without this an early
89
- # failure (missing jq/python, malformed JSON, etc.) would leave no trace at
90
- # all — which is exactly how this script went dark without anyone noticing.
91
- trap 'rc=$?; log_action "ERROR" "scheduler aborted at line ${LINENO} (rc=${rc})"' ERR
92
-
93
- # ---------------------------------------------------------------------------
94
- # Gate 1: auto_worker.enabled
95
- # ---------------------------------------------------------------------------
96
-
97
- if [ ! -f "$TOKEN_BUDGET_JSON" ]; then
98
- log_action "SKIP" "token-budget.json not found — auto_worker not configured"
99
- ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled -- token-budget.json not found"
100
- exit 0
101
- fi
102
-
103
- enabled=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.enabled")
104
- if [ "$enabled" != "true" ]; then
105
- log_action "SKIP" "auto_worker.enabled is false or absent in token-budget.json"
106
- ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled"
107
- exit 0
108
- fi
109
-
110
- # ---------------------------------------------------------------------------
111
- # Gate 1b: Eligible work must exist — at least one [auto] / [auto:last] item
112
- # ---------------------------------------------------------------------------
113
- # Without this gate, every trigger spins up a full `claude --print` worker even
114
- # when TODO.md has zero tagged items — the worker then no-ops, burning a launch
115
- # (and tokens) for nothing. This was the pattern behind the 42 historical
116
- # launches that completed no work, and it is also why a repo with no TODO.md at
117
- # all (e.g. a fresh sibling) must SKIP rather than launch into an error.
118
- # The tag set is the eligibility boundary defined in
119
- # standards/tooling/auto-worker-eligibility.md.
120
- # NOTE: \[auto\] does NOT match [auto:last] — the regex MUST be \[auto(:last)?\].
121
- TODO_FILE="TODO.md"
122
- if [ ! -f "$TODO_FILE" ]; then
123
- log_action "SKIP" "No TODO.md in repo root — no eligible work to schedule"
124
- ai_log_usage "script/auto-worker-scheduler" "skip" "no TODO.md present"
125
- exit 0
126
- fi
127
- if ! grep -qE '\[auto(:last)?\]' "$TODO_FILE"; then
128
- log_action "SKIP" "No eligible work — no [auto] or [auto:last] items in TODO.md"
129
- ai_log_usage "script/auto-worker-scheduler" "skip" "no [auto]/[auto:last] items in TODO.md"
130
- exit 0
131
- fi
132
-
133
- # ---------------------------------------------------------------------------
134
- # Gate 2: Conflict guard — check for active Claude session via session.lock
135
- # ---------------------------------------------------------------------------
136
-
137
- if [ -f "$SESSION_LOCK" ]; then
138
- lock_content=""
139
- if [ -r "$SESSION_LOCK" ]; then
140
- lock_content=$(cat "$SESSION_LOCK" 2>/dev/null || true)
141
- fi
142
- log_action "SKIP" "Active Claude session detected (session.lock present: ${lock_content})"
143
- ai_log_usage "script/auto-worker-scheduler" "skip" "session.lock present"
144
- exit 0
145
- fi
146
-
147
- # ---------------------------------------------------------------------------
148
- # Gate 3: Token budget threshold check
149
- # ---------------------------------------------------------------------------
150
- # Find the current ISO week KPI file. Format: weekly-YYYY-WNN.json
151
- # We compute ISO week ourselves for portability.
152
-
153
- iso_week() {
154
- # Returns YYYY-WNN string for today (ISO 8601 week numbering)
155
- if command -v python3 >/dev/null 2>&1; then
156
- python3 -c "
157
- import datetime
158
- today = datetime.date.today()
159
- iso = today.isocalendar()
160
- print('%04d-W%02d' % (iso[0], iso[1]))
161
- "
162
- elif command -v python >/dev/null 2>&1; then
163
- python -c "
164
- import datetime
165
- today = datetime.date.today()
166
- iso = today.isocalendar()
167
- print('%04d-W%02d' % (iso[0], iso[1]))
168
- "
169
- else
170
- # Fallback: use date command Week-of-year (not strictly ISO but close enough)
171
- date +"%Y-W%V" 2>/dev/null || date +"%Y-W%U"
172
- fi
173
- }
174
-
175
- KPI_DIR=".ai-assistance/local/kpi"
176
- WEEK_KEY=$(iso_week)
177
- KPI_FILE="${KPI_DIR}/weekly-${WEEK_KEY}.json"
178
-
179
- # Read quota and usage from KPI file (if present)
180
- weekly_tokens_used=0
181
- weekly_quota=0
182
- window_start_ts="" # ISO timestamp of first session start in current 5-hour window
183
-
184
- if [ -f "$KPI_FILE" ] && command -v jq >/dev/null 2>&1; then
185
- # Sum tokens_used across all sessions
186
- weekly_tokens_used=$(jq '[.sessions[]?.tokens_used // 0] | add // 0' "$KPI_FILE" 2>/dev/null || echo "0")
187
- weekly_quota=$(jq -r '.quota // 0' "$KPI_FILE" 2>/dev/null || echo "0")
188
-
189
- # Find most recent session start to compute reset window
190
- window_start_ts=$(jq -r '
191
- [.sessions[]?.session_start // empty] | sort | last // empty
192
- ' "$KPI_FILE" 2>/dev/null || true)
193
- fi
194
-
195
- # Read quota from token-budget.json as fallback
196
- if [ "$weekly_quota" = "0" ] || [ -z "$weekly_quota" ]; then
197
- quota_from_config=$(json_field "$TOKEN_BUDGET_JSON" "weekly_quota.total_tokens")
198
- if [ "$quota_from_config" != "null" ] && [ -n "$quota_from_config" ]; then
199
- weekly_quota="$quota_from_config"
200
- fi
201
- fi
202
-
203
- # If no quota is set (null), skip the threshold gate entirely
204
- if [ "$weekly_quota" = "0" ] || [ "$weekly_quota" = "null" ] || [ -z "$weekly_quota" ]; then
205
- log_action "OK" "No weekly quota set — skipping token threshold gate"
206
- else
207
- # Calculate usage percentage
208
- if command -v python3 >/dev/null 2>&1; then
209
- usage_pct=$(python3 -c "
210
- used = float('${weekly_tokens_used}' or '0')
211
- quota = float('${weekly_quota}')
212
- print('%.1f' % (used / quota * 100) if quota > 0 else '0')
213
- ")
214
- else
215
- usage_pct=0
216
- fi
217
-
218
- # Determine time remaining in current 5-hour reset window
219
- # 5-hour window = 18000 seconds
220
- window_seconds_elapsed=0
221
- if [ -n "$window_start_ts" ] && command -v python3 >/dev/null 2>&1; then
222
- window_seconds_elapsed=$(python3 -c "
223
- import datetime, sys
224
- try:
225
- ts = '${window_start_ts}'.replace('Z', '+00:00')
226
- start = datetime.datetime.fromisoformat(ts)
227
- now = datetime.datetime.now(datetime.timezone.utc)
228
- elapsed = (now - start).total_seconds()
229
- print(int(max(0, elapsed)))
230
- except Exception:
231
- print(0)
232
- ")
233
- fi
234
-
235
- WINDOW_DURATION=18000 # 5 hours in seconds
236
- window_remaining=$(( WINDOW_DURATION - window_seconds_elapsed ))
237
- if [ "$window_remaining" -lt 0 ]; then
238
- window_remaining=0
239
- fi
240
- window_remaining_min=$(( window_remaining / 60 ))
241
-
242
- # Determine threshold based on time remaining in window (ADR-007 table)
243
- # >120 min -> 65%; 60-120 -> 75%; 45-60 -> 80%; 30-45 -> 90%; 15-30 -> 95%; <15 -> skip
244
- if [ "$window_remaining_min" -lt 15 ]; then
245
- log_action "SKIP" "Less than 15 min remaining in token reset window — will not launch"
246
- ai_log_usage "script/auto-worker-scheduler" "skip" "token budget -- less than 15 min remaining in reset window"
247
- exit 0
248
- elif [ "$window_remaining_min" -lt 30 ]; then
249
- threshold=95
250
- elif [ "$window_remaining_min" -lt 45 ]; then
251
- threshold=90
252
- elif [ "$window_remaining_min" -lt 60 ]; then
253
- threshold=80
254
- elif [ "$window_remaining_min" -lt 120 ]; then
255
- threshold=75
256
- else
257
- threshold=65
258
- fi
259
-
260
- # Compare usage_pct against threshold (integer comparison via python3)
261
- if command -v python3 >/dev/null 2>&1; then
262
- over_threshold=$(python3 -c "
263
- pct = float('${usage_pct}' or '0')
264
- thr = float('${threshold}')
265
- print('true' if pct >= thr else 'false')
266
- ")
267
- else
268
- over_threshold="false"
269
- fi
270
-
271
- if [ "$over_threshold" = "true" ]; then
272
- log_action "SKIP" "Token usage ${usage_pct}% >= threshold ${threshold}% (${window_remaining_min} min remaining in window)"
273
- ai_log_usage "script/auto-worker-scheduler" "skip" "token budget at ${usage_pct}% (threshold ${threshold}%)"
274
- exit 0
275
- fi
276
-
277
- log_action "OK" "Token gate passed: ${usage_pct}% used, threshold ${threshold}% (${window_remaining_min} min remaining)"
278
- fi
279
-
280
- # ---------------------------------------------------------------------------
281
- # Gate 4: Determine cadence (informational — actual cron cadence managed by
282
- # CronCreate; this script just logs the recommendation)
283
- # ---------------------------------------------------------------------------
284
-
285
- recommended_cadence="2hr"
286
- if [ -f "$LAST_RUN_JSON" ] && command -v jq >/dev/null 2>&1; then
287
- completed_count=$(jq '.completed | length' "$LAST_RUN_JSON" 2>/dev/null || echo "0")
288
- if [ "$completed_count" -gt "0" ]; then
289
- recommended_cadence="30min"
290
- fi
291
- fi
292
-
293
- # ---------------------------------------------------------------------------
294
- # Gate 5: dangerously_skip_permissions is REQUIRED
295
- # ---------------------------------------------------------------------------
296
- # A headless `claude --print` worker has no way to answer interactive
297
- # permission prompts — without skip-permissions it stalls indefinitely and
298
- # does no work. Require explicit opt-in rather than launching a worker that
299
- # can only hang.
300
-
301
- skip_perms=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.dangerously_skip_permissions")
302
- if [ "$skip_perms" != "true" ]; then
303
- log_action "SKIP" "auto_worker.dangerously_skip_permissions is not true — a headless (--print) worker cannot answer permission prompts and would stall. Set it to true in token-budget.json to enable."
304
- ai_log_usage "script/auto-worker-scheduler" "skip" "dangerously_skip_permissions not enabled -- headless worker requires it"
305
- exit 0
306
- fi
307
-
308
- AUTO_WORKER_PROMPT="Run the auto-worker for this repo.
309
-
310
- Instructions:
311
- 1. Read TODO.md and collect all items tagged \`[auto]\` or \`[auto:last]\`.
312
- 2. If no \`[auto]\` items exist, exit cleanly with a brief log entry.
313
- 3. Plan execution order as a dependency graph — do NOT follow TODO list order.
314
- - Items without dependencies on each other may run in parallel (branch out when the work is self-contained enough to isolate).
315
- - \`[auto:last]\` items run only after all \`[auto]\` items complete or are skipped.
316
- 4. For each item:
317
- - Execute the task.
318
- - Mark it \`[x]\` in TODO.md on completion.
319
- - Add any discovered follow-up tasks as new TODO items; tag \`[auto]\` only if they meet the eligibility criteria in standards/tooling/auto-worker-eligibility.md.
320
- 5. Do NOT touch items that are not tagged \`[auto]\` or \`[auto:last]\`.
321
- 6. At the end, write a one-paragraph summary of what was done, skipped, and added to TODO."
322
-
323
- # Write the prompt to a file and feed it to claude via stdin. This avoids the
324
- # quoting/newline hazards of passing a multi-line prompt as a CLI argument —
325
- # the previous Windows path crammed the whole prompt (newlines, quotes,
326
- # backticks) into a single PowerShell -ArgumentList string, which produced a
327
- # broken command that launched nothing while still logging "LAUNCHED".
328
- mkdir -p "$(dirname "$PROMPT_FILE")"
329
- printf '%s\n' "$AUTO_WORKER_PROMPT" > "$PROMPT_FILE"
330
-
331
- # ---------------------------------------------------------------------------
332
- # Dry run: log what would happen, spawn nothing.
333
- # ---------------------------------------------------------------------------
334
- if [ "$DRY_RUN" = "1" ]; then
335
- log_action "DRY-RUN" "All gates passed. Would launch: claude --dangerously-skip-permissions --print < ${PROMPT_FILE} > ${STDOUT_LOG} (cadence recommendation: ${recommended_cadence})"
336
- ai_log_usage "script/auto-worker-scheduler" "dry-run" "all gates passed -- would launch auto-todo-worker"
337
- exit 0
338
- fi
339
-
340
- # ---------------------------------------------------------------------------
341
- # Launch: run auto-todo-worker as a detached background process.
342
- # Output is ALWAYS redirected to STDOUT_LOG/STDERR_LOG so a failed or empty
343
- # run leaves a trace (the previous Windows path captured nothing).
344
- # ---------------------------------------------------------------------------
345
-
346
- log_action "LAUNCH" "Launching auto-todo-worker (cadence recommendation: ${recommended_cadence})"
347
- ai_log_usage "script/auto-worker-scheduler" "launch" "launching auto-todo-worker"
348
-
349
- launch_pid=""
350
- if [[ "${OSTYPE:-}" == "msys" ]] || [[ "${OSTYPE:-}" == "cygwin" ]] || \
351
- [[ "${OS:-}" == "Windows_NT" ]]; then
352
- # Windows via Git Bash. Start-Process detaches the worker from this hook's
353
- # shell so it outlives the Stop hook. -RedirectStandardInput feeds the
354
- # prompt file (no fragile arg quoting); -RedirectStandardOutput/Error
355
- # capture the run; -PassThru returns the REAL worker PID (the old code
356
- # logged the wrapper job's PID via `$!`, which was meaningless).
357
- # Note: stdout and stderr MUST be different files — Start-Process rejects
358
- # redirecting both to the same path.
359
- abs_prompt="$(cygpath -w "$PROMPT_FILE" 2>/dev/null || printf '%s' "${PWD}/${PROMPT_FILE}")"
360
- abs_stdout="$(cygpath -w "$STDOUT_LOG" 2>/dev/null || printf '%s' "${PWD}/${STDOUT_LOG}")"
361
- abs_stderr="$(cygpath -w "$STDERR_LOG" 2>/dev/null || printf '%s' "${PWD}/${STDERR_LOG}")"
362
- abs_cwd="$(cygpath -w "$PWD" 2>/dev/null || printf '%s' "${PWD}")"
363
- launch_pid=$(powershell -NoProfile -Command "
364
- \$p = Start-Process -FilePath 'claude' \
365
- -ArgumentList '--dangerously-skip-permissions','--print' \
366
- -RedirectStandardInput '${abs_prompt}' \
367
- -RedirectStandardOutput '${abs_stdout}' \
368
- -RedirectStandardError '${abs_stderr}' \
369
- -WorkingDirectory '${abs_cwd}' \
370
- -NoNewWindow -PassThru
371
- \$p.Id" 2>/dev/null | tr -d '[:space:]') || true
372
- else
373
- # Unix / macOS / WSL2: feed prompt via stdin, capture output, detach.
374
- nohup claude --dangerously-skip-permissions --print \
375
- < "$PROMPT_FILE" > "$STDOUT_LOG" 2>&1 &
376
- launch_pid=$!
377
- fi
378
-
379
- if [ -n "${launch_pid:-}" ]; then
380
- log_action "LAUNCHED" "Background worker PID ${launch_pid} — output -> ${STDOUT_LOG}"
381
- ai_log_usage "script/auto-worker-scheduler" "launched" "worker PID ${launch_pid}"
382
- else
383
- log_action "ERROR" "Launch returned no PID — worker likely failed to start. Check ${STDERR_LOG}."
384
- ai_log_usage "script/auto-worker-scheduler" "error" "launch returned no PID"
385
- fi
386
- exit 0
data/tests/actions_get.rb DELETED
@@ -1,7 +0,0 @@
1
- require_relative 'local_libs'
2
-
3
- api = Ecoportal::API::GraphQL.new
4
- api.currentOrganization.actions.each_with_index do |action, idx|
5
- break if idx >= 14
6
- puts "#{idx+1}. '#{action.name}' (#{action.altId}): #{action.status} (#{action.relativeStatus})"
7
- end
@@ -1,20 +0,0 @@
1
- require_relative 'local_libs'
2
- require 'ecoportal/api-graphql'
3
-
4
- api = Ecoportal::API::GraphQL.new
5
- api.currentOrganization.contractorEntities.each_with_index do |entity, idx|
6
- break if idx >= 14
7
- puts "#{idx+1}. #{entity.name}"
8
- end
9
-
10
- result = api.currentOrganization.contractorEntities(first: 10)
11
- result.nodes.each {|entity| puts "• #{entity.name}"}
12
-
13
- result = api.currentOrganization.contractorEntities(first: 5) {
14
- nodes {
15
- id
16
- name
17
- approved
18
- }
19
- }
20
- result.nodes.each {|entity| puts "#{entity.name} => approved? #{entity.approved}"}
@@ -1,19 +0,0 @@
1
- require_relative 'local_libs'
2
-
3
- api = Ecoportal::API::GraphQL.new
4
- response = api.createContractorEntity(input: {
5
- name: "Caras Galathon",
6
- active: true,
7
- approved: true,
8
- associatedPeopleIds: [
9
- "5adfbef76fb80400047535b3"
10
- ],
11
- leadContractorIds: [
12
- "5adfbef76fb80400047535b3"
13
- ],
14
- clientMutationId: ""
15
- })
16
-
17
- if response.success? && (entity = response.item)
18
- puts "Created contractor entity '#{entity.name}' (approved? #{entity.approved})"
19
- end
@@ -1,20 +0,0 @@
1
- require_relative 'local_libs'
2
-
3
- api = Ecoportal::API::GraphQL.new
4
- response = api.contractorEntity.update(input: {
5
- id: "62b04d83fa9170001d3d303f",
6
- name: "Caras Galathon",
7
- active: true,
8
- approved: true,
9
- # associatedPeopleIds: [
10
- # "5adfbef76fb80400047535b3"
11
- # ],
12
- leadContractorIds: {
13
- additions: ["5adfbef76fb80400047535b3"]
14
- },
15
- clientMutationId: ""
16
- })
17
-
18
- if response.success? && (entity = response.item)
19
- puts "Updated contractor entity '#{entity.name}' (approved? #{entity.approved})"
20
- end
@@ -1,74 +0,0 @@
1
- require_relative 'local_libs'
2
- require 'json'
3
- require 'fileutils'
4
-
5
- # Dump a page's GraphQL model (the raw CommonPageUnion doc) to a JSON file, so a real page
6
- # can be captured as a spec fixture / stub. Run it BEFORE and AFTER a submit/sign_off (or any
7
- # mutation) to capture both states and diff them — the dumps double as regression fixtures.
8
- #
9
- # Usage (run in an env where the GraphQL connection is configured, like the other tests/):
10
- # ruby tests/dump_page_model.rb <PAGE_ID> [--out PATH] [--stage NAME_OR_ID] [--label before|after]
11
- #
12
- # Examples:
13
- # # capture the TOOCS page before submitting
14
- # ruby tests/dump_page_model.rb 6a3225532b97b00073c23aa8 --label before
15
- # # ...run the case live (or a manual updatePage)...
16
- # # capture it after
17
- # ruby tests/dump_page_model.rb 6a3225532b97b00073c23aa8 --label after
18
- # # then diff the two JSON files to see exactly what the submit changed.
19
- #
20
- # Default output path: spec/fixtures/pages/<page_id>[_<label>].json
21
- #
22
- # A --stage NAME_OR_ID also prints the StageView's stage id (the value that becomes stageId on
23
- # a submit) so you can confirm the right stage was resolved.
24
-
25
- def parse_args(argv)
26
- opts = { page_id: nil, out: nil, stage: nil, label: nil }
27
- rest = argv.dup
28
- opts[:page_id] = rest.shift
29
- until rest.empty?
30
- flag = rest.shift
31
- case flag
32
- when '--out' then opts[:out] = rest.shift
33
- when '--stage' then opts[:stage] = rest.shift
34
- when '--label' then opts[:label] = rest.shift
35
- else
36
- warn "Unknown argument: #{flag}"
37
- end
38
- end
39
- opts
40
- end
41
-
42
- opts = parse_args(ARGV)
43
-
44
- unless opts[:page_id]
45
- warn 'Usage: ruby tests/dump_page_model.rb <PAGE_ID> [--out PATH] [--stage NAME_OR_ID] [--label before|after]'
46
- exit 1
47
- end
48
-
49
- default_dir = File.expand_path('../spec/fixtures/pages', __dir__)
50
- default_name = "#{[opts[:page_id], opts[:label]].compact.join('_')}.json"
51
- out_path = opts[:out] || File.join(default_dir, default_name)
52
-
53
- api = Ecoportal::API::GraphQL.new
54
- page = api.pages.get(opts[:page_id], stage_id: opts[:stage])
55
-
56
- if page.nil?
57
- warn "Could not fetch page '#{opts[:page_id]}'"
58
- exit 2
59
- end
60
-
61
- # StageView delegates #doc to the underlying full page; dump the full page doc either way.
62
- doc = page.respond_to?(:doc) ? page.doc : page.instance_variable_get(:@page)&.doc
63
-
64
- FileUtils.mkdir_p(File.dirname(out_path))
65
- File.write(out_path, JSON.pretty_generate(doc))
66
-
67
- puts "Dumped page '#{opts[:page_id]}' → #{out_path}"
68
- puts " name: #{page.respond_to?(:name) ? page.name : '(n/a)'}"
69
- puts " patchVer: #{page.respond_to?(:patchVer) ? page.patchVer : '(n/a)'}"
70
- if opts[:stage]
71
- resolved = page.respond_to?(:current_stage_id) ? page.current_stage_id : nil
72
- puts " stage '#{opts[:stage]}' → stageId that a submit would target: #{resolved.inspect}"
73
- end
74
- puts 'Tip: capture --label before and --label after a submit, then diff the two JSON files.'
@@ -1,90 +0,0 @@
1
- require_relative 'local_libs'
2
- require 'json'
3
- require 'fileutils'
4
-
5
- # Dump a TEMPLATE's GraphQL model to JSON, so a real template can be captured as the golden
6
- # fixture for the template-editing pipeline (build-from-scratch + diff-and-update).
7
- #
8
- # A template IS a page in this API (Query::Templates returns PageUnion nodes), so its structural
9
- # skeleton — stages -> sections -> components(fields) — comes from the ordinary page model, exactly
10
- # like tests/dump_page_model.rb. On top of that, this tool also captures the page's *workflow
11
- # command log* (Query::PagesWorkflowCommands) — the applied command history that drives the
12
- # workflow builder (callbacks / operations / strategies / recipients config).
13
- #
14
- # Two artefacts are written per template:
15
- # spec/fixtures/templates/<id>[_<label>].json -> structural page doc (golden master)
16
- # spec/fixtures/templates/<id>[_<label>].commands.json -> workflow command log (if readable)
17
- #
18
- # Usage (run where the GraphQL connection is configured, like the other tests/):
19
- # ruby tests/dump_template_model.rb <TEMPLATE_ID> [--out PATH] [--label before|after]
20
- #
21
- # Reference target (see .ai-assistance/projects/template-maintenance/DESIGN.md):
22
- # ruby tests/dump_template_model.rb 6a3fa5b8f89e07c758df622b --label golden
23
- #
24
- # Run --label before / --label after around an executeWorkflowCommands batch to diff exactly
25
- # what a command sequence changed — the same before/after workflow dump_page_model.rb enables.
26
-
27
- def parse_args(argv)
28
- opts = { template_id: nil, out: nil, label: nil }
29
- rest = argv.dup
30
- opts[:template_id] = rest.shift
31
- until rest.empty?
32
- flag = rest.shift
33
- case flag
34
- when '--out' then opts[:out] = rest.shift
35
- when '--label' then opts[:label] = rest.shift
36
- else
37
- warn "Unknown argument: #{flag}"
38
- end
39
- end
40
- opts
41
- end
42
-
43
- opts = parse_args(ARGV)
44
-
45
- unless opts[:template_id]
46
- warn 'Usage: ruby tests/dump_template_model.rb <TEMPLATE_ID> [--out PATH] [--label before|after]'
47
- exit 1
48
- end
49
-
50
- default_dir = File.expand_path('../spec/fixtures/templates', __dir__)
51
- default_name = [opts[:template_id], opts[:label]].compact.join('_')
52
- struct_path = opts[:out] || File.join(default_dir, "#{default_name}.json")
53
- commands_path = File.join(File.dirname(struct_path), "#{File.basename(struct_path, '.json')}.commands.json")
54
-
55
- api = Ecoportal::API::GraphQL.new
56
-
57
- # 1) Structural skeleton — a template is a page, so fetch it via the page model.
58
- page = api.pages.get(opts[:template_id])
59
-
60
- if page.nil?
61
- warn "Could not fetch template '#{opts[:template_id]}' as a page"
62
- exit 2
63
- end
64
-
65
- doc = page.respond_to?(:doc) ? page.doc : page.instance_variable_get(:@page)&.doc
66
-
67
- FileUtils.mkdir_p(File.dirname(struct_path))
68
- File.write(struct_path, JSON.pretty_generate(doc))
69
-
70
- puts "Dumped template structure '#{opts[:template_id]}' -> #{struct_path}"
71
- puts " name: #{page.respond_to?(:name) ? page.name : '(n/a)'}"
72
- puts " patchVer: #{page.respond_to?(:patchVer) ? page.patchVer : '(n/a)'}"
73
-
74
- # 2) Workflow command log — the applied-command history behind the workflow builder.
75
- # Wrapped in a rescue: the PagesWorkflow read model is still partial (stages is a bare
76
- # passarray as of SCHEMA_VERSION 20260605), so treat this as best-effort.
77
- begin
78
- query = Ecoportal::API::GraphQL::Query::PagesWorkflowCommands.new(api.client)
79
- commands = []
80
- query.each(id: opts[:template_id], first: 100) do |cmd|
81
- commands << (cmd.respond_to?(:doc) ? cmd.doc : cmd)
82
- end
83
- File.write(commands_path, JSON.pretty_generate(commands))
84
- puts "Dumped #{commands.size} workflow command(s) -> #{commands_path}"
85
- rescue StandardError => e
86
- warn "Workflow command log not captured (#{e.class}: #{e.message})"
87
- warn ' -> structural fixture is still valid; the command log read may be WIP for this org.'
88
- end
89
-
90
- puts 'Tip: capture --label before and --label after an executeWorkflowCommands batch, then diff the JSON.'
@@ -1,10 +0,0 @@
1
- require_relative 'local_libs'
2
-
3
- STRUCT_ID = "592642a4ae0c6a00236b110d".freeze # mini test
4
- # STRUCT_ID = "626745bcba616a001f7e5011" # wcc
5
-
6
- api = Ecoportal::API::GraphQL.new
7
- api.currentOrganization.locationStructure(id: STRUCT_ID).tap do |tree|
8
- puts "'#{tree.name}' Structure (#{tree.id})"
9
- pp tree.treeify
10
- end