When to invoke (auto-trigger)
Invoke automatically whenever:
- The user mentions CI is failing, red, or blocked
gh pr checksshows a failure- A push triggers a failed workflow run
Do NOT invoke for: local test failures (use test-fix instead), lint-only failures with an obvious single-line fix.
Self-Healing CI Pipeline with Handoff Memory
When to Use This Skill
Use this skill when:
- CI checks are failing on a PR
- You want to avoid repeating previously failed fix attempts
- You need systematic CI debugging with context preservation
- Multiple CI check types are failing (lint, tests, build, etc.)
Critical Setup
This skill requires a .handoff/ directory structure:
.handoff/
├── latest.md # Most recent session handoff
└── known-issues.md # Persistent log of failed approaches
If these don't exist, the skill will create them on first run.
Pipeline Steps
Phase 1: Ingest Context (Before ANY Action)
Read the handoff file
cat .handoff/latest.mdThis contains:
- What was tried in the previous session
- What worked and what didn't
- Current branch state
- Open questions or blockers
Read the known issues log
cat .handoff/known-issues.mdThis contains:
- Approaches that have been tried and failed
- Specific commands or fixes that don't work
- Patterns to avoid
- Environmental quirks
Acknowledge context Present a brief summary:
## Context Loaded ### From Previous Session [Key points from latest.md] ### Known Failed Approaches [List from known-issues.md] ### Starting Fresh Investigation
Phase 2: Get Current CI Status
Fetch current CI check status
gh pr checks [PR_NUMBER]For each failing check, get full logs
gh pr checks [PR_NUMBER] --watch # OR gh api repos/{owner}/{repo}/commits/{sha}/check-runsCreate TodoWrite checklist
- [ ] Fix lint failures - [ ] Fix TypeScript errors - [ ] Fix test failures - [ ] Fix build errors
Phase 3: Cross-Reference Against Known Issues
For each failure:
Check if this exact error was seen before
- Search known-issues.md for error message
- Search latest.md for previous attempts
If found in known-issues.md
⚠️ Known Issue Detected Failure: [error message] Previously Tried: [approach from known-issues.md] Result: Failed Skipping this approach. Trying alternative: [different approach]If NOT found
New failure detected: [error message] No prior attempts recorded. Proceeding with standard fix approach.
Phase 4: Fix Failures (Priority Order)
Fix in this priority order:
- Build failures first (they may cascade to other failures)
- Lint failures second (fix all in one pass)
- Test failures third (individual tests, one at a time)
- Type errors fourth (often caused by other fixes)
For Each Failure Type:
Lint Failures
# Get all lint errors
npm run lint 2>&1 | tee lint-errors.txt
# Apply fixes
npm run lint:fix
npm run lint:css:fix
# Verify
npm run lint && npm run lint:css
Update checklist: Mark lint item as done if passed
Test Failures
# Get all failing tests
npm test -- --listTests --json 2>&1 | tee test-failures.txt
# Fix one test at a time
npm test -- [specific-test-file]
# Apply 3-attempt limit per test (use test-fix principles)
Update checklist: Mark each test as done when fixed
Build Failures
# Get build errors
npm run build 2>&1 | tee build-errors.txt
# Fix errors
[apply fixes]
# Verify
npm run build
Update checklist: Mark build as done if passed
Phase 5: Dev Server Protection
Critical checks before running tests:
# Check if dev server is running
DEV_SERVER_PID=$(lsof -ti:3000)
if [ -n "$DEV_SERVER_PID" ]; then
echo "✅ Dev server running (PID: $DEV_SERVER_PID)"
echo "Tests will use existing server"
else
echo "⚠️ No dev server found"
echo "Ask user: Should I start dev server?"
fi
Never:
- Kill the dev server
- Restart the dev server
- Run commands that interfere with port 3000
Phase 6: Commit Safety
Before committing, verify:
# Check for merge conflict markers
git diff --check
# Check for common mistakes
grep -r "<<<<<<< HEAD" src/ && echo "❌ Merge conflicts found" || echo "✅ No merge conflicts"
grep -r "console.log" src/ && echo "⚠️ Debug code found" || echo "✅ No debug code"
grep -r "debugger" src/ && echo "⚠️ Debugger statements found" || echo "✅ No debugger"
If all checks pass:
git add -u
git commit -m "fix: resolve CI failures
[List of what was fixed]
Applied via ci-fix-with-memory skill"
git push origin [BRANCH]
Phase 7: Update Memory Files
Update .handoff/latest.md
# Session Handoff: [DATE] ## PR Context - PR #[NUMBER]: [TITLE] - Branch: [BRANCH_NAME] - Status: [CI status after fixes] ## What Was Done - [List of fixes applied] - [Commands run] - [Tests that passed/failed] ## What Worked - [Successful approaches] ## What Didn't Work - [Failed approaches - add to known-issues.md] ## Current State - Lint: [PASS/FAIL] - Tests: [PASS/FAIL] - Build: [PASS/FAIL] - Type Check: [PASS/FAIL] ## Open Questions - [Any blockers or unclear issues] ## Next Steps - [What should happen next]Update .handoff/known-issues.md
# Known Issues Log ## [DATE] - [Issue Category] **Error**: [exact error message] **Attempted Fix**: [what was tried] **Result**: Failed **Why It Failed**: [explanation] **Alternative Approach**: [what to try instead] --- [Previous entries...]
Phase 8: Report Status
Present final summary:
## CI Fix Pipeline Results
### Context Used
- Loaded previous session handoff
- Cross-referenced [N] known failed approaches
- Avoided repeating [specific approaches]
### Fixes Applied
✅ [List of successful fixes]
❌ [List of failed fixes with reasons]
⏭️ [List of skipped issues]
### Final CI Status
- **Lint**: [PASS/FAIL]
- **Type Check**: [PASS/FAIL]
- **Tests**: [PASS/FAIL - with count]
- **Build**: [PASS/FAIL]
### Memory Updated
- Updated .handoff/latest.md with session details
- Added [N] new entries to .handoff/known-issues.md
### Next Steps
[What user should do next]
### Handoff for Next Session
[Key context to carry forward]
Example Known Issues Log Entry
## 2025-01-15 - E2E Test Timeout
**Error**: `Timeout waiting for element: [data-testid="submit-button"]`
**Attempted Fix**: Added `page.waitForSelector('[data-testid="submit-button"]', { timeout: 10000 })`
**Result**: Failed - timeout still occurred
**Why It Failed**: Button exists but is covered by a loading overlay during test
**Alternative Approach**: Wait for overlay to disappear before clicking
**Reference**: See `.handoff/latest.md` from 2025-01-15 session
---
## 2025-01-12 - Lint Rule Conflict
**Error**: `Unexpected var, use let or const instead (no-var)`
**Attempted Fix**: Ran `eslint --fix` to auto-convert var to let
**Result**: Failed - introduced scope bugs in loop
**Why It Failed**: var has function scope, let has block scope - not always safe to auto-convert
**Alternative Approach**: Manually review each var and decide let vs const based on reassignment
---
Example Handoff Entry
# Session Handoff: 2025-01-15 14:30
## PR Context
- PR #89: Fix async form submission race condition
- Branch: `fix/form-submit-timing`
- Status: CI failing (2 of 4 checks)
## What Was Done
- Fixed lint errors in `SubmitButton.tsx` and `FormContext.tsx`
- Attempted to fix E2E test `form-submit.spec.ts` (3 attempts, failed)
- Updated TypeScript types for form state
## What Worked
- Lint fixes: Changed `var` to `const` manually (not auto-fix)
- Type fixes: Added proper typing for `FormState` interface
## What Didn't Work
- E2E test still timing out on submit button click
- Root cause: Loading overlay covers button before it becomes interactive
- Tried: Waiting for selector (failed), adding explicit delay (failed), clicking with force (failed)
## Current State
- Lint: PASS
- Tests: FAIL (1 test: form-submit.spec.ts)
- Build: PASS
- Type Check: PASS
## Open Questions
- Should we dismiss the overlay programmatically in test setup?
- Is this a timing issue in the component or the test?
## Next Steps
- May need browser DevTools inspection to confirm overlay z-index
- Consider checking if overlay has a data-testid we can wait on
Success Metrics
Track these outcomes:
- Number of repeat fix attempts avoided
- Time saved by checking known-issues log first
- How many handoffs actually helped next session
- Patterns discovered through known-issues tracking
Notes
- Addresses context loss between sessions (cached state, unknown file locations)
- Prevents "merge-conflict-markers-left-in-files" class of errors
- Known-issues log becomes useful project documentation over time