feat: Implement LispGenerators module with StreamData generators (#130) #281
Workflow file for this run
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: 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)" |