Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/self-heal-schedule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
schedule: "0 0 * * 1"
rationale: "Dormant tier computed from low commit volume."
last_updated: "2024-05-20T00:00:00.000Z"
68 changes: 68 additions & 0 deletions .github/workflows/compute-schedule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Compute Self-Heal Schedule

on:
schedule:
- cron: "0 1 * * 0" # Runs weekly to evaluate if schedule needs to change
workflow_dispatch:

concurrency:
group: compute-schedule-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write
pull-requests: write

jobs:
compute-schedule:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: npm ci

- name: Compute New Schedule
run: node scripts/compute_schedule.mjs

- name: Check for Schedule Changes
id: diff
run: |
if [ -n "$(git status --porcelain .github/self-heal-schedule.yml .github/workflows/self-heal.yml)" ]; then
echo "has_diff=true" >> $GITHUB_OUTPUT
else
echo "has_diff=false" >> $GITHUB_OUTPUT
fi

- name: Create PR for Schedule Change
if: steps.diff.outputs.has_diff == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Avoid duplicates
if gh pr list --label "self-heal-schedule" --state open --json headRefName | grep -q "schedule-update-"; then
echo "A schedule update PR is already open. Skipping."
exit 0
fi

BRANCH_NAME="schedule-update-$(date +%s)"
git checkout -b "$BRANCH_NAME"

git add .github/self-heal-schedule.yml .github/workflows/self-heal.yml
git commit -m "chore: update self-heal cadence"
git push origin "$BRANCH_NAME"

gh pr create \
--title "[Self-Heal Schedule] Update cadence" \
--body "Automated PR to update the self-healing pipeline schedule based on recent repository telemetry." \
--label "self-heal-schedule" \
--label "automation"
117 changes: 117 additions & 0 deletions .github/workflows/self-heal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: Self-Heal Pipeline

on:
schedule:
- cron: "0 0 * * 1" # AUTO-UPDATED
workflow_run:
workflows: ["ci"]
types: [completed]
workflow_dispatch:

concurrency:
group: selfheal-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write
pull-requests: write
actions: read

jobs:
repair:
# Only run if not on a selfheal branch to prevent loops
# AND if manually triggered, OR schedule on default branch, OR CI failed
if: |
!startsWith(github.ref_name, 'selfheal-') && (
github.event_name == 'workflow_dispatch' ||
github.event_name == 'schedule' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure')
)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Initial Setup (Dependencies)
run: npm ci

- name: Pre-Healthcheck (Informational)
id: pre
run: node scripts/healthcheck.mjs || echo "Pre-healthcheck failed, this is expected if we need repair."

- name: Run Self-Heal Repair
id: repair
run: |
node scripts/self_heal.mjs || exit_code=$?
if [ "${exit_code}" != "0" ]; then
echo "Repair script failed or couldn't completely fix issues."
exit 1
fi

- name: Post-Healthcheck (Gate)
id: post
run: node scripts/healthcheck.mjs

- name: Check for Diff
id: diff
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "has_diff=true" >> $GITHUB_OUTPUT
else
echo "has_diff=false" >> $GITHUB_OUTPUT
fi

- name: Scan for Secrets
if: steps.post.outcome == 'success' && steps.diff.outputs.has_diff == 'true'
run: |
git add -A 2>/dev/null || true
if git diff --staged -G'(?i)(api[_-]?key|secret|token|password)' --name-only | grep -q .; then
echo "Potential secrets found in diff. Aborting self-heal."
exit 1
fi
git reset HEAD

- name: Create PR
if: steps.post.outcome == 'success' && steps.diff.outputs.has_diff == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Avoid duplicates
if gh pr list --label "self-heal" --state open --json headRefName | grep -q "selfheal-"; then
echo "A self-heal PR is already open. Skipping."
exit 0
fi

BRANCH_NAME="selfheal-$(date +%s)"
git checkout -b "$BRANCH_NAME"

# Only stage allowed directories
for path in src tests scripts package.json package-lock.json; do
git add "$path" 2>/dev/null || true
done

git commit -m "chore: automated self-heal repairs"
git push origin "$BRANCH_NAME"

TITLE="[Self-Heal] Auto-repair workflow"
if [ "${{ github.event_name }}" == "schedule" ]; then
TITLE="[Self-Heal Scheduled] Drift fixes"
elif [ "${{ github.event_name }}" == "workflow_run" ]; then
TITLE="[Self-Heal Reactive] CI fix"
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
TITLE="[Self-Heal Manual] Repair"
fi

gh pr create \
--title "$TITLE" \
--body "Automated self-healing pipeline detected and repaired issues. Trigger: ${{ github.event_name }}." \
--label "self-heal" \
--label "automation"
38 changes: 38 additions & 0 deletions SELF_HEAL_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Self-Heal Coding Agent Workflow

This repository includes an automated self-healing CI pipeline configured via GitHub Actions. It is designed to automatically repair code drift, fix linting and formatting issues, update test snapshots, and correct dependency resolution issues without requiring a human intervention for routine maintenance.

## Architecture

1. **`scripts/healthcheck.mjs`**: The foundational gate. Verifies if the system passes types, tests, and builds successfully.
2. **`scripts/self_heal.mjs`**: The repair automation script. Executes a 6-step idempotent repair process (Install -> Format -> Snapshots -> Types -> Deps -> Assets), checking health between each step.
3. **`scripts/compute_schedule.mjs`**: The self-scheduling automation. Gathers repository telemetry (commit history) over a 30-day window to compute an optimal repair cadence.
4. **`.github/workflows/self-heal.yml`**: The main workflow. Triggers the repair pipeline based on the computed schedule, any CI failure (`workflow_run`), or manual dispatch. Safely creates a Pull Request with the repairs.
5. **`.github/workflows/compute-schedule.yml`**: A periodic workflow that re-computes the optimal self-heal schedule using telemetry.
6. **`.github/self-heal-schedule.yml`**: Contains metadata about the current schedule configuration.

## Repair Pipeline Steps

The `scripts/self_heal.mjs` script performs the following idempotent steps:
1. **Rebuild/reinstall**: Cleans and installs tooling and dependencies (`npm ci`).
2. **Lint/format auto-fix**: Attempts to run ESLint with autofix and Prettier (`npx eslint --fix` and `npx prettier -w`).
3. **Snapshot/generated updates**: Updates Vitest snapshots (`npx vitest run -u`).
4. **Type stubs/analyzer config**: Installs or regenerates necessary types (`npm install`).
5. **Dependency re-resolve**: Resolves dependency updates (`npm update`).
6. **Static asset regeneration**: Runs the build command to generate assets (`npm run build`).

## Self-Scheduling

The scheduling process is completely autonomous:
- Commit volume dictates the cadence. High churn leads to frequent checks, low churn leads to infrequent checks (up to weekly).
- If you need to manually override the schedule, you can safely modify `.github/self-heal-schedule.yml`.

## Manual Override / Troubleshooting
- **To manually trigger self-heal**: Navigate to the Actions tab on GitHub, select "Self-Heal Pipeline", and click "Run workflow".
- **If self-heal keeps failing**: Review the generated PR and `self-heal` logs. The pipeline gracefully exits if it cannot fully fix an issue.

## Reviewer Checklist
When reviewing a self-heal PR:
- [ ] Ensure the diff does not contain any leaked secrets or environment variables.
- [ ] Review any snapshot updates to verify they reflect expected changes.
- [ ] Confirm no substantive business logic has been inadvertently altered.
Loading