Skip to content

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

Draft
badMade wants to merge 1 commit into
mainfrom
feat/self-healing-pipeline-13008234447552265971
Draft

[Self-Heal] Add self-scheduling auto-repair workflow#50
badMade wants to merge 1 commit into
mainfrom
feat/self-healing-pipeline-13008234447552265971

Conversation

@badMade

@badMade badMade commented May 27, 2026

Copy link
Copy Markdown
Owner

Adds an adaptive, self-healing continuous integration pipeline.

It introduces two GitHub Actions workflows (self-heal.yml and compute-schedule.yml) with reactive, scheduled, and manual triggers. Telemetry logic allows the proactive scheduler to analyze repository activity (commit/PR velocity) and calculate optimal cadence dynamically.

The idempotent repair pipeline addresses lockfile updates, stubs acquisition, lint fixes, and test snapshot regeneration while safeguarding against secrets commits and infinite automated PR loops. Detailed configurations, overrides, and instructions are provided in SELF_HEAL_SETUP.md.


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

Implemented a comprehensive self-healing CI pipeline using GitHub Actions to automatically detect drift, recalculate scheduling based on telemetry, and perform idempotent repairs (formatting, snapshots, type stubs, and dependencies) safely.

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-healing continuous integration and drift repair pipeline, adding scripts to compute a dynamic schedule based on repository telemetry, perform health checks, and execute automated repair steps. It also adds ESLint, Prettier, and TypeScript-ESLint to the project's devDependencies. The review comments point out several critical improvements: warning about shallow clones affecting git telemetry, ignoring untracked files in git status checks to prevent false positives, handling null values when parsing YAML, validating that the cron placeholder replacement actually occurred, adding the missing typesync package to package.json to avoid slow runtime downloads, and optimizing the repair loop by skipping health checks if a repair command fails.

Comment on lines +38 to +40
// Commit count
const commitsStr = execSync(`git rev-list --count HEAD --since="${sinceDate}"`, { encoding: 'utf-8' });
commitCount = parseInt(commitsStr.trim(), 10) || 0;

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

In GitHub Actions, the default checkout action performs a shallow clone (fetch-depth: 1). Running git rev-list --count HEAD --since="..." on a shallow clone will only count the single checked-out commit, leading to inaccurate telemetry. This will cause the script to incorrectly assume the repository is dormant and scale back the schedule to weekly.

Consider checking if the repository is shallow and logging a warning, or fetching the history if needed. At a minimum, we should detect and warn the user so they know to configure fetch-depth: 0 in their workflow.

    // Commit count (warn if shallow clone)
    let isShallow = false;
    try {
      isShallow = execSync('git rev-parse --is-shallow-repository', { encoding: 'utf-8' }).trim() === 'true';
    } catch (e) {}
    if (isShallow) {
      console.warn('Warning: Shallow clone detected. Telemetry (commit count) will be inaccurate. Consider checking out with fetch-depth: 0.');
    }

    const commitsStr = execSync("git rev-list --count HEAD --since=\"" + sinceDate + "\"", { encoding: 'utf-8' });
    commitCount = parseInt(commitsStr.trim(), 10) || 0;

Comment thread scripts/self_heal.mjs
Comment on lines +24 to +30
let hasDiff = false;
try {
const diff = execSync('git status --porcelain', { encoding: 'utf-8' });
if (diff.trim().length > 0) {
hasDiff = true;
}
} catch (err) {}

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

git status --porcelain includes untracked files (prefixed with ??). If any untracked files (such as build artifacts, logs, or temporary files) are generated during the build or test steps and are not ignored in .gitignore, hasDiff will falsely evaluate to true. This will cause the script to prematurely exit with success and potentially create a PR with no actual tracked changes or with unwanted untracked files.

Using the -uno flag with git status --porcelain ignores untracked files, ensuring we only detect changes to tracked files.

