ecoportal-api-graphql 1.3.13 → 1.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -0
  3. data/lib/ecoportal/api/common/graphql/client.rb +0 -2
  4. data/lib/ecoportal/api/common/graphql/http_client.rb +6 -0
  5. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -17
  6. data/lib/ecoportal/api/graphql/base/file_container.rb +14 -14
  7. data/lib/ecoportal/api/graphql/base/location_classification_type.rb +15 -15
  8. data/lib/ecoportal/api/graphql/base/location_structure.rb +27 -27
  9. data/lib/ecoportal/api/graphql/base/organization.rb +15 -15
  10. data/lib/ecoportal/api/graphql/base/page/task.rb +24 -0
  11. data/lib/ecoportal/api/graphql/base/page.rb +17 -16
  12. data/lib/ecoportal/api/graphql/base/template.rb +35 -0
  13. data/lib/ecoportal/api/graphql/base.rb +37 -35
  14. data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +41 -41
  15. data/lib/ecoportal/api/graphql/compat/response.rb +35 -35
  16. data/lib/ecoportal/api/graphql/connection/action.rb +11 -11
  17. data/lib/ecoportal/api/graphql/connection/action_category.rb +11 -11
  18. data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +11 -11
  19. data/lib/ecoportal/api/graphql/connection/page.rb +11 -11
  20. data/lib/ecoportal/api/graphql/connection/person_member.rb +11 -11
  21. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -11
  22. data/lib/ecoportal/api/graphql/connection.rb +17 -17
  23. data/lib/ecoportal/api/graphql/file_upload/client.rb +21 -10
  24. data/lib/ecoportal/api/graphql/fragment/field_configuration.rb +38 -0
  25. data/lib/ecoportal/api/graphql/fragment/force.rb +14 -10
  26. data/lib/ecoportal/api/graphql/fragment/location_node.rb +26 -26
  27. data/lib/ecoportal/api/graphql/fragment/page_task.rb +39 -0
  28. data/lib/ecoportal/api/graphql/fragment/template.rb +51 -0
  29. data/lib/ecoportal/api/graphql/fragment.rb +3 -0
  30. data/lib/ecoportal/api/graphql/input/contractor_entity.rb +14 -14
  31. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -16
  32. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -18
  33. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -21
  34. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -28
  35. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -18
  36. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -23
  37. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -20
  38. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -20
  39. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -24
  40. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -20
  41. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -20
  42. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -20
  43. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -18
  44. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -18
  45. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -18
  46. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -251
  47. data/lib/ecoportal/api/graphql/input.rb +24 -24
  48. data/lib/ecoportal/api/graphql/logic/connection.rb +34 -34
  49. data/lib/ecoportal/api/graphql/model/page/task.rb +16 -0
  50. data/lib/ecoportal/api/graphql/model/page.rb +16 -15
  51. data/lib/ecoportal/api/graphql/model/person_member.rb +15 -15
  52. data/lib/ecoportal/api/graphql/model/template.rb +15 -0
  53. data/lib/ecoportal/api/graphql/model.rb +32 -30
  54. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -45
  55. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -40
  56. data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +14 -14
  57. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -40
  58. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +38 -40
  59. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +38 -40
  60. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +38 -40
  61. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +7 -7
  62. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +8 -7
  63. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +38 -40
  64. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -40
  65. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -40
  66. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -40
  67. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +34 -35
  68. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -35
  69. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -36
  70. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -40
  71. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -13
  72. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -46
  73. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +1 -1
  74. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +1 -1
  75. data/lib/ecoportal/api/graphql/mutation.rb +20 -20
  76. data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +14 -14
  77. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +36 -21
  78. data/lib/ecoportal/api/graphql/payload/page/draft.rb +26 -13
  79. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -13
  80. data/lib/ecoportal/api/graphql/payload/page/review_task_batch.rb +23 -0
  81. data/lib/ecoportal/api/graphql/payload/page.rb +20 -19
  82. data/lib/ecoportal/api/graphql/payload/preset_view.rb +15 -11
  83. data/lib/ecoportal/api/graphql/payload/register.rb +15 -11
  84. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +1 -1
  85. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +1 -1
  86. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +1 -1
  87. data/lib/ecoportal/api/graphql/payload.rb +23 -23
  88. data/lib/ecoportal/api/graphql/query/location_structures.rb +57 -57
  89. data/lib/ecoportal/api/graphql/query/page.rb +45 -45
  90. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +9 -3
  91. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +9 -3
  92. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -78
  93. data/lib/ecoportal/api/graphql/query.rb +27 -27
  94. data/lib/ecoportal/api/graphql.rb +9 -5
  95. data/lib/ecoportal/api/graphql_version.rb +1 -1
  96. metadata +9 -180
  97. data/.ai-assistance/bridge/CLAUDE.md +0 -338
  98. data/.ai-assistance/bridge/archive/.gitkeep +0 -0
  99. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +0 -29
  100. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +0 -18
  101. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +0 -42
  102. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +0 -115
  103. data/.ai-assistance/bridge/context/gemini-review-prompt.txt +0 -48
  104. data/.ai-assistance/bridge/context/gemini-review-response.md +0 -104
  105. data/.ai-assistance/bridge/context/project.md +0 -42
  106. data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
  107. data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
  108. data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +0 -48
  109. data/.ai-assistance/bridge/queue/.gitkeep +0 -1
  110. data/.ai-assistance/capabilities/CLAUDE.md +0 -27
  111. data/.ai-assistance/capabilities/assumptions-log.md +0 -80
  112. data/.ai-assistance/capabilities/code.md +0 -47
  113. data/.ai-assistance/capabilities/connectors.md +0 -37
  114. data/.ai-assistance/capabilities/cowork.md +0 -55
  115. data/.ai-assistance/code/OVERVIEW.md +0 -155
  116. data/.ai-assistance/code/data_fields.md +0 -242
  117. data/.ai-assistance/code/dependencies.md +0 -151
  118. data/.ai-assistance/code/diff_as_input.md +0 -234
  119. data/.ai-assistance/code/diff_pairing_engine.md +0 -243
  120. data/.ai-assistance/code/diff_service_deep_dive.md +0 -192
  121. data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +0 -55
  122. data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +0 -181
  123. data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +0 -192
  124. data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +0 -147
  125. data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +0 -277
  126. data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +0 -200
  127. data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +0 -228
  128. data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +0 -197
  129. data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +0 -243
  130. data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +0 -196
  131. data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +0 -132
  132. data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +0 -187
  133. data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +0 -236
  134. data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +0 -183
  135. data/.ai-assistance/code/ecoportal_schema_reference.md +0 -240
  136. data/.ai-assistance/code/filter_contract_matrix.md +0 -177
  137. data/.ai-assistance/code/graphql_domain_knowledge.md +0 -240
  138. data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +0 -71
  139. data/.ai-assistance/code/refactoring/opportunities.md +0 -251
  140. data/.ai-assistance/code/schema_analysis.md +0 -321
  141. data/.ai-assistance/code/search_filters.md +0 -868
  142. data/.ai-assistance/code/spec_coverage.md +0 -73
  143. data/.ai-assistance/code/template_diff_pairing_domain.md +0 -175
  144. data/.ai-assistance/code/workflow-command-guide.md +0 -466
  145. data/.ai-assistance/code/workflow-space.md +0 -353
  146. data/.ai-assistance/conventions/CLAUDE.md +0 -30
  147. data/.ai-assistance/conventions/code-working-tree-protocol.md +0 -199
  148. data/.ai-assistance/conventions/gitignore-rules.md +0 -42
  149. data/.ai-assistance/conventions/permission-guidance.md +0 -120
  150. data/.ai-assistance/integrations/README.md +0 -70
  151. data/.ai-assistance/integrations/gitkraken-mcp.md +0 -107
  152. data/.ai-assistance/integrations/gitlab-mcp.md +0 -123
  153. data/.ai-assistance/integrations/local-git.md +0 -60
  154. data/.ai-assistance/local_paths.example.md +0 -17
  155. data/.ai-assistance/projects/TODO.md +0 -118
  156. data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +0 -168
  157. data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +0 -60
  158. data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +0 -267
  159. data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +0 -53
  160. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +0 -102
  161. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +0 -139
  162. data/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +0 -244
  163. data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +0 -93
  164. data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +0 -244
  165. data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +0 -266
  166. data/.ai-assistance/projects/eco-helpers-compat/TODO.md +0 -86
  167. data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +0 -101
  168. data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +0 -177
  169. data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +0 -161
  170. data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +0 -125
  171. data/.ai-assistance/projects/ooze-graphql-native-migration/INVENTORY.md +0 -136
  172. data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +0 -126
  173. data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +0 -261
  174. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +0 -122
  175. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +0 -148
  176. data/.ai-assistance/projects/page-model/DECISIONS.md +0 -245
  177. data/.ai-assistance/projects/page-model/TODO.md +0 -190
  178. data/.ai-assistance/projects/qa-services-delivery/DECISIONS.md +0 -93
  179. data/.ai-assistance/projects/qa-services-delivery/INTENT.md +0 -76
  180. data/.ai-assistance/projects/qa-services-delivery/PHASE3-SCOPE.md +0 -115
  181. data/.ai-assistance/projects/qa-services-delivery/ROADMAP.md +0 -99
  182. data/.ai-assistance/projects/qa-services-delivery/TODO.md +0 -81
  183. data/.ai-assistance/projects/search-filter-builder/INTENT.md +0 -107
  184. data/.ai-assistance/projects/search-filter-builder/TODO.md +0 -131
  185. data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +0 -87
  186. data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +0 -108
  187. data/.ai-assistance/projects/template-diff-deploy/INTENT.md +0 -12
  188. data/.ai-assistance/projects/template-diff-deploy/TODO.md +0 -9
  189. data/.ai-assistance/projects/template-maintenance/DESIGN.md +0 -134
  190. data/.ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md +0 -93
  191. data/.ai-assistance/projects/template-maintenance/README.md +0 -14
  192. data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +0 -236
  193. data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +0 -243
  194. data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +0 -154
  195. data/.ai-assistance/projects/workflow-space/TODO.md +0 -213
  196. data/.ai-assistance/reinstall-claude-desktop-windows.md +0 -136
  197. data/.ai-assistance/scripts/CLAUDE.md +0 -150
  198. data/.ai-assistance/scripts/bridge-inbox-check.sh +0 -75
  199. data/.ai-assistance/scripts/bridge-init.sh +0 -86
  200. data/.ai-assistance/scripts/bridge-status.sh +0 -44
  201. data/.ai-assistance/scripts/capabilities-check.ts +0 -104
  202. data/.ai-assistance/scripts/check-outbox.sh +0 -43
  203. data/.ai-assistance/scripts/dep_graph.rb +0 -91
  204. data/.ai-assistance/scripts/lock-acquire.sh +0 -103
  205. data/.ai-assistance/scripts/lock-multi.sh +0 -124
  206. data/.ai-assistance/scripts/lock-queue.sh +0 -94
  207. data/.ai-assistance/scripts/setup-mcps.test.ts +0 -188
  208. data/.ai-assistance/scripts/setup-mcps.ts +0 -234
  209. data/.ai-assistance/scripts/task-complete.ts +0 -74
  210. data/.ai-assistance/scripts/task-create.ts +0 -75
  211. data/.ai-assistance/scripts/task-read.ts +0 -125
  212. data/.ai-assistance/scripts/token-logger.js +0 -220
  213. data/.ai-assistance/scripts/token-report.ts +0 -158
  214. data/.ai-assistance/scripts/token-session-start.js +0 -66
  215. data/.ai-assistance/skills/ai-instructions/SKILL.md +0 -48
  216. data/.ai-assistance/skills/code-specs/SKILL.md +0 -69
  217. data/.ai-assistance/skills/corporate-policies/SKILL.md +0 -201
  218. data/.ai-assistance/skills/dep-graph/SKILL.md +0 -139
  219. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +0 -417
  220. data/.ai-assistance/skills/gemini-assist/SKILL.md +0 -63
  221. data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +0 -205
  222. data/.ai-assistance/skills/gemini-assist/gemini_ask.py +0 -1
  223. data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +0 -240
  224. data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +0 -25
  225. data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +0 -261
  226. data/.ai-assistance/skills/procedural-memory/SKILL.md +0 -319
  227. data/.ai-assistance/skills/project-cycle/SKILL.md +0 -177
  228. data/.ai-assistance/skills/project-self-docs/SKILL.md +0 -181
  229. data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +0 -378
  230. data/.ai-assistance/skills/refactor/SKILL.md +0 -62
  231. data/.ai-assistance/skills/rubocop/SKILL.md +0 -93
  232. data/.ai-assistance/skills/ruby-scripting/SKILL.md +0 -215
  233. data/.ai-assistance/skills/spec-generation/SKILL.md +0 -72
  234. data/.ai-assistance/standards-version.json +0 -22
  235. data/.ai-assistance/token-budget.json +0 -32
  236. data/.ai-assistance/version.json +0 -39
  237. data/.claude/settings.json +0 -150
  238. data/.env.example +0 -18
  239. data/.gitattributes +0 -15
  240. data/.gitignore +0 -37
  241. data/.gitlab-ci.yml +0 -45
  242. data/.markdownlint.json +0 -4
  243. data/.rspec +0 -3
  244. data/.rubocop.yml +0 -121
  245. data/.ruby-version +0 -1
  246. data/.yardopts +0 -10
  247. data/CLAUDE.md +0 -243
  248. data/Gemfile +0 -30
  249. data/Rakefile +0 -90
  250. data/bin/console +0 -14
  251. data/bin/setup +0 -8
  252. data/bin/setup.ps1 +0 -6
  253. data/docs/self-docs/ARCHITECTURE.md +0 -88
  254. data/docs/self-docs/CHANGES.jsonl +0 -12
  255. data/docs/self-docs/COMPLIANCE.md +0 -79
  256. data/docs/self-docs/CONVENTIONS.md +0 -74
  257. data/docs/self-docs/INTEGRATIONS.md +0 -65
  258. data/docs/self-docs/OPERATIONS.md +0 -76
  259. data/docs/self-docs/OVERVIEW.md +0 -64
  260. data/docs/self-docs/STATUS.md +0 -73
  261. data/docs/self-docs/self-docs-index.json +0 -51
  262. data/docs/worklog.md +0 -977
  263. data/ecoportal-api-graphql.gemspec +0 -40
  264. data/scripts/auto-worker-scheduler.sh +0 -386
  265. data/tests/actions_get.rb +0 -7
  266. data/tests/contractor_entities_get.rb +0 -20
  267. data/tests/contractor_entity_create.rb +0 -19
  268. data/tests/contractor_entity_udpate.rb +0 -20
  269. data/tests/dump_page_model.rb +0 -74
  270. data/tests/dump_template_model.rb +0 -90
  271. data/tests/loc_structure_get.rb +0 -10
  272. data/tests/loc_structure_update.rb +0 -51
  273. data/tests/loc_structures_get.rb +0 -15
  274. data/tests/local_libs.rb +0 -20
  275. data/tests/validate_queries.rb +0 -127
