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,126 +0,0 @@
1
- # Risks & Blockers — Ooze → Native GraphQL Migration
2
-
3
- ---
4
-
5
- ## BLOCKER (external dependency, UNCONFIRMED): GraphQL forces not yet verified end-to-end
6
-
7
- **~50% of all ooze cases use forces** (`target.forces`, `force.bindings.{get_by_name,add,
8
- delete!}`, `force.custom_script`, `force.script`).
9
-
10
- **Correction (2026-06-30):** the GraphQL force endpoint is NOT simply "missing" — the gem
11
- already carries client code for BOTH read and mutate:
12
- - **Read:** `Ecoportal::API::GraphQL::Query::PageWithForces` fetches `page.forces { ...ForceFields }`.
13
- - **Mutate:** `Mutation::Page::ExecuteForceCommands` → the `executeWorkflowCommands` mutation,
14
- supporting `editForce { id, customScript }`, `addBinding`, `removeBinding`, `removeForce`
15
- (plus `addForce`/`reorderForces` `WorkflowCommandInput` variants). So a force WRITE path
16
- exists via the workflow-command bus — it is **not** read-only at the client layer.
17
-
18
- What is still UNCONFIRMED (and why this stays a blocker until verified live, not via code-read):
19
- - `OozeRedirect.force_support?` is hard-wired to return `false`
20
- (`lib/eco/api/usecases/graphql/compat/ooze_redirect.rb`) — forces are gated OFF.
21
- - The `ooze_redirect.rb` comment claims `PageWithForces` currently fails schema validation
22
- (selections on PageUnion; `id` on `DataFieldBinding`/`SectionBinding`; unused `ForceFields`)
23
- and that the backend is "in progress" — but that note may be STALE given the mutation classes
24
- now present. **Phase 0 must live-test PageWithForces + a no-op editForce against a real org**
25
- to establish actual readiness rather than trusting the comment.
26
- - Activating `ForceCompat` on the mere *existence* of those classes would make **every**
27
- OozeRedirect case (even non-force ones like TOOCS) fetch via a possibly-broken force query.
28
-
29
- **Impact:** Force-dependent cases are **held pending the Phase 0 live readiness test** of
30
- `PageWithForces` + `executeWorkflowCommands`. If that test passes, forces come INTO scope and
31
- these cases migrate like any other (just with an extra force-command write path). If it fails,
32
- they are partitioned into the "blocked on forces" table in `INVENTORY.md` and remain on the v2
33
- path, and the `OozeRedirect` shim cannot be deleted in full until either (a) all blocked cases
34
- are retired by the customer, or (b) the endpoint is fixed and a follow-up project migrates them.
35
-
36
- **Blocked-on-forces seed list** (from `ooze_redirect.rb` lines 68–77 — re-verify in Phase 0):
37
-
38
- | Org | Cases |
39
- |---|---|
40
- | act-gov | 5× `20240130_act_*_case`, `rearrage_page_sites_case` |
41
- | briscoes | `remove_induction_sections`, `310524_Briscoes_Remove_Tasks` |
42
- | chorus | 4× `audit_update` |
43
- | hcc | `update_enterprise_risk_case` |
44
- | lic | `update_life_cycle_force_case` |
45
- | mitre10 | `rich_text_update`, `update_location_force`, `updating_template` |
46
- | npdc | `contractor_title_force`, `risk_titile_force`, `fix_title_syncing`, `reminder_date_fields`, `10092024_NPDC_CP_Add_Force` |
47
- | profile-group | `int_training_review`, `20231026_profile_wellness` |
48
- | turners-growers | `event_changes`, `inj_cost_calc`, `remove_line_force` |
49
- | twg | `hide_attached_risks`, `add_new_force` |
50
-
51
- **Mitigation / trigger:** Phase 0 live-tests `PageWithForces` (read) and a no-op
52
- `editForce`/`addBinding` via `executeWorkflowCommands` (write) against a real org. If both pass,
53
- restore a real `force_support?` readiness check, fold forces into the main migration scope, and
54
- drop this from the blocker list. If either fails, track the fix with Engineering and keep the
55
- cases above on v2; do not delete `force_compat.rb` or the v2 fallback path that serves them.
56
-
57
- ---
58
-
59
- ## Risk: monkeypatch regressions during coexistence
60
-
61
- While native and shim coexist, the global `FieldPatches` (v2-class `===`/`is_a?` reopening,
62
- GraphQL field `prepend`s) remain process-wide. A native case loaded in the same process as
63
- a shimmed case shares those patches.
64
-
65
- **Mitigation:** Native cases must not *depend* on the patches (they dispatch on GraphQL
66
- types directly). Spec the native classes in isolation **without** loading `OozeRedirect`, to
67
- prove independence. Only retire a given `FieldPatch` once the last case needing it is
68
- flipped (tracked in DECISIONS.md).
69
-
70
- ---
71
-
72
- ## Risk: silent behavioural divergence (the bug that motivated this)
73
-
74
- The 2026-06-30 incident (KPIs read 0, dry-run diff suppressed) was a *silent* divergence
75
- between the shim and the base loop. Native rewrites can re-introduce silent divergence
76
- (e.g. a KPI counter or dedup edge missed).
77
-
78
- **Mitigation:** Per-case A/B parity gate against the captured v2 baseline (KPI block + diff)
79
- is mandatory before flipping. Specs must assert the full KPI set and the dry-run diff shape
80
- (`get_body(page)['page']`), not just "it ran".
81
-
82
- ---
83
-
84
- ## Risk: BC surface drift (a missed override point)
85
-
86
- If a native class omits an override point a customer script relies on (e.g. `search`,
87
- `filters`, `custom_processing`, `excluded_field_hooks`, `paired_fields_post_callback`,
88
- `to_field`, `before_loading_new_target`), that script breaks — a goal regression.
89
-
90
- **Mitigation:** Phase 0 `INVENTORY.md` enumerates every override method per consumer
91
- script. Native classes must expose every one. Re-run each affected live script unchanged as
92
- the final per-phase check.
93
-
94
- ---
95
-
96
- ## Risk: write-path / field-type gaps in the gem
97
-
98
- The compat layer has known gaps (from `eco-helpers-compat/INTENT.md`): `add_field` /
99
- `components.add` (`dataFields.additions`) not implemented; file/image upload unimplemented;
100
- some field types read-only. A case that needs an unsupported write cannot reach parity.
101
-
102
- **Mitigation:** The 21-type field matrix (parity plan) classifies clean-write vs partial vs
103
- read-only. A case requiring a gap is **held** (logged in DECISIONS.md) until the gem closes
104
- the gap — not flipped. `RegisterMigrationCase` (typed field copy) is the highest-exposure
105
- case here.
106
-
107
- ---
108
-
109
- ## Risk: interactive prompts in automated runs
110
-
111
- The base loop reintroduces the v2-faithful `prompt_user` "proceed (y/N)" preview
112
- (local default 'N', 10s timeout). Native cases reproduce this; an automated/remote run that
113
- expects no prompt could hang or abort.
114
-
115
- **Mitigation:** Preserve the existing `run_mode_remote?` → default 'Y' behaviour and the
116
- timeout exactly as the v2 cases have it. Cover it in specs.
117
-
118
- ---
119
-
120
- ## Risk: scope creep into the compat façade or forces
121
-
122
- This project retires the **OozeRedirect monkeypatch**, not the `Compat::Pages`/`Registers`
123
- façade (native cases call it) and not forces.
124
-
125
- **Mitigation:** Non-goals are explicit in INTENT.md. Forces work is a separate project
126
- gated on the endpoint.
@@ -1,261 +0,0 @@
1
- # TODOs — Ooze → Native GraphQL Migration
2
-
3
- Legend: `[ ]` todo · `[~]` in progress · `[x]` done · `[!]` blocked (see Risks/DECISIONS)
4
-
5
- Each phase ends with a gate. **Do not flip an old class name to delegate until that
6
- case's parity result is recorded in DECISIONS.md.**
7
-
8
- > **STATUS 2026-07-02:** Phase 0 inventory DONE → see `INVENTORY.md`. Scope narrowed by Oscar:
9
- > **build the native GraphQL classes ONLY this cycle** — no delegation flip, no shim removal yet
10
- > (those come "long after we know everything works on the native samples"). So Phases 1–2 (native
11
- > classes + specs) are IN; the flip/removal steps stay `[ ]` until Oscar green-lights.
12
-
13
- ---
14
-
15
- ## Phase 0 — Inventory & Baseline (no code changes) ✅ DONE 2026-07-02 (see INVENTORY.md)
16
-
17
- ### 0a. Map the full ooze case inheritance/include tree
18
- - [ ] Confirm the inheritance chain (verified 2026-06-30, re-confirm before building):
19
- - `Common::Loaders::UseCase`
20
- - `OozeSamples::OozeBaseCase` — `include Helpers`, `include Helpers::Rescuable`
21
- - `OozeSamples::OozeRunBaseCase` (single-ooze runner)
22
- - `OozeSamples::OozeUpdateCase`
23
- - `OozeSamples::OozeFromDocCase`
24
- - `OozeSamples::RegisterUpdateCase` — `include Helpers::Creatable`
25
- - `OozeSamples::TargetOozesUpdateCase` ← **first cutover case** (act-gov TOOCS)
26
- - `OozeSamples::RegisterMigrationCase` — `include HelpersMigration`
27
- - `OozeSamples::RegisterExportCase` (standalone — NOT under OozeBaseCase)
28
- - `OozeCases::ExportRegisterCase` — `include Helpers::ExportableRegister`
29
- - [ ] Map the helper module surface each case relies on:
30
- - `OozeSamples::Helpers` aggregate → `Shortcuts`, `Filters`, `OozeHandlers`,
31
- `Creatable`, `Rescuable`, `ExportableOoze`, `ExportableRegister`
32
- - `OozeSamples::HelpersMigration` → `Copying`, `TypedFieldsPairing`
33
- - [ ] For each helper, classify: **v2-coupled** (references
34
- `Ecoportal::API::V2::Page::*`, e.g. `OozeHandlers#merge_values`, `Shortcuts#object_reference`,
35
- `Filters#field_key_name`) vs **pure** (string/array utils — `Shortcuts#same_string?`,
36
- `simplify_string`, `OozeHandlers#merge_arrays`). Pure helpers port verbatim; v2-coupled
37
- helpers need GraphQL-typed re-expression.
38
- - [ ] Record which case methods are leaf v2 ops vs control flow (the line that the shim bug
39
- blurred): control flow = `main`, `with_each_entry`, `update_ooze`, `update_oozes`,
40
- `process_ooze`, `ooze`, `new_target`, the KPI counters, dry-run feedback. Leaf ops =
41
- field/stage access, `apiv2.pages.{get,get_new,create,update,get_body}`,
42
- `apiv2.registers.search`.
43
-
44
- ### 0b. Inventory every downstream script include site (BC surface)
45
- - [ ] In the training repo (`C:\ruby_scripts\implementation\training`) and any other
46
- consumer repos, grep for the public names that scripts subclass/include:
47
- - `OozeSamples::OozeBaseCase`, `OozeRunBaseCase`, `OozeUpdateCase`, `OozeFromDocCase`
48
- - `OozeSamples::RegisterUpdateCase`, `TargetOozesUpdateCase`, `RegisterMigrationCase`
49
- - `OozeSamples::RegisterExportCase`, `OozeCases::ExportRegisterCase`
50
- - `OozeSamples::Helpers::Creatable` and any other helper included directly
51
- - `GraphQL::Compat::OozeRedirect` (the include line scripts add today)
52
- - [ ] Produce `INVENTORY.md` in this folder: per public name, list (a) every consumer
53
- script that subclasses/includes it, (b) which overridable methods that script defines
54
- (`process_ooze`, `search`, `filters`, `custom_processing`, `excluded_field_hooks`, etc.),
55
- (c) whether the script touches forces (→ blocked partition).
56
- - [ ] **Forces live readiness test (decides the partition below).** Do NOT assume forces are
57
- blocked — the gem ships read (`Query::PageWithForces`) AND write
58
- (`executeWorkflowCommands`: `editForce`/`addBinding`/`removeBinding`/`removeForce`) client
59
- code, gated off via `OozeRedirect.force_support? == false`. Against a real org: (a) run
60
- `PageWithForces` on a force-bearing page and confirm the query is schema-valid + returns
61
- populated forces; (b) run a no-op `editForce`/`addBinding` via `executeWorkflowCommands` and
62
- confirm it round-trips. Record the verdict — it determines whether forces are IN scope.
63
- - [ ] **Partition the inventory into two tables: "migratable now" vs "blocked on forces"**
64
- — gated on the readiness test above. If forces pass, there is NO blocked partition. If they
65
- fail, seed the blocked list from `ooze_redirect.rb` lines 68–77 (act-gov, briscoes, chorus,
66
- hcc, lic, mitre10, npdc, profile-group, turners-growers, twg). This partition is the
67
- master schedule for Phases 2..N.
68
-
69
- ### 0c. Capture the v2 baseline behaviour (parity reference)
70
- - [ ] For each migratable case, run the existing v2 path under `-simulate` against the
71
- `mini` test org and save the dry-run diff + KPI block as the golden reference
72
- (`baselines/<case>.txt`). This is the A side of every later A/B comparison.
73
- - [ ] Confirm the A/B parity harness exists / is buildable — see memory
74
- `project-parity-test-plan` (all-21-field template in `mini`, A/B standalone harness in
75
- e.g. `mini/graphql_parity/`, plus the eco-helpers session-backend swap). If the harness
76
- is not yet built, that is a prerequisite sub-task here, not in Phase 2.
77
-
78
- **Gate 0:** INVENTORY.md complete, partitioned, and baselines captured for the migratable set.
79
-
80
- ---
81
-
82
- ## Phase 1 — Extract shared concerns to `graphql/helpers/pages/*`
83
-
84
- Goal: build the native shared substrate **before** any case, so case classes stay thin.
85
- Mirror the existing native helper convention (`graphql/helpers/base/{case_env,graphql_env,
86
- error_handling}.rb`, `graphql/samples/pages/page/base.rb`).
87
-
88
- - [ ] `graphql/helpers/pages.rb` — namespace aggregator (mirrors `helpers/location.rb`).
89
- - [ ] `graphql/helpers/pages/shortcuts.rb` — port the **pure** `OozeSamples::Helpers::Shortcuts`
90
- methods (`same_string?`, `simplify_string`, `titleize`, `normalize_string`,
91
- `clean_question`, `to_i`, `is_number?`). Re-express `object_reference` for GraphQL
92
- page/section/component model types (drop the `Ecoportal::API::V2::*` `case/when`).
93
- - [ ] `graphql/helpers/pages/filters.rb` — port `Filters`, re-expressing `field_key_name`
94
- for GraphQL field types (`value.ref_backend` / `value.label` → GraphQL equivalents).
95
- - [ ] `graphql/helpers/pages/ooze_handlers.rb` — re-express `merge_values` `case/when` against
96
- `Base::Page::DataField::*` subclasses instead of `V2::Page::Component::*`; `merge_arrays`
97
- is pure → port verbatim.
98
- - [ ] `graphql/helpers/pages/rescuable.rb` — port `Rescuable` (validate base type ==
99
- `GraphQL::Base` instead of `Common::Loaders::Base`).
100
- - [ ] `graphql/helpers/pages/creatable.rb` — native `Creatable`: `creating_new_page`,
101
- `drafting_entry` (→ `graphql.pages.get_new`), `create_entry`/`create_ooze`
102
- (→ `graphql.pages.create`), with dry-run feedback. Base-type guard → native register case.
103
- - [ ] `graphql/helpers/pages/patch_feedback.rb` — extract the dry-run/dirty machinery that
104
- the shim bug exposed: `dirty?(page)` (`!as_update.nil?` OR pending submit/sign-off),
105
- `display_patch` / `backup_patch!` reading `graphql.pages.get_body(page)['page']`,
106
- `dry_run_feedback` + `DRY_COUNT` sampling. **This is the one-source-of-truth replacement
107
- for the bug-prone shim-vs-baseloop split.**
108
- - [ ] Spec each helper module in `spec/eco/api/usecases/graphql/helpers/pages/*_spec.rb`.
109
- - [ ] `bundle exec rubocop` clean on all new helper files.
110
-
111
- **Gate 1:** helper specs green; no production case yet depends on them (pure addition).
112
-
113
- ---
114
-
115
- ## Phase 2 — `RegisterUpdateCase` + `Helpers::Creatable` (jamestrong CANS upsert)
116
-
117
- > Sequenced first alongside Phase 3 because both are exercised in the live cutover and
118
- > `TargetOozesUpdateCase` **inherits** `RegisterUpdateCase`. Build the parent first.
119
-
120
- - [ ] Build native `GraphQL::Samples::Pages::Register::Base` (working title) under
121
- `graphql/samples/pages/register/base.rb`, modelled on the existing
122
- `graphql/samples/pages/page/base.rb` (KPIs, `each_page`, `update_page`, `simulate?` guard)
123
- but reproducing the **RegisterUpdateCase** semantics faithfully:
124
- - batched search via `graphql.registers.search` / `graphql.pages.each(searchConf:)`
125
- - dedup by page id (`ooze_result_ids`), queue/`enqueue`/`queue_shift` batching
126
- - full KPI set: total_search / retrieved / non_retrieved / dupped / updated /
127
- failed_update / attempted_updates / created / create_attempts (match `kpis_message`)
128
- - `before_loading_new_target` flush-on-requeue behaviour
129
- - `results_preview` + interactive `prompt_user` proceed (y/N) (v2-faithful)
130
- - [ ] `include GraphQL::Helpers::Pages::Creatable` + `PatchFeedback` + `Shortcuts`.
131
- - [ ] **Specs first:** `spec/eco/api/usecases/graphql/samples/pages/register/base_spec.rb`
132
- — cover KPI counting, dedup, dry-run diff printing, create path, submit/sign-off fold.
133
- Port the assertions from `spec/.../compat/pages_spec.rb` regression specs where relevant.
134
- - [ ] **Parity-prove** against the v2 `RegisterUpdateCase` baseline using the A/B harness on
135
- `mini` (read-only first, then a 1-row CANS-style upsert). Record result in DECISIONS.md.
136
- - [ ] **Flip the old name:** make
137
- `Eco::API::UseCases::OozeSamples::RegisterUpdateCase` a thin subclass/delegator of the
138
- native class (preserve `name 'register-update-case'`, `batch_size`, attr_readers,
139
- `process_ooze` override point, `main(..., mode: :legacy|:delegate)` signature).
140
- `OozeSamples::Helpers::Creatable` → delegate to / alias the native `Creatable`
141
- (keep `validate_base_type!` accepting the native register base).
142
- - [ ] **Remove shim code for this case:** delete the redirect paths now dead for register
143
- cases. (Keep `FieldPatches` until the *last* case that needs them is flipped — track in
144
- DECISIONS.md which patches each flip retires.)
145
- - [ ] Re-run jamestrong CANS script **unchanged** on `mini`; confirm parity with baseline.
146
-
147
- **Gate 2:** CANS parity recorded; old `RegisterUpdateCase` name resolves to native; eco-helpers suite green.
148
-
149
- ---
150
-
151
- ## Phase 3 — `TargetOozesUpdateCase` (act-gov TOOCS coding)
152
-
153
- - [ ] Build native counterpart subclassing the Phase-2 native register base, reproducing
154
- `TargetOozesUpdateCase`: CSV `target_ids` source, `batched_target_ids`,
155
- `target_ids_preview` (duplicate detection + proceed prompt), `with_each_entry` over ids.
156
- - [ ] **Specs first:** id-batching, duplicate detection, requeue-flush, KPI counts,
157
- cross-reference set + submit (the TOOCS coding shape), dry-run diff.
158
- - [ ] **Parity-prove** against the v2 `TargetOozesUpdateCase` baseline (act-gov TOOCS,
159
- 1-row CSV) on `mini`. Record in DECISIONS.md.
160
- - [ ] **Flip the old name** `OozeSamples::TargetOozesUpdateCase` → native subclass.
161
- - [ ] Remove now-dead shim code for this case.
162
- - [ ] Re-run act-gov TOOCS script **unchanged**; confirm parity.
163
-
164
- **Gate 3:** TOOCS parity recorded; old name resolves to native; both live cutover scripts green on native.
165
-
166
- ---
167
-
168
- ## Phase 4 — `OozeBaseCase` / `OozeRunBaseCase` / `OozeUpdateCase` / `OozeFromDocCase` (single-ooze chain)
169
-
170
- > Single-ooze runners (`options[:source][:ooze_id]`). Lower live-traffic but needed for
171
- > completeness and as the base several scripts subclass directly.
172
-
173
- - [ ] Native `GraphQL::Samples::Pages::Ooze::Base` reproducing `OozeBaseCase`
174
- (target, `process_ooze` block contract, `new_page`/`create_page`, `with_fields`/
175
- `with_sections`/`with_stage`, dry-run feedback via `PatchFeedback`).
176
- - [ ] Native single-ooze runner (`OozeRunBaseCase`: `ooze_id`/`stage_id` from options,
177
- `exit_if_no_changes!`, `prompt_to_confirm!`).
178
- - [ ] Native `OozeUpdateCase` (`to_field` resolver — drop the v2 `Component`/`Binding`
179
- branches; resolve via GraphQL `components.get_by_id` / name match).
180
- - [ ] Native `OozeFromDocCase` (docx table reading is v2-agnostic — port; only the field
181
- access underneath changes).
182
- - [ ] Specs for each; parity-prove each single-ooze runner on `mini`.
183
- - [ ] Flip names `OozeBaseCase` → `OozeRunBaseCase` → `OozeUpdateCase` → `OozeFromDocCase`
184
- bottom-up; remove dead shim code per flip.
185
-
186
- **Gate 4:** single-ooze chain on native; names preserved; specs + parity recorded.
187
-
188
- ---
189
-
190
- ## Phase 5 — `RegisterMigrationCase` (+ `HelpersMigration`)
191
-
192
- > Heaviest helper surface (`copy_pairings`, `copy_generic_paired_fields`,
193
- > `copy_mapped_fields`, typed-field pairing). Confirm none of the target customer
194
- > migrations rely on forces before scheduling (Phase 0 partition decides this).
195
-
196
- - [ ] Port `HelpersMigration::{Copying,TypedFieldsPairing}` to
197
- `graphql/helpers/pages/migration/*`, re-expressing typed-field copy against GraphQL
198
- `DataField::*` types (the v2 `Component` type matrix → GraphQL type matrix).
199
- - [ ] Native `RegisterMigrationCase` subclassing the native register base; reproduce the
200
- field-maps json loading, `custom_processing`/block contract, `report_*` pairing reports.
201
- - [ ] Specs (typed copy correctness is the high-risk area — cover each field type's copy).
202
- - [ ] Parity-prove a real migration on `mini` (src→dst page, diff field-by-field via the
203
- 21-type matrix from the parity plan).
204
- - [ ] Flip name; remove dead shim code.
205
-
206
- **Gate 5:** migration parity recorded (field-type matrix); name preserved.
207
-
208
- ---
209
-
210
- ## Phase 6 — Export cases (`RegisterExportCase`, `OozeCases::ExportRegisterCase`)
211
-
212
- > Read-only (no update path) → safest. `RegisterExportCase` is **not** under `OozeBaseCase`
213
- > and has its own `ooze`/`stage`/`apiv2` — note `build_full_ooze` stitches all stages into
214
- > one `PageStage` (a v2-specific merge GraphQL gets for free by fetching the full page).
215
-
216
- - [ ] Native `GraphQL::Samples::Pages::Register::Export` reproducing `RegisterExportCase`
217
- (batched search, `build_full_ooze` → simply the full GraphQL PageUnion, no stage stitch).
218
- - [ ] Port `Helpers::ExportableRegister` / `ExportableOoze` to native (CSV header/values
219
- generation over GraphQL fields; honour `only_indexed`/`only_labeled`/`only_with_ref`).
220
- - [ ] Native `ExportRegisterCase` (tags/date-range filters via native `Filters`).
221
- - [ ] Specs (CSV header + row parity against v2 export of the same register on `mini`).
222
- - [ ] Flip names; remove dead shim code.
223
-
224
- **Gate 6:** export CSV byte-parity (or documented intentional diffs) on `mini`.
225
-
226
- ---
227
-
228
- ## Phase 7 — Retire the OozeRedirect shim entirely
229
-
230
- - [ ] Confirm every **migratable** case is flipped and no migratable consumer still
231
- `include`s `GraphQL::Compat::OozeRedirect`.
232
- - [ ] Delete `lib/eco/api/usecases/graphql/compat/ooze_redirect.rb` and the
233
- `ooze_redirect/{dirty_array,field_patches}.rb` parts **for all retired patches**.
234
- - [ ] **Keep** `ooze_redirect/force_compat.rb` + the minimal include path **iff** any
235
- force-blocked case still rides the v2 path and needs it. Otherwise delete too.
236
- - [ ] Remove the `FieldPatches.apply!` global v2-class reopening (`patch_v2_type_dispatch!`)
237
- and GraphQL field `prepend`s — these only existed to make v2 `case/when` dispatch work;
238
- native cases dispatch on GraphQL types directly.
239
- - [ ] Verify **no customer script edits were needed** against the Phase 0 inventory; any
240
- script that still references a removed symbol is a defect to fix in this project, not a
241
- customer change.
242
- - [ ] Full `bundle exec rspec` green in eco-helpers; gem suite green; rubocop clean.
243
- - [ ] Update `eco-helpers` CHANGELOG; update memory + this project's status → complete.
244
-
245
- **Final gate:** shim deleted (modulo the explicitly-documented forces holdout), all names
246
- preserved, zero script edits, suites green.
247
-
248
- ---
249
-
250
- ## Standing constraints (apply in every phase)
251
-
252
- - [ ] Specs-first for each native class (parity-proving) — never flip a name before its
253
- spec suite and recorded parity result exist.
254
- - [ ] Native class is the **real implementation**; old `OozeSamples::*` name is a thin
255
- subclass/delegator. **Never reopen/monkeypatch a class to migrate it.**
256
- - [ ] Preserve every public class name, `name '...'` registration, `type :other`,
257
- class-level `batch_size`/`register_id`, attr_readers, and documented override points.
258
- - [ ] Each flip removes the shim code it makes dead — but only retire a `FieldPatch` once
259
- the last case needing it is flipped (track in DECISIONS.md).
260
- - [ ] Follow the working-tree safety protocol
261
- (`.ai-assistance/conventions/code-working-tree-protocol.md`) for every eco-helpers change.
@@ -1,122 +0,0 @@
1
- # Deep review — TOOCS + CANS cutover workflow (2026-06-30)
2
-
3
- Exhaustive pre-live audit of the two production dry-runs (act-gov TOOCS coding, jamestrong
4
- CANS upsert) on branch `fix/ooze-redirect-faithful-dryrun`. Goal: trust nothing that "looks
5
- clean", enumerate every hypothesis (however unlikely), prove/disprove each with code evidence,
6
- fix what's real, and harden what's latent. Four parallel sub-investigations fed this; verdicts
7
- and fixes below. All gem fixes are committed with specs; full suite 465/0/2 pending, rubocop clean.
8
-
9
- ## The two dry-run bodies under audit
10
-
11
- TOOCS (after the stageId fix — now correct):
12
- ```
13
- {:id=>"6a3225532b97b00073c23aa8", :patchVer=>229,
14
- :stageId=>"6a3225522b97b00073c22d7d", :submit=>true,
15
- :task=>{:completePageTask=>{:forcedComplete=>true}},
16
- :dataFields=>{:updates=>[{:people=>{:id=>"6a3225522b97b00073c22cea",
17
- :peopleIds=>["65d6d3dcacb0b3000773f547"]}}]}}
18
- ```
19
- CANS (update-shaped body, but the entry was actually a CREATE — see H8/H-create):
20
- ```
21
- {:id=>"656e802f2d489c005ad0a6af", :patchVer=>22,
22
- :dataFields=>{:updates=>[ 5×plainText, 2×number (one =0.0), 1×select ]}}
23
- ```
24
-
25
- ---
26
-
27
- ## Findings table (every hypothesis explored)
28
-
29
- | # | Hypothesis | Verdict | Action |
30
- |---|---|---|---|
31
- | H1 | People `<<` REPLACES (wipes) existing people | **NOT a bug on normal path** — DirtyArray seeds from the read; backend is full-replace but gem sends the full set. | — |
32
- | H1b | People reader has no fallback → wipe if read returns `peopleIds:null` while `people{id}` nodes exist | **LATENT risk** (real, not active under $content reads) | **FIXED** — reader falls back to `people` nodes |
33
- | H2 | `peopleIds` is the wrong input key | **NOT a bug** — backend `PeopleInput.people_ids: [ID]`, flat array (not IdDiff). | — |
34
- | H3 | Number empty CSV cell → `0.0` written over server value | **CONFIRMED footgun** — `"".to_f == 0.0`, no blank guard | **FIXED** — `value=` blank→nil |
35
- | H4 | Select shape (all options w/ selected flags) wrong | **NOT a bug** — matches `OptionInput{id,selected}`; merge+deselect semantics; sending only-selected would fail to deselect | — |
36
- | H5 | PlainText `""` clear is unintended | **NOT a bug** — schema String, real clear; dirty vs baseline | — |
37
- | H6 | Phantom-dirty (PlainText/Number/Select) vs baseline | **NOT a bug** — diff uses `original_doc`; no read/write key asymmetry for these types | — |
38
- | H7 | Value types wrong (Float/String/Boolean) | **NOT a bug** — match backend input types | — |
39
- | H8 | CANS upsert mis-routes existing page → create (duplicate risk) | **NOT a bug** — routing by `search_by_id`; the entry genuinely was NOT found → correct CREATE; no duplicate risk | — |
40
- | H9 | CANS KPIs lie ("Total 0 / Created out of 1 / Updated 0") | **NOT a bug** — numerically correct: 0 existing targets, 1 create attempt, dry-run so 0 created | — |
41
- | H-create | CANS create dry-run previews as "would **update**" (wrong envelope: draft id+patchVer, no templateId) | **CONFIRMED display bug** — `new_draft?` only checked `patchVer.nil?`, but buildFromTemplate drafts carry a patchVer | **FIXED** — `_compat_source_template_id` marker |
42
- | H10 | The new stageId guard breaks other ooze cases | **NOT a bug** — only TOOCS+CANS submit on the GraphQL path, both via `with_stage` → stage resolves; metlifecare is pure APIv2 | — |
43
- | H11 | TOOCS recorded stageId is the wrong stage | **NOT a bug (by construction)** — StageView records the RESOLVED 'Coding' stage id; replay spec asserts it | — (verify id in UI) |
44
-
45
- ---
46
-
47
- ## Confirmed bugs + fixes (all gem, all spec-covered)
48
-
49
- ### 1. Number blank → 0.0 (live-corruption footgun) — `base/page/data_field/number.rb`
50
- `def value=(numeric); doc['value'] = numeric&.to_f; end` coerces `""` to `0.0` (Ruby `"".to_f`).
51
- The CANS case assigns `fld.value = row[csv_header]&.strip` with no blank guard, so an empty CSV
52
- numeric cell wrote a real `0.0`, and the diff (correctly) shipped it over the server value.
53
- **Fix:** blank (nil / empty / whitespace string) → `nil`, never `0.0`; genuine numbers still
54
- coerce to Float. Specs: 8 cases (string/Numeric/"0"/""/whitespace/nil/clear-over-value/no-op).
55
- **Residual:** a non-numeric garbage string (`"abc"`) still `.to_f`→`0.0` — lesser concern, noted.
56
- **Caller policy:** whether a blank should CLEAR vs be LEFT ALONE is the case's call (TOOCS skips
57
- blanks for people; CANS does not for numbers) — the gem only guarantees blank ≠ 0.0.
58
-
59
- ### 2. People reader has no node fallback (latent wipe) — `base/page/data_field/people.rb`
60
- Reader read only `doc['peopleIds']`; if a response ever returned it null/empty while the
61
- `people{id}` nodes were populated, `people_ids << x` would seed from `[]` and the full-replace
62
- backend would WIPE the field. **Fix:** fall back to the `people` nodes when `peopleIds` is blank
63
- (mirrors CrossReference's `references.nodes` fallback). Read key == write key, so no phantom-dirty.
64
- Specs: fallback read, append-preserves-from-nodes, prefer-peopleIds-when-both-present.
65
-
66
- ### 3. CANS create dry-run mislabel — `compat/pages.rb` + `interface/base_page.rb`
67
- A `buildPageFromTemplate` draft carries a server patchVer, so `new_draft?` (patchVer-nil only)
68
- classified it as an update → the create previewed as "would update" with a misleading envelope.
69
- The read fragment fetches neither `draft` nor `sourceTemplateId`, so neither could be used.
70
- **Fix:** `Compat::Pages#get_new` records `_compat_source_template_id` (new accessor on
71
- `Interface::BasePage`); `new_draft?` treats that marker as the authoritative create signal;
72
- `create_body` uses it for the templateId. Specs: get_new sets marker, marked-draft-with-patchVer
73
- previews as create (templateId, no patchVer). Affects DRY-RUN ONLY (live create already correct).
74
-
75
- ---
76
-
77
- ## Verified-correct (no action)
78
- - **People append preserves** existing people (DirtyArray seeds from read; gem sends full set;
79
- backend `set_people_ids` is full-replace, so sending the full set is exactly right). `peopleIds`
80
- is the correct flat `[ID]` key (backend `PeopleInput`).
81
- - **Select** "all options with selected flags" is correct and is the only shape that reliably
82
- DESELECTS (`SelectBuilder` merges by id; options not sent are untouched).
83
- - **PlainText `""`** is a legitimate clear; **dirty** baselines on `original_doc` for all three
84
- scalar/array types; **value types** match backend inputs.
85
- - **CANS routing**: existing→update, missing→create, decided by `search_by_id`; no duplicate-create.
86
- The KPIs are numerically correct. (The "would update" wording was the only defect — fixed.)
87
- - **Guard blast radius**: only TOOCS + CANS submit on the GraphQL path; both via `with_stage`
88
- (records the active stage) → guard satisfied. metlifecare `resident_events` is pure APIv2.
89
-
90
- ## Backend evidence (from C:\docker\ecoPortal_master, via sub-investigations)
91
- - `app/graphql/types/fill_in_page/inputs/data_fields/people_input.rb` → `people_ids: [ID]` flat.
92
- - `app/services/new_ep/pages/assign_attributes_service.rb:175` → `set_people_ids` = full replace.
93
- - `select_builder.rb` → merge-by-id; deselect requires sending `selected:false`.
94
- - `{plain_text,number}_input.rb` → `String` / `Float` value types.
95
-
96
- ---
97
-
98
- ## Open questions — RESOLVED / remaining (updated 2026-07-01)
99
- 1. **CANS field `...61` `value=>0.0` — RESOLVED: real zero, not an artifact.** The CANS CSV row
100
- `FE15TR04715` has `STANDARD COST` = `0` (UNQUOTED literal) → `"0".to_f == 0.0`, correctly sent.
101
- (`SITE="15"` → `…60 = 15.0`.) Confirmed against `QEST_Components_20260629.csv`. The Number fix
102
- was never implicated in this value.
103
- - **Eco::CSV empty-cell behavior (verified, `eco-helpers/lib/eco/csv.rb`):** `Eco::CSV.parse`
104
- = `{headers: true, skip_blanks: true}` + stdlib `::CSV` with NO `nil_value`/`empty_value`
105
- override → **unquoted** empty (`,,`) becomes `nil`; **quoted** empty (`""`) stays `""`. The
106
- feed mixes both (e.g. `CUSTOMER_ITEM=""` → `""` → surfaced as `plainText value:""`). So a
107
- quoted-empty NUMERIC cell WOULD hit `"".to_f → 0.0` — the Number/Gauge `blank→nil` fix is the
108
- correct safety net even though this particular row didn't trigger it. ("empties → nil" only
109
- holds for unquoted empties.) `skip_blanks` skips whole-blank ROWS, not empty cells.
110
- - **Gauge had the identical footgun** (`numeric&.to_f`) — now fixed the same way. Both Number
111
- and Gauge route through a single shared `DataField#numeric_or_nil` helper so they can't drift.
112
- 2. **Created CANS pages** unsubmitted (first stage): parked — out of scope for now (Oscar).
113
- 3. **TOOCS stage**: confirm `6a3225522b97b00073c22d7d` is the 'Coding' stage in act-gov (the
114
- model dump tool can verify against the live page). Non-blocking.
115
-
116
- ## Tooling delivered
117
- - `tests/dump_page_model.rb` — pulls a page's GraphQL model (CommonPageUnion doc) to JSON,
118
- before/after a submit, for capturing real fixtures + diffing what a submit changed.
119
- - `spec/fixtures/pages/toocs_coding_page.json` — TOOCS page fixture (observed ids).
120
- - `spec/ecoportal/api/graphql/integration/toocs_submit_replay_spec.rb` — replays the case flow,
121
- asserts the EXACT observed body, and proves the regression (stageless submit raises; stage view
122
- resolves it).