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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -0
- data/lib/ecoportal/api/common/graphql/client.rb +0 -2
- data/lib/ecoportal/api/common/graphql/http_client.rb +6 -0
- data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -17
- data/lib/ecoportal/api/graphql/base/file_container.rb +14 -14
- data/lib/ecoportal/api/graphql/base/location_classification_type.rb +15 -15
- data/lib/ecoportal/api/graphql/base/location_structure.rb +27 -27
- data/lib/ecoportal/api/graphql/base/organization.rb +15 -15
- data/lib/ecoportal/api/graphql/base/page/task.rb +24 -0
- data/lib/ecoportal/api/graphql/base/page.rb +17 -16
- data/lib/ecoportal/api/graphql/base/template.rb +35 -0
- data/lib/ecoportal/api/graphql/base.rb +37 -35
- data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +41 -41
- data/lib/ecoportal/api/graphql/compat/response.rb +35 -35
- data/lib/ecoportal/api/graphql/connection/action.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/action_category.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/page.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/person_member.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -11
- data/lib/ecoportal/api/graphql/connection.rb +17 -17
- data/lib/ecoportal/api/graphql/file_upload/client.rb +21 -10
- data/lib/ecoportal/api/graphql/fragment/field_configuration.rb +38 -0
- data/lib/ecoportal/api/graphql/fragment/force.rb +14 -10
- data/lib/ecoportal/api/graphql/fragment/location_node.rb +26 -26
- data/lib/ecoportal/api/graphql/fragment/page_task.rb +39 -0
- data/lib/ecoportal/api/graphql/fragment/template.rb +51 -0
- data/lib/ecoportal/api/graphql/fragment.rb +3 -0
- data/lib/ecoportal/api/graphql/input/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -16
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -21
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -28
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -23
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -24
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -251
- data/lib/ecoportal/api/graphql/input.rb +24 -24
- data/lib/ecoportal/api/graphql/logic/connection.rb +34 -34
- data/lib/ecoportal/api/graphql/model/page/task.rb +16 -0
- data/lib/ecoportal/api/graphql/model/page.rb +16 -15
- data/lib/ecoportal/api/graphql/model/person_member.rb +15 -15
- data/lib/ecoportal/api/graphql/model/template.rb +15 -0
- data/lib/ecoportal/api/graphql/model.rb +32 -30
- data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -45
- data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +7 -7
- data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +8 -7
- data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +34 -35
- data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -35
- data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -36
- data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -13
- data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -46
- data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +1 -1
- data/lib/ecoportal/api/graphql/mutation.rb +20 -20
- data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/payload/ok_payload.rb +36 -21
- data/lib/ecoportal/api/graphql/payload/page/draft.rb +26 -13
- data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -13
- data/lib/ecoportal/api/graphql/payload/page/review_task_batch.rb +23 -0
- data/lib/ecoportal/api/graphql/payload/page.rb +20 -19
- data/lib/ecoportal/api/graphql/payload/preset_view.rb +15 -11
- data/lib/ecoportal/api/graphql/payload/register.rb +15 -11
- data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/payload/template/update_information.rb +1 -1
- data/lib/ecoportal/api/graphql/payload.rb +23 -23
- data/lib/ecoportal/api/graphql/query/location_structures.rb +57 -57
- data/lib/ecoportal/api/graphql/query/page.rb +45 -45
- data/lib/ecoportal/api/graphql/query/page_with_forces.rb +9 -3
- data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +9 -3
- data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -78
- data/lib/ecoportal/api/graphql/query.rb +27 -27
- data/lib/ecoportal/api/graphql.rb +9 -5
- data/lib/ecoportal/api/graphql_version.rb +1 -1
- metadata +9 -180
- data/.ai-assistance/bridge/CLAUDE.md +0 -338
- data/.ai-assistance/bridge/archive/.gitkeep +0 -0
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +0 -29
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +0 -18
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +0 -42
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +0 -115
- data/.ai-assistance/bridge/context/gemini-review-prompt.txt +0 -48
- data/.ai-assistance/bridge/context/gemini-review-response.md +0 -104
- data/.ai-assistance/bridge/context/project.md +0 -42
- data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
- data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
- data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +0 -48
- data/.ai-assistance/bridge/queue/.gitkeep +0 -1
- data/.ai-assistance/capabilities/CLAUDE.md +0 -27
- data/.ai-assistance/capabilities/assumptions-log.md +0 -80
- data/.ai-assistance/capabilities/code.md +0 -47
- data/.ai-assistance/capabilities/connectors.md +0 -37
- data/.ai-assistance/capabilities/cowork.md +0 -55
- data/.ai-assistance/code/OVERVIEW.md +0 -155
- data/.ai-assistance/code/data_fields.md +0 -242
- data/.ai-assistance/code/dependencies.md +0 -151
- data/.ai-assistance/code/diff_as_input.md +0 -234
- data/.ai-assistance/code/diff_pairing_engine.md +0 -243
- data/.ai-assistance/code/diff_service_deep_dive.md +0 -192
- data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +0 -55
- data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +0 -181
- data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +0 -192
- data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +0 -147
- data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +0 -277
- data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +0 -200
- data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +0 -228
- data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +0 -197
- data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +0 -243
- data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +0 -196
- data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +0 -132
- data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +0 -187
- data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +0 -236
- data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +0 -183
- data/.ai-assistance/code/ecoportal_schema_reference.md +0 -240
- data/.ai-assistance/code/filter_contract_matrix.md +0 -177
- data/.ai-assistance/code/graphql_domain_knowledge.md +0 -240
- data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +0 -71
- data/.ai-assistance/code/refactoring/opportunities.md +0 -251
- data/.ai-assistance/code/schema_analysis.md +0 -321
- data/.ai-assistance/code/search_filters.md +0 -868
- data/.ai-assistance/code/spec_coverage.md +0 -73
- data/.ai-assistance/code/template_diff_pairing_domain.md +0 -175
- data/.ai-assistance/code/workflow-command-guide.md +0 -466
- data/.ai-assistance/code/workflow-space.md +0 -353
- data/.ai-assistance/conventions/CLAUDE.md +0 -30
- data/.ai-assistance/conventions/code-working-tree-protocol.md +0 -199
- data/.ai-assistance/conventions/gitignore-rules.md +0 -42
- data/.ai-assistance/conventions/permission-guidance.md +0 -120
- data/.ai-assistance/integrations/README.md +0 -70
- data/.ai-assistance/integrations/gitkraken-mcp.md +0 -107
- data/.ai-assistance/integrations/gitlab-mcp.md +0 -123
- data/.ai-assistance/integrations/local-git.md +0 -60
- data/.ai-assistance/local_paths.example.md +0 -17
- data/.ai-assistance/projects/TODO.md +0 -118
- data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +0 -168
- data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +0 -60
- data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +0 -267
- data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +0 -53
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +0 -102
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +0 -139
- data/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +0 -244
- data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +0 -93
- data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +0 -244
- data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +0 -266
- data/.ai-assistance/projects/eco-helpers-compat/TODO.md +0 -86
- data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +0 -101
- data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +0 -177
- data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +0 -161
- data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +0 -125
- data/.ai-assistance/projects/ooze-graphql-native-migration/INVENTORY.md +0 -136
- data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +0 -126
- data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +0 -261
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +0 -122
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +0 -148
- data/.ai-assistance/projects/page-model/DECISIONS.md +0 -245
- data/.ai-assistance/projects/page-model/TODO.md +0 -190
- data/.ai-assistance/projects/qa-services-delivery/DECISIONS.md +0 -93
- data/.ai-assistance/projects/qa-services-delivery/INTENT.md +0 -76
- data/.ai-assistance/projects/qa-services-delivery/PHASE3-SCOPE.md +0 -115
- data/.ai-assistance/projects/qa-services-delivery/ROADMAP.md +0 -99
- data/.ai-assistance/projects/qa-services-delivery/TODO.md +0 -81
- data/.ai-assistance/projects/search-filter-builder/INTENT.md +0 -107
- data/.ai-assistance/projects/search-filter-builder/TODO.md +0 -131
- data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +0 -87
- data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +0 -108
- data/.ai-assistance/projects/template-diff-deploy/INTENT.md +0 -12
- data/.ai-assistance/projects/template-diff-deploy/TODO.md +0 -9
- data/.ai-assistance/projects/template-maintenance/DESIGN.md +0 -134
- data/.ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md +0 -93
- data/.ai-assistance/projects/template-maintenance/README.md +0 -14
- data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +0 -236
- data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +0 -243
- data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +0 -154
- data/.ai-assistance/projects/workflow-space/TODO.md +0 -213
- data/.ai-assistance/reinstall-claude-desktop-windows.md +0 -136
- data/.ai-assistance/scripts/CLAUDE.md +0 -150
- data/.ai-assistance/scripts/bridge-inbox-check.sh +0 -75
- data/.ai-assistance/scripts/bridge-init.sh +0 -86
- data/.ai-assistance/scripts/bridge-status.sh +0 -44
- data/.ai-assistance/scripts/capabilities-check.ts +0 -104
- data/.ai-assistance/scripts/check-outbox.sh +0 -43
- data/.ai-assistance/scripts/dep_graph.rb +0 -91
- data/.ai-assistance/scripts/lock-acquire.sh +0 -103
- data/.ai-assistance/scripts/lock-multi.sh +0 -124
- data/.ai-assistance/scripts/lock-queue.sh +0 -94
- data/.ai-assistance/scripts/setup-mcps.test.ts +0 -188
- data/.ai-assistance/scripts/setup-mcps.ts +0 -234
- data/.ai-assistance/scripts/task-complete.ts +0 -74
- data/.ai-assistance/scripts/task-create.ts +0 -75
- data/.ai-assistance/scripts/task-read.ts +0 -125
- data/.ai-assistance/scripts/token-logger.js +0 -220
- data/.ai-assistance/scripts/token-report.ts +0 -158
- data/.ai-assistance/scripts/token-session-start.js +0 -66
- data/.ai-assistance/skills/ai-instructions/SKILL.md +0 -48
- data/.ai-assistance/skills/code-specs/SKILL.md +0 -69
- data/.ai-assistance/skills/corporate-policies/SKILL.md +0 -201
- data/.ai-assistance/skills/dep-graph/SKILL.md +0 -139
- data/.ai-assistance/skills/ep-ai-manager/SKILL.md +0 -417
- data/.ai-assistance/skills/gemini-assist/SKILL.md +0 -63
- data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +0 -205
- data/.ai-assistance/skills/gemini-assist/gemini_ask.py +0 -1
- data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +0 -240
- data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +0 -25
- data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +0 -261
- data/.ai-assistance/skills/procedural-memory/SKILL.md +0 -319
- data/.ai-assistance/skills/project-cycle/SKILL.md +0 -177
- data/.ai-assistance/skills/project-self-docs/SKILL.md +0 -181
- data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +0 -378
- data/.ai-assistance/skills/refactor/SKILL.md +0 -62
- data/.ai-assistance/skills/rubocop/SKILL.md +0 -93
- data/.ai-assistance/skills/ruby-scripting/SKILL.md +0 -215
- data/.ai-assistance/skills/spec-generation/SKILL.md +0 -72
- data/.ai-assistance/standards-version.json +0 -22
- data/.ai-assistance/token-budget.json +0 -32
- data/.ai-assistance/version.json +0 -39
- data/.claude/settings.json +0 -150
- data/.env.example +0 -18
- data/.gitattributes +0 -15
- data/.gitignore +0 -37
- data/.gitlab-ci.yml +0 -45
- data/.markdownlint.json +0 -4
- data/.rspec +0 -3
- data/.rubocop.yml +0 -121
- data/.ruby-version +0 -1
- data/.yardopts +0 -10
- data/CLAUDE.md +0 -243
- data/Gemfile +0 -30
- data/Rakefile +0 -90
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/setup.ps1 +0 -6
- data/docs/self-docs/ARCHITECTURE.md +0 -88
- data/docs/self-docs/CHANGES.jsonl +0 -12
- data/docs/self-docs/COMPLIANCE.md +0 -79
- data/docs/self-docs/CONVENTIONS.md +0 -74
- data/docs/self-docs/INTEGRATIONS.md +0 -65
- data/docs/self-docs/OPERATIONS.md +0 -76
- data/docs/self-docs/OVERVIEW.md +0 -64
- data/docs/self-docs/STATUS.md +0 -73
- data/docs/self-docs/self-docs-index.json +0 -51
- data/docs/worklog.md +0 -977
- data/ecoportal-api-graphql.gemspec +0 -40
- data/scripts/auto-worker-scheduler.sh +0 -386
- data/tests/actions_get.rb +0 -7
- data/tests/contractor_entities_get.rb +0 -20
- data/tests/contractor_entity_create.rb +0 -19
- data/tests/contractor_entity_udpate.rb +0 -20
- data/tests/dump_page_model.rb +0 -74
- data/tests/dump_template_model.rb +0 -90
- data/tests/loc_structure_get.rb +0 -10
- data/tests/loc_structure_update.rb +0 -51
- data/tests/loc_structures_get.rb +0 -15
- data/tests/local_libs.rb +0 -20
- data/tests/validate_queries.rb +0 -127
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# RCA — "Forces are blocked on GraphQL" was wrong: forces are mutable via the Workflow* command-bus
|
|
2
|
-
|
|
3
|
-
**Date:** 2026-07-01
|
|
4
|
-
**Author:** retrospective analysis (AI agent), reviewed-by: pending (Oscar)
|
|
5
|
-
**Scope:** Why a clarified platform fact — *forces are created/edited/removed on GraphQL via `Workflow*` command types through `executeWorkflowCommands`* — was repeatedly missed, leaving a stale "blocked on forces" narrative in `eco-helpers` that disabled ~50% of ooze cases.
|
|
6
|
-
**Constraint:** analysis + recommendations only. No source code was changed.
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 1. Executive summary
|
|
11
|
-
|
|
12
|
-
**What was missed.** ecoPortal FORCES are fully mutable on GraphQL through the `WorkflowCommandInput` command-bus — `addForce` / `editForce` / `removeForce` / `reorderForces` / `addBinding` / `editBinding` / `removeBinding` — applied via the `executeWorkflowCommands` mutation. The gem has shipped these command inputs **and** the mutation client since 2026-06-10/11. There is **no** separate "forces endpoint" and there never needed to be one for *writes*.
|
|
13
|
-
|
|
14
|
-
**The one-line root cause.** The `OozeRedirect.force_support?` readiness gate keyed on the wrong signal — the presence of a **broken READ query** (`Query::PageWithForces`) — and when that signal flipped true it routed *every* ooze case through the broken read. The fix was to hard-disable the gate (`force_support? => false`), which threw out the **working MUTATE path** as collateral and froze a comment that over-generalises "the broken read" into "forces are NOT usable on GraphQL." The READ-vs-MUTATE distinction was never made explicit at the gate, so a read-only defect masqueraded as a total-capability block.
|
|
15
|
-
|
|
16
|
-
**Net effect.** `ooze_redirect.rb` simultaneously (a) ships a `ForceCompat` that *correctly* drives writes through `executeWorkflowCommands`, and (b) asserts in prose that "Forces are NOT usable on GraphQL yet … blocked on GraphQL endpoint," listing ~50% of ooze cases as blocked. The codebase contradicts itself.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 2. The contradiction, with file:line evidence
|
|
21
|
-
|
|
22
|
-
### Side A — "forces are blocked" (eco-helpers)
|
|
23
|
-
|
|
24
|
-
`C:\ruby_scripts\git\eco-helpers\lib\eco\api\usecases\graphql\compat\ooze_redirect.rb`
|
|
25
|
-
|
|
26
|
-
- **L42-49** — `## Limitations`: "Force / binding operations (`target.forces`, `force.bindings`) are not available in GraphQL — those calls will raise NoMethodError."
|
|
27
|
-
- **L51-77** — `## TODO: Force / binding support (blocked on GraphQL endpoint)`: "Engineering is working on exposing 'legacy forces' via the GraphQL API. Once that endpoint is available, extend this module with a `ForceCompat` sub-module …" — followed by a v2-shaped API sketch (`target.forces.get_by_name`, `force.bindings.add`, `force.custom_script = new_script`) and a roster of ~22 named cases across 10 orgs declared "currently blocked … ~50% of all ooze cases."
|
|
28
|
-
- **L113-122** — `force_support?`:
|
|
29
|
-
```ruby
|
|
30
|
-
# Forces are NOT usable on GraphQL yet: Query::PageWithForces is a WIP whose query
|
|
31
|
-
# currently fails schema validation … and the backend forces endpoint is still in
|
|
32
|
-
# progress. … Keep OFF until forces actually work …
|
|
33
|
-
def self.force_support?
|
|
34
|
-
false
|
|
35
|
-
end
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Side B — "forces are mutable, right now" (the gem, already shipping)
|
|
39
|
-
|
|
40
|
-
- `lib\ecoportal\api\graphql\input\workflow_command.rb` **L106-117** — the `# Forces` block of `COMMAND_MAP` wires `addForce`, `removeForce`, `editForce`, `reorderForces`, `addBinding`, `editBinding`, `removeBinding` (+ `addLinkedHelper`/`editLinkedHelper`/`removeLinkedHelper`). These are first-class members of the command-bus, on the same footing as `addStage`, `addField`, etc.
|
|
41
|
-
- `lib\ecoportal\api\graphql\input\workflow_command\add_force.rb` — `VALID_KEYS = %i[placeholderId name customScript url contentB64]`, `SCHEMA_VERSION '20260605'`.
|
|
42
|
-
- `…\edit_force.rb` — `VALID_KEYS = %i[id name customScript url contentB64]`.
|
|
43
|
-
- `…\add_binding.rb` — `VALID_KEYS = %i[placeholderId forceId name referenceId type]`.
|
|
44
|
-
- `lib\ecoportal\api\graphql\mutation\page\execute_workflow_commands.rb` — `field_name :executeWorkflowCommands`; the generic command applier (`mutation(... commands: :WorkflowCommandInput!)`).
|
|
45
|
-
- `lib\ecoportal\api\graphql\mutation\page\execute_force_commands.rb` **L26-37** — `ExecuteForceCommands` is now a thin `@deprecated` alias that *delegates to the same `executeWorkflowCommands` mutation*. Its own doc-comment (L20-25) shows `{ editForce: {...} }`, `{ addBinding: {...} }`, `{ removeBinding: {...} }`, `{ removeForce: {...} }` as the command payloads.
|
|
46
|
-
- `lib\ecoportal\api\graphql\base\force.rb` **L33-60** — `Base::Force#custom_script=` queues `editForce`; `#all_pending_commands` emits `{ editForce: { id:, customScript: } }`; bindings queue `addBinding`/`removeBinding`. The write model is complete and unit-shaped.
|
|
47
|
-
|
|
48
|
-
### Side C — the smoking gun: the "v2-style" sketch is already the command-bus
|
|
49
|
-
|
|
50
|
-
The "to be built when the endpoint lands" sketch in `ooze_redirect.rb` (L79-89) describes exactly what the **already-shipped** `force_compat.rb` does — and that file does **not** call any forces endpoint:
|
|
51
|
-
|
|
52
|
-
`C:\ruby_scripts\git\eco-helpers\lib\eco\api\usecases\graphql\compat\ooze_redirect\force_compat.rb`
|
|
53
|
-
- **L43-46, L62-66** — `process_ooze` → `save_force_changes!` → `graphql.page.execute_force_commands(id:, patch_ver:, commands:)`, i.e. it submits `force_col.pending_commands` **through `executeWorkflowCommands`**. The write half was *finished* the same day the TODO was written.
|
|
54
|
-
|
|
55
|
-
So the prose ("blocked on endpoint," "to be built") and the code ("here is the working write path through the command-bus") are in direct contradiction *within the same module*.
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## 3. The read-vs-mutate conflation (the conceptual core)
|
|
60
|
-
|
|
61
|
-
Two genuinely distinct concerns were collapsed into one boolean:
|
|
62
|
-
|
|
63
|
-
| Concern | Mechanism | Status (as of 2026-06-30) |
|
|
64
|
-
|---|---|---|
|
|
65
|
-
| **(a) READ legacy forces on a page** | `Query::PageWithForces` — selects `forces { ...ForceFields }` on the page union | **WIP / broken**: per commit `de62d2d1`, the query "fails schema validation (PageUnion selections; `id` on DataFieldBinding/SectionBinding; unused ForceFields)." This is a *client query shape* defect. |
|
|
66
|
-
| **(b) MUTATE forces** | `executeWorkflowCommands(commands: [WorkflowCommandInput])` with `editForce`/`addBinding`/`removeForce`/… | **Working / shipped** since 2026-06-10 (`cd57b54`) and folded into the unified command-bus 2026-06-11 (`8e8f492`). |
|
|
67
|
-
|
|
68
|
-
The v2 mental model treats a force as a *page sub-object you read, then mutate in place*: `page.forces.get_by_name(...)`, `force.bindings.add(...)`, `force.custom_script = ...`, with a LISP `custom_script` you edit. In that model, reading and writing are the **same** object graph, so "I can't read forces" naturally implies "I can't do forces."
|
|
69
|
-
|
|
70
|
-
The new platform model is **imperative**: you don't read-then-mutate a live object; you *emit commands* (`editForce { id, customScript }`, `addBinding { forceId, ... }`) into `executeWorkflowCommands`, exactly like every other template/workflow change. In this model, **read and write are decoupled** — a write needs only the force/binding `id` and the new value; it does **not** require `PageWithForces` to be schema-valid.
|
|
71
|
-
|
|
72
|
-
**The conflation:** because the gate (`force_support?`) was attached to the *read* query, and because `force_compat.rb` *also* uses `PageWithForces` to fetch (`with_each_entry`, L30-40), the broken read poisoned the whole feature. The "blocked" conclusion was drawn from the broken READ query and then **over-generalised to ALL force operations** — including the writes that already worked. The v2 carryover (looking for a forces *endpoint*, i.e. a place to read forces from) is precisely what made the read query feel like the readiness signal.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## 4. Git timeline — what existed when
|
|
77
|
-
|
|
78
|
-
**Gem (`ecoportal-api-graphql`):**
|
|
79
|
-
|
|
80
|
-
| Date | Commit | Event |
|
|
81
|
-
|---|---|---|
|
|
82
|
-
| 2026-06-10 | `cd57b54` | `feat: Force model + fragment + mutation + query for ForceCompat support` — adds `Base::Force` (+ bindings, collection, `custom_script=` queues `editForce`), `Fragment::ForceFields`, **`Mutation::Page::ExecuteForceCommands` (write)** AND **`Query::PageWithForces` (read)** together. Writes were present from day one. |
|
|
83
|
-
| 2026-06-11 | `8e8f492` | `feat: expose PagesWorkflow space (W1-W5) — read models, command bus, fragments, specs, docs` — adds the full `WorkflowCommand` command-bus incl. `add_force/edit_force/remove_force/reorder_forces/add_binding/edit_binding/remove_binding`; `ExecuteForceCommands` becomes a deprecated delegator to `executeWorkflowCommands`. |
|
|
84
|
-
| 2026-06-16 | `bafbf80` | `fix: require all field_config byType modules in WorkflowCommand`. |
|
|
85
|
-
|
|
86
|
-
**eco-helpers (`ooze_redirect.rb` / `force_compat.rb`):**
|
|
87
|
-
|
|
88
|
-
| Date | Commit | Event |
|
|
89
|
-
|---|---|---|
|
|
90
|
-
| 2026-06-09 | `457eccd9` | `feat: GraphQL::Compat::OozeRedirect` — shim created. |
|
|
91
|
-
| 2026-06-09 | `244aff8a` | `docs: OozeRedirect Forces TODO block` — **the "blocked on GraphQL endpoint" prose written BEFORE the write path shipped.** At this moment the claim was *defensible*. |
|
|
92
|
-
| 2026-06-10 | `e3bc7f0a` | `feat: OozeRedirect::ForceCompat — force/binding support via GraphQL` — the working write path lands (`save_force_changes!` → `execute_force_commands`). **The TODO prose was now stale but was not removed.** |
|
|
93
|
-
| 2026-06-30 | `de62d2d1` | `fix(ooze-redirect): disable ForceCompat (forces WIP)` — `force_support?` was `defined?(Query::PageWithForces)`; once the gem force-loaded that became `true`, prepending `ForceCompat` for *every* case and routing `with_each_entry` through the broken read. Hard-disabled to `false`. **This correctly stopped the broken read from breaking toocs/cans — but the accompanying comment re-asserted the over-general "forces are NOT usable" claim, re-freezing the stale narrative instead of narrowing it to the read query.** |
|
|
94
|
-
| 2026-06-30 | `d12f5552` | `fix(ooze-redirect): faithful dry-run + slim shim` — current state. |
|
|
95
|
-
|
|
96
|
-
**Key finding:** the write command inputs existed (2026-06-10/11) *before* the "blocked" claim was last reinforced (2026-06-30). The reinforcement on 2026-06-30 had the gem's working force-write code sitting in the same dependency tree — yet the comment still said "blocked on GraphQL endpoint." The DECISIONS log entry of the **same day** (`[2026-06-30] Forces-dependent cases are HELD pending a Phase 0 live readiness test`, `ooze-graphql-native-migration/DECISIONS.md` L120-148) *did* finally record the correction ("the GraphQL force endpoint is NOT read-only or missing — the gem ships both … read AND … write"). So the correction landed in the **project decision log** but did **not** propagate back into the `ooze_redirect.rb` gate/comment that consumers and future agents actually read.
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## 5. Root-cause chain (ranked)
|
|
101
|
-
|
|
102
|
-
1. **Wrong readiness signal + read/write coupling (primary).** `force_support?` keyed on `defined?(Query::PageWithForces)` — a *read* class — as a proxy for "forces work." Because `force_compat.rb#with_each_entry` *fetches* via `PageWithForces`, the broken read could break unrelated cases, forcing a binary all-off switch. A capability that is two independent halves (read vs write) was gated by one boolean tied to the broken half. The write half was never given its own gate, so it died with the read.
|
|
103
|
-
|
|
104
|
-
2. **Stale doc never re-examined after the feature shipped (close second).** The "blocked on endpoint" TODO was written 2026-06-09 (`244aff8a`) when it was *true*, then the write path shipped 2026-06-10 (`e3bc7f0a`) **in the same module**, and the TODO was never reconciled. Doc rot: the prose became a fossil that subsequent edits (incl. 2026-06-30) treated as ground truth and even *expanded* rather than questioned.
|
|
105
|
-
|
|
106
|
-
3. **Verified platform fact lived only in machine-local memory, invisible across sessions/agents (structural).** The general command-bus architecture ("templates updated imperatively via ~80 `WorkflowCommandInput` types") was captured in `project_template_update_architecture.md` and `project_workflow_space.md` under `C:\Users\rella\.claude\projects\…\memory\` — a **per-machine, gitignored** store. That store is invisible to (a) other machines, (b) other agents/sessions, (c) anyone reading the repo. The fact was "known" in one silo but had no **code-adjacent, shared** form, so it could not contradict the stale comment at the point of use.
|
|
107
|
-
|
|
108
|
-
4. **The captured general fact was never specialised to the force inference (reasoning gap).** Even where the command-bus was known, nobody made the *specific* deduction: "forces are members of the command-bus → forces are mutable on GraphQL → `force_support?` must not be hard-false for writes." The general → specific link was never drawn. Knowing "templates are updated via commands" did not automatically surface "therefore the forces-blocked claim is wrong," because forces were mentally filed under "legacy read object," not "command-bus member."
|
|
109
|
-
|
|
110
|
-
5. **v2 endpoint mental model (contributing).** The team looked for a forces *endpoint* (a place to read/CRUD forces as objects) rather than recognising forces as just-another-command in an existing imperative bus. This is why the read query felt load-bearing and why the TODO sketch reinvented a v2-shaped `forces.get_by_name/.bindings.add` API that the command-bus had already obviated for writes.
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
## 6. Concrete corrections
|
|
115
|
-
|
|
116
|
-
### (a) `ooze_redirect.rb` — gate + comment (DESCRIBE; not applied)
|
|
117
|
-
|
|
118
|
-
- **Split the capability into READ vs WRITE.** Do not let a single `force_support?` boolean conflate them.
|
|
119
|
-
- **Writes** (`editForce`/`addBinding`/`removeBinding`/`removeForce` via `executeWorkflowCommands`) are available **now** and do not depend on `PageWithForces`. The write path in `force_compat.rb#save_force_changes!` is correct as written.
|
|
120
|
-
- **Reads** (`Query::PageWithForces`) are the only WIP part. The blocker is a *client query-shape* bug (PageUnion selection set; `id` on `DataFieldBinding`/`SectionBinding`; unused `ForceFields`), not a missing backend endpoint.
|
|
121
|
-
- **Re-scope the gate.** Replace the hard `false` with a gate that reflects the real dependency: ForceCompat's *fetch* (`with_each_entry` via `PageWithForces`) is what must be gated, not force mutation per se. Options:
|
|
122
|
-
- Gate ForceCompat activation on a **read-readiness** check (only prepend `ForceCompat::Infrastructure` when `PageWithForces` is schema-valid against the live org), so toocs/cans never route through the broken read; **or**
|
|
123
|
-
- Decouple the fetch from the mutate: let ForceCompat reuse the *standard* page fetch and only add the write-command accumulation, fetching forces via a separate call (or a fixed `PageWithForces`) only for cases that actually touch forces. This lets writes proceed even while the read query is being fixed.
|
|
124
|
-
- **Rewrite the prose (L42-89, L113-122).** Delete "Force / binding operations are not available in GraphQL" and "blocked on GraphQL endpoint." Replace with the accurate split: "Force/binding **writes** are available now via `executeWorkflowCommands` (`editForce`/`addBinding`/`removeBinding`/`removeForce`). The only WIP is **reading** existing forces on a page (`Query::PageWithForces` fails schema validation — fix the selection set). The ~50% 'blocked' roster is **not** blocked on a missing endpoint; it is at most blocked on the read query for cases that must *inspect* current forces before mutating." Keep the case roster but relabel it "force-touching cases (verify read-vs-write need per case)."
|
|
125
|
-
- **Cross-link** the comment to the DECISIONS entry (`ooze-graphql-native-migration/DECISIONS.md` L120-148) so the gate and the decision log can't drift apart again.
|
|
126
|
-
|
|
127
|
-
### (b) verified-platform-knowledge entry to capture
|
|
128
|
-
|
|
129
|
-
Create a durable, **repo-committed** platform-fact entry (not machine-local memory):
|
|
130
|
-
|
|
131
|
-
> **Forces on GraphQL are mutated via the `WorkflowCommandInput` command-bus, applied with the `executeWorkflowCommands` mutation.** Command keys: `addForce`, `editForce`, `removeForce`, `reorderForces`, `addBinding`, `editBinding`, `removeBinding` (gem: `Input::WorkflowCommand::*`, `COMMAND_MAP` L106-117). There is **no** separate forces write endpoint. A force `editForce` needs only `{ id, customScript }`; a binding needs `{ forceId, name, referenceId, type }`. Confirmed by Oscar (more than once) and by shipped gem code (`cd57b54` 2026-06-10, `8e8f492` 2026-06-11).
|
|
132
|
-
> **Only WIP part:** *reading* existing forces on a page via `Query::PageWithForces` — currently fails client-side schema validation (PageUnion selection set; `id` on `DataFieldBinding`/`SectionBinding`). This is a query-shape fix, not a backend gap.
|
|
133
|
-
> **Anti-pattern to retire:** the v2 "forces endpoint / `page.forces` live object" mental model. Forces are command-bus members, not a CRUD endpoint.
|
|
134
|
-
|
|
135
|
-
Suggested home: a committed `verified-platform-knowledge` doc (e.g. under `.ai-assistance/code/` or a `.ai-assistance/local/standards-requests/` entry that gets promoted to a shared, committed location — note: `.ai-assistance/local/standards-requests/` does **not currently exist** in this repo, see §7).
|
|
136
|
-
|
|
137
|
-
### (c) process fix to prevent recurrence
|
|
138
|
-
|
|
139
|
-
1. **Promote verified platform facts out of machine-local memory into committed, code-adjacent docs.** The command-bus fact lived only in `~/.claude/.../memory/` (gitignored, per-machine). A platform fact that gates real code MUST be captured where every agent/session/teammate and the code reviewer can see it — i.e. committed to the repo near the code it governs. This is the textbook case the `verified-platform-knowledge` standards request is meant to solve: a user-clarified fact that never became durable shared knowledge, so a stale "blocked" assertion survived and was reinforced.
|
|
140
|
-
2. **Doc-and-gate co-location invariant.** When a feature ships its write path (commit `e3bc7f0a`), any "TODO/blocked" prose describing that feature in the *same module* must be reconciled in the same change. A lint/check or a PR-checklist item: "Did this commit make any nearby 'blocked/TODO/not available' comment stale?"
|
|
141
|
-
3. **Never gate a multi-part capability on a single boolean tied to one part.** Capability gates should name the *specific* dependency (here: "ForceCompat fetch needs a schema-valid `PageWithForces`"), not a coarse "forces work." A gate comment should be falsifiable and cite the exact failing artifact.
|
|
142
|
-
4. **Close the loop from DECISIONS log back to code.** The 2026-06-30 DECISIONS entry recorded the correct fact but the `ooze_redirect.rb` gate/comment was not updated to match. Decision-log entries that contradict a code comment should spawn a tracked task to fix the comment, or the decision isn't "done."
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
## 7. Note on the standards mechanism
|
|
147
|
-
|
|
148
|
-
The task referenced `.ai-assistance/local/standards-requests/` (a `verified-platform-knowledge` request). **That directory does not exist in this repo as of 2026-07-01** (`.ai-assistance/local/` was not present). This RCA is therefore a concrete, documented justification for creating that mechanism: a verified platform fact (forces = `Workflow*` command-bus), clarified by the maintainer more than once, was never captured as durable, shared, **committed** knowledge — so a stale "blocked on endpoint" assertion persisted across three weeks and was actively reinforced even after the contradicting code shipped in the same module. A committed verified-platform-knowledge store, co-located with the gated code and cross-linked from the DECISIONS log, would have surfaced the contradiction at the gate.
|
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
# Decisions — Page Model Project
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## 2026-06-07 — Two search modalities: org search vs register search
|
|
6
|
-
|
|
7
|
-
**Context:** Scripts that need to find pages before updating them have two query paths,
|
|
8
|
-
with fundamentally different data sources, performance profiles, and coverage.
|
|
9
|
-
|
|
10
|
-
### Org search — `currentOrganization { pages(...) }`
|
|
11
|
-
|
|
12
|
-
- **Source:** PostgreSQL DB, rendered by the API layer
|
|
13
|
-
- **Returns:** `PageUnion` (`BasicPage | PhasedPage`) — the same model returned by
|
|
14
|
-
mutations, fully populated from the DB
|
|
15
|
-
- **Performance:** Slow — approximately 3 seconds per page on render due to API-layer
|
|
16
|
-
data assembly. **Implication:** avoid re-fetching a page after update if possible;
|
|
17
|
-
the mutation payload already returns the updated `PageUnion`.
|
|
18
|
-
- **Data completeness:** Full — includes `patchVer`, all field values, data field IDs
|
|
19
|
-
(server-assigned after DB creation), and the full type structure needed for mutations.
|
|
20
|
-
- **Archived pages:** Supported via a query argument — can search archived pages.
|
|
21
|
-
- **Best for:** Pre-update fetch (need `patchVer` + field IDs), post-build-from-template
|
|
22
|
-
fetch (need server-assigned field IDs to construct create input).
|
|
23
|
-
|
|
24
|
-
### Register search — `currentOrganization { register(id:) { previewPages(...) } }`
|
|
25
|
-
|
|
26
|
-
**Correction 2026-06-07 (verified from Insomnia production queries):** The field is
|
|
27
|
-
`previewPages`, not `pages`. Archived pages ARE supported via `includeArchived: Boolean`
|
|
28
|
-
parameter (defaults to `false`).
|
|
29
|
-
|
|
30
|
-
- **Source:** Elasticsearch (ES) — the org's search index, not the DB
|
|
31
|
-
- **Returns:** `PreviewPage` — field preview values from ES snapshots. No field IDs, no
|
|
32
|
-
`patchVer`, no typed data field models. Insufficient for building mutation inputs.
|
|
33
|
-
- **Performance:** Fast — sub-second, ES-powered.
|
|
34
|
-
- **Data completeness:** Partial — preview values only. Use to find page IDs, then switch
|
|
35
|
-
to org search for full data.
|
|
36
|
-
- **Archived pages:** Supported via `includeArchived: Boolean` parameter.
|
|
37
|
-
- **Extra args:** `presetViewId: ID` — applies a register PageView preset configuration.
|
|
38
|
-
- **Best for:** Fast lookup by metadata (`externalId`, state, location tags, date ranges)
|
|
39
|
-
to get page IDs before switching to org search for full data fetch.
|
|
40
|
-
|
|
41
|
-
### Search config — `SearchConf` (Hash type)
|
|
42
|
-
|
|
43
|
-
The `searchConf` argument is not a typed GraphQL model — it is passed as a `Hash`
|
|
44
|
-
(the schema shows it as `Search`). Engineering has backend classes for it but these are
|
|
45
|
-
not exposed as GraphQL input types.
|
|
46
|
-
|
|
47
|
-
**Verified structure (from Insomnia):**
|
|
48
|
-
|
|
49
|
-
```json
|
|
50
|
-
{
|
|
51
|
-
"filters": [ <filter_object>, ... ],
|
|
52
|
-
"sorters": { "key": "created_at", "direction": "asc" },
|
|
53
|
-
"query": "optional full-text string"
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**Filter operations (snake_case strings):**
|
|
58
|
-
- `exact_filter` — equality: `{ key: "external_id", value: "X" }`
|
|
59
|
-
- `date_filter` — date range: `{ key: "updated_at", gte: "ISO8601", time_zone: "UTC" }`
|
|
60
|
-
- `register_filter` — scope to registers (org search): `{ ids: ["id1"] }`
|
|
61
|
-
- `and_filter` — explicit AND group: `{ filters: [...] }`
|
|
62
|
-
- `or_filter` — OR group: `{ filters: [...] }`
|
|
63
|
-
|
|
64
|
-
**Sorter:** `{ "key": "field_name", "direction": "asc"|"desc" }` — note key field is
|
|
65
|
-
`key`, not `fieldName`. Can be a single object or an array.
|
|
66
|
-
|
|
67
|
-
Full reference with all production examples: `.ai-assistance/code/search_filters.md`
|
|
68
|
-
|
|
69
|
-
**Reference:** Search filter docs partially exist in Confluence APIDOCS (most common
|
|
70
|
-
filters). Full coverage is pending — see the Search Filter Builder project.
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 2026-06-07 — CommonPageUnion: unified fragment for all page integration patterns
|
|
75
|
-
|
|
76
|
-
**Context:** Customer integration scripts share three fundamental page interaction patterns,
|
|
77
|
-
all of which require reading the full page content (including data field IDs and structure):
|
|
78
|
-
|
|
79
|
-
1. **Build from template** — After calling `buildFromTemplate`, the server returns a built
|
|
80
|
-
draft whose data field IDs are already assigned by the DB. Scripts must fetch this draft
|
|
81
|
-
via `CommonPageUnion` to retrieve those IDs before they can construct the `dataFields`
|
|
82
|
-
input for the subsequent `createFromTemplate` call.
|
|
83
|
-
|
|
84
|
-
2. **Fetch before update** — Before calling `updatePage`, scripts must fetch the current page
|
|
85
|
-
via `CommonPageUnion` to get the current `patchVer` (required for optimistic concurrency)
|
|
86
|
-
AND the current data field IDs and values. Field IDs change after DB creation and cannot
|
|
87
|
-
be assumed — they must be read from the live page.
|
|
88
|
-
|
|
89
|
-
3. **Fetch after create/update** — To confirm the result, scripts re-fetch via
|
|
90
|
-
`CommonPageUnion` and inspect the returned state.
|
|
91
|
-
|
|
92
|
-
**Decision:** `Fragment::Pages::CommonPageUnion` is a single, parameterised fragment that
|
|
93
|
-
covers all three patterns. Rather than having separate lightweight/heavyweight variants,
|
|
94
|
-
three boolean query variables control how much is fetched in a given call:
|
|
95
|
-
|
|
96
|
-
| Variable | Purpose | Use in pattern |
|
|
97
|
-
|---|---|---|
|
|
98
|
-
| `$content` | Include field values, stage completions, people, files | (2) update, (3) verify |
|
|
99
|
-
| `$only_content` | Exclude structural/config fields — content values only | (3) verify result |
|
|
100
|
-
| `$fields` | Include data field definitions (labels, options, structure) | (1) build, (2) update |
|
|
101
|
-
|
|
102
|
-
**Design rationale — educating customer technicians:** The two-step sequences for create
|
|
103
|
-
and update were deliberately consolidated under one fragment to give customer technicians
|
|
104
|
-
a clear, documented path into the GraphQL integration:
|
|
105
|
-
|
|
106
|
-
- **CREATE sequence:** (a) `buildFromTemplate` → fetch built draft with `CommonPageUnion`
|
|
107
|
-
(`$fields: true`) to get data field IDs; (b) fill in values and call `createFromTemplate`.
|
|
108
|
-
- **UPDATE sequence:** (a) fetch current page with `CommonPageUnion` (`$content: true,
|
|
109
|
-
$fields: true`) to get `patchVer` + current field IDs and values; (b) build update
|
|
110
|
-
operations and call `updatePage`.
|
|
111
|
-
|
|
112
|
-
GraphQL's declarative nature (clients specify what they need) is new to many technicians.
|
|
113
|
-
The directive variables make it visible and learnable — they can start with
|
|
114
|
-
`$content: true, $fields: true` to get everything, then dial back once they understand
|
|
115
|
-
what each variable controls.
|
|
116
|
-
|
|
117
|
-
**Note on Build - Model (legacy pattern):** The Insomnia export contains an older
|
|
118
|
-
`Build - Model` request with its own inline fragment definitions (no `CommonPageUnion`).
|
|
119
|
-
This predates the unified fragment approach. It was used before the team settled on a
|
|
120
|
-
single documented pattern for customer support knowledge sharing. Treat it as legacy;
|
|
121
|
-
all new scripts should use the `Build - Standard` / `CommonPageUnion` approach.
|
|
122
|
-
|
|
123
|
-
**Note on compound mutations:** The `ArchivePage` Insomnia request runs two mutations in
|
|
124
|
-
one request — `updatePage` (blanks `externalId`) then `archivePage`. This is valid GraphQL
|
|
125
|
-
(multiple operations in one definition, executed in declaration order by the server). It was
|
|
126
|
-
designed for a customer whose middleware was stateless and could not sequence two separate HTTP
|
|
127
|
-
requests within a session. The `operationName` field in the JSON payload identifies the
|
|
128
|
-
primary named definition. This pattern is available to any integration that needs atomic
|
|
129
|
-
sequencing without server-side transactions.
|
|
130
|
-
|
|
131
|
-
**Reference:** Documented in Jira API Docs space (Confluence APIDOCS).
|
|
132
|
-
Fragment lives at: `lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb`
|
|
133
|
-
Accessed via: `fragment.pages.assemble(:CommonPageUnion)` or via `assemble_fragments`
|
|
134
|
-
when `...CommonPageUnion` appears in a query string.
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## 2026-06-05 — Page model is phased; DataFields deferred to Phase E
|
|
139
|
-
|
|
140
|
-
**Context:** Pages are the central EcoPortal entity with 50+ fields, two concrete types
|
|
141
|
-
(BasicPage / PhasedPage), stages, sections, 20 data field types, AI features, and
|
|
142
|
-
workflow. Attempting to implement everything at once is impractical.
|
|
143
|
-
|
|
144
|
-
**Decision:** Phase the work. Phase A (read) and Phase B (write) cover the core lifecycle
|
|
145
|
-
that customer scripts need. DataFields (Phase E) deferred — they require domain knowledge
|
|
146
|
-
of field types and are unlikely to be needed for structural automation scripts.
|
|
147
|
-
|
|
148
|
-
**Consequences:** Phase A/B output will not include `dataFields` in mutation inputs.
|
|
149
|
-
Scripts that need to update field values must wait for Phase E.
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## 2026-06-05 — Page creation requires a template; no blank page mutation exists
|
|
154
|
-
|
|
155
|
-
**Context:** The schema has no `createPage` mutation. All creation goes via
|
|
156
|
-
`createPageFromTemplate(input: CreateFromTemplateInput!)` or `buildPageFromTemplate`.
|
|
157
|
-
`CreateFromTemplateInput` takes `templateId: ID!` and optionally `dataFields`.
|
|
158
|
-
|
|
159
|
-
**Decision:** Phase D (creation) is scoped to template-based creation only. No attempt to
|
|
160
|
-
expose a blank-page creation API because none exists on the server.
|
|
161
|
-
|
|
162
|
-
**Consequences:** Scripts that create pages must know the target template ID. The gem
|
|
163
|
-
should provide a way to look up templates (`Query::Templates`).
|
|
164
|
-
|
|
165
|
-
---
|
|
166
|
-
|
|
167
|
-
## 2026-06-05 — Both BasicPage and PhasedPage mapped; Stage deferred to Phase C
|
|
168
|
-
|
|
169
|
-
**Context:** `PageUnion = BasicPage | PhasedPage`. PhasedPage adds stages; Stage has its
|
|
170
|
-
own `dataFields`, `tasks`, `sections`. Scripting typically works at the Page level (not
|
|
171
|
-
individual stage fields).
|
|
172
|
-
|
|
173
|
-
**Decision:** Phase A maps `BasicPage` and `PhasedPage` as concrete model classes
|
|
174
|
-
inheriting from `Interface::BasePage`. Stage model populated in Phase C. Scripts can
|
|
175
|
-
access stages via `page.stages` once Phase C is done.
|
|
176
|
-
|
|
177
|
-
**Consequences:** `PhasedPage#stages` returns raw data (passarray) until Phase C adds a
|
|
178
|
-
proper `Stage` model class.
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## 2026-06-05 — `patchVer` always queried and always passed on update
|
|
183
|
-
|
|
184
|
-
**Context:** `patchVer: Int!` is non-null on every page read. The `updatePage` mutation
|
|
185
|
-
accepts it optionally. The server uses it for optimistic concurrency control.
|
|
186
|
-
|
|
187
|
-
**Decision:** The Page fragment always includes `patchVer`. The `UpdatePage` mutation
|
|
188
|
-
input always includes `patchVer` sourced from the model. This is not optional in our gem
|
|
189
|
-
even though it is optional in the schema.
|
|
190
|
-
|
|
191
|
-
**Reason:** Silent concurrency failures (last-write-wins without warning) are worse than
|
|
192
|
-
an occasional stale-version rejection. Making `patchVer` mandatory in our usage prevents
|
|
193
|
-
data loss from concurrent edits.
|
|
194
|
-
|
|
195
|
-
**Consequences:** Any code that builds `UpdatePageInput` must have previously fetched the
|
|
196
|
-
page to get the current `patchVer`. You cannot update a page you haven't read first.
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
## 2026-06-05 — `stageId` in UpdatePageInput: Phase C concern
|
|
201
|
-
|
|
202
|
-
**Context:** `UpdatePageInput` has `stageId: ID` which targets a specific stage on a
|
|
203
|
-
phased page. Stage-specific operations (submit, review, complete) also use this.
|
|
204
|
-
|
|
205
|
-
**Decision:** Phase B implements page-level updates only (name, state, locations, tags,
|
|
206
|
-
`patchVer`). Stage-targeting (`stageId`, `submit`, `task` input) is Phase C.
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## 2026-06-06 — PageUnion implemented as a factory module, not a class
|
|
211
|
-
|
|
212
|
-
**Context:** The GraphQL `page` and `pages` fields return `PageUnion = BasicPage | PhasedPage`.
|
|
213
|
-
The response wrapping layer calls `response_class.new(data)` and connection node instantiation
|
|
214
|
-
calls `node_class.new(item, parent: self)`. Both require a single "class" that can dispatch to
|
|
215
|
-
the correct concrete type based on `__typename`.
|
|
216
|
-
|
|
217
|
-
**Decision:** `Model::PageUnion` is implemented as a Ruby module with a custom `.new` method
|
|
218
|
-
that inspects `doc['__typename']` and delegates to `Model::Page::Basic`, `Model::Page::Phased`,
|
|
219
|
-
or `Interface::BasePage` (fallback). It is used as both `item_class` on `Query::Page` and
|
|
220
|
-
`node_class` on `Connection::Page`.
|
|
221
|
-
|
|
222
|
-
`class_resolver` accepts modules since `resolve_class` uses `Kernel.const_get` for strings and
|
|
223
|
-
returns the constant as-is. The `.new` method passes through `**opts` so `parent:` and `key:`
|
|
224
|
-
from `embeds_many` reach the concrete constructor correctly.
|
|
225
|
-
|
|
226
|
-
**Consequences:** Callers always receive a concrete `Page::Basic` or `Page::Phased` instance —
|
|
227
|
-
`PageUnion` itself is never instantiated. The `__typename` field must be included in the
|
|
228
|
-
`PageFields` fragment for dispatch to work.
|
|
229
|
-
|
|
230
|
-
---
|
|
231
|
-
|
|
232
|
-
## 2026-06-05 — Fragment::Page initial scope
|
|
233
|
-
|
|
234
|
-
**Context:** `BasePageInterface` has 50 fields. Most are read-only context or authorization
|
|
235
|
-
flags. Customer scripts primarily need identity, state, location, and versioning fields.
|
|
236
|
-
|
|
237
|
-
**Decision (initial fragment scope):**
|
|
238
|
-
- **Always include:** `id`, `name`, `uid`, `icon`, `patchVer`, `state`, `archived`,
|
|
239
|
-
`archivedAt`, `draft`, `externalId`, `timeZone`, `otherTags`, `taskPriority`,
|
|
240
|
-
`sourceTemplateId`, `createdAt`, `updatedAt`, `creatorName`
|
|
241
|
-
- **Locations:** `locations { id name }` (via spread of `LocationNode` fragment)
|
|
242
|
-
- **Exclude initially:** `dataFields`, `sections`, `workflow`, `forces`, `tasks`,
|
|
243
|
-
`comments`, AI fields, authorization `can*` fields
|
|
244
|
-
|
|
245
|
-
Fields can be added to the fragment as needed by downstream scripts.
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
# TODOs — Page Model Project
|
|
2
|
-
|
|
3
|
-
> Created 2026-06-05 from live schema introspection.
|
|
4
|
-
> Natural implementation order: A → B → C → D → E
|
|
5
|
-
> Phases C, D, E can be deferred — Phase A + B covers the core script use case.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Phase A — Read (queries, fragment, models)
|
|
10
|
-
|
|
11
|
-
- [x] **A.1** Fragment::PageFields on BasePageInterface — all initial-scope fields + `...LocationNode`.
|
|
12
|
-
*Done 2026-06-07 — `lib/ecoportal/api/graphql/fragment/page.rb`*
|
|
13
|
-
|
|
14
|
-
- [x] **A.2** Interface::BasePage: added `passboolean :draft`, `passthrough :externalId`.
|
|
15
|
-
*Done 2026-06-07 — archivedAt was already present*
|
|
16
|
-
|
|
17
|
-
- [x] **A.3** `Base::Page::Basic < Interface::BasePage` (new); `Base::Page::Phased`: added `passarray :stages`.
|
|
18
|
-
*Done 2026-06-07 — naming follows existing nesting: Base::Page::Basic, Base::Page::Phased*
|
|
19
|
-
|
|
20
|
-
- [x] **A.4** `Model::Page::Basic < Base::Page::Basic` (new); `Model::Page::Phased` already existed with resolvers.
|
|
21
|
-
*Done 2026-06-07*
|
|
22
|
-
|
|
23
|
-
- [x] **A.5** `Query::Page` (single by id); `Model::PageUnion` factory dispatches on `__typename`.
|
|
24
|
-
*Done 2026-06-07 — PageUnion approach documented in DECISIONS.md*
|
|
25
|
-
|
|
26
|
-
- [x] **A.6** `Query::Pages` — paginated, `Connection::Page` with `PageUnion` node_class.
|
|
27
|
-
*Done 2026-06-07*
|
|
28
|
-
|
|
29
|
-
- [x] **A.7** `Model::Organization`: wired `query :page`, `query :pages`, `query :templates`.
|
|
30
|
-
*Done 2026-06-07*
|
|
31
|
-
|
|
32
|
-
- [x] **A.8** `Query::Templates` — reuses `Connection::Page` and `PageFields` fragment.
|
|
33
|
-
*Done 2026-06-07*
|
|
34
|
-
|
|
35
|
-
- [x] **A.9** RSpec specs for `Query::Page`, `Query::Pages`, fragment assembly.
|
|
36
|
-
`spec/ecoportal/api/graphql/query/page_spec.rb` — 7 examples (field_name, accepted_params,
|
|
37
|
-
item_class, PageFields/LocationNode/BasicPageFields/PhasedPageFields fragment assembly).
|
|
38
|
-
`spec/ecoportal/api/graphql/query/pages_spec.rb` — 7 examples (field_name, accepted_params,
|
|
39
|
-
item_class, connection_class, fragment assembly, SearchConf acceptance).
|
|
40
|
-
*Done 2026-06-07 — 14 examples, 0 failures*
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Phase B — Write (update mutation)
|
|
45
|
-
|
|
46
|
-
- [x] **B.1** `Input::Page` — `name/state/taskPriority/timeZone/externalId/locations/otherTags`.
|
|
47
|
-
*Done 2026-06-07 — `lib/ecoportal/api/graphql/input/page.rb`*
|
|
48
|
-
|
|
49
|
-
- [x] **B.2** `Input::Page::Update.from_model` — nested `page:` sub-hash, always injects `patchVer`, supports `publish:` flag.
|
|
50
|
-
*Done 2026-06-07 — `lib/ecoportal/api/graphql/input/page/update.rb`*
|
|
51
|
-
|
|
52
|
-
- [x] **B.3** `Mutation::Page::Update` + `Payload::Page::Update` (item via PageUnion).
|
|
53
|
-
*Done 2026-06-07*
|
|
54
|
-
|
|
55
|
-
- [x] **B.4** `Mutation::Page::Archive` + `Mutation::Page::Unarchive` + corresponding payloads + inputs.
|
|
56
|
-
*Done 2026-06-07*
|
|
57
|
-
|
|
58
|
-
- [x] **B.5** `Builder::Page`: `update/archive/unarchive` methods.
|
|
59
|
-
*Done 2026-06-07*
|
|
60
|
-
|
|
61
|
-
- [x] **B.6** `Ecoportal::API::GraphQL#page` → `Builder::Page`.
|
|
62
|
-
*Done 2026-06-07*
|
|
63
|
-
|
|
64
|
-
- [x] **B.7** RSpec specs: `Input::Page::Update.from_model` (no-change nil, patchVer, publish, clientMutationId); `Model::PageUnion` __typename dispatch.
|
|
65
|
-
*Done 2026-06-07 — archive/unarchive input specs deferred (low value, straightforward passthrough)*
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Phase C — PhasedPage stages
|
|
70
|
-
|
|
71
|
-
> Prerequisite: Phase A model classes exist.
|
|
72
|
-
|
|
73
|
-
- [x] **C.1** Populate `Base::Page::Phased::Stage` with fields from `Stage` type:
|
|
74
|
-
`id`, `name`, `ordering`, `state`, `active`, `started`; predicate methods `active?`/`started?`;
|
|
75
|
-
`sections` → SectionCollection, `components` → DataField::Collection, `[]` hash-style access.
|
|
76
|
-
*Done 2026-06-07 — 17 specs in `spec/ecoportal/api/graphql/base/page/phased/stage_spec.rb`*
|
|
77
|
-
|
|
78
|
-
- [x] **C.2** Add `embeds_many :stages, klass: Stage` on `Base::Page::Phased`.
|
|
79
|
-
*Done 2026-06-07 — `Compat::StageCollection#coerce_stage` wraps raw hashes in `Stage`*
|
|
80
|
-
|
|
81
|
-
- [x] **C.3** Add `stageId` support to `Input::Page::Update` and `Mutation::Page::Update`
|
|
82
|
-
for stage-targeted updates.
|
|
83
|
-
*Done 2026-06-07 (commit 6305f69) — stageId + submit kwargs; non-nil result for stage-only submit*
|
|
84
|
-
|
|
85
|
-
- [x] **C.4** Add submit/review task mutations: `approveReviewPageTask`, `rejectReviewPageTask`,
|
|
86
|
-
`batchUpdateReviewPageTask`, `restartReviewPageTask`, `undoReviewPageTask`.
|
|
87
|
-
`Input::Page::ReviewTask` (id, stageId, comment); `Payload::Page::ReviewTask` (item: PageUnion);
|
|
88
|
-
5 mutation classes; `Builder::Page` wired with `approve/reject/restart/undo/batch_update_review_task`.
|
|
89
|
-
*Done 2026-06-07 — 5 specs in `spec/ecoportal/api/graphql/input/page/review_task_spec.rb`*
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## Phase D — Creation from template
|
|
94
|
-
|
|
95
|
-
- [x] **D.1** `Input::Page::CreateFromTemplate` — `templateId`, `showHiddenData`, `stageId`,
|
|
96
|
-
`submit`, `attach`, `draft`, `clientMutationId`, `dataFields` (raw passthrough), `page` (embeds_one).
|
|
97
|
-
*Pre-existing*
|
|
98
|
-
|
|
99
|
-
- [x] **D.2** `Mutation::Page::CreateFromTemplate` — `createPageFromTemplate`, `CreateFromTemplateInput!`.
|
|
100
|
-
*Pre-existing*
|
|
101
|
-
|
|
102
|
-
- [x] **D.3** `Mutation::Page::BuildFromTemplate` — `buildPageFromTemplate`, `BuildFromTemplateInput!`.
|
|
103
|
-
*Pre-existing*
|
|
104
|
-
|
|
105
|
-
- [x] **D.4** Draft lifecycle mutations — all share `Payload::Page::Draft` (item: PageUnion):
|
|
106
|
-
- `Input::Page::CreateDraft` (templateId), `Mutation::Page::CreateDraft` (createPageDraft)
|
|
107
|
-
- `Input::Page::DeleteDraft` (id), `Mutation::Page::DeleteDraft` (deleteDraftPage)
|
|
108
|
-
- `Input::Page::PublishDraft` (id), `Mutation::Page::PublishDraft` (publishDraftPage)
|
|
109
|
-
*Done 2026-06-07 — 4 specs in `spec/ecoportal/api/graphql/input/page/create_draft_spec.rb`*
|
|
110
|
-
|
|
111
|
-
- [x] **D.5** `Builder::Page` wired for all template + draft operations:
|
|
112
|
-
`build_from_template`, `create_from_template`, `create_draft`, `delete_draft`, `publish_draft`.
|
|
113
|
-
*Done 2026-06-07*
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## Phase E — DataFields (deferred — complex)
|
|
118
|
-
|
|
119
|
-
> DataFields are the rich content of pages (form fields). 20 field types.
|
|
120
|
-
> Defer until Phases A–D are stable and there is a clear script use case.
|
|
121
|
-
|
|
122
|
-
- [x] **E.1** All 20 DataField types implemented in `lib/ecoportal/api/graphql/base/page/data_field/`.
|
|
123
|
-
10 writable types (PlainText, RichText, Date→DateField, Number, Gauge, Select, Checklist,
|
|
124
|
-
TagField, People, Geo, ContractorEntities, CrossReference, File→FileField, ImageGallery).
|
|
125
|
-
6 read-only stubs (Signature, Mailbox, ActionsList, Law, AiSummary, Table).
|
|
126
|
-
TYPE_MAP covers all 20 `__typename` → Ruby class mappings.
|
|
127
|
-
*Done 2026-06-08 — 68 examples in data_field_spec.rb*
|
|
128
|
-
|
|
129
|
-
- [x] **E.2** `DataFieldOneToManyInput { additions, updates, deletions }` fully wired:
|
|
130
|
-
`Collection#mark_for_deletion` + `#dirty_deletions`; `DataFieldAccess#data_fields_deletions`;
|
|
131
|
-
`Input::Page::Update.from_model` includes deletions in `build_data_fields`.
|
|
132
|
-
*Done 2026-06-08*
|
|
133
|
-
|
|
134
|
-
- [x] **E.3** Data field access wired on all page models via `Concerns::DataFieldAccess`
|
|
135
|
-
included in `Interface::BasePage` (`components`, `field_collection`, `data_fields_updates`,
|
|
136
|
-
`data_fields_additions`, `data_fields_deletions`). Stage exposes `components` via sections.
|
|
137
|
-
*Pre-existing + additions from E.2*
|
|
138
|
-
|
|
139
|
-
- [x] **E.4** Code-spec doc: `.ai-assistance/code/data_fields.md` — all 20 types, setter API,
|
|
140
|
-
Collection API, DataFieldOneToManyInput wiring, stage-level access, spec coverage table.
|
|
141
|
-
*Done 2026-06-08*
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Cross-cutting
|
|
146
|
-
|
|
147
|
-
- [x] **X.1** `Fragment::BasicPageFields on BasicPage` — sections (id, heading, __typename).
|
|
148
|
-
*Done 2026-06-07 — fragment/page.rb*
|
|
149
|
-
|
|
150
|
-
- [x] **X.2** `Fragment::PhasedPageFields on PhasedPage` — stagesIndex, currentStage, activeStages.
|
|
151
|
-
*Done 2026-06-07 — fragment/page.rb*
|
|
152
|
-
|
|
153
|
-
- [x] **X.3** `Query::PageDelta` — `pageDelta(deltaInput: [DeltaInput!]!, searchConf: Search)`.
|
|
154
|
-
Actual schema: takes `{ id, deltaAt: ISO8601DateTime! }` per page, not just ids.
|
|
155
|
-
Returns `[DeltaResult]` (UPDATED/NOT_FOUND/NEW). `Base::DeltaResult`, `Input::DeltaInput`.
|
|
156
|
-
*Done 2026-06-07*
|
|
157
|
-
- [x] **X.4** CHANGELOG and version bump — done 2026-06-07, bumped to 1.4.0.
|
|
158
|
-
- [x] **X.5** Cycle-end Gemini review — done 2026-06-07. Finding applied: PAGE_FIELD_KEYS
|
|
159
|
-
single source of truth in Input::Page.
|
|
160
|
-
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
## Search integration
|
|
164
|
-
|
|
165
|
-
> Two modalities — see DECISIONS.md "Two search modalities" entry for full comparison.
|
|
166
|
-
|
|
167
|
-
- [x] **S.1** Wire `Query::Pages` (org search) for pre-update fetch workflow:
|
|
168
|
-
documented in `search_filters.md` ("The Required Pattern: Find Before Mutate") and
|
|
169
|
-
as a code comment on `Query::Pages`. `RegisterPreviewPages` is insufficient — no
|
|
170
|
-
`patchVer` or field IDs. Both docs explain the required pattern.
|
|
171
|
-
*Done 2026-06-07*
|
|
172
|
-
|
|
173
|
-
- [x] **S.2** `Query::RegisterPreviewPages` + `Base::PreviewPage` + `Connection::PreviewPage`.
|
|
174
|
-
*Done 2026-06-07 — fast ES search, includeArchived supported, presetViewId supported*
|
|
175
|
-
|
|
176
|
-
- [~] ~~Wire register-scoped page search — `Query::RegisterPreviewPages`:~~
|
|
177
|
-
`currentOrganization { register(id: $id) { previewPages(searchConf: $conf, includeArchived: $includeArchived, presetViewId: $presetId, ...) } }`
|
|
178
|
-
Returns `PreviewPage` nodes (ES-backed, fast). No field IDs or `patchVer`.
|
|
179
|
-
Supports archived pages via `includeArchived: Boolean` (default false).
|
|
180
|
-
Useful for fast metadata lookup before switching to org search for full data.
|
|
181
|
-
|
|
182
|
-
- [x] **S.3** `.ai-assistance/code/search_filters.md` — complete filter reference.
|
|
183
|
-
Extracted from Insomnia_2026-06-07.yaml (real production queries) + Confluence APIDOCS.
|
|
184
|
-
Covers: find by externalId ✓, find by state ✓, find by location_id ✓, find by date range ✓,
|
|
185
|
-
nested AND/OR ✓. Also documents: two query modalities, SearchConf structure, silent failure
|
|
186
|
-
traps, compound archive pattern, operation constants.
|
|
187
|
-
*Done 2026-06-07*
|
|
188
|
-
|
|
189
|
-
- [ ] **S.4** Full search filter builder — see dedicated project:
|
|
190
|
-
`.ai-assistance/projects/search-filter-builder/`
|