Trinity Agent Queue Drain #1411
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Trinity Agent Queue Drain | |
| on: | |
| schedule: | |
| - cron: '*/5 * * * *' # Every 5 minutes | |
| workflow_dispatch: {} | |
| concurrency: | |
| group: agent-queue-drain | |
| cancel-in-progress: false | |
| jobs: | |
| drain-queue: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| actions: write | |
| steps: | |
| - name: Check for queued issues and free slots | |
| env: | |
| RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }} | |
| RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -e | |
| # 1. Check if any slots are free | |
| DEPLOYMENTS=$(curl -s -X POST "https://railway.com/graphql/v2" \ | |
| -H "Authorization: Bearer ${RAILWAY_API_TOKEN}" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{ | |
| \"query\": \"query(\$id: String!) { project(id: \$id) { services { edges { node { id name deployments(first: 1) { edges { node { status } } } } } } } }\", | |
| \"variables\": { \"id\": \"${RAILWAY_PROJECT_ID}\" } | |
| }") | |
| # Check pool services by ID | |
| POOL_0="acfee27a-74e8-4436-961c-698ae93508ca" # ubuntu | |
| POOL_1="12c2bdf9-d124-4a45-93ad-22921e842d1b" # Agents Anywhere | |
| # Count FREE slots (not BUILDING/DEPLOYING/QUEUED/INITIALIZING) | |
| BUSY_STATUSES='["BUILDING","DEPLOYING","QUEUED","INITIALIZING"]' | |
| FREE_SLOTS=$(echo "$DEPLOYMENTS" | jq --arg p0 "$POOL_0" --arg p1 "$POOL_1" --argjson busy "$BUSY_STATUSES" \ | |
| '[.data.project.services.edges[].node | select(.id == $p0 or .id == $p1) | .deployments.edges[0].node.status // "IDLE"] | map(select(. as $s | $busy | index($s) | not)) | length') | |
| echo "📊 Free pool slots: ${FREE_SLOTS}/2" | |
| if [ "$FREE_SLOTS" -eq 0 ]; then | |
| echo "⏳ Both pools busy — skipping drain" | |
| exit 0 | |
| fi | |
| # 2. Find N oldest queued issues (one per free slot) | |
| QUEUED_LIST=$(gh issue list \ | |
| --repo ${{ github.repository }} \ | |
| --label "agent:queued" \ | |
| --state open \ | |
| --json number \ | |
| --jq ".[0:${FREE_SLOTS}] | .[].number" 2>/dev/null || true) | |
| if [ -z "$QUEUED_LIST" ]; then | |
| echo "✅ No queued issues" | |
| exit 0 | |
| fi | |
| # 3. Spawn one issue per free slot | |
| for QUEUED in $QUEUED_LIST; do | |
| echo "🚀 Draining queue: spawning agent for issue #${QUEUED}" | |
| gh issue edit "${QUEUED}" \ | |
| --repo ${{ github.repository }} \ | |
| --remove-label "agent:queued" | |
| gh workflow run agent-spawn-pool.yml \ | |
| --repo ${{ github.repository }} \ | |
| -f issue_number="${QUEUED}" | |
| gh issue comment "${QUEUED}" \ | |
| --repo ${{ github.repository }} \ | |
| --body "🔄 **Queue drain**: Slot freed, re-triggering agent spawn via workflow_dispatch." | |
| sleep 2 # Small delay between spawns to avoid race | |
| done | |
| echo "✅ Drained ${FREE_SLOTS} issue(s) from queue" |