Direct answer
A self-healing CI infrastructure needs, for each common operational problem (stuck jobs, exhausted nodes, failed image pulls, flaky network), a specific detection signal and a specific automated remediation action, plus a rate limit on how aggressively it retries remediation and a clear point where it stops trying and hands off to a human with real context rather than looping forever.
Structured elaboration
Stuck jobs. Detection: a job that's been running significantly longer than its historical typical duration for that job type, with no recent log output (a heartbeat or liveness signal from the job itself going stale). Remediation: terminate and restart the job (if it's safely retryable) or, if it's not, flag it for human investigation rather than blindly restarting something that might not be idempotent.
Resource-exhausted nodes. Detection: a node reporting sustained high memory or disk pressure, or the container runtime failing to schedule new work on it. Remediation: cordon the node (stop scheduling new work there), drain existing work off it if possible, and either reprovision it fresh or remove it from the pool, rather than letting jobs keep landing on a node that's already struggling and likely to fail them.
Failed image pulls. Detection: a job failing specifically at the image-pull step, distinguishable from a build or test failure by the specific error signature. Remediation: retry against a mirror or secondary registry if one exists (tying back to the resilience-architecture discussion), or retry the pull itself a bounded number of times if it looks like a transient registry blip, distinguishing that from a genuinely missing or misnamed image, which retrying won't fix no matter how many times you try.
Flaky network. Detection: elevated error rates or latency on network calls between specific components (agent-to-controller, agent-to-registry), correlated by source to distinguish a broad platform issue from one specific problematic agent or network path. Remediation: for a single flaky agent, remove it from the pool and replace it; for a broader pattern, this is a signal for human investigation rather than automated remediation, since a widespread network issue is more likely to need actual infrastructure intervention than a scripted fix.
Rate limits to avoid thrash. Every remediation action needs a rate limit (don't reprovision a node more than N times in an hour, don't restart a given job more than a small, bounded number of times) so a persistent underlying problem doesn't cause the self-healing system to loop indefinitely, burning capacity and potentially masking the real issue behind an endless cycle of automated 'fixes' that never actually address the root cause.
Escalation. Once a rate limit is hit, or a problem's detection signal doesn't map cleanly to a known remediation (an error pattern the system hasn't seen before), the system should stop attempting automated fixes and escalate to a human with the diagnostic context already gathered (what was tried, what the detection signals showed), via a clear playbook, rather than silently giving up or continuing to loop.
Worked example
A build agent starts failing every job with an image-pull timeout. The self-healing system detects the specific error signature, retries against a secondary registry mirror once, and succeeds, logging the incident but not paging anyone since it self-resolved within its rate limit. An hour later, a different agent shows sustained high memory pressure; the system cordons it, drains its current job onto other capacity, and reprovisions a fresh replacement node, again within normal operation. When a third, unrelated agent hits the same reprovisioning remediation for the fourth time within an hour (exceeding its rate limit), the system stops attempting further automated fixes for that node, pages the on-call engineer with a summary of what was detected and what remediation was already attempted, and leaves the node cordoned (not scheduling new work on it) pending human investigation.
Trade-offs and pitfalls
The most common mistake is remediation without a rate limit, which for a persistent underlying problem (a genuinely bad node, a real network issue) produces an endless, resource-wasting loop of automated 'fixes' that never resolve anything and can actively mask the real problem from human attention for longer than if it had simply failed loudly the first time. The second is blindly restarting a stuck job without first confirming it's safely retryable, which for a job with a non-idempotent side effect can turn a stuck-job problem into a duplicated-side-effect problem, a strictly worse outcome than leaving it stuck for a human to investigate.