name: PR Failure Chack-Agent Dispatch on: workflow_run: workflows: ["PR-tests"] types: [completed] jobs: resolve_pr_context: if: > ${{ github.event.workflow_run.conclusion == 'failure' && !startsWith(github.event.workflow_run.head_commit.message || '', 'Fix CI failures for PR #') }} runs-on: ubuntu-latest permissions: pull-requests: read issues: read outputs: number: ${{ steps.pr_context.outputs.number }} author: ${{ steps.pr_context.outputs.author }} head_repo: ${{ steps.pr_context.outputs.head_repo }} head_branch: ${{ steps.pr_context.outputs.head_branch }} should_run: ${{ steps.pr_context.outputs.should_run }} steps: - name: Resolve PR context id: pr_context env: PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} GH_TOKEN: ${{ github.token }} run: | if [ -z "$PR_NUMBER" ] && [ -n "$HEAD_BRANCH" ]; then PR_NUMBER="$(gh pr list --state open --head "$HEAD_BRANCH" --json number --jq '.[0].number')" fi if [ -z "$PR_NUMBER" ]; then echo "No pull request found for workflow_run; skipping." { echo "number=" echo "author=" echo "head_repo=" echo "head_branch=${HEAD_BRANCH}" echo "should_run=false" } >> "$GITHUB_OUTPUT" exit 0 fi pr_author=$(gh api -H "Accept: application/vnd.github+json" \ /repos/${{ github.repository }}/pulls/${PR_NUMBER} \ --jq '.user.login') pr_head_repo=$(gh api -H "Accept: application/vnd.github+json" \ /repos/${{ github.repository }}/pulls/${PR_NUMBER} \ --jq '.head.repo.full_name') pr_head_branch=$(gh api -H "Accept: application/vnd.github+json" \ /repos/${{ github.repository }}/pulls/${PR_NUMBER} \ --jq '.head.ref') pr_labels=$(gh api -H "Accept: application/vnd.github+json" \ /repos/${{ github.repository }}/issues/${PR_NUMBER} \ --jq '.labels[].name') if echo "$pr_labels" | grep -q "^chack-agent-fix-attempted$"; then echo "chack-agent fix already attempted for PR #${PR_NUMBER}; skipping." should_run=false else should_run=true fi { echo "number=${PR_NUMBER}" echo "author=${pr_author}" echo "head_repo=${pr_head_repo}" echo "head_branch=${pr_head_branch}" echo "should_run=${should_run}" } >> "$GITHUB_OUTPUT" chack_agent_on_failure: needs: resolve_pr_context if: ${{ needs.resolve_pr_context.outputs.author == 'carlospolop' && needs.resolve_pr_context.outputs.should_run == 'true' }} runs-on: ubuntu-latest permissions: contents: write pull-requests: write issues: write actions: write env: CHACK_LOGS_HTTP_URL: ${{ secrets.CHACK_LOGS_HTTP_URL }} steps: - name: Comment on PR with failure info uses: actions/github-script@v7 env: PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }} RUN_URL: ${{ github.event.workflow_run.html_url }} WORKFLOW_NAME: ${{ github.event.workflow_run.name }} with: github-token: ${{ github.token }} script: | const prNumber = Number(process.env.PR_NUMBER); const body = `PR #${prNumber} had a failing workflow "${process.env.WORKFLOW_NAME}".\n\nRun: ${process.env.RUN_URL}\n\nLaunching Chack Agent to attempt a fix.`; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber, body, }); - name: Mark fix attempt env: PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }} GH_TOKEN: ${{ github.token }} run: | gh api -X POST -H "Accept: application/vnd.github+json" \ /repos/${{ github.repository }}/issues/${PR_NUMBER}/labels \ -f labels[]=chack-agent-fix-attempted - name: Checkout PR head uses: actions/checkout@v5 with: repository: ${{ needs.resolve_pr_context.outputs.head_repo }} ref: ${{ github.event.workflow_run.head_sha }} fetch-depth: 0 persist-credentials: true token: ${{ secrets.CHACK_AGENT_FIXER_TOKEN || github.token }} - name: Configure git author run: | git config user.name "chack-agent" git config user.email "chack-agent@users.noreply.github.com" - name: Fetch failure summary env: GH_TOKEN: ${{ github.token }} RUN_ID: ${{ github.event.workflow_run.id }} run: | gh api -H "Accept: application/vnd.github+json" \ /repos/${{ github.repository }}/actions/runs/$RUN_ID/jobs \ --paginate > /tmp/jobs.json python3 - <<'PY' import json data = json.load(open('/tmp/jobs.json')) lines = [] for job in data.get('jobs', []): if job.get('conclusion') == 'failure': lines.append(f"Job: {job.get('name')} (id {job.get('id')})") lines.append(f"URL: {job.get('html_url')}") for step in job.get('steps', []): if step.get('conclusion') == 'failure': lines.append(f" Step: {step.get('name')}") lines.append("") summary = "\n".join(lines).strip() or "No failing job details found." with open('chack_failure_summary.txt', 'w') as handle: handle.write(summary) PY - name: Create Chack Agent prompt env: PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }} RUN_URL: ${{ github.event.workflow_run.html_url }} HEAD_BRANCH: ${{ needs.resolve_pr_context.outputs.head_branch }} run: | { echo "You are fixing CI failures for PR #${PR_NUMBER} in ${{ github.repository }}." echo "The failing workflow run is: ${RUN_URL}" echo "The PR branch is: ${HEAD_BRANCH}" echo "" echo "Failure summary:" cat chack_failure_summary.txt echo "" echo "Please identify the cause, apply a easy, simple and minimal fix, and update files accordingly." echo "Do not edit or create any .md or .txt files and do not modify build_lists/regexes.yaml." echo "Run any fast checks you can locally (no network)." echo "Leave the repo in a state ready to commit as when you finish, it'll be automatically committed and pushed." } > chack_prompt.txt - name: Set up Node.js for Codex uses: actions/setup-node@v5 with: node-version: "20" - name: Install Codex CLI run: | npm install -g @openai/codex codex --version - name: Run Chack Agent id: run_chack uses: carlospolop/chack-agent@master with: provider: codex model_primary: CHEAP_BUT_QUALITY main_action: peass-ng-pr-failure max_turns: 125 sub_action: PR Failure Chack-Agent Dispatch system_prompt: | You are Chack Agent, an elite CI-fix engineer. Diagnose the failing workflow, propose the minimal safe fix, and implement it. Never edit any .md or .txt files. Don't create job or summary files of you actions. Never create or modify build_lists/regexes.yaml. Run only fast, local checks (no network). Leave the repo ready to commit. prompt_file: chack_prompt.txt tools_config_json: "{\"exec_enabled\": true}" session_config_json: "{\"long_term_memory_enabled\": false}" agent_config_json: "{\"self_critique_enabled\": false, \"require_task_steps_manager_init_first\": true}" openai_api_key: ${{ secrets.OPENAI_API_KEY }} - name: Commit and push if changed env: TARGET_BRANCH: ${{ needs.resolve_pr_context.outputs.head_branch }} PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }} ORIGINAL_HEAD_SHA: ${{ github.event.workflow_run.head_sha }} GH_TOKEN: ${{ github.token }} run: | rm -f chack_failure_summary.txt chack_prompt.txt pushed=false if ! git diff --quiet; then git add -A # Keep all fixer changes, including .github/workflows, and rely on permissioned tokens to push safely. # Only temporary tool artifacts are filtered out. git reset -- chack_failure_summary.txt chack_prompt.txt # Never commit generated or regenerated regex list files from this workflow. git reset -- build_lists/regexes.yaml || true # Never allow the agent to commit generated linpeas artifacts. git reset -- linpeas.sh linpeas_fat.sh || true while IFS= read -r forbidden_file; do git reset -- "$forbidden_file" || true done < <(git diff --name-only --cached | grep -E '(^|/)(linpeas\.sh|linpeas_fat\.sh)$' || true) while IFS= read -r file; do case "$file" in *.txt|*.md) git reset -- "$file" ;; esac done < <(git diff --name-only --cached) if git diff --cached --name-only | grep -Eq '(^|/)(linpeas\.sh|linpeas_fat\.sh)$'; then echo "Forbidden generated linpeas files are still staged; skipping push." echo "pushed=false" >> "$GITHUB_OUTPUT" exit 0 fi if ! git diff --cached --quiet; then git commit -m "Fix CI failures for PR #${PR_NUMBER}" fi fi after_head="$(git rev-parse HEAD)" if [ "$after_head" = "$ORIGINAL_HEAD_SHA" ]; then echo "No commit produced by Chack Agent for PR #${PR_NUMBER}." echo "pushed=false" >> "$GITHUB_OUTPUT" exit 0 fi if ! git push origin HEAD:${TARGET_BRANCH}; then echo "Push failed (likely token workflow permission limits); leaving run successful without push." echo "pushed=false" >> "$GITHUB_OUTPUT" exit 0 fi pushed=true if [ "$pushed" = "true" ]; then gh workflow run PR-tests.yml --ref "${TARGET_BRANCH}" fi - name: Comment with Chack Agent result if: ${{ steps.run_chack.outputs.final-message != '' }} uses: actions/github-script@v7 env: PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }} CHACK_MESSAGE: ${{ steps.run_chack.outputs.final-message }} with: github-token: ${{ github.token }} script: | await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: Number(process.env.PR_NUMBER), body: process.env.CHACK_MESSAGE, });