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,240 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Gemini Assist — packs files + prompt and queries the Gemini API.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ruby .ai-assistance/skills/gemini-assist/gemini_ask.rb \
|
|
7
|
+
# --prompt "Your question here" \
|
|
8
|
+
# --files lib/foo.rb lib/bar.rb
|
|
9
|
+
#
|
|
10
|
+
# ruby .ai-assistance/skills/gemini-assist/gemini_ask.rb \
|
|
11
|
+
# --prompt-file .ai-assistance/skills/gemini-assist/prompts/review.txt \
|
|
12
|
+
# --files "lib/**/*.rb"
|
|
13
|
+
#
|
|
14
|
+
# Requires: GEMINI_API_KEY environment variable (or set in .env)
|
|
15
|
+
# Optional: GEMINI_MODEL environment variable (default: gemini-1.5-pro)
|
|
16
|
+
|
|
17
|
+
require "json"
|
|
18
|
+
require "net/http"
|
|
19
|
+
require "openssl"
|
|
20
|
+
require "optparse"
|
|
21
|
+
require "rbconfig"
|
|
22
|
+
require "uri"
|
|
23
|
+
|
|
24
|
+
# Returns an SSL certificate store that works cross-platform.
|
|
25
|
+
# Priority: SSL_CERT_FILE env var → RubyInstaller bundled cacert.pem →
|
|
26
|
+
# OpenSSL compiled-in default → system paths.
|
|
27
|
+
# VERIFY_PEER is always on — verification is never disabled.
|
|
28
|
+
def ssl_cert_store
|
|
29
|
+
store = OpenSSL::X509::Store.new
|
|
30
|
+
|
|
31
|
+
candidates = [
|
|
32
|
+
ENV["FALLBACK_SSL_CERT_FILE"],
|
|
33
|
+
ENV["SSL_CERT_FILE"],
|
|
34
|
+
File.join(RbConfig::CONFIG["prefix"], "ssl", "cacert.pem"),
|
|
35
|
+
OpenSSL::X509::DEFAULT_CERT_FILE
|
|
36
|
+
].compact
|
|
37
|
+
|
|
38
|
+
cert_file = candidates.find { |f| File.file?(f) }
|
|
39
|
+
|
|
40
|
+
if cert_file
|
|
41
|
+
store.add_file(cert_file)
|
|
42
|
+
else
|
|
43
|
+
store.set_default_paths
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
store
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
DEFAULT_MODEL = "gemini-2.5-pro"
|
|
50
|
+
API_BASE = "https://generativelanguage.googleapis.com/v1beta/models"
|
|
51
|
+
|
|
52
|
+
SYSTEM_PREAMBLE = <<~PREAMBLE.strip
|
|
53
|
+
You are an expert Ruby developer reviewing code for the ecoportal-api-graphql gem.
|
|
54
|
+
This gem provides a GraphQL client layer. It uses graphql-ruby, graphlient, and graphql-client upstream,
|
|
55
|
+
and is consumed by eco-helpers downstream. Backwards compatibility matters.
|
|
56
|
+
|
|
57
|
+
When you identify issues, be specific: include file name, approximate line reference, and a concrete suggestion.
|
|
58
|
+
Flag hallucination risk: if you're unsure about something, say so rather than guessing.
|
|
59
|
+
PREAMBLE
|
|
60
|
+
|
|
61
|
+
# Loads key=value pairs from a .env file into ENV (skips already-set keys).
|
|
62
|
+
def load_dotenv(path = ".env")
|
|
63
|
+
return unless File.file?(path)
|
|
64
|
+
|
|
65
|
+
File.foreach(path) do |raw|
|
|
66
|
+
line = raw.strip
|
|
67
|
+
next if line.empty? || line.start_with?("#") || !line.include?("=")
|
|
68
|
+
|
|
69
|
+
key, value = line.split("=", 2)
|
|
70
|
+
key = key.strip
|
|
71
|
+
value = value.to_s.strip
|
|
72
|
+
.delete_prefix('"').delete_suffix('"')
|
|
73
|
+
.delete_prefix("'").delete_suffix("'")
|
|
74
|
+
ENV[key] ||= value if key
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Expands glob patterns and returns formatted file blocks.
|
|
79
|
+
def load_files(patterns)
|
|
80
|
+
patterns.flat_map do |pattern|
|
|
81
|
+
paths = Dir.glob(pattern, File::FNM_DOTMATCH).select { |p| File.file?(p) }.sort
|
|
82
|
+
paths = [pattern] if paths.empty?
|
|
83
|
+
|
|
84
|
+
paths.filter_map do |path|
|
|
85
|
+
unless File.file?(path)
|
|
86
|
+
warn "[warn] skipping non-file: #{path}"
|
|
87
|
+
next
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
text = File.read(path, encoding: "utf-8:utf-8", invalid: :replace, undef: :replace)
|
|
91
|
+
"### FILE: #{path}\n```ruby\n#{text}\n```"
|
|
92
|
+
rescue => e
|
|
93
|
+
warn "[warn] could not read #{path}: #{e.message}"
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def build_prompt(user_prompt, file_contents)
|
|
100
|
+
parts = [SYSTEM_PREAMBLE, ""]
|
|
101
|
+
|
|
102
|
+
if file_contents.any?
|
|
103
|
+
parts << "## Files provided for context\n"
|
|
104
|
+
parts.concat(file_contents)
|
|
105
|
+
parts << ""
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
parts << "## Task\n"
|
|
109
|
+
parts << user_prompt.strip
|
|
110
|
+
parts.join("\n")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def list_models(api_key)
|
|
115
|
+
url = URI("#{API_BASE.sub('/models', '')}/models?key=#{api_key}&pageSize=50")
|
|
116
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
117
|
+
http.use_ssl = true
|
|
118
|
+
http.cert_store = ssl_cert_store
|
|
119
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
120
|
+
|
|
121
|
+
response = http.get(url)
|
|
122
|
+
abort "[error] HTTP #{response.code}: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
|
123
|
+
|
|
124
|
+
models = JSON.parse(response.body).fetch("models", [])
|
|
125
|
+
models
|
|
126
|
+
.select { |m| m["name"]&.include?("gemini") }
|
|
127
|
+
.sort_by { |m| m["name"] }
|
|
128
|
+
.each do |m|
|
|
129
|
+
methods = Array(m["supportedGenerationMethods"]).join(", ")
|
|
130
|
+
puts " #{m['name'].sub('models/', '').ljust(40)} #{m.fetch('displayName', '')} [#{methods}]"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def call_gemini(prompt, model:, api_key:)
|
|
135
|
+
url = URI("#{API_BASE}/#{model}:generateContent?key=#{api_key}")
|
|
136
|
+
|
|
137
|
+
payload = {
|
|
138
|
+
contents: [{ parts: [{ text: prompt }] }],
|
|
139
|
+
generationConfig: {
|
|
140
|
+
temperature: 0.2,
|
|
141
|
+
maxOutputTokens: 8192
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
146
|
+
http.use_ssl = true
|
|
147
|
+
http.cert_store = ssl_cert_store
|
|
148
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
149
|
+
http.read_timeout = 120
|
|
150
|
+
http.write_timeout = 120
|
|
151
|
+
|
|
152
|
+
request = Net::HTTP::Post.new(url)
|
|
153
|
+
request["Content-Type"] = "application/json"
|
|
154
|
+
request.body = payload.to_json
|
|
155
|
+
|
|
156
|
+
response = http.request(request)
|
|
157
|
+
|
|
158
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
159
|
+
abort "[error] HTTP #{response.code}: #{response.body}"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
result = JSON.parse(response.body)
|
|
163
|
+
result.dig("candidates", 0, "content", "parts", 0, "text") or
|
|
164
|
+
abort "[error] unexpected response shape:\n#{JSON.pretty_generate(result)}"
|
|
165
|
+
rescue JSON::ParserError => e
|
|
166
|
+
abort "[error] could not parse Gemini response: #{e.message}"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def parse_options
|
|
170
|
+
options = { files: [], model: nil, output: nil }
|
|
171
|
+
|
|
172
|
+
parser = OptionParser.new do |opts|
|
|
173
|
+
opts.banner = "Usage: gemini_ask.rb --prompt TEXT [--files GLOB...] [options]"
|
|
174
|
+
|
|
175
|
+
opts.on("--prompt TEXT", "Prompt text inline") { |v| options[:prompt] = v }
|
|
176
|
+
opts.on("--prompt-file PATH", "Path to a file with prompt") { |v| options[:prompt_file] = v }
|
|
177
|
+
opts.on("--files GLOBS", "Files or glob patterns (repeatable)") { |v| options[:files] << v }
|
|
178
|
+
opts.on("--model MODEL", "Gemini model override") { |v| options[:model] = v }
|
|
179
|
+
opts.on("--output PATH", "Write response to file") { |v| options[:output] = v }
|
|
180
|
+
opts.on("--list-models", "List available Gemini models") { |_| options[:list_models] = true }
|
|
181
|
+
opts.on("-h", "--help", "Show this help") { puts opts; exit }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
parser.parse!
|
|
185
|
+
options
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def resolve_prompt(options)
|
|
189
|
+
if options[:prompt]
|
|
190
|
+
options[:prompt]
|
|
191
|
+
elsif options[:prompt_file]
|
|
192
|
+
File.read(options[:prompt_file], encoding: "utf-8")
|
|
193
|
+
else
|
|
194
|
+
abort "[error] must supply --prompt or --prompt-file"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
#--
|
|
199
|
+
# Entry point
|
|
200
|
+
#++
|
|
201
|
+
load_dotenv
|
|
202
|
+
|
|
203
|
+
options = parse_options
|
|
204
|
+
|
|
205
|
+
if options[:list_models]
|
|
206
|
+
load_dotenv
|
|
207
|
+
api_key = ENV.fetch("GEMINI_API_KEY") { abort "[error] GEMINI_API_KEY not set" }
|
|
208
|
+
puts "\nAvailable Gemini models:\n"
|
|
209
|
+
list_models(api_key)
|
|
210
|
+
exit
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
api_key = ENV.fetch("GEMINI_API_KEY") { abort "[error] GEMINI_API_KEY is not set (check .env)" }
|
|
214
|
+
model = options[:model] || ENV.fetch("GEMINI_MODEL", DEFAULT_MODEL)
|
|
215
|
+
|
|
216
|
+
user_prompt = resolve_prompt(options)
|
|
217
|
+
file_contents = load_files(options[:files])
|
|
218
|
+
|
|
219
|
+
warn "[info] Loaded #{file_contents.size} file(s) into context." if file_contents.any?
|
|
220
|
+
|
|
221
|
+
full_prompt = build_prompt(user_prompt, file_contents)
|
|
222
|
+
est_tokens = full_prompt.length / 4
|
|
223
|
+
warn "[info] Model: #{model} | Estimated input tokens: ~#{est_tokens.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse}"
|
|
224
|
+
warn "[info] Querying Gemini..."
|
|
225
|
+
|
|
226
|
+
response_text = call_gemini(full_prompt, model: model, api_key: api_key)
|
|
227
|
+
|
|
228
|
+
if options[:output]
|
|
229
|
+
File.write(options[:output], response_text, encoding: "utf-8")
|
|
230
|
+
warn "[info] Response written to #{options[:output]}"
|
|
231
|
+
else
|
|
232
|
+
puts
|
|
233
|
+
puts "=" * 60
|
|
234
|
+
puts "GEMINI RESPONSE"
|
|
235
|
+
puts "=" * 60
|
|
236
|
+
puts response_text
|
|
237
|
+
puts "=" * 60
|
|
238
|
+
puts
|
|
239
|
+
puts "[reminder] Always verify Gemini's claims against actual code before acting on them."
|
|
240
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
You are doing a cycle-end code review for the ecoportal-api-graphql Ruby gem.
|
|
2
|
+
|
|
3
|
+
Review the provided files for:
|
|
4
|
+
1. Correctness — logic errors, edge cases not handled, incorrect assumptions.
|
|
5
|
+
2. Backwards compatibility — anything that could break eco-helpers (the downstream consumer).
|
|
6
|
+
3. Consistency — does the code follow the patterns established elsewhere in this gem?
|
|
7
|
+
4. Completeness — are there obvious gaps (missing specs, missing error handling, etc.)?
|
|
8
|
+
|
|
9
|
+
Respond in this format:
|
|
10
|
+
|
|
11
|
+
## Summary
|
|
12
|
+
One paragraph: overall assessment.
|
|
13
|
+
|
|
14
|
+
## Issues Found
|
|
15
|
+
For each issue:
|
|
16
|
+
- **Severity:** [critical | major | minor]
|
|
17
|
+
- **File:** `path/to/file.rb`
|
|
18
|
+
- **Concern:** What the problem is.
|
|
19
|
+
- **Suggestion:** What to do about it.
|
|
20
|
+
|
|
21
|
+
## No Issues Found In
|
|
22
|
+
List areas that look good.
|
|
23
|
+
|
|
24
|
+
## Uncertain / Needs Human Verification
|
|
25
|
+
Anything you're not confident about — flag it here rather than guessing.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# SKILL: GraphQL Schema Analysis
|
|
2
|
+
|
|
3
|
+
**Purpose:** Systematically analyse a GraphQL schema (introspection JSON) to understand
|
|
4
|
+
type coverage, identify mutation/query patterns, compare against a Ruby gem implementation,
|
|
5
|
+
and produce structured documentation for AI agents.
|
|
6
|
+
|
|
7
|
+
**When to use:** Before implementing new gem coverage for any GraphQL API area, or when
|
|
8
|
+
auditing existing coverage against a live schema.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
1. An introspection JSON file from the live API — standard `__schema` introspection format.
|
|
15
|
+
- Fetch it from a live or staging instance.
|
|
16
|
+
- Store it in `.ai-assistance/tmp/` (git-ignored).
|
|
17
|
+
- File naming convention: `YYYYMMDDTHHMMSS_<env>_graphql_schema.json`
|
|
18
|
+
|
|
19
|
+
2. The existing gem code for context (read `lib/` and `.ai-assistance/code/`).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Step 1 — Orientate on Scale
|
|
24
|
+
|
|
25
|
+
Before diving in, understand the size:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# File size
|
|
29
|
+
(Get-Item schema.json).Length / 1KB # PowerShell
|
|
30
|
+
wc -c schema.json # Unix
|
|
31
|
+
|
|
32
|
+
# Count types and mutations
|
|
33
|
+
ruby -rjson -e '
|
|
34
|
+
s = JSON.parse(File.read("schema.json")).dig("data","__schema")
|
|
35
|
+
types = s["types"].reject { |t| t["name"].start_with?("__") }
|
|
36
|
+
puts "Types: #{types.count}"
|
|
37
|
+
puts "Objects: #{types.count { |t| t["kind"] == "OBJECT" }}"
|
|
38
|
+
puts "Inputs: #{types.count { |t| t["kind"] == "INPUT_OBJECT" }}"
|
|
39
|
+
puts "Enums: #{types.count { |t| t["kind"] == "ENUM" }}"
|
|
40
|
+
puts "Unions: #{types.count { |t| t["kind"] == "UNION" }}"
|
|
41
|
+
puts "Interfaces:#{types.count { |t| t["kind"] == "INTERFACE" }}"
|
|
42
|
+
mut = types.find { |t| t["name"] == "Mutation" }
|
|
43
|
+
puts "Mutations: #{mut ? mut["fields"].count : 0}"
|
|
44
|
+
q = types.find { |t| t["name"] == "Query" }
|
|
45
|
+
puts "Queries: #{q ? q["fields"].count : 0}"
|
|
46
|
+
'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Step 2 — Map Top-Level Structure
|
|
52
|
+
|
|
53
|
+
Extract the root Query and Mutation fields. These are the entry points for all API calls:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
ruby -rjson -e '
|
|
57
|
+
s = JSON.parse(File.read("schema.json")).dig("data","__schema","types")
|
|
58
|
+
type_map = s.each_with_object({}) { |t, h| h[t["name"]] = t }
|
|
59
|
+
|
|
60
|
+
def type_str(t)
|
|
61
|
+
return "?" unless t
|
|
62
|
+
case t["kind"]
|
|
63
|
+
when "NON_NULL" then "#{type_str(t["ofType"])}!"
|
|
64
|
+
when "LIST" then "[#{type_str(t["ofType"])}]"
|
|
65
|
+
else t["name"]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
puts "=== QUERY ==="
|
|
70
|
+
type_map["Query"]["fields"].each { |f| puts " #{f["name"]}: #{type_str(f["type"])}" }
|
|
71
|
+
|
|
72
|
+
puts "\n=== MUTATIONS ==="
|
|
73
|
+
type_map["Mutation"]["fields"].each { |f| puts " #{f["name"]}" }
|
|
74
|
+
'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Key questions to answer:**
|
|
78
|
+
- Is there a primary gateway object? (e.g. `currentOrganization`) Under which everything nests?
|
|
79
|
+
- Which areas have the most mutations? (Group by prefix: `create*/update*/destroy*`)
|
|
80
|
+
- Are mutations consistent? (All use `input: SomeType!` or mixed styles?)
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Step 3 — Identify Core Patterns
|
|
85
|
+
|
|
86
|
+
Look for schema-wide patterns before diving into individual types. These inform how the
|
|
87
|
+
Ruby gem should be designed:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
ruby -rjson -e '
|
|
91
|
+
s = JSON.parse(File.read("schema.json")).dig("data","__schema","types")
|
|
92
|
+
type_map = s.each_with_object({}) { |t, h| h[t["name"]] = t }
|
|
93
|
+
|
|
94
|
+
# Check mutation argument patterns
|
|
95
|
+
muts = type_map["Mutation"]["fields"]
|
|
96
|
+
single_input = muts.count { |f| f["args"].length == 1 && f["args"][0]["name"] == "input" }
|
|
97
|
+
puts "Mutations with single input: #{single_input}/#{muts.count}"
|
|
98
|
+
|
|
99
|
+
# Find shared patterns
|
|
100
|
+
puts "\nTypes containing patchVer:"
|
|
101
|
+
s.select { |t| (t["fields"] || []).any? { |f| f["name"] == "patchVer" } }.each { |t| puts " #{t["name"]}" }
|
|
102
|
+
|
|
103
|
+
puts "\nInput types using IdDiffInput:"
|
|
104
|
+
s.select { |t| t["kind"] == "INPUT_OBJECT" }.each do |t|
|
|
105
|
+
t["inputFields"].each do |f|
|
|
106
|
+
next unless ["name","ofType"].all? { |k| f["type"].to_s.include?("IdDiff") } ||
|
|
107
|
+
f["type"]["name"] == "IdDiffInput" ||
|
|
108
|
+
(f["type"]["ofType"] || {})["name"] == "IdDiffInput"
|
|
109
|
+
puts " #{t["name"]}.#{f["name"]}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Patterns to look for:**
|
|
116
|
+
- **Concurrency control:** Any type with `patchVer` (optimistic locking — must query before update)
|
|
117
|
+
- **Delta sync:** `*Delta` query fields returning `[DeltaResult]` — incremental sync support
|
|
118
|
+
- **IdDiff pattern:** `IdDiffInput { additions, removals }` for array relationship updates
|
|
119
|
+
- **OneToMany inputs:** `*OneToManyInput { additions, updates, deletions }` for complex nested updates
|
|
120
|
+
- **Payload pattern:** Do mutations return `{ item, errors }` or `{ payload, errors }` or other?
|
|
121
|
+
- **Connection pattern:** Paginated lists use Relay-style `*Connection { edges { node }, pageInfo }`
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Step 4 — Extract a Specific Type
|
|
126
|
+
|
|
127
|
+
For each area you're implementing, extract the full type definition:
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
ruby -rjson -e '
|
|
131
|
+
s = JSON.parse(File.read("schema.json")).dig("data","__schema","types")
|
|
132
|
+
type_map = s.each_with_object({}) { |t, h| h[t["name"]] = t }
|
|
133
|
+
|
|
134
|
+
def type_str(t); return "?" unless t; case t["kind"]; when "NON_NULL" then "#{type_str(t["ofType"])}!"; when "LIST" then "[#{type_str(t["ofType"])}]"; else t["name"]; end; end
|
|
135
|
+
|
|
136
|
+
def print_type(type_map, name)
|
|
137
|
+
t = type_map[name]; return puts "NOT FOUND: #{name}" unless t
|
|
138
|
+
puts "\n=== #{name} (#{t["kind"]}) ==="
|
|
139
|
+
if t["kind"] == "ENUM"
|
|
140
|
+
(t["enumValues"] || []).each { |v| puts " #{v["name"]}" }
|
|
141
|
+
return
|
|
142
|
+
end
|
|
143
|
+
(t["fields"] || t["inputFields"] || []).each do |f|
|
|
144
|
+
args = (f["args"] || []).map { |a| "#{a["name"]}: #{type_str(a["type"])}" }
|
|
145
|
+
arg_str = args.empty? ? "" : "(#{args.join(", ")})"
|
|
146
|
+
puts " #{f["name"]}#{arg_str}: #{type_str(f["type"])}"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Replace with the type names you need:
|
|
151
|
+
%w[ TypeNameHere InputTypeHere ].each { |n| print_type(type_map, n) }
|
|
152
|
+
'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**For each area, extract:**
|
|
156
|
+
1. The output type(s) — what the query/mutation returns
|
|
157
|
+
2. The input type(s) — what the mutation accepts
|
|
158
|
+
3. The mutation args — confirm `input: InputType!` single-wrapper vs separate args
|
|
159
|
+
4. Related enums — field options, state values
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Step 5 — Compare Against Gem Implementation
|
|
164
|
+
|
|
165
|
+
For each implemented area, systematically check coverage:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
Schema field | Ruby gem mapping | Status
|
|
169
|
+
----------------------|-------------------------------|--------
|
|
170
|
+
id: ID! | passkey :id | ✓
|
|
171
|
+
name: String! | passthrough :name | ✓
|
|
172
|
+
patchVer: Int! | passthrough :patchVer | ✓ (queried)
|
|
173
|
+
| (not in mutation input yet) | ✗ (not injected)
|
|
174
|
+
recurrence: Recurrence| — | ✗ MISSING
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Gem mapping patterns (ecoportal-api-graphql):**
|
|
178
|
+
- `passkey :fieldName` — ID primary key
|
|
179
|
+
- `passthrough :fieldName` — scalar, string, enum
|
|
180
|
+
- `passboolean :fieldName` — boolean field
|
|
181
|
+
- `passdate :fieldName` — DateTime (read-only dates)
|
|
182
|
+
- `passarray :fieldName` — array of scalars
|
|
183
|
+
- `embeds_one :fieldName, klass: SomeClass` — nested object
|
|
184
|
+
- `embeds_many :fieldName, klass: SomeClass` — array of objects
|
|
185
|
+
- `read_only!` modifier — field cannot be written
|
|
186
|
+
- `root!` on class — excluded from diff (lookup/reference, not owned)
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Step 6 — Document Findings
|
|
191
|
+
|
|
192
|
+
Write to `.ai-assistance/code/<area>.md` covering:
|
|
193
|
+
1. Schema type inventory (output types, input types, enums)
|
|
194
|
+
2. Mutation/query signatures
|
|
195
|
+
3. Coverage gap table
|
|
196
|
+
4. Key patterns and invariants (patchVer, IdDiff, etc.)
|
|
197
|
+
5. Decisions made (deferred features, simplifications)
|
|
198
|
+
|
|
199
|
+
Reference: see `.ai-assistance/code/schema_analysis.md` and `diff_as_input.md` for examples.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Key Extraction Scripts (Quick Reference)
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
# Find all types mentioning a keyword
|
|
207
|
+
type_map.keys.select { |k| k.match?(/keyword/i) }.sort
|
|
208
|
+
|
|
209
|
+
# Find all mutations for an area
|
|
210
|
+
type_map["Mutation"]["fields"].select { |f| f["name"].match?(/area/i) }.map { |f| f["name"] }
|
|
211
|
+
|
|
212
|
+
# Find which types have a specific field
|
|
213
|
+
type_map.values.select { |t| (t["fields"] || []).any? { |f| f["name"] == "fieldName" } }.map { |t| t["name"] }
|
|
214
|
+
|
|
215
|
+
# Get all enums with their values
|
|
216
|
+
type_map.values.select { |t| t["kind"] == "ENUM" }.each do |t|
|
|
217
|
+
puts "#{t["name"]}: #{t["enumValues"].map { |v| v["name"] }.join(" | ")}"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Check mutation argument pattern for one mutation
|
|
221
|
+
mut = type_map["Mutation"]["fields"].find { |f| f["name"] == "mutationName" }
|
|
222
|
+
puts mut["args"].map { |a| "#{a["name"]}: #{type_str(a["type"])}" }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Common Gotchas
|
|
228
|
+
|
|
229
|
+
1. **Schema vs source code divergence.** The introspection JSON is from the live API.
|
|
230
|
+
Source code may be a different version. Always trust the introspection JSON.
|
|
231
|
+
|
|
232
|
+
2. **Non-null vs nullable.** `field: Type!` means non-null; `field: Type` is nullable.
|
|
233
|
+
For mutations, non-null input fields are required. Always check nullability before
|
|
234
|
+
setting `required: true` in a Ruby input class.
|
|
235
|
+
|
|
236
|
+
3. **Input type vs output type names.** `UpdateActionInput` ≠ `Action`. Always check
|
|
237
|
+
the specific input type, not the output type, when building mutation inputs.
|
|
238
|
+
|
|
239
|
+
4. **Relay Connection types.** `FooConnection { edges { node: Foo }, pageInfo }` —
|
|
240
|
+
the actual data is under `edges.node` or (with the `nodes` shortcut) directly `nodes`.
|
|
241
|
+
|
|
242
|
+
5. **Union types.** `PageUnion = BasicPage | PhasedPage` — always include `__typename`
|
|
243
|
+
in queries on union types so you can dispatch to the right Ruby class.
|
|
244
|
+
|
|
245
|
+
6. **`patchVer` = must read before write.** Any type with `patchVer` in the schema
|
|
246
|
+
requires you to fetch the current value before updating. You cannot construct a
|
|
247
|
+
valid update input without it.
|
|
248
|
+
|
|
249
|
+
7. **`IdDiffInput` in Update vs `[ID!]` in Create.** Create mutations often take flat
|
|
250
|
+
ID arrays; update mutations use `IdDiffInput`. Build separate Create and Update
|
|
251
|
+
input classes rather than sharing one.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Output: What to Write
|
|
256
|
+
|
|
257
|
+
After analysis, produce:
|
|
258
|
+
- `.ai-assistance/code/<area>.md` — technical spec for the area
|
|
259
|
+
- `.ai-assistance/projects/<area>/TODO.md` — phased implementation plan
|
|
260
|
+
- `.ai-assistance/projects/<area>/DECISIONS.md` — rationale for scope choices
|
|
261
|
+
- Update `.ai-assistance/code/schema_analysis.md` coverage table with findings
|