Skip to content

[Self-Heal] Add self-scheduling auto-repair workflow#60

Draft
badMade wants to merge 1 commit into
mainfrom
selfheal-workflow-11919046782672604664
Draft

[Self-Heal] Add self-scheduling auto-repair workflow#60
badMade wants to merge 1 commit into
mainfrom
selfheal-workflow-11919046782672604664

Conversation

@badMade

@badMade badMade commented Jun 7, 2026

Copy link
Copy Markdown
Owner

[Self-Heal] Add self-scheduling auto-repair workflow

This PR introduces an automated, self-adapting repair pipeline to continuously detect and resolve code drift (e.g., formatting, outdated snapshots, or missing type stubs).

Triggers

  • Scheduled: Runs proactively based on an adaptive schedule.
  • Reactive: Automatically triggers when the main ci workflow fails.
  • Manual: Can be dispatched directly via GitHub Actions.

Self-Scheduling & Initial Telemetry

The pipeline adjusts its scheduled frequency based on project velocity (analyzed via commit frequency).

  • Initial Schedule: 0 0 * * 0
  • Rationale: Low velocity (<= 5 commits in the last 7 days) -> runs weekly.

To manually override this schedule, modify .github/self-heal-schedule.yml.

Reviewer Checklist

When reviewing future self-healing PRs, always check:

  • The Pre-check and Post-check artifacts attached to the workflow run.
  • The resulting diff involves only automated safe-fixes (formatters, dependencies, snapshots).
  • The healthcheck explicitly passes.

For more details on the setup, view the SELF_HEAL_SETUP.md documentation included in this commit.


PR created automatically by Jules for task 11919046782672604664 started by @badMade

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown

👋 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 @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a self-adapting, self-healing automated repair pipeline, adding setup documentation, ESLint configuration, and scripts to compute schedules, run healthchecks, and execute repair steps. Key feedback includes resolving a fatal ESLint error caused by the invalid preserve-caught-error rule, and ensuring self_heal.mjs exits with code 0 instead of 1 when the initial healthcheck passes to prevent workflow failures. Additionally, it is recommended to split the chained lint and format commands, add defensive checks for empty or invalid YAML configurations, verify successful cron schedule updates, and remove a redundant TypeScript compilation step to optimize execution speed.

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.

Comment thread eslint.config.mjs
Comment on lines +24 to +25
'prefer-const': 'warn',
'preserve-caught-error': 'warn'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The rule preserve-caught-error is not a valid core ESLint rule. Configuring a non-existent rule without a plugin prefix will cause ESLint to throw a fatal error and fail to run, which will break the healthcheck and self-heal scripts.

      'prefer-const': 'warn'

