[Self-Heal] Add self-scheduling auto-repair workflow#61
Conversation
Implements an adaptive self-healing CI pipeline that: - Proactively checks for codebase drift on a schedule - Reactively triggers on CI failures - Includes an idempotent 6-step repair process (deps, lint, snapshots, etc.) - Dynamically recalculates its own schedule based on PR telemetry - Strictly gates repairs to ensure no logic is changed and secrets are protected Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Code Review
This pull request introduces an adaptive, automated self-healing pipeline to maintain codebase health, including scripts for computing schedules, running health checks, and executing repair steps, alongside ESLint and Prettier configurations. The review feedback highlights several critical issues: hasDiff() in the self-heal script incorrectly detects its own log file as a change, ESLint contains a non-existent rule (preserve-caught-error) that will cause it to crash, and the schedule computation script needs better error handling for telemetry failures and should avoid writing to the configuration file when the schedule remains unchanged to prevent unnecessary dirty git states.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| function hasDiff() { | ||
| const status = execSync('git status --porcelain').toString().trim(); | ||
| return status !== ''; | ||
| } |
There was a problem hiding this comment.
Since the script writes logs to repair.log in the workspace, git status --porcelain will always detect repair.log as an untracked/modified file. This causes hasDiff() to always return true, making the script exit early with a 'success' status after the very first step (npm ci), even if no actual repairs or changes were made to the codebase. We should filter out .log files when checking for git diffs.
| function hasDiff() { | |
| const status = execSync('git status --porcelain').toString().trim(); | |
| return status !== ''; | |
| } | |
| function hasDiff() { | |
| const status = execSync('git status --porcelain').toString().trim(); | |
| if (!status) return false; | |
| const lines = status.split('\n').filter(line => { | |
| const file = line.slice(3).trim(); | |
| return !file.endsWith('.log'); | |
| }); | |
| return lines.length > 0; | |
| } |
| "prefer-const": "off", | ||
| "preserve-caught-error": "off" |
There was a problem hiding this comment.
| let prs = []; | ||
| try { | ||
| const output = execSync('gh pr list --state merged --json mergedAt --limit 100').toString(); | ||
| prs = JSON.parse(output); | ||
| } catch (e) { | ||
| console.warn('Failed to fetch PR telemetry:', e.message); | ||
| } |
There was a problem hiding this comment.
If the gh pr list command fails (e.g., due to rate limits, network issues, or missing authentication in CI), the prs array will remain empty. This will silently set recentPrs to 0 and downgrade the schedule to the 'Dormant tier (Weekly)' even if the repository is highly active. We should guard against this by skipping the schedule update if the telemetry fetch fails.
| let prs = []; | |
| try { | |
| const output = execSync('gh pr list --state merged --json mergedAt --limit 100').toString(); | |
| prs = JSON.parse(output); | |
| } catch (e) { | |
| console.warn('Failed to fetch PR telemetry:', e.message); | |
| } | |
| let prs = []; | |
| let telemetrySuccess = false; | |
| try { | |
| const output = execSync('gh pr list --state merged --json mergedAt --limit 100').toString(); | |
| prs = JSON.parse(output); | |
| telemetrySuccess = true; | |
| } catch (e) { | |
| console.warn('Failed to fetch PR telemetry:', e.message); | |
| } | |
| if (!telemetrySuccess) { | |
| console.log('Skipping schedule update due to telemetry fetch failure.'); | |
| process.exit(0); | |
| } |
| const steps = [ | ||
| { name: 'Rebuild/Reinstall', cmd: 'npm ci' }, | ||
| { name: 'Lint/Format Auto-fix', cmd: 'npx eslint . --fix && npx prettier -w .' }, | ||
| { name: 'Snapshot Regeneration', cmd: 'npx vitest run -u --passWithNoTests' }, | ||
| { name: 'Type Stubs', cmd: 'npx typesync || true' }, | ||
| { name: 'Dependency Re-resolve', cmd: 'npm update' }, | ||
| { name: 'Static Assets', cmd: 'npm run build' } | ||
| ]; |
There was a problem hiding this comment.
There are two main issues with the current repair steps:
npx eslint . --fix && npx prettier -w .uses&&. If ESLint fails (e.g., due to unfixable syntax/lint errors), Prettier formatting will be skipped entirely. Splitting them into separate steps ensures both run independently.npx typesynconly modifiespackage.jsonbut does not install the newly added types. Runningnpm installimmediately after is necessary so that the types are installed andpackage-lock.jsonis updated before the healthcheck runs.
| const steps = [ | |
| { name: 'Rebuild/Reinstall', cmd: 'npm ci' }, | |
| { name: 'Lint/Format Auto-fix', cmd: 'npx eslint . --fix && npx prettier -w .' }, | |
| { name: 'Snapshot Regeneration', cmd: 'npx vitest run -u --passWithNoTests' }, | |
| { name: 'Type Stubs', cmd: 'npx typesync || true' }, | |
| { name: 'Dependency Re-resolve', cmd: 'npm update' }, | |
| { name: 'Static Assets', cmd: 'npm run build' } | |
| ]; | |
| const steps = [ | |
| { name: 'Rebuild/Reinstall', cmd: 'npm ci' }, | |
| { name: 'Lint Auto-fix', cmd: 'npx eslint . --fix || true' }, | |
| { name: 'Format Auto-fix', cmd: 'npx prettier -w .' }, | |
| { name: 'Snapshot Regeneration', cmd: 'npx vitest run -u --passWithNoTests' }, | |
| { name: 'Type Stubs', cmd: 'npx typesync && npm install || npm install' }, | |
| { name: 'Dependency Re-resolve', cmd: 'npm update' }, | |
| { name: 'Static Assets', cmd: 'npm run build' } | |
| ]; |
| if (newSchedule === currentConfig.schedule) { | ||
| console.log('Schedule unchanged based on telemetry. No update needed.'); | ||
| // We still update the lastUpdated timestamp so we don't keep checking | ||
| const newConfig = { | ||
| ...currentConfig, | ||
| lastUpdated: now | ||
| }; | ||
| const yamlOutput = yaml.dump(newConfig, { forceQuotes: true }); | ||
| fs.writeFileSync(SCHEDULE_FILE, yamlOutput); | ||
| process.exit(0); | ||
| } |
There was a problem hiding this comment.
When the computed schedule is unchanged, updating the lastUpdated timestamp and writing it back to .github/self-heal-schedule.yml will create a dirty git state. This will cause the CI workflow to commit the change and open a new PR every few days just to update a timestamp, creating unnecessary noise for maintainers. If the schedule is unchanged, we should simply exit without writing the file.
if (newSchedule === currentConfig.schedule) {
console.log('Schedule unchanged based on telemetry. No update needed.');
process.exit(0);
}
This PR introduces an automated, self-adapting "self-healing" CI pipeline for maintaining code health, resolving drift, and handling routine fixes like dependency resolution and linting.
Key Components:
scripts/self_heal.mjs): Reinstalls deps, formats/lints, regenerates snapshots, fetches types, updates deps, and rebuilds assets.scripts/compute_schedule.mjs): A weekly workflow recalculates the frequency of the self-heal schedule based on repository PR velocity, saving CI time during dormant periods.See
SELF_HEAL_SETUP.mdfor detailed reviewer checklists and manual override instructions.Generated by Jules via Claude Code
PR created automatically by Jules for task 11992912434856171913 started by @badMade