@@ -1,44 +0,0 @@
1
- #!/bin/bash
2
- # bridge-status.sh
3
- # Quick dashboard of bridge state. One line output for agent consumption.
4
- #
5
- # Usage:
6
- # bash .ai-assistance/scripts/bridge-status.sh
7
- #
8
- # Output:
9
- # pending:2 in_progress:1 done:0 failed:0 archived:12 last_activity:2026-06-03T14:32Z
10
-
11
- set -euo pipefail
12
-
13
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
- BRIDGE_DIR="$SCRIPT_DIR/../bridge"
15
-
16
- count_status() {
17
- local dir="$1"
18
- local pattern="$2"
19
- if [ ! -d "$dir" ]; then echo 0; return; fi
20
- grep -rl "^STATUS: $pattern" "$dir" --include="*.md" 2>/dev/null | wc -l | tr -d ' '
21
- }
22
-
23
- PENDING=$(count_status "$BRIDGE_DIR/inbox" "PENDING")
24
- IN_PROGRESS=$(count_status "$BRIDGE_DIR/inbox" "IN_PROGRESS")
25
- DONE=$(count_status "$BRIDGE_DIR/outbox" "DONE")
26
- FAILED=$(count_status "$BRIDGE_DIR/outbox" "FAILED")
27
-
28
- ARCHIVED=0
29
- if [ -d "$BRIDGE_DIR/archive" ]; then
30
- ARCHIVED=$(find "$BRIDGE_DIR/archive" -name "outbox-*.md" 2>/dev/null | wc -l | tr -d ' ')
31
- fi
32
-
33
- # Last activity: most recently modified file in bridge/
34
- LAST_ACTIVITY="none"
35
- if command -v stat &>/dev/null; then
36
- LAST_FILE=$(find "$BRIDGE_DIR" -name "*.md" -not -name ".gitkeep" \
37
- -exec stat -f "%m %N" {} \; 2>/dev/null \
38
- | sort -rn | head -1 | awk '{print $2}' || true)
39
- if [ -n "${LAST_FILE:-}" ]; then
40
- LAST_ACTIVITY=$(date -r "$LAST_FILE" -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "unknown")
41
- fi
42
- fi
43
-
44
- echo "pending:$PENDING in_progress:$IN_PROGRESS done:$DONE failed:$FAILED archived:$ARCHIVED last_activity:$LAST_ACTIVITY"
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env npx ts-node
2
- /**
3
- * capabilities-check.ts
4
- * Periodic assumptions review agent.
5
- * Uses Claude Haiku (cheap + fast) to verify stale entries in assumptions-log.md.
6
- * Updates log with verification results and timestamps.
7
- *
8
- * Usage:
9
- * npx ts-node .ai-assistance/scripts/capabilities-check.ts
10
- *
11
- * Requires:
12
- * ANTHROPIC_API_KEY env var
13
- * npm install @anthropic-ai/sdk
14
- */
15
-
16
- import Anthropic from "@anthropic-ai/sdk";
17
- import { readFileSync, writeFileSync } from "fs";
18
- import { join, dirname } from "path";
19
-
20
- const STALE_DAYS = 30;
21
-
22
- const scriptDir = dirname(process.argv[1]);
23
- const logPath = join(scriptDir, "../capabilities/assumptions-log.md");
24
-
25
- // --- Read log ---
26
- const log = readFileSync(logPath, "utf8");
27
-
28
- // --- Find stale or NEEDS_REVIEW entries ---
29
- const entryPattern = /### (\d{4}-\d{2}-\d{2}) — (.+?)\nSTATUS: (CONFIRMED|NEEDS_REVIEW|RESOLVED|INVALIDATED)\nLast checked: (\d{4}-\d{2}-\d{2})/g;
30
-
31
- const now = new Date();
32
- const staleEntries: { title: string; date: string; status: string }[] = [];
33
-
34
- let match;
35
- while ((match = entryPattern.exec(log)) !== null) {
36
- const [, , title, status, lastChecked] = match;
37
- const checkedDate = new Date(lastChecked);
38
- const daysSince = (now.getTime() - checkedDate.getTime()) / (1000 * 60 * 60 * 24);
39
-
40
- if (status === "NEEDS_REVIEW" || daysSince > STALE_DAYS) {
41
- staleEntries.push({ title, date: lastChecked, status });
42
- }
43
- }
44
-
45
- if (staleEntries.length === 0) {
46
- console.log("All assumptions current. No review needed.");
47
- process.exit(0);
48
- }
49
-
50
- console.log(`Found ${staleEntries.length} stale/flagged assumption(s). Reviewing...`);
51
-
52
- // --- Use Haiku to check each ---
53
- const client = new Anthropic();
54
-
55
- async function checkAssumption(title: string): Promise<string> {
56
- const message = await client.messages.create({
57
- model: "claude-haiku-4-5-20251001",
58
- max_tokens: 300,
59
- messages: [
60
- {
61
- role: "user",
62
- content: `You are verifying a technical assumption about Claude/Anthropic products.
63
- Assumption: "${title}"
64
- Today's date: ${now.toISOString().slice(0, 10)}
65
-
66
- Search your knowledge for whether this is still accurate.
67
- Reply in this format exactly:
68
- STATUS: CONFIRMED | NEEDS_REVIEW | RESOLVED | INVALIDATED
69
- NOTES: <one sentence explaining current state>`,
70
- },
71
- ],
72
- });
73
-
74
- const text = message.content[0].type === "text" ? message.content[0].text : "";
75
- return text.trim();
76
- }
77
-
78
- // --- Update log entries ---
79
- let updatedLog = log;
80
- const todayStr = now.toISOString().slice(0, 10);
81
-
82
- for (const entry of staleEntries) {
83
- const result = await checkAssumption(entry.title);
84
- const statusLine = result.match(/^STATUS: (.+)$/m)?.[1]?.trim() ?? "NEEDS_REVIEW";
85
- const notesLine = result.match(/^NOTES: (.+)$/m)?.[1]?.trim() ?? "Auto-check inconclusive";
86
-
87
- console.log(` [${entry.title}] → ${statusLine}: ${notesLine}`);
88
-
89
- // Update the entry in the log
90
- updatedLog = updatedLog.replace(
91
- new RegExp(`(### ${entry.date} — ${entry.title}\\nSTATUS:) [A-Z_]+\\n(Last checked:) [0-9-]+`),
92
- `$1 ${statusLine}\n$2 ${todayStr}`
93
- );
94
- }
95
-
96
- // Append a review run summary
97
- updatedLog += `\n### ${todayStr} — Automated review run
98
- STATUS: CONFIRMED
99
- Last checked: ${todayStr}
100
- Source: capabilities-check.ts
101
- Notes: Reviewed ${staleEntries.length} stale entries. See individual entries for results.\n`;
102
-
103
- writeFileSync(logPath, updatedLog, "utf8");
104
- console.log(`Updated assumptions-log.md`);
@@ -1,43 +0,0 @@
1
- #!/bin/bash
2
- # check-outbox.sh
3
- # Check if Code has responded to a specific bridge task.
4
- # Used by CoWork to confirm handoff completion without reading raw files.
5
- #
6
- # Usage:
7
- # bash .ai-assistance/scripts/check-outbox.sh oscar-a3f2b1c-list-open-mrs.md
8
- #
9
- # Output:
10
- # DONE | <first 120 chars of result>
11
- # PENDING | no response yet
12
- # FAILED | <notes>
13
- # ERROR | <reason>
14
-
15
- set -euo pipefail
16
-
17
- TASK_FILE="${1:-}"
18
- if [ -z "$TASK_FILE" ]; then
19
- echo "ERROR | usage: check-outbox.sh <task-filename>"
20
- exit 1
21
- fi
22
-
23
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
- OUTBOX_DIR="$SCRIPT_DIR/../bridge/outbox"
25
- ARCHIVE_DIR="$SCRIPT_DIR/../bridge/archive"
26
- OUTBOX_PATH="$OUTBOX_DIR/$TASK_FILE"
27
- ARCHIVE_OUTBOX_PATH="$ARCHIVE_DIR/outbox-$TASK_FILE"
28
-
29
- # Check outbox first, then archive
30
- if [ -f "$OUTBOX_PATH" ]; then
31
- TARGET="$OUTBOX_PATH"
32
- elif [ -f "$ARCHIVE_OUTBOX_PATH" ]; then
33
- TARGET="$ARCHIVE_OUTBOX_PATH"
34
- else
35
- echo "PENDING | no response yet"
36
- exit 0
37
- fi
38
-
39
- STATUS=$(grep "^STATUS:" "$TARGET" | head -1 | awk '{print $2}')
40
- RESULT=$(awk '/^## Result/{found=1; next} found && /^## /{exit} found{print}' "$TARGET" \
41
- | head -3 | tr '\n' ' ' | cut -c1-120)
42
-
43
- echo "$STATUS | $RESULT"
@@ -1,91 +0,0 @@
1
- # .ai-assistance/scripts/dep_graph.rb
2
- #
3
- # Generates a JSON structural map of the gem's class/module hierarchy and
4
- # inter-file relationships. No gem dependencies — Ruby stdlib only.
5
- #
6
- # Usage (via rake dep_graph):
7
- # DepGraph.run(lib_dir: 'lib', output_path: '.ai-assistance/code/dep-graph.json')
8
- #
9
- # Output per file:
10
- # defines — fully-qualified module/class names defined in the file
11
- # requires — require_relative paths
12
- # includes — include/extend/prepend targets
13
-
14
- require 'json'
15
-
16
- module DepGraph
17
- module_function
18
-
19
- def run(lib_dir: 'lib', output_path: '.ai-assistance/code/dep-graph.json')
20
- files = Dir.glob(File.join(lib_dir, '**', '*.rb')).sort
21
- result = files.each_with_object({}) do |file, map|
22
- entry = analyse(file)
23
- map[file] = entry unless entry.values.all?(&:empty?)
24
- end
25
-
26
- File.write(output_path, JSON.pretty_generate(result))
27
- result
28
- end
29
-
30
- def analyse(file)
31
- src = File.read(file, encoding: 'utf-8')
32
- lines = src.lines
33
-
34
- defines = extract_defines(lines)
35
- requires = extract_requires(lines)
36
- includes = extract_includes(lines)
37
-
38
- { 'defines' => defines, 'requires' => requires, 'includes' => includes }
39
- end
40
-
41
- # ---------------------------------------------------------------------------
42
- # Extract fully-qualified module/class names by tracking a nesting stack.
43
- # Handles:
44
- # module Foo; module Bar (inline)
45
- # class Baz::Qux (compact namespace)
46
- # end (pops one level per bare `end`)
47
- # ---------------------------------------------------------------------------
48
- def extract_defines(lines)
49
- stack = []
50
- defines = []
51
-
52
- lines.each do |line|
53
- stripped = line.strip
54
-
55
- # module Foo or class Bar (or class Bar < Baz)
56
- if (m = stripped.match(/\A(module|class)\s+([\w:]+)/))
57
- parts = m[2].split('::')
58
- new_name = (stack + parts).join('::')
59
- defines << new_name
60
- stack.push(*parts)
61
-
62
- # bare `end` — pop one stack level
63
- elsif stripped == 'end'
64
- stack.pop unless stack.empty?
65
- end
66
- end
67
-
68
- defines.uniq
69
- end
70
-
71
- # ---------------------------------------------------------------------------
72
- # Extract require_relative paths (strips quotes and .rb suffix).
73
- # ---------------------------------------------------------------------------
74
- def extract_requires(lines)
75
- lines.filter_map do |line|
76
- m = line.strip.match(/\Arequire_relative\s+['"](.+?)['"]/)
77
- m && m[1].delete_suffix('.rb')
78
- end.uniq
79
- end
80
-
81
- # ---------------------------------------------------------------------------
82
- # Extract include / extend / prepend targets (module names only).
83
- # Ignores method calls with arguments beyond the module name.
84
- # ---------------------------------------------------------------------------
85
- def extract_includes(lines)
86
- lines.filter_map do |line|
87
- m = line.strip.match(/\A(?:include|extend|prepend)\s+([\w:]+)/)
88
- m && m[1]
89
- end.uniq
90
- end
91
- end
@@ -1,103 +0,0 @@
1
- #!/bin/bash
2
- # lock-acquire.sh
3
- # Atomically acquire the bridge LOCK file.
4
- #
5
- # Atomicity: writes content to a temp file, then hard-links it to LOCK.
6
- # Hard-link creation is atomic on POSIX filesystems (ext4, APFS, NTFS via Git Bash).
7
- # Only one of two concurrent callers can win the ln race — the loser gets exit 1.
8
- # Falls back to bash noclobber if hard links are unavailable (cross-filesystem edge case).
9
- #
10
- # Usage:
11
- # bash lock-acquire.sh \
12
- # --agent code \
13
- # --user oscar \
14
- # --intent "Implementing Phase D mutations" \
15
- # [--expiry-minutes 60] # default: 60
16
- # [--lock-file /custom/path] # default: bridge/LOCK
17
- #
18
- # Exit codes:
19
- # 0 — lock acquired; JSON: {"acquired":true,"lock_file":"..."}
20
- # 1 — lock already held or contention; JSON: {"acquired":false,"reason":"...","held_by":"..."}
21
-
22
- set -euo pipefail
23
-
24
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
- LOCK_FILE="${LOCK_FILE:-$SCRIPT_DIR/../bridge/LOCK}"
26
- EXPIRY_MINUTES=60
27
- AGENT=""
28
- USER_SLUG=""
29
- INTENT=""
30
-
31
- # --- Parse args ---
32
- while [[ $# -gt 0 ]]; do
33
- case "$1" in
34
- --agent) AGENT="$2"; shift 2 ;;
35
- --user) USER_SLUG="$2"; shift 2 ;;
36
- --intent) INTENT="$2"; shift 2 ;;
37
- --expiry-minutes) EXPIRY_MINUTES="$2"; shift 2 ;;
38
- --lock-file) LOCK_FILE="$2"; shift 2 ;;
39
- *) echo "{\"acquired\":false,\"reason\":\"unknown argument: $1\"}"; exit 1 ;;
40
- esac
41
- done
42
-
43
- if [ -z "$AGENT" ] || [ -z "$USER_SLUG" ] || [ -z "$INTENT" ]; then
44
- echo '{"acquired":false,"reason":"missing required argument (--agent, --user, --intent)"}'
45
- exit 1
46
- fi
47
-
48
- # --- Check for existing lock ---
49
- if [ -f "$LOCK_FILE" ]; then
50
- # Determine age via stat (GNU stat on Linux/Git Bash: -c %Y; BSD/macOS: -f %m)
51
- mtime=$(stat -c %Y "$LOCK_FILE" 2>/dev/null || stat -f %m "$LOCK_FILE" 2>/dev/null || echo 0)
52
- now=$(date +%s)
53
- age=$(( now - mtime ))
54
- max_age=$(( EXPIRY_MINUTES * 60 ))
55
-
56
- if [ "$age" -lt "$max_age" ]; then
57
- # Lock is active — report who holds it
58
- held_agent=$(grep "^AGENT:" "$LOCK_FILE" 2>/dev/null | sed 's/^AGENT: //' || echo "unknown")
59
- held_user=$(grep "^USER:" "$LOCK_FILE" 2>/dev/null | sed 's/^USER: //' || echo "unknown")
60
- held_intent=$(grep "^INTENT:" "$LOCK_FILE" 2>/dev/null | sed 's/^INTENT: //' || echo "")
61
- echo "{\"acquired\":false,\"reason\":\"locked\",\"held_by\":\"$held_agent/$held_user\",\"intent\":\"$held_intent\",\"age_seconds\":$age}"
62
- exit 1
63
- fi
64
- # Lock is stale — fall through and overwrite it
65
- rm -f "$LOCK_FILE"
66
- fi
67
-
68
- # --- Build lock content ---
69
- NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
70
- # Compute expiry — GNU date: -d "+N minutes"; BSD/macOS: -v +NM
71
- EXPIRES=$(date -u -d "+${EXPIRY_MINUTES} minutes" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
72
- || date -u -v +${EXPIRY_MINUTES}M +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
73
- || echo "unknown")
74
-
75
- CONTENT="AGENT: ${AGENT}
76
- USER: ${USER_SLUG}
77
- ACQUIRED: ${NOW}
78
- EXPIRES: ${EXPIRES}
79
- INTENT: ${INTENT}"
80
-
81
- # --- Atomic acquisition: temp file + hard link ---
82
- LOCK_DIR="$(dirname "$LOCK_FILE")"
83
- TMPFILE=$(mktemp "$LOCK_DIR/.lock-tmp.XXXXXX")
84
- printf '%s\n' "$CONTENT" > "$TMPFILE"
85
-
86
- if ln "$TMPFILE" "$LOCK_FILE" 2>/dev/null; then
87
- # Hard link succeeded — we own the lock
88
- rm -f "$TMPFILE"
89
- echo "{\"acquired\":true,\"lock_file\":\"$LOCK_FILE\",\"expires\":\"$EXPIRES\"}"
90
- exit 0
91
- fi
92
-
93
- # Hard link failed (LOCK re-appeared in the race window, or cross-fs)
94
- rm -f "$TMPFILE"
95
-
96
- # Fallback: bash noclobber (not fully atomic, but good enough for single-user scenarios)
97
- if ( set -o noclobber; printf '%s\n' "$CONTENT" > "$LOCK_FILE" ) 2>/dev/null; then
98
- echo "{\"acquired\":true,\"lock_file\":\"$LOCK_FILE\",\"expires\":\"$EXPIRES\",\"method\":\"noclobber\"}"
99
- exit 0
100
- fi
101
-
102
- echo "{\"acquired\":false,\"reason\":\"contention\"}"
103
- exit 1
@@ -1,124 +0,0 @@
1
- #!/usr/bin/env bash
2
- # lock-multi.sh
3
- # Acquire the bridge LOCK across multiple repos atomically.
4
- # Locks repos in order; rolls back already-acquired locks on failure.
5
- # Checks queue for higher-priority waiters before acquiring.
6
- #
7
- # Cross-repo lock timeout policy:
8
- # --expiry-minutes sets a hard LOCK expiry (default: 60).
9
- # --estimated-minutes sets the ESTIMATED_RELEASE field in each LOCK —
10
- # a human-readable "I expect to be done in N minutes" hint for other agents.
11
- # If the acquiring agent finishes early, it should release all locks immediately.
12
- # If it crashes, lock-acquire.sh on the next attempt will detect stale locks
13
- # (mtime > expiry) and overwrite them automatically.
14
- #
15
- # Usage:
16
- # bash lock-multi.sh \
17
- # --agent code \
18
- # --user oscar \
19
- # --intent "Cross-repo refactor" \
20
- # --repo /path/to/repo1 \
21
- # --repo /path/to/repo2 \
22
- # [--expiry-minutes 60] # hard timeout (default: 60)
23
- # [--estimated-minutes 20] # expected work duration (default: = expiry)
24
- #
25
- # Exit 0: all locks acquired. Prints JSON with acquired lock paths.
26
- # Exit 1: one or more locks could not be acquired. All acquired locks rolled back.
27
-
28
- set -euo pipefail
29
-
30
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
- LOCK_SCRIPT="$SCRIPT_DIR/lock-acquire.sh"
32
-
33
- AGENT="" USER_SLUG="" INTENT="" EXPIRY_MINUTES=60 ESTIMATED_MINUTES=""
34
- REPOS=()
35
-
36
- while [[ $# -gt 0 ]]; do
37
- case "$1" in
38
- --agent) AGENT="$2"; shift 2 ;;
39
- --user) USER_SLUG="$2"; shift 2 ;;
40
- --intent) INTENT="$2"; shift 2 ;;
41
- --repo) REPOS+=("$2"); shift 2 ;;
42
- --expiry-minutes) EXPIRY_MINUTES="$2"; shift 2 ;;
43
- --estimated-minutes) ESTIMATED_MINUTES="$2"; shift 2 ;;
44
- *) echo "{\"acquired\":false,\"reason\":\"unknown argument: $1\"}"; exit 1 ;;
45
- esac
46
- done
47
-
48
- [[ -z "$AGENT" || -z "$USER_SLUG" || -z "$INTENT" ]] && {
49
- echo '{"acquired":false,"reason":"missing --agent, --user, or --intent"}'; exit 1; }
50
- [[ ${#REPOS[@]} -eq 0 ]] && REPOS=("$(pwd)")
51
-
52
- # Default estimated = expiry (conservative)
53
- [[ -z "$ESTIMATED_MINUTES" ]] && ESTIMATED_MINUTES="$EXPIRY_MINUTES"
54
-
55
- NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
56
- ESTIMATED_RELEASE=$(date -u -d "+${ESTIMATED_MINUTES} minutes" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
57
- || date -u -v +${ESTIMATED_MINUTES}M +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
58
- || echo "unknown")
59
-
60
- ACQUIRED=()
61
- FAILED=""
62
- QUEUE_NOTICES=()
63
-
64
- for repo in "${REPOS[@]}"; do
65
- LOCK_FILE="$repo/.ai-assistance/bridge/LOCK"
66
-
67
- # ── Stale lock enforcement: explicitly check mtime before delegating ──────
68
- # lock-acquire.sh handles this too, but we do it here for cross-repo clarity.
69
- if [[ -f "$LOCK_FILE" ]]; then
70
- mtime=$(stat -c %Y "$LOCK_FILE" 2>/dev/null || stat -f %m "$LOCK_FILE" 2>/dev/null || echo 0)
71
- age=$(( $(date +%s) - mtime ))
72
- max_age=$(( EXPIRY_MINUTES * 60 ))
73
- if [[ "$age" -ge "$max_age" ]]; then
74
- rm -f "$LOCK_FILE"
75
- echo " [lock-multi] stale lock removed at $repo (age: ${age}s > ${max_age}s)" >&2
76
- fi
77
- fi
78
-
79
- # ── Check queue for higher-priority waiters ───────────────────────────────
80
- QUEUE_DIR="$repo/.ai-assistance/bridge/queue"
81
- if [[ -d "$QUEUE_DIR" ]]; then
82
- for qf in "$QUEUE_DIR"/1-critical-*.json "$QUEUE_DIR"/2-high-*.json; do
83
- [[ -f "$qf" ]] || continue
84
- pri=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(d.get('priority','?'))" "$qf" 2>/dev/null || echo "?")
85
- agt=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(d.get('agent','?'))" "$qf" 2>/dev/null || echo "?")
86
- intent_q=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(d.get('intent','?'))" "$qf" 2>/dev/null || echo "?")
87
- QUEUE_NOTICES+=("repo=$(basename "$repo") priority=$pri agent=$agt intent=\"$intent_q\"")
88
- done
89
- fi
90
-
91
- # ── Acquire lock via lock-acquire.sh ─────────────────────────────────────
92
- # Include ESTIMATED_RELEASE in the intent so it appears in the LOCK file text
93
- FULL_INTENT="${INTENT} | estimated_release:${ESTIMATED_RELEASE} | multi-repo:$(printf '%s,' "${REPOS[@]}" | sed 's/,$//')"
94
-
95
- result=$(bash "$LOCK_SCRIPT" \
96
- --agent "$AGENT" --user "$USER_SLUG" \
97
- --intent "$FULL_INTENT" \
98
- --expiry-minutes "$EXPIRY_MINUTES" \
99
- --lock-file "$LOCK_FILE" 2>/dev/null || true)
100
-
101
- ok=$(echo "$result" | python3 -c "import json,sys; d=json.load(sys.stdin); print('yes' if d.get('acquired') else 'no')" 2>/dev/null || echo "no")
102
-
103
- if [[ "$ok" == "yes" ]]; then
104
- ACQUIRED+=("$LOCK_FILE")
105
- else
106
- FAILED="$repo — $(echo "$result" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('reason','unknown'))" 2>/dev/null || echo 'unknown')"
107
- break
108
- fi
109
- done
110
-
111
- # ── Rollback all acquired locks if any failed ─────────────────────────────
112
- if [[ -n "$FAILED" ]]; then
113
- for lf in "${ACQUIRED[@]}"; do rm -f "$lf"; done
114
- echo "{\"acquired\":false,\"reason\":\"$FAILED\",\"rolled_back\":${#ACQUIRED[@]}}"
115
- exit 1
116
- fi
117
-
118
- # ── Output ────────────────────────────────────────────────────────────────
119
- FILES_JSON=$(printf '"%s",' "${ACQUIRED[@]}" | sed 's/,$//')
120
- NOTICES_JSON=$(printf '"%s",' "${QUEUE_NOTICES[@]:-}" | sed 's/,$//')
121
- echo "{\"acquired\":true,\"locks\":[$FILES_JSON],\"estimated_release\":\"$ESTIMATED_RELEASE\",\"expiry_minutes\":$EXPIRY_MINUTES,\"queue_notices\":[$NOTICES_JSON]}"
122
- echo "" >&2
123
- [[ ${#QUEUE_NOTICES[@]} -gt 0 ]] && \
124
- echo " [lock-multi] NOTE: higher-priority waiters in queue — wrap up within ${ESTIMATED_MINUTES} min" >&2
@@ -1,94 +0,0 @@
1
- #!/usr/bin/env bash
2
- # lock-queue.sh
3
- # Drop a priority turn-request into the bridge queue of one or more repos.
4
- # Used when a lock is held and this agent wants to wait with declared priority.
5
- #
6
- # Usage:
7
- # bash lock-queue.sh \
8
- # --agent cowork \
9
- # --user oscar \
10
- # --intent "Review auth changes" \
11
- # --priority high \
12
- # --target-project ep-claude-aws-platform \
13
- # [--repo /path/to/repo1 --repo /path/to/repo2 ...] \
14
- # [--expiry-minutes 90]
15
- #
16
- # Writes: .ai-assistance/bridge/queue/<priority>-<ts>-<id>.json in each repo
17
- # Exit 0: request(s) queued. Prints JSON with queue file paths.
18
-
19
- set -euo pipefail
20
-
21
- AGENT="" USER_SLUG="" INTENT="" PRIORITY="medium" TARGET_PROJECT=""
22
- EXPIRY_MINUTES=90 ESTIMATED_WAIT_MINUTES=""
23
- REPOS=()
24
-
25
- while [[ $# -gt 0 ]]; do
26
- case "$1" in
27
- --agent) AGENT="$2"; shift 2 ;;
28
- --user) USER_SLUG="$2"; shift 2 ;;
29
- --intent) INTENT="$2"; shift 2 ;;
30
- --priority) PRIORITY="$2"; shift 2 ;;
31
- --target-project) TARGET_PROJECT="$2"; shift 2 ;;
32
- --estimated-wait) ESTIMATED_WAIT_MINUTES="$2"; shift 2 ;;
33
- --repo) REPOS+=("$2"); shift 2 ;;
34
- --expiry-minutes) EXPIRY_MINUTES="$2"; shift 2 ;;
35
- *) echo "{\"queued\":false,\"reason\":\"unknown argument: $1\"}"; exit 1 ;;
36
- esac
37
- done
38
-
39
- [[ -z "$AGENT" || -z "$USER_SLUG" || -z "$INTENT" ]] && {
40
- echo '{"queued":false,"reason":"missing --agent, --user, or --intent"}'; exit 1; }
41
-
42
- # Default: current repo
43
- [[ ${#REPOS[@]} -eq 0 ]] && REPOS=("$(pwd)")
44
-
45
- NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
46
- EXPIRES=$(date -u -d "+${EXPIRY_MINUTES} minutes" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
47
- || date -u -v +${EXPIRY_MINUTES}M +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null \
48
- || echo "unknown")
49
- REQUEST_ID="req-$(date +%s)-$$"
50
-
51
- # Priority sort prefix: 1=critical 2=high 3=medium 4=low
52
- case "$PRIORITY" in
53
- critical) PFX="1" ;; high) PFX="2" ;; medium) PFX="3" ;; low) PFX="4" ;; *) PFX="3" ;;
54
- esac
55
-
56
- QUEUED_FILES=()
57
-
58
- # estimated_wait: read from the current LOCK file if present (it may contain estimated_release)
59
- get_estimated_release() {
60
- local lock="$1/.ai-assistance/bridge/LOCK"
61
- [[ -f "$lock" ]] || { echo "unknown"; return; }
62
- grep "estimated_release:" "$lock" 2>/dev/null | sed 's/.*estimated_release://' | tr -d ' ' | head -1 || echo "unknown"
63
- }
64
-
65
- for repo in "${REPOS[@]}"; do
66
- QUEUE_DIR="$repo/.ai-assistance/bridge/queue"
67
- [[ -d "$QUEUE_DIR" ]] || mkdir -p "$QUEUE_DIR"
68
- QFILE="$QUEUE_DIR/${PFX}-${PRIORITY}-$(date +%s%3N)-${REQUEST_ID}.json"
69
-
70
- # Read current lock's estimated release so queue entry shows the expected wait
71
- LOCK_ESTIMATED_RELEASE=$(get_estimated_release "$repo")
72
- EFFECTIVE_WAIT="${ESTIMATED_WAIT_MINUTES:-unknown}"
73
-
74
- cat > "$QFILE" << JSON
75
- {
76
- "request_id": "$REQUEST_ID",
77
- "agent": "$AGENT",
78
- "user": "$USER_SLUG",
79
- "intent": "$INTENT",
80
- "priority": "$PRIORITY",
81
- "target_project": "$TARGET_PROJECT",
82
- "repo": "$repo",
83
- "requested_at": "$NOW",
84
- "expires_at": "$EXPIRES",
85
- "estimated_wait_minutes": "$EFFECTIVE_WAIT",
86
- "current_lock_estimated_release": "$LOCK_ESTIMATED_RELEASE"
87
- }
88
- JSON
89
- QUEUED_FILES+=("$QFILE")
90
- done
91
-
92
- # Report
93
- FILES_JSON=$(printf '"%s",' "${QUEUED_FILES[@]}" | sed 's/,$//')
94
- echo "{\"queued\":true,\"request_id\":\"$REQUEST_ID\",\"priority\":\"$PRIORITY\",\"files\":[$FILES_JSON]}"