ecoportal-api-graphql 1.3.5 → 1.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ai-assistance/bridge/CLAUDE.md +338 -0
- data/.ai-assistance/bridge/archive/.gitkeep +0 -0
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +29 -0
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +18 -0
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +42 -0
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +115 -0
- data/.ai-assistance/bridge/context/gemini-review-prompt.txt +48 -0
- data/.ai-assistance/bridge/context/gemini-review-response.md +104 -0
- data/.ai-assistance/bridge/context/project.md +42 -0
- 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 +48 -0
- data/.ai-assistance/bridge/queue/.gitkeep +1 -0
- data/.ai-assistance/capabilities/CLAUDE.md +27 -0
- data/.ai-assistance/capabilities/assumptions-log.md +80 -0
- data/.ai-assistance/capabilities/code.md +47 -0
- data/.ai-assistance/capabilities/connectors.md +37 -0
- data/.ai-assistance/capabilities/cowork.md +55 -0
- data/.ai-assistance/code/OVERVIEW.md +155 -0
- data/.ai-assistance/code/data_fields.md +242 -0
- data/.ai-assistance/code/dependencies.md +151 -0
- data/.ai-assistance/code/diff_as_input.md +234 -0
- data/.ai-assistance/code/diff_service_deep_dive.md +192 -0
- data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +55 -0
- data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +181 -0
- data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +192 -0
- data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +147 -0
- data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +277 -0
- data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +200 -0
- data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +228 -0
- data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +197 -0
- data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +243 -0
- data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +196 -0
- data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +132 -0
- data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +187 -0
- data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +236 -0
- data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +183 -0
- data/.ai-assistance/code/ecoportal_schema_reference.md +240 -0
- data/.ai-assistance/code/graphql_domain_knowledge.md +230 -0
- data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +71 -0
- data/.ai-assistance/code/refactoring/opportunities.md +251 -0
- data/.ai-assistance/code/schema_analysis.md +321 -0
- data/.ai-assistance/code/search_filters.md +868 -0
- data/.ai-assistance/code/spec_coverage.md +73 -0
- data/.ai-assistance/code/workflow-command-guide.md +438 -0
- data/.ai-assistance/code/workflow-space.md +353 -0
- data/.ai-assistance/conventions/CLAUDE.md +30 -0
- data/.ai-assistance/conventions/code-working-tree-protocol.md +199 -0
- data/.ai-assistance/conventions/gitignore-rules.md +42 -0
- data/.ai-assistance/conventions/permission-guidance.md +120 -0
- data/.ai-assistance/integrations/README.md +70 -0
- data/.ai-assistance/integrations/gitkraken-mcp.md +107 -0
- data/.ai-assistance/integrations/gitlab-mcp.md +123 -0
- data/.ai-assistance/integrations/local-git.md +60 -0
- data/.ai-assistance/local_paths.example.md +17 -0
- data/.ai-assistance/projects/TODO.md +97 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +168 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +60 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +267 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +53 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +102 -0
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +139 -0
- data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +93 -0
- data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +244 -0
- data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +266 -0
- data/.ai-assistance/projects/eco-helpers-compat/TODO.md +86 -0
- data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +101 -0
- data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +177 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +161 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +125 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +126 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +256 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +122 -0
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +148 -0
- data/.ai-assistance/projects/page-model/DECISIONS.md +245 -0
- data/.ai-assistance/projects/page-model/TODO.md +190 -0
- data/.ai-assistance/projects/search-filter-builder/INTENT.md +107 -0
- data/.ai-assistance/projects/search-filter-builder/TODO.md +131 -0
- data/.ai-assistance/projects/template-maintenance/DESIGN.md +134 -0
- data/.ai-assistance/projects/workflow-space/TODO.md +213 -0
- data/.ai-assistance/reinstall-claude-desktop-windows.md +136 -0
- data/.ai-assistance/scripts/CLAUDE.md +150 -0
- data/.ai-assistance/scripts/bridge-init.sh +86 -0
- data/.ai-assistance/scripts/bridge-status.sh +44 -0
- data/.ai-assistance/scripts/capabilities-check.ts +104 -0
- data/.ai-assistance/scripts/check-outbox.sh +43 -0
- data/.ai-assistance/scripts/dep_graph.rb +91 -0
- data/.ai-assistance/scripts/lock-acquire.sh +103 -0
- data/.ai-assistance/scripts/lock-multi.sh +124 -0
- data/.ai-assistance/scripts/lock-queue.sh +94 -0
- data/.ai-assistance/scripts/setup-mcps.test.ts +188 -0
- data/.ai-assistance/scripts/setup-mcps.ts +234 -0
- data/.ai-assistance/scripts/task-complete.ts +74 -0
- data/.ai-assistance/scripts/task-create.ts +75 -0
- data/.ai-assistance/scripts/task-read.ts +125 -0
- data/.ai-assistance/scripts/token-logger.js +220 -0
- data/.ai-assistance/scripts/token-report.ts +158 -0
- data/.ai-assistance/scripts/token-session-start.js +66 -0
- data/.ai-assistance/skills/ai-instructions/SKILL.md +48 -0
- data/.ai-assistance/skills/code-specs/SKILL.md +69 -0
- data/.ai-assistance/skills/corporate-policies/SKILL.md +201 -0
- data/.ai-assistance/skills/dep-graph/SKILL.md +139 -0
- data/.ai-assistance/skills/ep-ai-manager/SKILL.md +417 -0
- data/.ai-assistance/skills/gemini-assist/SKILL.md +63 -0
- data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +205 -0
- data/.ai-assistance/skills/gemini-assist/gemini_ask.py +1 -0
- data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +240 -0
- data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +25 -0
- data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +261 -0
- data/.ai-assistance/skills/project-cycle/SKILL.md +177 -0
- data/.ai-assistance/skills/refactor/SKILL.md +62 -0
- data/.ai-assistance/skills/rubocop/SKILL.md +93 -0
- data/.ai-assistance/skills/ruby-scripting/SKILL.md +215 -0
- data/.ai-assistance/skills/spec-generation/SKILL.md +72 -0
- data/.ai-assistance/standards-version.json +21 -0
- data/.ai-assistance/token-budget.json +32 -0
- data/.ai-assistance/version.json +39 -0
- data/.claude/settings.json +146 -0
- data/.env.example +18 -0
- data/.gitattributes +15 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +121 -97
- data/CHANGELOG.md +673 -477
- data/CLAUDE.md +232 -0
- data/Gemfile +30 -6
- data/Rakefile +90 -38
- data/docs/worklog.md +574 -0
- data/ecoportal-api-graphql.gemspec +40 -40
- data/lib/ecoportal/api/common/graphql/CLAUDE.md +36 -0
- data/lib/ecoportal/api/common/graphql/client.rb +1 -1
- data/lib/ecoportal/api/common/graphql/http_client.rb +35 -3
- data/lib/ecoportal/api/common/graphql/model/CLAUDE.md +28 -0
- data/lib/ecoportal/api/common/graphql/model/as_input.rb +8 -5
- data/lib/ecoportal/api/common/graphql/model/diffable/classic_diff_service.rb +3 -1
- data/lib/ecoportal/api/common/graphql/model/diffable/diff_service.rb +31 -23
- data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff_nesting.rb +54 -1
- data/lib/ecoportal/api/graphql/CLAUDE.md +37 -0
- data/lib/ecoportal/api/graphql/base/CLAUDE.md +50 -0
- data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -0
- data/lib/ecoportal/api/graphql/base/delta_result.rb +16 -0
- data/lib/ecoportal/api/graphql/base/force/binding.rb +19 -0
- data/lib/ecoportal/api/graphql/base/force/binding_collection.rb +62 -0
- data/lib/ecoportal/api/graphql/base/force/collection.rb +47 -0
- data/lib/ecoportal/api/graphql/base/force.rb +65 -0
- data/lib/ecoportal/api/graphql/base/kickstand/job.rb +17 -0
- data/lib/ecoportal/api/graphql/base/kickstand/workflow.rb +18 -0
- data/lib/ecoportal/api/graphql/base/kickstand.rb +13 -0
- data/lib/ecoportal/api/graphql/base/page/basic.rb +38 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/actions_list.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/ai_summary.rb +18 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/checklist.rb +29 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +112 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/cross_reference.rb +54 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/date_field.rb +23 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/gauge.rb +19 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/geo.rb +24 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/law.rb +8 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/mailbox.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/number.rb +20 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/people.rb +35 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/plain_text.rb +17 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/rich_text.rb +26 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/select.rb +41 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/signature.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/smart_fill.rb +17 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/table.rb +9 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/tag_field.rb +21 -0
- data/lib/ecoportal/api/graphql/base/page/data_field.rb +137 -12
- data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +68 -14
- data/lib/ecoportal/api/graphql/base/page/phased.rb +1 -0
- data/lib/ecoportal/api/graphql/base/page/section.rb +36 -0
- data/lib/ecoportal/api/graphql/base/page/section_collection.rb +79 -0
- data/lib/ecoportal/api/graphql/base/page.rb +2 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/action_type_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/callback_type.rb +28 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_change.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_change_message.rb +15 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_es_change.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/command_interface.rb +36 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/email_config.rb +18 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/escalation_level.rb +19 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/in_system_config.rb +16 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/mailbox_field_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operation_interface.rb +39 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operations/assign_to.rb +27 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operations/create_page.rb +21 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/operations/send_notification.rb +30 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/people_field_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/recipient_config.rb +34 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/register_field.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/task_config_selection.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/time_delay_config.rb +16 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/trigger_interface.rb +26 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/triggers/conditional_logic.rb +17 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow/user_selection.rb +16 -0
- data/lib/ecoportal/api/graphql/base/pages_workflow.rb +35 -0
- data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -0
- data/lib/ecoportal/api/graphql/base/preview_page.rb +23 -0
- data/lib/ecoportal/api/graphql/base/register.rb +18 -0
- data/lib/ecoportal/api/graphql/base.rb +11 -0
- data/lib/ecoportal/api/graphql/builder/CLAUDE.md +65 -0
- data/lib/ecoportal/api/graphql/builder/kickstand.rb +73 -0
- data/lib/ecoportal/api/graphql/builder/page.rb +210 -41
- data/lib/ecoportal/api/graphql/builder/register/preset_view.rb +84 -0
- data/lib/ecoportal/api/graphql/builder/register.rb +27 -19
- data/lib/ecoportal/api/graphql/builder/template.rb +80 -0
- data/lib/ecoportal/api/graphql/builder.rb +2 -0
- data/lib/ecoportal/api/graphql/compat/filter_translator.rb +107 -0
- data/lib/ecoportal/api/graphql/compat/page_reference.rb +23 -0
- data/lib/ecoportal/api/graphql/compat/pages.rb +212 -0
- data/lib/ecoportal/api/graphql/compat/registers.rb +84 -0
- data/lib/ecoportal/api/graphql/compat/response.rb +35 -0
- data/lib/ecoportal/api/graphql/compat/search_results.rb +33 -0
- data/lib/ecoportal/api/graphql/compat/stage_collection.rb +70 -0
- data/lib/ecoportal/api/graphql/compat/stage_view.rb +76 -0
- data/lib/ecoportal/api/graphql/compat.rb +17 -0
- data/lib/ecoportal/api/graphql/concerns/data_field_access.rb +71 -0
- data/lib/ecoportal/api/graphql/concerns/deprecation.rb +20 -0
- data/lib/ecoportal/api/graphql/concerns/fragment_definitions.rb +21 -28
- data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -0
- data/lib/ecoportal/api/graphql/concerns/snake_camel_access.rb +60 -0
- data/lib/ecoportal/api/graphql/concerns.rb +4 -0
- data/lib/ecoportal/api/graphql/connection/page.rb +11 -0
- data/lib/ecoportal/api/graphql/connection/pages_workflow_command.rb +13 -0
- data/lib/ecoportal/api/graphql/connection/preset_view.rb +11 -0
- data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -0
- data/lib/ecoportal/api/graphql/connection.rb +4 -0
- data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -0
- data/lib/ecoportal/api/graphql/file_upload.rb +10 -0
- data/lib/ecoportal/api/graphql/fragment/action.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/action_category.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/contractor_entity.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/force.rb +30 -0
- data/lib/ecoportal/api/graphql/fragment/location_draft.rb +2 -2
- data/lib/ecoportal/api/graphql/fragment/location_node.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/locations_error.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment/page.rb +85 -0
- data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +395 -0
- data/lib/ecoportal/api/graphql/fragment/pages.rb +15 -0
- data/lib/ecoportal/api/graphql/fragment/pages_workflow.rb +172 -0
- data/lib/ecoportal/api/graphql/fragment/pagination.rb +1 -1
- data/lib/ecoportal/api/graphql/fragment.rb +37 -27
- data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +25 -0
- data/lib/ecoportal/api/graphql/input/delta_input.rb +16 -0
- data/lib/ecoportal/api/graphql/input/page/archive.rb +14 -0
- data/lib/ecoportal/api/graphql/input/page/build_from_template.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/create_draft.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/create_from_template.rb +18 -0
- data/lib/ecoportal/api/graphql/input/page/delete_draft.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/publish_draft.rb +13 -0
- data/lib/ecoportal/api/graphql/input/page/review_task.rb +14 -0
- data/lib/ecoportal/api/graphql/input/page/unarchive.rb +14 -0
- data/lib/ecoportal/api/graphql/input/page/update.rb +140 -0
- data/lib/ecoportal/api/graphql/input/page.rb +26 -0
- data/lib/ecoportal/api/graphql/input/preset_view/create.rb +18 -0
- data/lib/ecoportal/api/graphql/input/preset_view/permission.rb +16 -0
- data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -0
- data/lib/ecoportal/api/graphql/input/preset_view.rb +14 -0
- data/lib/ecoportal/api/graphql/input/register/create.rb +18 -0
- data/lib/ecoportal/api/graphql/input/register/update.rb +15 -0
- data/lib/ecoportal/api/graphql/input/register.rb +13 -0
- data/lib/ecoportal/api/graphql/input/search_conf/ai_generator.rb +234 -0
- data/lib/ecoportal/api/graphql/input/search_conf.rb +367 -0
- data/lib/ecoportal/api/graphql/input/variable_binding.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_action_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_binding.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_comment_tagging_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_default_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_default_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_force.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_gauge_field_stop.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_field_config.rb +23 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_helper.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_operation.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_action_type.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_filter.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_people_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_task_config.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback.rb +23 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback_action.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_task.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_task_assignment_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/add_workflow_callback.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/collapse_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_binding.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_creator_permissions.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_default_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_gauge_field_stop.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_field_config.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_helper.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_reminder.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_required_sign_offs.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_comment_tagging.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_task_assignment.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_scheduled_callback.rb +22 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_section_header.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +19 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_task_due.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_trigger.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/expand_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +24 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/gauge.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/location_field.rb +24 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/signature.rb +20 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/table.rb +25 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/move_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_action_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_binding.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_callback.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_comment_tagging_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_force.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_gauge_field_stop.rb +17 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_field_config.rb +17 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_helper.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_direct_strategy_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_action_type.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_filter.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_people_field.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_task_config.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_user.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_scheduled_callback.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_select_field_option.rb +17 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_tag.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_strategy.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_assignment_user_group.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_due.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_priority_level.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -0
- data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -0
- data/lib/ecoportal/api/graphql/input.rb +8 -0
- data/lib/ecoportal/api/graphql/interface/base_page.rb +100 -58
- data/lib/ecoportal/api/graphql/interface/location_structure/nodes.rb +27 -28
- data/lib/ecoportal/api/graphql/logic/base_model.rb +2 -0
- data/lib/ecoportal/api/graphql/logic/base_query.rb +45 -2
- data/lib/ecoportal/api/graphql/logic/input.rb +15 -0
- data/lib/ecoportal/api/graphql/model/ai_summary_version.rb +10 -0
- data/lib/ecoportal/api/graphql/model/organization.rb +65 -55
- data/lib/ecoportal/api/graphql/model/page/basic.rb +14 -0
- data/lib/ecoportal/api/graphql/model/page/phased.rb +82 -20
- data/lib/ecoportal/api/graphql/model/page.rb +1 -0
- data/lib/ecoportal/api/graphql/model/page_union.rb +21 -0
- data/lib/ecoportal/api/graphql/model/pages_workflow.rb +20 -0
- data/lib/ecoportal/api/graphql/model/preset_view.rb +10 -0
- data/lib/ecoportal/api/graphql/model/preview_page.rb +10 -0
- data/lib/ecoportal/api/graphql/model/register.rb +10 -0
- data/lib/ecoportal/api/graphql/model.rb +8 -0
- data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -0
- data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/ai_summary.rb +13 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_jobs.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_workflows.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/fail_job.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/fail_workflow.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/start_job.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/start_workflow.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand/stop_workflow.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/kickstand.rb +18 -0
- data/lib/ecoportal/api/graphql/mutation/location_structure/apply_commands.rb +3 -3
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/add_commands.rb +2 -2
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/create.rb +2 -2
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/drop_bad_commands.rb +3 -3
- data/lib/ecoportal/api/graphql/mutation/location_structure/draft/publish.rb +3 -3
- data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/archive.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/build_from_template.rb +50 -0
- data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/create_from_template.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +69 -0
- data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +51 -0
- data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/unarchive.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/update.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/page/update_variable_bindings.rb +44 -0
- data/lib/ecoportal/api/graphql/mutation/page.rb +28 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/create.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/permission.rb +37 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view/update.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/preset_view.rb +15 -0
- data/lib/ecoportal/api/graphql/mutation/register/create.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/register/update.rb +35 -0
- data/lib/ecoportal/api/graphql/mutation/register.rb +14 -0
- data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -0
- data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -0
- data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -0
- data/lib/ecoportal/api/graphql/mutation/template/create.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -0
- data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/template/publish.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/template/unpublish.rb +39 -0
- data/lib/ecoportal/api/graphql/mutation/template/update.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +43 -0
- data/lib/ecoportal/api/graphql/mutation/template.rb +18 -0
- data/lib/ecoportal/api/graphql/mutation.rb +8 -0
- data/lib/ecoportal/api/graphql/payload/ai_summary_generate.rb +12 -0
- data/lib/ecoportal/api/graphql/payload/execute_workflow_commands.rb +36 -0
- data/lib/ecoportal/api/graphql/payload/force_commands.rb +31 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +36 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +36 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/job.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/kickstand/workflow.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/kickstand.rb +15 -0
- data/lib/ecoportal/api/graphql/payload/location_structure/draft/create.rb +33 -34
- data/lib/ecoportal/api/graphql/payload/ok_payload.rb +21 -0
- data/lib/ecoportal/api/graphql/payload/page/archive.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/build_from_template.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/create_from_template.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/draft.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/unarchive.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/update.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page/update_variable_bindings.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/page.rb +19 -0
- data/lib/ecoportal/api/graphql/payload/preset_view.rb +11 -0
- data/lib/ecoportal/api/graphql/payload/register.rb +11 -0
- data/lib/ecoportal/api/graphql/payload/template/create.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/publish.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/unpublish.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/update.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template/update_information.rb +13 -0
- data/lib/ecoportal/api/graphql/payload/template.rb +18 -0
- data/lib/ecoportal/api/graphql/payload.rb +11 -0
- data/lib/ecoportal/api/graphql/query/action.rb +1 -1
- data/lib/ecoportal/api/graphql/query/action_categories.rb +1 -1
- data/lib/ecoportal/api/graphql/query/actions.rb +2 -2
- data/lib/ecoportal/api/graphql/query/contractor_entities.rb +1 -1
- data/lib/ecoportal/api/graphql/query/file_upload_signature.rb +76 -0
- data/lib/ecoportal/api/graphql/query/location_structure/draft.rb +2 -2
- data/lib/ecoportal/api/graphql/query/location_structure.rb +1 -1
- data/lib/ecoportal/api/graphql/query/location_structures.rb +1 -1
- data/lib/ecoportal/api/graphql/query/page.rb +45 -0
- data/lib/ecoportal/api/graphql/query/page_delta.rb +47 -0
- data/lib/ecoportal/api/graphql/query/page_with_forces.rb +43 -0
- data/lib/ecoportal/api/graphql/query/pages.rb +59 -0
- data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +59 -0
- data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -0
- data/lib/ecoportal/api/graphql/query/register_preview_pages.rb +83 -0
- data/lib/ecoportal/api/graphql/query/templates.rb +53 -0
- data/lib/ecoportal/api/graphql/query.rb +11 -0
- data/lib/ecoportal/api/graphql.rb +60 -2
- data/lib/ecoportal/api/graphql_version.rb +5 -5
- data/scripts/auto-worker-scheduler.sh +386 -0
- data/tests/contractor_entity_create.rb +19 -19
- data/tests/contractor_entity_udpate.rb +20 -20
- data/tests/dump_page_model.rb +74 -0
- data/tests/loc_structure_get.rb +1 -2
- data/tests/loc_structure_update.rb +51 -51
- data/tests/loc_structures_get.rb +15 -15
- metadata +436 -5
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# auto-worker-scheduler.sh
|
|
3
|
+
# Scheduler for auto-todo-worker. Called by the Claude Code Stop hook.
|
|
4
|
+
# Reads local config, checks token budget and conflict guard, then
|
|
5
|
+
# launches the worker as a background process if all gates pass.
|
|
6
|
+
#
|
|
7
|
+
# Log format: TIMESTAMP | ACTION | REASON
|
|
8
|
+
# Log file: .ai-assistance/local/auto-worker-changes.log
|
|
9
|
+
#
|
|
10
|
+
# Usage: bash scripts/auto-worker-scheduler.sh
|
|
11
|
+
# (called automatically by Stop hook — no arguments)
|
|
12
|
+
#
|
|
13
|
+
# Requires: jq (for JSON parsing). Falls back to grep/sed on missing jq.
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Paths (all relative — script must be run from repo root)
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
TOKEN_BUDGET_JSON=".ai-assistance/token-budget.json"
|
|
21
|
+
LAST_RUN_JSON=".ai-assistance/local/auto-worker-last-run.json"
|
|
22
|
+
SESSION_LOCK=".ai-assistance/local/session.lock"
|
|
23
|
+
CHANGES_LOG=".ai-assistance/local/auto-worker-changes.log"
|
|
24
|
+
STDOUT_LOG=".ai-assistance/local/auto-worker-stdout.log"
|
|
25
|
+
STDERR_LOG=".ai-assistance/local/auto-worker-stderr.log"
|
|
26
|
+
PROMPT_FILE=".ai-assistance/local/auto-worker-prompt.txt"
|
|
27
|
+
|
|
28
|
+
# --dry-run: run every gate and log what WOULD launch, but spawn nothing.
|
|
29
|
+
# Use this to smoke-test the gates without burning tokens on a real worker.
|
|
30
|
+
DRY_RUN=0
|
|
31
|
+
if [ "${1:-}" = "--dry-run" ]; then
|
|
32
|
+
DRY_RUN=1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Helpers
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
# ai_log_usage <component> <action> <detail> [session_id]
|
|
40
|
+
# Inline copy — scheduler is self-contained; common.sh is not sourced here.
|
|
41
|
+
# Never fails fatally; all errors suppressed.
|
|
42
|
+
ai_log_usage() {
|
|
43
|
+
local component="${1:-unknown}"
|
|
44
|
+
local action="${2:-run}"
|
|
45
|
+
local detail="${3:-}"
|
|
46
|
+
local session_id="${4:-}"
|
|
47
|
+
local log_dir=".ai-assistance/local/kpi"
|
|
48
|
+
local week
|
|
49
|
+
week=$(date +%Y-W%V 2>/dev/null || echo "unknown")
|
|
50
|
+
local log_file="${log_dir}/usage-${week}.jsonl"
|
|
51
|
+
local ts
|
|
52
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "unknown")
|
|
53
|
+
mkdir -p "$log_dir" 2>/dev/null || true
|
|
54
|
+
printf '{"component":"%s","action":"%s","detail":"%s","ts":"%s","session_id":"%s"}\n' \
|
|
55
|
+
"$component" "$action" "$detail" "$ts" "$session_id" >> "$log_file" 2>/dev/null || true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
log_action() {
|
|
59
|
+
local action="$1"
|
|
60
|
+
local reason="$2"
|
|
61
|
+
local ts
|
|
62
|
+
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date +"%Y-%m-%dT%H:%M:%SZ")
|
|
63
|
+
mkdir -p "$(dirname "$CHANGES_LOG")"
|
|
64
|
+
printf "%s | %s | %s\n" "$ts" "$action" "$reason" >> "$CHANGES_LOG"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Read a JSON field using jq if available, otherwise grep+sed fallback.
|
|
68
|
+
# Usage: json_field <file> <dotted.key> e.g. json_field config.json auto_worker.enabled
|
|
69
|
+
json_field() {
|
|
70
|
+
local file="$1"
|
|
71
|
+
local key="$2"
|
|
72
|
+
if command -v jq >/dev/null 2>&1; then
|
|
73
|
+
jq -r ".${key} // empty" "$file" 2>/dev/null
|
|
74
|
+
else
|
|
75
|
+
# Minimal fallback: grep for the last segment key, extract bare value.
|
|
76
|
+
local leaf
|
|
77
|
+
leaf=$(printf '%s' "$key" | sed 's/.*\.//')
|
|
78
|
+
grep -o "\"${leaf}\"[[:space:]]*:[[:space:]]*[^,}]*" "$file" 2>/dev/null \
|
|
79
|
+
| tail -1 \
|
|
80
|
+
| sed 's/.*:[[:space:]]*//' \
|
|
81
|
+
| tr -d '"' \
|
|
82
|
+
| tr -d ' '
|
|
83
|
+
fi
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Make silent failures visible. With `set -e`, any unexpected non-zero exit
|
|
87
|
+
# fires this trap and records an ERROR line before the script dies. The Stop
|
|
88
|
+
# hook swallows stderr (`2>/dev/null || true`), so without this an early
|
|
89
|
+
# failure (missing jq/python, malformed JSON, etc.) would leave no trace at
|
|
90
|
+
# all — which is exactly how this script went dark without anyone noticing.
|
|
91
|
+
trap 'rc=$?; log_action "ERROR" "scheduler aborted at line ${LINENO} (rc=${rc})"' ERR
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Gate 1: auto_worker.enabled
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
if [ ! -f "$TOKEN_BUDGET_JSON" ]; then
|
|
98
|
+
log_action "SKIP" "token-budget.json not found — auto_worker not configured"
|
|
99
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled -- token-budget.json not found"
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
enabled=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.enabled")
|
|
104
|
+
if [ "$enabled" != "true" ]; then
|
|
105
|
+
log_action "SKIP" "auto_worker.enabled is false or absent in token-budget.json"
|
|
106
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled"
|
|
107
|
+
exit 0
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# Gate 1b: Eligible work must exist — at least one [auto] / [auto:last] item
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
# Without this gate, every trigger spins up a full `claude --print` worker even
|
|
114
|
+
# when TODO.md has zero tagged items — the worker then no-ops, burning a launch
|
|
115
|
+
# (and tokens) for nothing. This was the pattern behind the 42 historical
|
|
116
|
+
# launches that completed no work, and it is also why a repo with no TODO.md at
|
|
117
|
+
# all (e.g. a fresh sibling) must SKIP rather than launch into an error.
|
|
118
|
+
# The tag set is the eligibility boundary defined in
|
|
119
|
+
# standards/tooling/auto-worker-eligibility.md.
|
|
120
|
+
# NOTE: \[auto\] does NOT match [auto:last] — the regex MUST be \[auto(:last)?\].
|
|
121
|
+
TODO_FILE="TODO.md"
|
|
122
|
+
if [ ! -f "$TODO_FILE" ]; then
|
|
123
|
+
log_action "SKIP" "No TODO.md in repo root — no eligible work to schedule"
|
|
124
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "no TODO.md present"
|
|
125
|
+
exit 0
|
|
126
|
+
fi
|
|
127
|
+
if ! grep -qE '\[auto(:last)?\]' "$TODO_FILE"; then
|
|
128
|
+
log_action "SKIP" "No eligible work — no [auto] or [auto:last] items in TODO.md"
|
|
129
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "no [auto]/[auto:last] items in TODO.md"
|
|
130
|
+
exit 0
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
# Gate 2: Conflict guard — check for active Claude session via session.lock
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
if [ -f "$SESSION_LOCK" ]; then
|
|
138
|
+
lock_content=""
|
|
139
|
+
if [ -r "$SESSION_LOCK" ]; then
|
|
140
|
+
lock_content=$(cat "$SESSION_LOCK" 2>/dev/null || true)
|
|
141
|
+
fi
|
|
142
|
+
log_action "SKIP" "Active Claude session detected (session.lock present: ${lock_content})"
|
|
143
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "session.lock present"
|
|
144
|
+
exit 0
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# Gate 3: Token budget threshold check
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
# Find the current ISO week KPI file. Format: weekly-YYYY-WNN.json
|
|
151
|
+
# We compute ISO week ourselves for portability.
|
|
152
|
+
|
|
153
|
+
iso_week() {
|
|
154
|
+
# Returns YYYY-WNN string for today (ISO 8601 week numbering)
|
|
155
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
156
|
+
python3 -c "
|
|
157
|
+
import datetime
|
|
158
|
+
today = datetime.date.today()
|
|
159
|
+
iso = today.isocalendar()
|
|
160
|
+
print('%04d-W%02d' % (iso[0], iso[1]))
|
|
161
|
+
"
|
|
162
|
+
elif command -v python >/dev/null 2>&1; then
|
|
163
|
+
python -c "
|
|
164
|
+
import datetime
|
|
165
|
+
today = datetime.date.today()
|
|
166
|
+
iso = today.isocalendar()
|
|
167
|
+
print('%04d-W%02d' % (iso[0], iso[1]))
|
|
168
|
+
"
|
|
169
|
+
else
|
|
170
|
+
# Fallback: use date command Week-of-year (not strictly ISO but close enough)
|
|
171
|
+
date +"%Y-W%V" 2>/dev/null || date +"%Y-W%U"
|
|
172
|
+
fi
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
KPI_DIR=".ai-assistance/local/kpi"
|
|
176
|
+
WEEK_KEY=$(iso_week)
|
|
177
|
+
KPI_FILE="${KPI_DIR}/weekly-${WEEK_KEY}.json"
|
|
178
|
+
|
|
179
|
+
# Read quota and usage from KPI file (if present)
|
|
180
|
+
weekly_tokens_used=0
|
|
181
|
+
weekly_quota=0
|
|
182
|
+
window_start_ts="" # ISO timestamp of first session start in current 5-hour window
|
|
183
|
+
|
|
184
|
+
if [ -f "$KPI_FILE" ] && command -v jq >/dev/null 2>&1; then
|
|
185
|
+
# Sum tokens_used across all sessions
|
|
186
|
+
weekly_tokens_used=$(jq '[.sessions[]?.tokens_used // 0] | add // 0' "$KPI_FILE" 2>/dev/null || echo "0")
|
|
187
|
+
weekly_quota=$(jq -r '.quota // 0' "$KPI_FILE" 2>/dev/null || echo "0")
|
|
188
|
+
|
|
189
|
+
# Find most recent session start to compute reset window
|
|
190
|
+
window_start_ts=$(jq -r '
|
|
191
|
+
[.sessions[]?.session_start // empty] | sort | last // empty
|
|
192
|
+
' "$KPI_FILE" 2>/dev/null || true)
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# Read quota from token-budget.json as fallback
|
|
196
|
+
if [ "$weekly_quota" = "0" ] || [ -z "$weekly_quota" ]; then
|
|
197
|
+
quota_from_config=$(json_field "$TOKEN_BUDGET_JSON" "weekly_quota.total_tokens")
|
|
198
|
+
if [ "$quota_from_config" != "null" ] && [ -n "$quota_from_config" ]; then
|
|
199
|
+
weekly_quota="$quota_from_config"
|
|
200
|
+
fi
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# If no quota is set (null), skip the threshold gate entirely
|
|
204
|
+
if [ "$weekly_quota" = "0" ] || [ "$weekly_quota" = "null" ] || [ -z "$weekly_quota" ]; then
|
|
205
|
+
log_action "OK" "No weekly quota set — skipping token threshold gate"
|
|
206
|
+
else
|
|
207
|
+
# Calculate usage percentage
|
|
208
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
209
|
+
usage_pct=$(python3 -c "
|
|
210
|
+
used = float('${weekly_tokens_used}' or '0')
|
|
211
|
+
quota = float('${weekly_quota}')
|
|
212
|
+
print('%.1f' % (used / quota * 100) if quota > 0 else '0')
|
|
213
|
+
")
|
|
214
|
+
else
|
|
215
|
+
usage_pct=0
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# Determine time remaining in current 5-hour reset window
|
|
219
|
+
# 5-hour window = 18000 seconds
|
|
220
|
+
window_seconds_elapsed=0
|
|
221
|
+
if [ -n "$window_start_ts" ] && command -v python3 >/dev/null 2>&1; then
|
|
222
|
+
window_seconds_elapsed=$(python3 -c "
|
|
223
|
+
import datetime, sys
|
|
224
|
+
try:
|
|
225
|
+
ts = '${window_start_ts}'.replace('Z', '+00:00')
|
|
226
|
+
start = datetime.datetime.fromisoformat(ts)
|
|
227
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
|
228
|
+
elapsed = (now - start).total_seconds()
|
|
229
|
+
print(int(max(0, elapsed)))
|
|
230
|
+
except Exception:
|
|
231
|
+
print(0)
|
|
232
|
+
")
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
WINDOW_DURATION=18000 # 5 hours in seconds
|
|
236
|
+
window_remaining=$(( WINDOW_DURATION - window_seconds_elapsed ))
|
|
237
|
+
if [ "$window_remaining" -lt 0 ]; then
|
|
238
|
+
window_remaining=0
|
|
239
|
+
fi
|
|
240
|
+
window_remaining_min=$(( window_remaining / 60 ))
|
|
241
|
+
|
|
242
|
+
# Determine threshold based on time remaining in window (ADR-007 table)
|
|
243
|
+
# >120 min -> 65%; 60-120 -> 75%; 45-60 -> 80%; 30-45 -> 90%; 15-30 -> 95%; <15 -> skip
|
|
244
|
+
if [ "$window_remaining_min" -lt 15 ]; then
|
|
245
|
+
log_action "SKIP" "Less than 15 min remaining in token reset window — will not launch"
|
|
246
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "token budget -- less than 15 min remaining in reset window"
|
|
247
|
+
exit 0
|
|
248
|
+
elif [ "$window_remaining_min" -lt 30 ]; then
|
|
249
|
+
threshold=95
|
|
250
|
+
elif [ "$window_remaining_min" -lt 45 ]; then
|
|
251
|
+
threshold=90
|
|
252
|
+
elif [ "$window_remaining_min" -lt 60 ]; then
|
|
253
|
+
threshold=80
|
|
254
|
+
elif [ "$window_remaining_min" -lt 120 ]; then
|
|
255
|
+
threshold=75
|
|
256
|
+
else
|
|
257
|
+
threshold=65
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
# Compare usage_pct against threshold (integer comparison via python3)
|
|
261
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
262
|
+
over_threshold=$(python3 -c "
|
|
263
|
+
pct = float('${usage_pct}' or '0')
|
|
264
|
+
thr = float('${threshold}')
|
|
265
|
+
print('true' if pct >= thr else 'false')
|
|
266
|
+
")
|
|
267
|
+
else
|
|
268
|
+
over_threshold="false"
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
if [ "$over_threshold" = "true" ]; then
|
|
272
|
+
log_action "SKIP" "Token usage ${usage_pct}% >= threshold ${threshold}% (${window_remaining_min} min remaining in window)"
|
|
273
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "token budget at ${usage_pct}% (threshold ${threshold}%)"
|
|
274
|
+
exit 0
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
log_action "OK" "Token gate passed: ${usage_pct}% used, threshold ${threshold}% (${window_remaining_min} min remaining)"
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
# Gate 4: Determine cadence (informational — actual cron cadence managed by
|
|
282
|
+
# CronCreate; this script just logs the recommendation)
|
|
283
|
+
# ---------------------------------------------------------------------------
|
|
284
|
+
|
|
285
|
+
recommended_cadence="2hr"
|
|
286
|
+
if [ -f "$LAST_RUN_JSON" ] && command -v jq >/dev/null 2>&1; then
|
|
287
|
+
completed_count=$(jq '.completed | length' "$LAST_RUN_JSON" 2>/dev/null || echo "0")
|
|
288
|
+
if [ "$completed_count" -gt "0" ]; then
|
|
289
|
+
recommended_cadence="30min"
|
|
290
|
+
fi
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# ---------------------------------------------------------------------------
|
|
294
|
+
# Gate 5: dangerously_skip_permissions is REQUIRED
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
# A headless `claude --print` worker has no way to answer interactive
|
|
297
|
+
# permission prompts — without skip-permissions it stalls indefinitely and
|
|
298
|
+
# does no work. Require explicit opt-in rather than launching a worker that
|
|
299
|
+
# can only hang.
|
|
300
|
+
|
|
301
|
+
skip_perms=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.dangerously_skip_permissions")
|
|
302
|
+
if [ "$skip_perms" != "true" ]; then
|
|
303
|
+
log_action "SKIP" "auto_worker.dangerously_skip_permissions is not true — a headless (--print) worker cannot answer permission prompts and would stall. Set it to true in token-budget.json to enable."
|
|
304
|
+
ai_log_usage "script/auto-worker-scheduler" "skip" "dangerously_skip_permissions not enabled -- headless worker requires it"
|
|
305
|
+
exit 0
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
AUTO_WORKER_PROMPT="Run the auto-worker for this repo.
|
|
309
|
+
|
|
310
|
+
Instructions:
|
|
311
|
+
1. Read TODO.md and collect all items tagged \`[auto]\` or \`[auto:last]\`.
|
|
312
|
+
2. If no \`[auto]\` items exist, exit cleanly with a brief log entry.
|
|
313
|
+
3. Plan execution order as a dependency graph — do NOT follow TODO list order.
|
|
314
|
+
- Items without dependencies on each other may run in parallel (branch out when the work is self-contained enough to isolate).
|
|
315
|
+
- \`[auto:last]\` items run only after all \`[auto]\` items complete or are skipped.
|
|
316
|
+
4. For each item:
|
|
317
|
+
- Execute the task.
|
|
318
|
+
- Mark it \`[x]\` in TODO.md on completion.
|
|
319
|
+
- Add any discovered follow-up tasks as new TODO items; tag \`[auto]\` only if they meet the eligibility criteria in standards/tooling/auto-worker-eligibility.md.
|
|
320
|
+
5. Do NOT touch items that are not tagged \`[auto]\` or \`[auto:last]\`.
|
|
321
|
+
6. At the end, write a one-paragraph summary of what was done, skipped, and added to TODO."
|
|
322
|
+
|
|
323
|
+
# Write the prompt to a file and feed it to claude via stdin. This avoids the
|
|
324
|
+
# quoting/newline hazards of passing a multi-line prompt as a CLI argument —
|
|
325
|
+
# the previous Windows path crammed the whole prompt (newlines, quotes,
|
|
326
|
+
# backticks) into a single PowerShell -ArgumentList string, which produced a
|
|
327
|
+
# broken command that launched nothing while still logging "LAUNCHED".
|
|
328
|
+
mkdir -p "$(dirname "$PROMPT_FILE")"
|
|
329
|
+
printf '%s\n' "$AUTO_WORKER_PROMPT" > "$PROMPT_FILE"
|
|
330
|
+
|
|
331
|
+
# ---------------------------------------------------------------------------
|
|
332
|
+
# Dry run: log what would happen, spawn nothing.
|
|
333
|
+
# ---------------------------------------------------------------------------
|
|
334
|
+
if [ "$DRY_RUN" = "1" ]; then
|
|
335
|
+
log_action "DRY-RUN" "All gates passed. Would launch: claude --dangerously-skip-permissions --print < ${PROMPT_FILE} > ${STDOUT_LOG} (cadence recommendation: ${recommended_cadence})"
|
|
336
|
+
ai_log_usage "script/auto-worker-scheduler" "dry-run" "all gates passed -- would launch auto-todo-worker"
|
|
337
|
+
exit 0
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
# ---------------------------------------------------------------------------
|
|
341
|
+
# Launch: run auto-todo-worker as a detached background process.
|
|
342
|
+
# Output is ALWAYS redirected to STDOUT_LOG/STDERR_LOG so a failed or empty
|
|
343
|
+
# run leaves a trace (the previous Windows path captured nothing).
|
|
344
|
+
# ---------------------------------------------------------------------------
|
|
345
|
+
|
|
346
|
+
log_action "LAUNCH" "Launching auto-todo-worker (cadence recommendation: ${recommended_cadence})"
|
|
347
|
+
ai_log_usage "script/auto-worker-scheduler" "launch" "launching auto-todo-worker"
|
|
348
|
+
|
|
349
|
+
launch_pid=""
|
|
350
|
+
if [[ "${OSTYPE:-}" == "msys" ]] || [[ "${OSTYPE:-}" == "cygwin" ]] || \
|
|
351
|
+
[[ "${OS:-}" == "Windows_NT" ]]; then
|
|
352
|
+
# Windows via Git Bash. Start-Process detaches the worker from this hook's
|
|
353
|
+
# shell so it outlives the Stop hook. -RedirectStandardInput feeds the
|
|
354
|
+
# prompt file (no fragile arg quoting); -RedirectStandardOutput/Error
|
|
355
|
+
# capture the run; -PassThru returns the REAL worker PID (the old code
|
|
356
|
+
# logged the wrapper job's PID via `$!`, which was meaningless).
|
|
357
|
+
# Note: stdout and stderr MUST be different files — Start-Process rejects
|
|
358
|
+
# redirecting both to the same path.
|
|
359
|
+
abs_prompt="$(cygpath -w "$PROMPT_FILE" 2>/dev/null || printf '%s' "${PWD}/${PROMPT_FILE}")"
|
|
360
|
+
abs_stdout="$(cygpath -w "$STDOUT_LOG" 2>/dev/null || printf '%s' "${PWD}/${STDOUT_LOG}")"
|
|
361
|
+
abs_stderr="$(cygpath -w "$STDERR_LOG" 2>/dev/null || printf '%s' "${PWD}/${STDERR_LOG}")"
|
|
362
|
+
abs_cwd="$(cygpath -w "$PWD" 2>/dev/null || printf '%s' "${PWD}")"
|
|
363
|
+
launch_pid=$(powershell -NoProfile -Command "
|
|
364
|
+
\$p = Start-Process -FilePath 'claude' \
|
|
365
|
+
-ArgumentList '--dangerously-skip-permissions','--print' \
|
|
366
|
+
-RedirectStandardInput '${abs_prompt}' \
|
|
367
|
+
-RedirectStandardOutput '${abs_stdout}' \
|
|
368
|
+
-RedirectStandardError '${abs_stderr}' \
|
|
369
|
+
-WorkingDirectory '${abs_cwd}' \
|
|
370
|
+
-NoNewWindow -PassThru
|
|
371
|
+
\$p.Id" 2>/dev/null | tr -d '[:space:]') || true
|
|
372
|
+
else
|
|
373
|
+
# Unix / macOS / WSL2: feed prompt via stdin, capture output, detach.
|
|
374
|
+
nohup claude --dangerously-skip-permissions --print \
|
|
375
|
+
< "$PROMPT_FILE" > "$STDOUT_LOG" 2>&1 &
|
|
376
|
+
launch_pid=$!
|
|
377
|
+
fi
|
|
378
|
+
|
|
379
|
+
if [ -n "${launch_pid:-}" ]; then
|
|
380
|
+
log_action "LAUNCHED" "Background worker PID ${launch_pid} — output -> ${STDOUT_LOG}"
|
|
381
|
+
ai_log_usage "script/auto-worker-scheduler" "launched" "worker PID ${launch_pid}"
|
|
382
|
+
else
|
|
383
|
+
log_action "ERROR" "Launch returned no PID — worker likely failed to start. Check ${STDERR_LOG}."
|
|
384
|
+
ai_log_usage "script/auto-worker-scheduler" "error" "launch returned no PID"
|
|
385
|
+
fi
|
|
386
|
+
exit 0
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
require_relative 'local_libs'
|
|
2
|
-
|
|
3
|
-
api = Ecoportal::API::GraphQL.new
|
|
4
|
-
response = api.createContractorEntity(input: {
|
|
5
|
-
name:
|
|
6
|
-
active:
|
|
7
|
-
approved:
|
|
8
|
-
associatedPeopleIds: [
|
|
9
|
-
"5adfbef76fb80400047535b3"
|
|
10
|
-
],
|
|
11
|
-
leadContractorIds:
|
|
12
|
-
"5adfbef76fb80400047535b3"
|
|
13
|
-
],
|
|
14
|
-
clientMutationId:
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
if response.success? && entity = response.item
|
|
18
|
-
puts "Created contractor entity '#{entity.name}' (approved? #{entity.approved})"
|
|
19
|
-
end
|
|
1
|
+
require_relative 'local_libs'
|
|
2
|
+
|
|
3
|
+
api = Ecoportal::API::GraphQL.new
|
|
4
|
+
response = api.createContractorEntity(input: {
|
|
5
|
+
name: "Caras Galathon",
|
|
6
|
+
active: true,
|
|
7
|
+
approved: true,
|
|
8
|
+
associatedPeopleIds: [
|
|
9
|
+
"5adfbef76fb80400047535b3"
|
|
10
|
+
],
|
|
11
|
+
leadContractorIds: [
|
|
12
|
+
"5adfbef76fb80400047535b3"
|
|
13
|
+
],
|
|
14
|
+
clientMutationId: ""
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
if response.success? && (entity = response.item)
|
|
18
|
+
puts "Created contractor entity '#{entity.name}' (approved? #{entity.approved})"
|
|
19
|
+
end
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
require_relative 'local_libs'
|
|
2
|
-
|
|
3
|
-
api = Ecoportal::API::GraphQL.new
|
|
4
|
-
response = api.contractorEntity.update(input: {
|
|
5
|
-
id:
|
|
6
|
-
name:
|
|
7
|
-
active:
|
|
8
|
-
approved:
|
|
9
|
-
# associatedPeopleIds: [
|
|
10
|
-
# "5adfbef76fb80400047535b3"
|
|
11
|
-
# ],
|
|
12
|
-
leadContractorIds: {
|
|
13
|
-
additions: ["5adfbef76fb80400047535b3"]
|
|
14
|
-
},
|
|
15
|
-
clientMutationId:
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
if response.success? && entity = response.item
|
|
19
|
-
puts "Updated contractor entity '#{entity.name}' (approved? #{entity.approved})"
|
|
20
|
-
end
|
|
1
|
+
require_relative 'local_libs'
|
|
2
|
+
|
|
3
|
+
api = Ecoportal::API::GraphQL.new
|
|
4
|
+
response = api.contractorEntity.update(input: {
|
|
5
|
+
id: "62b04d83fa9170001d3d303f",
|
|
6
|
+
name: "Caras Galathon",
|
|
7
|
+
active: true,
|
|
8
|
+
approved: true,
|
|
9
|
+
# associatedPeopleIds: [
|
|
10
|
+
# "5adfbef76fb80400047535b3"
|
|
11
|
+
# ],
|
|
12
|
+
leadContractorIds: {
|
|
13
|
+
additions: ["5adfbef76fb80400047535b3"]
|
|
14
|
+
},
|
|
15
|
+
clientMutationId: ""
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
if response.success? && (entity = response.item)
|
|
19
|
+
puts "Updated contractor entity '#{entity.name}' (approved? #{entity.approved})"
|
|
20
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require_relative 'local_libs'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
# Dump a page's GraphQL model (the raw CommonPageUnion doc) to a JSON file, so a real page
|
|
6
|
+
# can be captured as a spec fixture / stub. Run it BEFORE and AFTER a submit/sign_off (or any
|
|
7
|
+
# mutation) to capture both states and diff them — the dumps double as regression fixtures.
|
|
8
|
+
#
|
|
9
|
+
# Usage (run in an env where the GraphQL connection is configured, like the other tests/):
|
|
10
|
+
# ruby tests/dump_page_model.rb <PAGE_ID> [--out PATH] [--stage NAME_OR_ID] [--label before|after]
|
|
11
|
+
#
|
|
12
|
+
# Examples:
|
|
13
|
+
# # capture the TOOCS page before submitting
|
|
14
|
+
# ruby tests/dump_page_model.rb 6a3225532b97b00073c23aa8 --label before
|
|
15
|
+
# # ...run the case live (or a manual updatePage)...
|
|
16
|
+
# # capture it after
|
|
17
|
+
# ruby tests/dump_page_model.rb 6a3225532b97b00073c23aa8 --label after
|
|
18
|
+
# # then diff the two JSON files to see exactly what the submit changed.
|
|
19
|
+
#
|
|
20
|
+
# Default output path: spec/fixtures/pages/<page_id>[_<label>].json
|
|
21
|
+
#
|
|
22
|
+
# A --stage NAME_OR_ID also prints the StageView's stage id (the value that becomes stageId on
|
|
23
|
+
# a submit) so you can confirm the right stage was resolved.
|
|
24
|
+
|
|
25
|
+
def parse_args(argv)
|
|
26
|
+
opts = { page_id: nil, out: nil, stage: nil, label: nil }
|
|
27
|
+
rest = argv.dup
|
|
28
|
+
opts[:page_id] = rest.shift
|
|
29
|
+
until rest.empty?
|
|
30
|
+
flag = rest.shift
|
|
31
|
+
case flag
|
|
32
|
+
when '--out' then opts[:out] = rest.shift
|
|
33
|
+
when '--stage' then opts[:stage] = rest.shift
|
|
34
|
+
when '--label' then opts[:label] = rest.shift
|
|
35
|
+
else
|
|
36
|
+
warn "Unknown argument: #{flag}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
opts
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
opts = parse_args(ARGV)
|
|
43
|
+
|
|
44
|
+
unless opts[:page_id]
|
|
45
|
+
warn 'Usage: ruby tests/dump_page_model.rb <PAGE_ID> [--out PATH] [--stage NAME_OR_ID] [--label before|after]'
|
|
46
|
+
exit 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
default_dir = File.expand_path('../spec/fixtures/pages', __dir__)
|
|
50
|
+
default_name = "#{[opts[:page_id], opts[:label]].compact.join('_')}.json"
|
|
51
|
+
out_path = opts[:out] || File.join(default_dir, default_name)
|
|
52
|
+
|
|
53
|
+
api = Ecoportal::API::GraphQL.new
|
|
54
|
+
page = api.pages.get(opts[:page_id], stage_id: opts[:stage])
|
|
55
|
+
|
|
56
|
+
if page.nil?
|
|
57
|
+
warn "Could not fetch page '#{opts[:page_id]}'"
|
|
58
|
+
exit 2
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# StageView delegates #doc to the underlying full page; dump the full page doc either way.
|
|
62
|
+
doc = page.respond_to?(:doc) ? page.doc : page.instance_variable_get(:@page)&.doc
|
|
63
|
+
|
|
64
|
+
FileUtils.mkdir_p(File.dirname(out_path))
|
|
65
|
+
File.write(out_path, JSON.pretty_generate(doc))
|
|
66
|
+
|
|
67
|
+
puts "Dumped page '#{opts[:page_id]}' → #{out_path}"
|
|
68
|
+
puts " name: #{page.respond_to?(:name) ? page.name : '(n/a)'}"
|
|
69
|
+
puts " patchVer: #{page.respond_to?(:patchVer) ? page.patchVer : '(n/a)'}"
|
|
70
|
+
if opts[:stage]
|
|
71
|
+
resolved = page.respond_to?(:current_stage_id) ? page.current_stage_id : nil
|
|
72
|
+
puts " stage '#{opts[:stage]}' → stageId that a submit would target: #{resolved.inspect}"
|
|
73
|
+
end
|
|
74
|
+
puts 'Tip: capture --label before and --label after a submit, then diff the two JSON files.'
|
data/tests/loc_structure_get.rb
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
require_relative 'local_libs'
|
|
2
2
|
|
|
3
|
-
STRUCT_ID = "592642a4ae0c6a00236b110d" # mini test
|
|
3
|
+
STRUCT_ID = "592642a4ae0c6a00236b110d".freeze # mini test
|
|
4
4
|
# STRUCT_ID = "626745bcba616a001f7e5011" # wcc
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
api = Ecoportal::API::GraphQL.new
|
|
8
7
|
api.currentOrganization.locationStructure(id: STRUCT_ID).tap do |tree|
|
|
9
8
|
puts "'#{tree.name}' Structure (#{tree.id})"
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
require_relative 'local_libs'
|
|
2
|
-
|
|
3
|
-
STRUCT_ID = "592642a4ae0c6a00236b110d" # mini test
|
|
4
|
-
# STRUCT_ID = "626745bcba616a001f7e5011" # wcc
|
|
5
|
-
|
|
6
|
-
api = Ecoportal::API::GraphQL.new
|
|
7
|
-
|
|
8
|
-
# DESIRED DESIGN: (to review: it might be that draft tree model already stores the commands?)
|
|
9
|
-
# tree = api.currentOrganization.LocationStructure(id: STRUCT_ID)
|
|
10
|
-
# node = tree.node["SYDNEY"]
|
|
11
|
-
# node.name = "Sydney City"
|
|
12
|
-
# tree.node["UPS"].move("AUCKLAND") # or tree.move("UPS", to: "AUCKLAND")
|
|
13
|
-
# tree.node["FFFF."].delete
|
|
14
|
-
# tree.node["UPS"].insert("CHILD", name: "Child").insert("GRAND CHILD", name: "Grand Child")
|
|
15
|
-
# tree.change_commands # => return Ecoportal::API::GraphQL::Input::ApplyCommands
|
|
16
|
-
|
|
17
|
-
INPUT = {
|
|
18
|
-
change: {
|
|
19
|
-
clientMutationId: "",
|
|
20
|
-
id:
|
|
21
|
-
commands:
|
|
22
|
-
{ update: { nodeId: "SYDNEY", name: "Sydney City" } },
|
|
23
|
-
{ move: { nodeId: "UPS", parentId: "AUCKLAND" } },
|
|
24
|
-
{ archive: { nodeId: "FFFF." } },
|
|
25
|
-
{ insert: { parentId: "UPS", nodeId: "CHILD", name: "Child" } },
|
|
26
|
-
{ insert: { parentId: "CHILD", nodeId: "GRAND CHILD", name: "Grand Child" } }
|
|
27
|
-
]
|
|
28
|
-
},
|
|
29
|
-
revert: {
|
|
30
|
-
clientMutationId: "",
|
|
31
|
-
id:
|
|
32
|
-
commands:
|
|
33
|
-
{ update: { nodeId: "SYDNEY", name: "Sydney" } },
|
|
34
|
-
{ move: { nodeId: "UPS", parentId: "SUB'TAGs" } },
|
|
35
|
-
{ unarchive: { nodeId: "FFFF." } },
|
|
36
|
-
{ archive: { nodeId: "CHILD" } }
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
operation = :change
|
|
42
|
-
response = api.locationStructure.applyCommands(input: INPUT[operation])
|
|
43
|
-
|
|
44
|
-
if response.success? && structure = response.structure
|
|
45
|
-
puts "Applied '#{operation}' changes to '#{structure.name}' structure:"
|
|
46
|
-
pp structure.treeify
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
puts "Some more feedback:"
|
|
50
|
-
#pp response.results.as_json
|
|
51
|
-
pp response.results.map(&:as_json)
|
|
1
|
+
require_relative 'local_libs'
|
|
2
|
+
|
|
3
|
+
STRUCT_ID = "592642a4ae0c6a00236b110d".freeze # mini test
|
|
4
|
+
# STRUCT_ID = "626745bcba616a001f7e5011" # wcc
|
|
5
|
+
|
|
6
|
+
api = Ecoportal::API::GraphQL.new
|
|
7
|
+
|
|
8
|
+
# DESIRED DESIGN: (to review: it might be that draft tree model already stores the commands?)
|
|
9
|
+
# tree = api.currentOrganization.LocationStructure(id: STRUCT_ID)
|
|
10
|
+
# node = tree.node["SYDNEY"]
|
|
11
|
+
# node.name = "Sydney City"
|
|
12
|
+
# tree.node["UPS"].move("AUCKLAND") # or tree.move("UPS", to: "AUCKLAND")
|
|
13
|
+
# tree.node["FFFF."].delete
|
|
14
|
+
# tree.node["UPS"].insert("CHILD", name: "Child").insert("GRAND CHILD", name: "Grand Child")
|
|
15
|
+
# tree.change_commands # => return Ecoportal::API::GraphQL::Input::ApplyCommands
|
|
16
|
+
|
|
17
|
+
INPUT = {
|
|
18
|
+
change: {
|
|
19
|
+
clientMutationId: "",
|
|
20
|
+
id: STRUCT_ID,
|
|
21
|
+
commands: [
|
|
22
|
+
{ update: { nodeId: "SYDNEY", name: "Sydney City" } },
|
|
23
|
+
{ move: { nodeId: "UPS", parentId: "AUCKLAND" } },
|
|
24
|
+
{ archive: { nodeId: "FFFF." } },
|
|
25
|
+
{ insert: { parentId: "UPS", nodeId: "CHILD", name: "Child" } },
|
|
26
|
+
{ insert: { parentId: "CHILD", nodeId: "GRAND CHILD", name: "Grand Child" } }
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
revert: {
|
|
30
|
+
clientMutationId: "",
|
|
31
|
+
id: STRUCT_ID,
|
|
32
|
+
commands: [
|
|
33
|
+
{ update: { nodeId: "SYDNEY", name: "Sydney" } },
|
|
34
|
+
{ move: { nodeId: "UPS", parentId: "SUB'TAGs" } },
|
|
35
|
+
{ unarchive: { nodeId: "FFFF." } },
|
|
36
|
+
{ archive: { nodeId: "CHILD" } }
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
operation = :change
|
|
42
|
+
response = api.locationStructure.applyCommands(input: INPUT[operation])
|
|
43
|
+
|
|
44
|
+
if response.success? && (structure = response.structure)
|
|
45
|
+
puts "Applied '#{operation}' changes to '#{structure.name}' structure:"
|
|
46
|
+
pp structure.treeify
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
puts "Some more feedback:"
|
|
50
|
+
#pp response.results.as_json
|
|
51
|
+
pp response.results.map(&:as_json)
|