Auto-Fix Failing CI Builds
Add a CI Action that calls Opulent to fix failing builds on your PRs.
Store the Opulent API key in GitHub
The workflow calls the Opulent API to create runs programmatically. Create a service user in the Opulent workspace, copy its token, and store it as a GitHub Actions secret.
Add the organization ID as a second secret. The workflow uses both to authenticate each fix request.
Secrets to add in GitHub: - OPULENT_API_KEY: token from the service user - OPULENT_ORG_ID: your organization ID Permissions needed: - ManageOrgSessions or the equivalent for creating runs. Scope: limit the secret to the repositories where the workflow will run.
Create a dedicated service user for CI fixes rather than reusing a personal token. It makes rotation easier and keeps audit logs clean.
Add the workflow file
Create a GitHub Action that fires when your existing CI workflow completes with a failure. It extracts the failing job names and calls the Opulent API to start a fix run.
Use the tags field to track which failures already triggered runs, so a flaky job does not spawn duplicate fix attempts.
name: Auto-fix CI with Opulent
on:
workflow_run:
workflows: ["CI"]
types: [completed]
jobs:
trigger-opulent-fix:
if: >
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.pull_requests[0]
runs-on: ubuntu-latest
steps:
- name: Get failure details
id: failure
uses: actions/github-script@v7
with:
script: |
const run = context.payload.workflow_run;
const pr = run.pull_requests[0];
const jobs = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: run.id
});
const failed = jobs.data.jobs
.filter(j => j.conclusion === 'failure')
.map(j => j.name);
core.setOutput('pr_number', pr.number);
core.setOutput('branch', pr.head.ref);
core.setOutput('failed_jobs', failed.join(', '));
core.setOutput('run_url', run.html_url);
- name: Trigger Opulent run
run: |
curl -s -X POST "https://api.opulentia.ai/v3/organizations/${{ secrets.OPULENT_ORG_ID }}/runs" \
-H "Authorization: Bearer ${{ secrets.OPULENT_API_KEY }}" \
-H "Content-Type: application/json" \
-d "{
\"prompt\": \"CI failed on PR #${{ steps.failure.outputs.pr_number }} in ${{ github.repository }}. Failed jobs: ${{ steps.failure.outputs.failed_jobs }}. Run: ${{ steps.failure.outputs.run_url }}. Branch: ${{ steps.failure.outputs.branch }}. Read the CI logs, identify the root cause, and push a fix to the branch.\",
\"tags\": [\"ci-fix\", \"pr-${{ steps.failure.outputs.pr_number }}\"]
}"Scope it to the right failures
Not every CI failure benefits from an auto-fix, infrastructure timeouts and Docker build issues will not be solved by a code change. Add a condition so only relevant job failures trigger Opulent.
Keep fixes reviewable. Opulent pushes a fix commit, but the PR still requires human review before merging. Treat auto-fixes as a head start, not a replacement for code review.
Add a guard before the trigger step: if: > contains(steps.failure.outputs.failed_jobs, 'test') || contains(steps.failure.outputs.failed_jobs, 'lint') || contains(steps.failure.outputs.failed_jobs, 'typecheck') This skips infra timeouts, flaky environment setup, and Docker build failures that need human diagnosis.
What happens when CI fails
When a PR's CI run fails, the Action extracts the failure details and passes them to Opulent. Opulent reads the CI logs, traces the error to the relevant file on the PR branch, and pushes a targeted fix commit.
It then comments on the PR with the root cause and the change. If it cannot resolve the failure, it comments with what it found so an engineer can pick up from there.
Example PR comment: CI failure in test-unit, fixed Root cause: UserList.tsx:34 calls .map() on props.users, which is undefined when the API returns an empty response body instead of []. Fix: Added a fallback, const users = props.users ?? []. Added a test case for the empty-response scenario. All 312 tests passing.
Iterate on the workflow
After a week, review the fix PRs. Expand the job-name guard if Opulent consistently fixes additional job types; shrink it if it produces noisy or wrong fixes.
Add memory entries for recurring failure patterns so future runs need less context in the prompt.
Sharpen the CI-fix loop
When Opulent fixes the same kind of failure repeatedly, add the pattern to the playbook or to memory (the notes a run recalls next time): 'always add a null fallback when .map() is called on API data.'
When a fix is rejected, add an eval (an automated test for the run) that checks the proposed fix against the team's style and correctness rules before it pushes.
The natural chain: when a CI failure reveals a deeper bug, hand the run to Debug a Bug Report End-to-End or Daily Sentry Error Fixes; when the fix requires changes across many repos, use Fleet-Wide Maintenance Across Thousands of Repositories.