Suggested change
let hasDiff = false;
try {
const diff = execSync('git status --porcelain', { encoding: 'utf-8' });
if (diff.trim().length > 0) {
hasDiff = true;
}
} catch (err) {}
let hasDiff = false;
try {
const diff = execSync('git status --porcelain -uno', { encoding: 'utf-8' });
if (diff.trim().length > 0) {
hasDiff = true;
}
} catch (err) {}

Comment on lines +11 to +14
let currentScheduleData;
try {
const rawYaml = await fs.readFile(SCHEDULE_FILE, 'utf8');
currentScheduleData = yaml.load(rawYaml);

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 schedule file is empty or invalid, yaml.load can return null or undefined. This will cause subsequent property accesses (like currentScheduleData.LAST_UPDATED or currentScheduleData.SCHEDULE) to throw a TypeError and crash the script. Enforce defensive programming by providing a fallback empty object.

Suggested change
let currentScheduleData;
try {
const rawYaml = await fs.readFile(SCHEDULE_FILE, 'utf8');
currentScheduleData = yaml.load(rawYaml);
let currentScheduleData;
try {
const rawYaml = await fs.readFile(SCHEDULE_FILE, 'utf8');
currentScheduleData = yaml.load(rawYaml) || {};

Comment on lines +88 to +91
const updatedWorkflow = workflowContent.replace(
/- cron: ".*" # AUTO-UPDATED/,
`- cron: "${newCron}" # AUTO-UPDATED`
);

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 placeholder - cron: "..." # AUTO-UPDATED is missing or malformed in the workflow file, workflowContent.replace will silently do nothing. The script will then write the unmodified content back to the file, leading to a silent failure where the schedule tracker is updated but the actual GitHub Actions workflow schedule is not.

We should verify that the replacement actually occurred and throw an error if the placeholder was not found.

  const updatedWorkflow = workflowContent.replace(
    /- cron: ".*" # AUTO-UPDATED/,
    '- cron: "' + newCron + '" # AUTO-UPDATED'
  );

  if (updatedWorkflow === workflowContent) {
    console.error('Error: Could not find the cron placeholder \'- cron: "..." # AUTO-UPDATED\' in ' + WORKFLOW_FILE + '.');
    process.exit(1);
  }

Comment thread package.json
Comment on lines 33 to +35
"devDependencies": {
"@anthropic-ai/sdk": "^0.33.1",
"@eslint/js": "^10.0.1",

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

typesync is executed via npx typesync in scripts/self_heal.mjs, but it is not listed in the devDependencies of package.json. This forces npx to download and install typesync from the npm registry on every single run of the self-healing script, which is slow, inefficient, and susceptible to network or registry failures.

Please add typesync to the devDependencies in package.json to ensure it is installed locally and runs deterministically.

Suggested change
"devDependencies": {
"@anthropic-ai/sdk": "^0.33.1",
"@eslint/js": "^10.0.1",
"devDependencies": {
"@anthropic-ai/sdk": "^0.33.1",
"@eslint/js": "^10.0.1",
"typesync": "^0.11.1",

Comment thread scripts/self_heal.mjs
Comment on lines +52 to +56
for (const step of repairSteps) {
console.log(`\n--- Attempting: ${step.name} ---`);
runCmd(step.cmd);

const { passed, hasDiff } = checkHealthAndDiff();

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 return value of runCmd(step.cmd) is currently ignored. If a critical repair step fails (for example, npm ci fails due to network issues or lockfile conflicts), the script will still proceed to run the heavy checkHealthAndDiff() function. This runs the full build, lint, and test suites, which will almost certainly fail, wasting significant CI execution time and cluttering the logs with redundant errors.

We should check the return value of runCmd and skip the healthcheck/diff check if the command itself failed.

for (const step of repairSteps) {
  console.log("\n--- Attempting: " + step.name + " ---");
  if (!runCmd(step.cmd)) {
    console.log("Step " + step.name + " failed. Skipping healthcheck.");
    continue;
  }

  const { passed, hasDiff } = checkHealthAndDiff();

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