When CI fails, the healing begins
The continuous integration pipeline is the heartbeat of the codebase. Every push triggers a cascade of tests and validations. When something breaks, the system notices instantly.
on: workflow_run: workflows: ["ci"] types: [completed] # The signal propagates to four parallel responders # Each with a specialized role in the healing process
The workflow_run event is the nervous signal that initiates healing.
It fires when CI completes—allowing downstream workflows to respond with full context.
Four workflows respond to a single signal
Like the body's immune response, multiple systems activate simultaneously. Each workflow has a singular responsibility, and together they orchestrate the repair.
The single source of truth for issue creation. Creates GitHub issues and fix branches.
Analyzes failure logs with AI, generates fix suggestions, creates draft PRs.
Handles notifications—Slack alerts, Notion logging. The messenger.
Routes failures by severity. Adds labels, escalates to humans when needed.
# SINGLE SOURCE OF TRUTH for CI failure → Issue creation # Other workflows delegate to cursor-fix-trigger.yml concurrency: group: cursor-fix-${{ github.event.workflow_run.id }} cancel-in-progress: false # Never cancel
Deduplication is critical. Without it, racing workflows would spawn duplicate issues. The solution: one workflow owns issue creation, others enhance.
From issue to pull request
When an issue is created with the cursor-agent label, Cursor's Background Agent activates.
It reads the failure context, analyzes logs, and commits a fix to cursor/ci-fix-{runId}.
# Branch naming links fix to original failure cursor/ci-fix-12345678901 # This naming is critical for: # 1. Tracing fixes to their source failure # 2. Detecting when a fix branch fails # 3. Automatic PR association
The branch name is the thread connecting failure to healing
When healing succeeds, the fix flows home
The auto-merge-fixes.yml workflow watches for CI completion on fix branches.
When all checks pass, it approves the PR, merges it, and closes the original issue.
# Only proceed if CI passed if: | github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' # Eligibility: auto-fix branch, checks passed, mergeable
The merge is a squash merge—collapsing the fix into a single commit. The fix branch is deleted. Clean. Traceable. Healed.
Breaking the infinite loop
What happens when a fix branch itself fails CI? Without careful design, this creates an infinite loop: failure → issue → fix → failure → ...
# CRITICAL: Detect fix branch failures HEAD_BRANCH="$HEAD_BRANCH" if [[ "$HEAD_BRANCH" =~ ^cursor/ci-fix- ]]; then # NO new issue - prevents infinite loops # Route to handle_fix_branch_failure fi
When a fix fails, the system doesn't create a new issue. Instead, it finds the original issue,
adds a fix-failed label, and reopens it. Manual intervention requested.
Some wounds require a human touch
From whisper to alarm
Not all failures are equal. A flaky test differs from a security vulnerability. The escalation system routes failures based on severity and consecutive failure count.
The escalation workflow never creates duplicate issues. It finds the existing issue and enhances it with labels appropriate to the severity.
Failure is not the opposite of success—it is the signal that guides improvement. The self-healing system watches, responds, repairs, and learns. When it cannot heal alone, it asks for help.