Comment thread scripts/self_heal.mjs
Comment on lines +37 to +40
if (checkHealth()) {
console.log('✅ Initial healthcheck passed. No repairs needed.');
process.exit(1); // Exit 1 to prevent PR creation if no repairs are needed.
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Exiting with code 1 when the initial healthcheck passes will cause the GitHub Actions workflow run to be marked as failed. Since a healthy repository is a successful state, the script should exit with 0. The subsequent PR creation step in the workflow will naturally skip creating a PR if there are no git changes.

Suggested change
if (checkHealth()) {
console.log('✅ Initial healthcheck passed. No repairs needed.');
process.exit(1); // Exit 1 to prevent PR creation if no repairs are needed.
}
if (checkHealth()) {
console.log('✅ Initial healthcheck passed. No repairs needed.');
process.exit(0);
}

Comment on lines +18 to +24
if (existsSync(SCHEDULE_FILE)) {
try {
currentConfig = yaml.load(readFileSync(SCHEDULE_FILE, 'utf8'));
} catch (e) {
console.warn('Could not parse existing schedule file, using defaults.');
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the .github/self-heal-schedule.yml file is empty or invalid, yaml.load can return null or undefined. Accessing properties on it later (e.g., currentConfig.last_updated) will throw a TypeError and crash the script. It is safer to defensively verify that the parsed result is an object before merging it.

if (existsSync(SCHEDULE_FILE)) {
  try {
    const parsed = yaml.load(readFileSync(SCHEDULE_FILE, 'utf8'));
    if (parsed && typeof parsed === 'object') {
      currentConfig = Object.assign({}, currentConfig, parsed);
    }
  } catch (e) {
    console.warn('Could not parse existing schedule file, using defaults.');
  }
}

Comment on lines +83 to +90
let content = readFileSync(WORKFLOW_FILE, 'utf8');
// Regex matches the cron line with the inline marker
content = content.replace(
/cron:\s*['"][^'"]+['"]\s*#\s*AUTO-UPDATED/,
`cron: "${newSchedule}" # AUTO-UPDATED`
);
writeFileSync(WORKFLOW_FILE, content, 'utf8');
console.log('Successfully updated self-heal.yml workflow.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the regex replacement does not find a match (for example, if the # AUTO-UPDATED marker is missing or formatted differently), content.replace will silently do nothing, and the script will write the unmodified content back while still logging a success message. Adding a check to verify if the content actually changed provides better visibility and prevents silent failures.

    let content = readFileSync(WORKFLOW_FILE, 'utf8');
    const updatedContent = content.replace(
      /cron:\s*['"][^'"]+['"]\s*#\s*AUTO-UPDATED/,
      'cron: "' + newSchedule + '" # AUTO-UPDATED'
    );
    if (content === updatedContent) {
      console.warn('Warning: Could not find a matching cron schedule with the "# AUTO-UPDATED" marker in self-heal.yml.');
    } else {
      writeFileSync(WORKFLOW_FILE, updatedContent, 'utf8');
      console.log('Successfully updated self-heal.yml workflow.');
    }

Comment thread scripts/self_heal.mjs
Comment on lines +42 to +49
const steps = [
{ name: '1. Rebuild/reinstall', cmd: 'npm install' },
{ name: '2. Lint/format auto-fix', cmd: 'npx eslint --fix . && npx prettier --write .' },
{ name: '3. Snapshot/generated updates', cmd: 'npx vitest run -u --passWithNoTests' },
{ name: '4. Type stubs/analyzer config', cmd: 'npx typesync || true' },
{ name: '5. Dependency re-resolve', cmd: 'npm update' },
{ name: '6. Static asset regeneration', cmd: 'npm run build' }
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Chaining the lint and format commands with && means that if ESLint exits with a non-zero code (which happens when there are remaining un-fixable lint errors), the Prettier formatting step will be skipped entirely. Splitting them into separate steps ensures both run independently and are committed/healthchecked properly.

Suggested change
const steps = [
{ name: '1. Rebuild/reinstall', cmd: 'npm install' },
{ name: '2. Lint/format auto-fix', cmd: 'npx eslint --fix . && npx prettier --write .' },
{ name: '3. Snapshot/generated updates', cmd: 'npx vitest run -u --passWithNoTests' },
{ name: '4. Type stubs/analyzer config', cmd: 'npx typesync || true' },
{ name: '5. Dependency re-resolve', cmd: 'npm update' },
{ name: '6. Static asset regeneration', cmd: 'npm run build' }
];
const steps = [
{ name: '1. Rebuild/reinstall', cmd: 'npm install' },
{ name: '2. Lint auto-fix', cmd: 'npx eslint --fix .' },
{ name: '3. Format auto-fix', cmd: 'npx prettier --write .' },
{ name: '4. Snapshot/generated updates', cmd: 'npx vitest run -u --passWithNoTests' },
{ name: '5. Type stubs/analyzer config', cmd: 'npx typesync || true' },
{ name: '6. Dependency re-resolve', cmd: 'npm update' },
{ name: '7. Static asset regeneration', cmd: 'npm run build' }
];

Comment thread scripts/healthcheck.mjs
Comment on lines +23 to +28
const main = () => {
const steps = [
{ name: 'TypeScript Compilation', cmd: 'npx tsc --build' },
{ name: 'Lint', cmd: 'npx eslint .' },
{ name: 'Build', cmd: 'npm run build' }
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The TypeScript Compilation step is redundant because the subsequent Build step (npm run build) already runs tsc -build as part of its script. Removing this redundant step will significantly speed up the healthcheck, which is run multiple times during the self-heal pipeline.

const main = () => {
  const steps = [
    { name: 'Lint', cmd: 'npx eslint .' },
    { name: 'Build', cmd: 'npm run build' }
  ];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant