name: 🔄 State Machine Automation # Automatically manage Issue/PR state based on labels # Constitutional requirement: "Labels are the OS's state management" on: issues: types: [opened, labeled, unlabeled, assigned, closed, reopened] pull_request: types: [opened, labeled, unlabeled, closed, reopened, ready_for_review] issue_comment: types: [created] permissions: issues: write pull-requests: write jobs: # ========================================================================== # 1. Initial Triage - New Issues # ========================================================================== initial-triage: runs-on: ubuntu-latest if: github.event_name == 'issues' && github.event.action == 'opened' name: Initial Triage steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Set initial state to pending env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_OWNER: ${{ github.repository_owner }} GITHUB_REPO: ${{ github.event.repository.name }} run: | npm run state:transition -- \ --issue=${{ github.event.issue.number }} \ --to=pending \ --reason="New issue created, awaiting triage" - name: Auto-assign priority uses: actions/github-script@v7 with: script: | const labels = context.payload.issue.labels.map(l => l.name); let priority = '📊 priority:P2-Medium'; // Check for priority indicators in title/body const title = context.payload.issue.title.toLowerCase(); const body = (context.payload.issue.body || '').toLowerCase(); if (title.includes('critical') || body.includes('production down') || body.includes('security')) { priority = '🔥 priority:P0-Critical'; } else if (title.includes('urgent') || title.includes('bug') || body.includes('broken')) { priority = '⚠️ priority:P1-High'; } else if (title.includes('enhancement') || title.includes('feature')) { priority = '📊 priority:P2-Medium'; } await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.name, issue_number: context.payload.issue.number, labels: [priority] }); # ========================================================================== # 2. Coordinator Assignment - Trigger Analysis # ========================================================================== coordinator-assignment: runs-on: ubuntu-latest if: | github.event_name == 'issues' && github.event.action == 'labeled' && contains(github.event.label.name, 'agent:coordinator') name: Coordinator Assignment steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Transition to analyzing env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_OWNER: ${{ github.repository_owner }} GITHUB_REPO: ${{ github.event.repository.name }} run: | npm run state:transition -- \ --issue=${{ github.event.issue.number }} \ --to=analyzing \ --reason="CoordinatorAgent assigned, starting analysis" # ========================================================================== # 3. Specialist Assignment - Start Implementation # ========================================================================== specialist-assignment: runs-on: ubuntu-latest if: | github.event_name == 'issues' && github.event.action == 'labeled' && (contains(github.event.label.name, 'agent:codegen') || contains(github.event.label.name, 'agent:issue') || contains(github.event.label.name, 'agent:pr')) name: Specialist Assignment steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Transition to implementing env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_OWNER: ${{ github.repository_owner }} GITHUB_REPO: ${{ github.event.repository.name }} run: | npm run state:transition -- \ --issue=${{ github.event.issue.number }} \ --to=implementing \ --reason="Specialist agent assigned, starting implementation" # ========================================================================== # 4. PR Created - Start Review # ========================================================================== pr-created: runs-on: ubuntu-latest if: github.event_name == 'pull_request' && github.event.action == 'opened' name: PR Created - Start Review steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Find linked issue id: find-issue uses: actions/github-script@v7 with: script: | const body = context.payload.pull_request.body || ''; const match = body.match(/#(\d+)/); if (match) { return match[1]; } return null; result-encoding: string - name: Transition linked issue to reviewing if: steps.find-issue.outputs.result != 'null' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_OWNER: ${{ github.repository_owner }} GITHUB_REPO: ${{ github.event.repository.name }} run: | npm run state:transition -- \ --issue=${{ steps.find-issue.outputs.result }} \ --to=reviewing \ --reason="PR #${{ github.event.pull_request.number }} created" - name: Add review agent label to PR uses: actions/github-script@v7 with: script: | await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.name, issue_number: context.payload.pull_request.number, labels: ['🤖 agent:review', '👀 state:reviewing'] }); # ========================================================================== # 5. PR Merged - Mark as Done # ========================================================================== pr-merged: runs-on: ubuntu-latest if: | github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged == true name: PR Merged - Mark Done steps: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Find linked issue id: find-issue uses: actions/github-script@v7 with: script: | const body = context.payload.pull_request.body || ''; const match = body.match(/#(\d+)/); if (match) { return match[1]; } return null; result-encoding: string - name: Transition linked issue to done if: steps.find-issue.outputs.result != 'null' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_OWNER: ${{ github.repository_owner }} GITHUB_REPO: ${{ github.event.repository.name }} run: | npm run state:transition -- \ --issue=${{ steps.find-issue.outputs.result }} \ --to=done \ --reason="PR #${{ github.event.pull_request.number }} merged successfully" # ========================================================================== # 6. Blocked Label - Escalate # ========================================================================== blocked-escalation: runs-on: ubuntu-latest if: | github.event_name == 'issues' && github.event.action == 'labeled' && contains(github.event.label.name, 'state:blocked') name: Blocked - Escalate steps: - name: Create escalation comment uses: actions/github-script@v7 with: script: | await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.name, issue_number: context.payload.issue.number, body: `🚨 **BLOCKED - Guardian Escalation** This issue has been marked as blocked and requires Guardian intervention. @${{ github.repository_owner }} - Please review and resolve. **Next Steps**: 1. Review blocker reason 2. Resolve dependencies or technical issues 3. Remove \`🔴 state:blocked\` label when ready 4. Add appropriate state label to resume --- *Automated by [State Machine](../.github/workflows/state-machine.yml)*` }); - name: Add severity label uses: actions/github-script@v7 with: script: | await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.name, issue_number: context.payload.issue.number, labels: ['⚠️ severity:Sev.2-High'] });