Skip to content

feat: Implement LispGenerators module with StreamData generators (#130) #281

feat: Implement LispGenerators module with StreamData generators (#130)

feat: Implement LispGenerators module with StreamData generators (#130) #281

Workflow file for this run

name: Claude PM
on:
# Run when PR is merged to main
pull_request:
types: [closed]
branches: [main]
# Run when an issue becomes ready for implementation
issues:
types: [labeled]
# Periodic check to pick up next work (catches gaps when all work is cleared)
schedule:
- cron: '0 * * * *' # Every hour
# Manual trigger for testing or recovery
workflow_dispatch:
inputs:
action:
description: 'Action to perform'
required: true
default: 'next-issue'
type: choice
options:
- next-issue # Normal: find/create next issue to work on
- status-only # Just report status, don't create/trigger issues
- reset-stuck # Reset stuck state and resume
jobs:
pm:
# Run on:
# - Merged PRs (not closed without merge)
# - Issues labeled 'ready-for-implementation'
# - Scheduled (hourly)
# - Manual trigger
if: |
(github.event_name == 'pull_request' && github.event.pull_request.merged == true) ||
(github.event_name == 'issues' && github.event.label.name == 'ready-for-implementation') ||
github.event_name == 'schedule' ||
github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
# Prevent concurrent PM operations
concurrency:
group: claude-pm
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
env:
MIX_ENV: test
steps:
- name: Check PAT availability
run: |
if [ -z "${{ secrets.PAT_WORKFLOW_TRIGGER }}" ]; then
echo "::warning::PAT_WORKFLOW_TRIGGER secret not set - implementation triggers won't work"
echo "::warning::@claude comments posted by PM won't trigger the claude.yml workflow"
fi
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PAT_WORKFLOW_TRIGGER || github.token }}
- name: Setup Elixir environment
uses: ./.github/actions/setup-elixir
- name: Install Claude Code CLI
run: |
npm install -g @anthropic-ai/claude-code
claude --version
- name: Check for open PRs
id: open-prs
env:
GH_TOKEN: ${{ github.token }}
run: |
# Check if there are any open PRs (excluding the just-merged one)
OPEN_PR_COUNT=$(gh pr list --repo "${{ github.repository }}" --state open --json number | jq length)
echo "open_pr_count=$OPEN_PR_COUNT" >> $GITHUB_OUTPUT
if [ "$OPEN_PR_COUNT" -gt 0 ]; then
echo "::notice::Found $OPEN_PR_COUNT open PR(s) - will skip creating new work"
OPEN_PRS=$(gh pr list --repo "${{ github.repository }}" --state open --json number,title --jq '.[] | "#\(.number): \(.title)"')
echo "Open PRs:"
echo "$OPEN_PRS"
fi
- name: Check current PM status
id: status
env:
GH_TOKEN: ${{ github.token }}
run: |
# Query GitHub directly for PM state (source of truth)
# Check for stuck state via label
STUCK_ISSUES=$(gh issue list --repo "${{ github.repository }}" --label "pm-stuck" --state open --json number | jq length)
if [ "$STUCK_ISSUES" -gt 0 ]; then
echo "is_stuck=true" >> $GITHUB_OUTPUT
echo "::warning::PM workflow is in STUCK state (found issue with pm-stuck label)"
else
echo "is_stuck=false" >> $GITHUB_OUTPUT
fi
# Count recent failures via label (issues that failed PM attempts)
FAILURE_COUNT=$(gh issue list --repo "${{ github.repository }}" --label "pm-failed-attempt" --state open --json number | jq length)
echo "failure_count=$FAILURE_COUNT" >> $GITHUB_OUTPUT
if [ "$FAILURE_COUNT" -ge 3 ]; then
echo "::warning::$FAILURE_COUNT consecutive PM failures detected"
fi
- name: Handle stuck state
if: steps.status.outputs.is_stuck == 'true' && github.event.inputs.action != 'reset-stuck' && github.event.inputs.action != 'status-only'
run: |
echo "::error::PM workflow is stuck. Use workflow_dispatch with 'reset-stuck' action to resume."
echo "Check issues with 'pm-stuck' label for details on what went wrong."
exit 1
- name: Reset stuck state
if: github.event.inputs.action == 'reset-stuck'
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "Resetting stuck state..."
# Remove pm-stuck label from all issues
for issue in $(gh issue list --label "pm-stuck" --state open --json number --jq '.[].number'); do
echo "Removing pm-stuck label from issue #$issue"
gh issue edit "$issue" --remove-label "pm-stuck" || true
done
# Remove pm-failed-attempt label from all issues
for issue in $(gh issue list --label "pm-failed-attempt" --state open --json number --jq '.[].number'); do
echo "Removing pm-failed-attempt label from issue #$issue"
gh issue edit "$issue" --remove-label "pm-failed-attempt" || true
done
echo "Stuck state reset complete"
- name: Gather phase status
id: phase-status
env:
GH_TOKEN: ${{ github.token }}
run: |
# Determine current phase by finding first phase with open issues
# or the next phase to start if all previous are complete
PHASES=("api-refactor" "parser" "analyzer" "eval" "integration" "property-testing" "polish")
PHASE_NAMES=("API Refactor" "Parser" "Analyzer" "Eval" "Integration" "Property Testing" "Polish")
current_phase=""
current_phase_name=""
open_in_phase=0
for i in "${!PHASES[@]}"; do
phase="${PHASES[$i]}"
open=$(gh issue list --label "phase:$phase" --state open --json number | jq length)
if [ "$open" -gt 0 ]; then
current_phase="$phase"
current_phase_name="${PHASE_NAMES[$i]}"
open_in_phase=$open
break
fi
done
# If no open phase issues, find next phase to start
if [ -z "$current_phase" ]; then
for i in "${!PHASES[@]}"; do
phase="${PHASES[$i]}"
closed=$(gh issue list --label "phase:$phase" --state closed --json number | jq length)
if [ "$closed" -eq 0 ]; then
current_phase="$phase"
current_phase_name="${PHASE_NAMES[$i]}"
open_in_phase=0
break
fi
done
fi
# Default to property-testing if somehow not determined
if [ -z "$current_phase" ]; then
current_phase="property-testing"
current_phase_name="Property Testing"
open_in_phase=0
fi
echo "current_phase=$current_phase" >> $GITHUB_OUTPUT
echo "current_phase_name=$current_phase_name" >> $GITHUB_OUTPUT
echo "open_in_phase=$open_in_phase" >> $GITHUB_OUTPUT
# Tech debt / from-pr-review count
tech_debt=$(gh issue list --label "from-pr-review" --state open --json number | jq length)
echo "tech_debt=$tech_debt" >> $GITHUB_OUTPUT
# Ready for implementation count
ready_issues=$(gh issue list --label "ready-for-implementation" --state open --json number | jq length)
echo "ready_issues=$ready_issues" >> $GITHUB_OUTPUT
# Needs review count (pending review)
needs_review=$(gh issue list --label "needs-review" --state open --json number | jq length)
echo "needs_review=$needs_review" >> $GITHUB_OUTPUT
echo "Phase status determined:"
echo " Current phase: $current_phase_name ($current_phase)"
echo " Open in phase: $open_in_phase"
echo " Tech debt: $tech_debt"
echo " Ready issues: $ready_issues"
echo " Needs review: $needs_review"
- name: Run Claude PM
id: claude-pm
# Skip if there are open PRs (unless status-only which is read-only)
if: steps.open-prs.outputs.open_pr_count == '0' || github.event.inputs.action == 'status-only'
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ secrets.PAT_WORKFLOW_TRIGGER || github.token }}
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
/pm-workflow **Repository**: ${{ github.repository }}
**Trigger**: ${{ github.event_name }}
**Action**: ${{ github.event.inputs.action || 'next-issue' }}
## Pre-computed State (no need to query GitHub for this)
- **Current phase**: ${{ steps.phase-status.outputs.current_phase_name }} (`${{ steps.phase-status.outputs.current_phase }}`)
- **Open issues in current phase**: ${{ steps.phase-status.outputs.open_in_phase }}
- **Ready for implementation**: ${{ steps.phase-status.outputs.ready_issues }}
- **Pending review**: ${{ steps.phase-status.outputs.needs_review }}
- **Tech debt (from-pr-review)**: ${{ steps.phase-status.outputs.tech_debt }}
- **Failure count**: ${{ steps.status.outputs.failure_count }}
claude_args: '--model claude-opus-4-5-20251101 --allowed-tools "WebSearch,WebFetch,Read,Edit,Write,Grep,Glob,Bash(gh:*),Bash(git:*),Bash(mix test:*),Bash(mix compile:*),Bash(ls:*),Bash(cat:*),Bash(find:*),Bash(wc:*)"'
show_full_output: true
- name: Check PM result
if: always()
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "PM workflow completed"
echo ""
echo "Current state:"
echo "- Open PRs: $(gh pr list --state open --json number | jq length)"
echo "- Ready issues: $(gh issue list --label ready-for-implementation --state open --json number | jq length)"
echo "- Stuck: $(gh issue list --label pm-stuck --state open --json number | jq length)"
echo ""
echo "Phase status:"
echo "- API Refactor open: $(gh issue list --label phase:api-refactor --state open --json number | jq length)"
echo "- API Refactor closed: $(gh issue list --label phase:api-refactor --state closed --json number | jq length)"
echo "- Parser open: $(gh issue list --label phase:parser --state open --json number | jq length)"
echo "- Parser closed: $(gh issue list --label phase:parser --state closed --json number | jq length)"
echo "- Analyzer open: $(gh issue list --label phase:analyzer --state open --json number | jq length)"
echo "- Analyzer closed: $(gh issue list --label phase:analyzer --state closed --json number | jq length)"
echo "- Eval open: $(gh issue list --label phase:eval --state open --json number | jq length)"
echo "- Eval closed: $(gh issue list --label phase:eval --state closed --json number | jq length)"
echo "- Integration open: $(gh issue list --label phase:integration --state open --json number | jq length)"
echo "- Integration closed: $(gh issue list --label phase:integration --state closed --json number | jq length)"
echo "- Property Testing open: $(gh issue list --label phase:property-testing --state open --json number | jq length)"
echo "- Property Testing closed: $(gh issue list --label phase:property-testing --state closed --json number | jq length)"
echo "- Polish open: $(gh issue list --label phase:polish --state open --json number | jq length)"
echo "- Polish closed: $(gh issue list --label phase:polish --state closed --json number | jq length)"
echo ""
echo "Deferred issues: $(gh issue list --label deferred --state open --json number | jq length)"