diff --git a/.learnings/HEALS.md b/.learnings/HEALS.md new file mode 100644 index 0000000..b951a5b --- /dev/null +++ b/.learnings/HEALS.md @@ -0,0 +1,37 @@ +# Self-healing log + +Verified fixes for runtime failures. Each entry documents a failure, its root cause, the applied fix, and the verification proof. + +**Pattern-Key discipline:** before filing a new HEAL, search this file for an existing Pattern-Key. If found, increment `Recurrence-Count` and update `Last-Seen` — do not duplicate. + +**Lifecycle:** verified heals at Recurrence-Count ≥ 3 across distinct tasks get a `Handoff` block for promotion to project memory (`CLAUDE.md`, `AGENTS.md`, or a skill). + +--- + +## [HEAL-YYYYMMDD-XXX] short_kebab_name + +**Logged**: ISO-8601 timestamp +**Status**: pending-verify +**Trigger**: tool-failure | missing-capability | env-issue | external-change | +**Area**: free-form tag (e.g. `build`, `tests`, `ci`, `auth`, `data-pipeline`) +**Priority**: low | medium | high | critical + +### Failure +Concrete error: command, error message, exit code, blocked action. + +### Diagnosis +Root cause as understood after investigation. What was verified during diagnosis. + +### Fix +Patch applied. Verbatim commands, code snippets, or pointers to `.learnings/heals//`. + +### Verification +What was run after the fix and what it returned. Exit code, output snippet, test pass count. **Proof.** + +### Metadata +- Related Files: path/to/file.ext +- See Also: HEAL-... | LRN-... | ERR-... +- Pattern-Key: lower.snake.case (e.g. `env.lockfile_mismatch`) +- Recurrence-Count: 1 +- First-Seen: YYYY-MM-DD +- Last-Seen: YYYY-MM-DD diff --git a/data/skills/boocode/self-healing/SKILL.md b/data/skills/boocode/self-healing/SKILL.md new file mode 100644 index 0000000..8b12e1b --- /dev/null +++ b/data/skills/boocode/self-healing/SKILL.md @@ -0,0 +1,287 @@ +--- +name: self-healing +description: "Active runtime recovery for coding agents: when something breaks mid-task, diagnose the root cause, write a fix, VERIFY by re-running the broken thing, then file a `HEAL-` entry to `.learnings/HEALS.md` with proof. Use whenever a command, test, build, or lint fails or exits non-zero; on missing tooling, dependency/lockfile mismatch, wrong runtime version, venv or permission errors, port conflicts, dirty git state, or a missing `.env`; when the agent needs a helper or one-off script that doesn't exist yet; when an external API, tool, or MCP errors or rate-limits; or when a test flakes. Search `HEALS.md` by `Pattern-Key` first — most heals are recurrences, so increment `Recurrence-Count` instead of duplicating. Verify is mandatory: mark `pending-verify` honestly if sandboxed, `abandoned` if the fix can't be made to work. Pairs with `self-improvement` (which promotes recurring heals to durable memory) but owns the verify-before-persist discipline self-improvement doesn't." +--- + +# Self-Healing + +Active runtime recovery for coding agents. When something breaks, run the loop: **diagnose → patch → verify → file**. Leave behind a reusable, verified artifact instead of a swept-under-the-rug failure. + +The premise mirrors [browser-use/browser-harness](https://github.com/browser-use/browser-harness): *the harness improves itself every run*. An agent that hits a gap doesn't fail — it writes the fix during execution, verifies it works, and files the durable artifact for future runs. Coding tasks deserve the same loop. + +## What this skill is for + +When a coding agent hits a wall mid-task, the default failure modes are: + +1. **Paper over it** — "let me try a different approach" — and lose the recovery +2. **Pretend the fix worked** — without re-running the broken thing +3. **Symptom-fix** — skip the test, swallow the error, retry until green + +All three turn a one-time failure into a recurrence. The next agent on the same project hits the same wall. + +This skill enforces one discipline: **verify before persist**. A patch isn't real until you've re-run the failing operation and watched it succeed. When it does, file the verified fix so the next run benefits. + +## Relationship to self-improvement + +These two skills are deliberately split. Run both — they feed each other but don't overlap. + +| Aspect | `self-healing` (this skill) | `self-improvement` | +| ----------- | -------------------------------------------------------------------- | ------------------------------------------------------------- | +| **When** | During execution, failure is live | After the fact, at natural breakpoints | +| **Verb** | Heal now — restore working state | Remember for later — accumulate knowledge | +| **Outcome** | Verified patch + (optional) reusable artifact | Logged learning, correction, request | +| **Verify** | **Mandatory** — no persist without proof | Not required | +| **Files** | `.learnings/HEALS.md` + `.learnings/heals//` (lazy) | `.learnings/ERRORS.md`, `LEARNINGS.md`, `FEATURE_REQUESTS.md` | +| **Trigger** | Failure observed mid-task | Correction, knowledge gap, feature request, recurrence | + +**Boundary rule:** if you're capturing a fact, a correction, or a wish — that's `self-improvement`. If you're applying and verifying a fix to a live failure — that's `self-healing`. + +## The Heal Loop + +``` + ● failure observed + │ + ● 1. DIAGNOSE capture context — command, error, env, what was attempted + │ search HEALS.md for the same Pattern-Key first + │ (most heals are recurrences; don't reinvent) + │ + ● 2. PATCH write the fix — script, helper, env tweak, alt command + │ artifacts → .learnings/heals// (only if needed) + │ + ● 3. VERIFY re-run the failing op — must succeed + │ ↻ if still failing: refine and retry, cap at 3 attempts + │ ✗ if uncrackable: file Status: abandoned with notes + │ + ● 4. FILE write HEAL-YYYYMMDD-XXX to .learnings/HEALS.md + │ with Pattern-Key, status, verification proof + │ + ✓ working state restored, heal persisted + + (conditional) PROMOTE if Pattern-Key recurrence ≥ 3 across distinct tasks, + append a Handoff block → self-improvement promotes to memory +``` + +If you abandon a heal mid-loop, don't pretend it succeeded. File a `HEAL-` entry with `Status: abandoned` and notes on what didn't work. The next agent learns from the dead end too. + +## When to trigger + +Self-healing fires on **active failures during execution** — the agent has just observed something not working and needs to make it work to continue. Five shapes: + +### 1. Tool failure (command / test / build / lint) +Any invocation exits non-zero or produces wrong output. Don't acknowledge and retry verbatim — diagnose, patch, verify. + +*Examples:* `npm install` errors when a `pnpm-lock.yaml` is present (switch tool); `pytest` fails with `ModuleNotFoundError` (activate the venv); `tsc` flags a stale type (regenerate the client); `eslint` reports a config error (install the missing parser). + +### 2. Missing capability / tool gap +The agent needs something that doesn't exist yet — a script, a helper, a wrapper, a glue function. Write it in the moment. This is the closest analog to browser-harness's `agent_helpers.py`. + +*Examples:* dedupe a CSV by custom key (write a small Python helper); bootstrap 12 microservices the same way (write `scripts/bootstrap-all.sh`); bulk-rename branches matching a pattern (write a `gh`-based shell helper). + +### 3. Environment issue +The local environment isn't what the project expects. Detect, patch, verify. + +*Examples:* runtime version mismatch (`nvm use`, `pyenv local`, `rustup override`); stale dependency cache after a branch switch; dirty git state blocking a checkout; missing `.env` (copy from `.env.example` and surface gaps). + +### 4. External service / API change +A service the agent depends on returns something unexpected. Find a workaround and capture it. + +*Examples:* an MCP tool returns `InputValidationError` because the schema changed (patch the call shape); a public API hits a rate limit (back off, switch endpoint, batch); an upstream lib bumped a default and broke a script (pin the version). + +### 5. About-to-retry-the-same-broken-approach +The agent catches itself about to redo the failing step. That self-recognition is a heal forming — capture the alternate approach as the patch. + +### Detection signals to watch for + +- Non-zero exit codes +- Stack traces in tool output +- The same operation failing twice with the same error +- "I'll try a different approach" — capture it as a heal +- `command not found` / `module not found` / `permission denied` +- Stale assertions, snapshot mismatches, type errors that weren't there before +- "Weird" output that suggests environmental rather than logical bugs + +## HEAL Entry Format + +Append to `.learnings/HEALS.md` (create if missing): + +```markdown +## [HEAL-YYYYMMDD-XXX] short_kebab_name + +**Logged**: ISO-8601 timestamp +**Status**: verified | pending-verify | abandoned +**Trigger**: tool-failure | missing-capability | env-issue | external-change | +**Active-Context**: (optional) — current skill, task phase, or workflow stage; omit if not applicable +**Area**: free-form tag — what part of the system (`build`, `tests`, `ci`, `auth`, `data-pipeline`, `mobile`, ...) +**Priority**: low | medium | high | critical + +### Failure +What broke — concrete: the command, the error message, the action that was blocked. Include exit codes and verbatim error lines. + +### Diagnosis +The root cause as understood after investigation. Why the obvious approach didn't work. Not a guess — what was actually verified during the heal. + +### Fix +The patch that was applied. Verbatim commands, code snippets, or pointers to files under `.learnings/heals//`. Keep it minimal — just enough to reproduce. + +### Verification +What was run after the fix and what it returned. Exit code, output snippet, test pass count. **This is the proof.** Without it, the entry is `pending-verify` or `abandoned`. + +### Artifacts +(omit this section if no files were generated; otherwise list relative paths under `.learnings/heals//`) + +### Metadata +- Related Files: path/to/file.ext +- See Also: HEAL-... | LRN-... | ERR-... (related entries) +- Pattern-Key: lower.snake.case key for recurrence detection (e.g. `env.lockfile_mismatch`) +- Recurrence-Count: 1 +- First-Seen / Last-Seen: YYYY-MM-DD + +--- +``` + +### Field guidance + +- **Status** — `verified` = the verify step passed. `pending-verify` = patch applied but couldn't be fully proven (sandboxed/offline/CI-only) — surface to the user. `abandoned` = patch didn't work or diagnosis was wrong — document what was tried. +- **Trigger** — free-form is fine. The listed values are common shapes; what matters is that the failure shape is described enough for future agents to match against. +- **Active-Context** — optional. Use it if your environment has a meaningful "what was I doing" tag (an active skill, a current task phase, a build stage, an agent role). Skip if not applicable. The browser-harness analog is the per-domain scoping of `domain-skills//`. +- **Area** — free-form. Pick whatever helps future agents find this. `frontend`, `data-pipeline`, `ci`, `auth`, `terraform`, `mobile`, `embedded` — anything that fits your project shape. +- **Pattern-Key** — lower.snake.case, stable, reusable across projects. Two heals with the same key are recurrences. `env.lockfile_mismatch` is good; `fixed_thing_tuesday` isn't. + +## ID generation + +Format: `HEAL-YYYYMMDD-XXX`. `XXX` is sequential 3-digit or 3-char random alphanumeric. Examples: `HEAL-20260524-001`, `HEAL-20260524-A7B`. + +## Artifacts directory (lazy) + +Only create `.learnings/heals//` when the heal generated something worth preserving. One-line fixes don't need a folder; the HEAL entry text is enough. Abandoned heals with no applied patch also skip the folder. + +``` +.learnings/ +├── HEALS.md +├── ERRORS.md / LEARNINGS.md / FEATURE_REQUESTS.md (self-improvement) +└── heals/ + └── HEAL-20260524-001/ + ├── helper.sh + ├── patch.diff + └── notes.md +``` + +**Put here:** generated scripts/helpers, patch files, supplementary notes, output captures that document the diagnosis. +**Don't put here:** project source changes (those go in the project tree, referenced via Related Files); secrets; output already captured in the HEAL text. + +## Verification rules + +Verify is the load-bearing wall. The whole point of self-healing over self-improvement is that the fix is *proven*, not theorized. + +### What counts as proof + +| Failure shape | Verification | +| ------------------------------------- | ------------------------------------------------------------------ | +| Tool / command / test / build / lint | Re-run the original invocation; expect exit 0 / pass | +| Missing capability | Invoke the helper end-to-end on a real input; expect the intent | +| Environment drift | Re-run the operation that triggered the diagnosis | +| External service workaround | Re-run the failed call with the patch; expect a usable response | + +### Sandboxed / offline / CI-only failures + +When you genuinely can't run the verify step (no network, no real remote, sandboxed shell, CI-only reproduction), file `Status: pending-verify` with: + +- The exact command the user / CI should run +- The acceptance criteria — what counts as proof +- A simulated proof if you can construct one (e.g. a dry-run mode, a stub of the failing call, a sandbox script) + +`pending-verify` is honest. Faking `verified` is the failure mode this skill exists to prevent. + +### When to invest in a proof script + +Most heals don't need a separate proof script — the verify step is just re-running the failing thing. Build a proper proof script when: + +- The heal generates a reusable helper that needs to be exercised across cases +- The failure can't be reproduced live but can be reproduced in a sandbox (clean git repo, mock service, fake input) +- You expect the heal to be re-applied across projects — the proof script then doubles as a regression check + +### If verification fails + +1. **Once** — refine the patch and retry. First diagnosis is often wrong. +2. **Twice** — step back and reconsider the diagnosis. Maybe the root cause is elsewhere. +3. **Three times** — stop. File `Status: abandoned` with notes on what you tried. Surface to the user. Don't flail. + +### What does NOT count as verification + +- "It looks right" / "I think this should work" +- Re-running a *different* command than the one that originally failed +- Suppressing the failure (`|| true`, `--ignore-errors`) — that's hiding +- Skipping or deleting the failing test — that's regression +- Passing because the cache was warm from before the fix + +### Reversibility + +Prefer reversible patches. If your heal modifies project files, capture the diff in `patch.diff`. If the heal is destructive (deletes generated files, rewrites locks), note it explicitly — a future agent reading the HEAL needs to know what was destroyed. + +## Recurrence and promotion + +Most heals are recurrences. Before filing a new HEAL, search: + +```bash +grep -n "Pattern-Key: " .learnings/HEALS.md +``` + +If found: + +- Increment `Recurrence-Count` +- Update `Last-Seen` +- Add the current occurrence as a See Also link +- **Do not** create a duplicate entry + +### Promotion threshold + +Add a `Handoff` block to an existing entry when **all** are true: + +- `Recurrence-Count >= 3` +- Seen across at least 2 distinct tasks +- The fix is generalizable (not project-specific in a way that's already in a memory file) + +```markdown +### Handoff +- **Promoted To**: self-improvement at YYYY-MM-DD +- **Promotion Target**: CLAUDE.md | AGENTS.md | .github/copilot-instructions.md | new-skill +- **Distilled Rule**: One-line prevention guidance derived from the heal +``` + +Then `self-improvement` (or a learning aggregator) takes over: distills the rule, writes it into the right context file, or extracts a reusable skill. The HEAL stays for traceability. + +## Anti-patterns + +1. **Logging without verifying.** A HEAL filed before the fix is proven turns this into noisier self-improvement. If verify hasn't passed, the entry is `pending-verify` or `abandoned`. +2. **Healing the symptom, not the cause.** A failing test isn't healed by skipping it (`pytest.skip`, `it.skip`, `xit`). A flaky CI isn't healed by `--retry`. Find the root cause; if you can't, abandon honestly. +3. **Generating a new fix without trying existing ones first.** Search `HEALS.md` by Pattern-Key. Most heals are recurrences. +4. **Inventing helpers when the project already has them.** Look in `scripts/`, `Makefile`, `justfile`, `package.json`, `pyproject.toml` first. Heal = write what's missing, not what's there. +5. **Scope creep.** A heal is scoped to one failure. Cleanup belongs in a quality pass; refactors are features. Scope creep makes heals unreviewable. +6. **Empty artifact folders.** Don't create `.learnings/heals//` if nothing goes in it. + +## Best practices + +1. **Heal eagerly, file always.** Even abandoned heals teach the next agent what doesn't work. +2. **Verify before persist.** The non-negotiable rule. +3. **Minimal and reversible patches.** A 3-line fix is a heal; a 300-line refactor is a feature. +4. **Stable Pattern-Keys.** `env.node_version_mismatch` is reusable; `fixed_the_thing_on_tuesday` isn't. +5. **Reference, don't duplicate.** Cross-link related HEAL/LRN/ERR via See Also. +6. **Hand off recurrences.** A heal seen 3 times deserves to be in the project's permanent memory. +7. **Don't gate the main tree on heal artifacts.** Files under `.learnings/heals/` are reference material; if a script becomes load-bearing, promote it to `scripts/`. + +## Setup + +```bash +mkdir -p .learnings # heals/ is lazy — created only when artifacts exist +touch .learnings/HEALS.md +``` + +Gitignore choices match `self-improvement`. Keep heals local (`.learnings/` in `.gitignore`) or share them as team knowledge (don't gitignore — they become reviewable durable context). + +## Multi-agent use + +The skill is agent-agnostic. The `.learnings/HEALS.md` format is plain markdown — any agent (Claude Code, BooCode agents, OpenCode, Copilot, Cursor, Aider, ...) can read and write it. + +## See also + +- [`references/examples.md`](references/examples.md) — canonical HEAL entry shapes (command failure, missing capability, env drift, external API workaround, abandoned heal) diff --git a/data/skills/boocode/self-healing/eval.yaml b/data/skills/boocode/self-healing/eval.yaml new file mode 100644 index 0000000..25256e9 --- /dev/null +++ b/data/skills/boocode/self-healing/eval.yaml @@ -0,0 +1,35 @@ +skill: self-healing +tasks: + - prompt: "I'm in a project root that has pnpm-lock.yaml present but no package-lock.json. I just tried to run `npm install` and it failed. Get me to a working state so I can keep working — I have other things to do, just unblock me. After fixing it, make sure future agents in this project know what happened." + grader: + - the response invokes the self-healing skill + - the response diagnoses pnpm vs npm mismatch as the root cause + - the response runs pnpm install successfully + - the response files a HEAL entry to .learnings/HEALS.md with Status: verified + - the HEAL entry has Trigger: tool-failure + - the HEAL entry has a Pattern-Key resembling env.lockfile_mismatch + - the HEAL entry includes the verification output + - prompt: "I need to bulk-rename 8 git branches in this repo from `feat-XXX-name` to `feat/XXX-name`. There's no existing script for this and `gh` doesn't have a bulk-rename. Write what's needed, prove it works on a dry run, and capture the work so it's not lost if I need it again." + grader: + - the response invokes the self-healing skill + - the response recognizes this as a missing-capability heal + - the response writes a helper script under .learnings/heals/HEAL--/ + - the response runs a dry-run verification + - the response files a HEAL entry with Status: verified and Trigger: missing-capability + - the HEAL entry references the helper script in the Artifacts section + - prompt: "I just ran `pytest` and got `ModuleNotFoundError: No module named 'pydantic'`. There's already a `.learnings/HEALS.md` in this project with a prior heal for a similar venv-not-activated issue. Fix this, and do the right thing with the heal records." + grader: + - the response invokes the self-healing skill + - the response searches HEALS.md first (using find-similar-heals.sh or grep) before writing a new fix + - the response finds the existing HEAL entry and applies its fix (activate venv) + - the response increments Recurrence-Count on the existing entry + - the response updates Last-Seen on the existing entry + - the response does NOT create a duplicate HEAL entry + - prompt: "A test in this repo is failing intermittently — the snapshot for `Card.test.tsx` flakes. I've already tried fixing it once by stubbing the date; it passes twice then flakes again because there's a UUID that's also non-deterministic. I don't have time to refactor the Card component to inject dependencies. Just do the right thing — get me to a state that's honest about what's known and not known, and don't pretend the heal worked." + grader: + - the response invokes the self-healing skill + - the response diagnoses that the initial patch attempt was incomplete + - the response files a HEAL entry with Status: abandoned + - the HEAL entry documents what was tried and why it failed + - the response does NOT mark anything as verified + - the response surfaces the situation honestly to the user diff --git a/data/skills/boocode/self-healing/references/examples.md b/data/skills/boocode/self-healing/references/examples.md new file mode 100644 index 0000000..fe7667d --- /dev/null +++ b/data/skills/boocode/self-healing/references/examples.md @@ -0,0 +1,248 @@ +# Self-Healing Examples + +Concrete HEAL entries showing the format applied to real failure shapes. Use these as templates when filing your own heals. All examples use the iteration-2 schema (free-form `Trigger` / `Area`, optional `Active-Context`, no `Source` field, lazy artifact folders). + +--- + +## Example 1 — Tool failure (lockfile mismatch) + +```markdown +## [HEAL-20260524-001] npm_install_pnpm_lockfile + +**Logged**: 2026-05-24T14:22:01Z +**Status**: verified +**Trigger**: tool-failure +**Area**: build +**Priority**: medium + +### Failure +`npm install` exited 1 with `npm ERR! code EUSAGE` and a notice that `pnpm-lock.yaml` is present but `package-lock.json` is missing. The project uses pnpm workspaces; npm refuses to install against a pnpm lockfile. + +### Diagnosis +Project root contains `pnpm-lock.yaml`. The README and CI both invoke `pnpm`. `npm` was a habit from previous projects, not the actual project's package manager. + +### Fix +Use pnpm instead: +```bash +pnpm install +``` + +### Verification +``` +$ pnpm install +Lockfile is up to date, resolution step is skipped +Already up to date +✓ Done in 1.4s +``` +Exit 0. + +### Metadata +- Related Files: package.json, pnpm-lock.yaml +- See Also: (none yet) +- Pattern-Key: env.lockfile_mismatch +- Recurrence-Count: 1 +- First-Seen: 2026-05-24 +- Last-Seen: 2026-05-24 + +--- +``` + +Pattern-Key `env.lockfile_mismatch` is reusable across projects (yarn.lock, bun.lockb, etc.). At Recurrence ≥ 3, this should be promoted to `CLAUDE.md` or `AGENTS.md` as a verification step. + +No Artifacts section — the fix is a tool swap, no files generated. Lazy folder pattern: nothing to put in `.learnings/heals/HEAL-20260524-001/`, so the folder isn't created. + +--- + +## Example 2 — Missing capability (helper written on the fly) + +```markdown +## [HEAL-20260524-002] bulk_rename_branches_helper + +**Logged**: 2026-05-24T15:10:44Z +**Status**: verified +**Trigger**: missing-capability +**Area**: ci +**Priority**: low + +### Failure +Need to rename 12 feature branches from `feat-XXX-name` to `feat/XXX-name`. No existing project script handles this; `gh` doesn't have a bulk-rename primitive. + +### Diagnosis +This is glue work, not a project bug. A small shell helper using `gh api` per branch is the right level — not worth a top-level script, but worth keeping the file for the next time someone asks. + +### Fix +Wrote `.learnings/heals/HEAL-20260524-002/rename-branches.sh`: + +```bash +#!/usr/bin/env bash +set -euo pipefail +git fetch --all +for branch in $(git branch -r | grep 'origin/feat-' | sed 's|origin/||'); do + new="${branch/feat-/feat/}" + echo "$branch → $new" + gh api -X POST "repos/{owner}/{repo}/git/refs" \ + -f "ref=refs/heads/$new" \ + -f "sha=$(git rev-parse "origin/$branch")" + gh api -X DELETE "repos/{owner}/{repo}/git/refs/heads/$branch" +done +``` + +### Verification +Dry-run (commented out the API calls) printed the 12 expected mappings. +Live run renamed all 12; `git branch -r | grep 'feat-' | wc -l` returns 0. + +### Artifacts +- `.learnings/heals/HEAL-20260524-002/rename-branches.sh` + +### Metadata +- Related Files: (none — operates on git refs) +- See Also: (none) +- Pattern-Key: tool.gh.bulk_branch_rename +- Recurrence-Count: 1 +- First-Seen: 2026-05-24 +- Last-Seen: 2026-05-24 + +--- +``` + +Helper script lives under `.learnings/heals//` — referenceable, but not assumed to be load-bearing. If it gets reused frequently, promote to `scripts/`. + +--- + +## Example 3 — Environment issue (runtime version) + +```markdown +## [HEAL-20260524-003] nvm_use_project_node + +**Logged**: 2026-05-24T16:01:12Z +**Status**: verified +**Trigger**: env-issue +**Active-Context**: verify-gate +**Area**: tests +**Priority**: medium + +### Failure +`pnpm test` exited 1 with `engine "node" is incompatible with this module. Expected version "^20.10.0". Got "18.19.0"`. + +### Diagnosis +`.nvmrc` requests node 20.10.0; current shell has 18.19.0 from a previous project context. The shell's nvm wasn't switched after `cd`-ing into the repo. + +### Fix +```bash +nvm use # reads .nvmrc +``` + +### Verification +``` +$ node --version +v20.10.0 +$ pnpm test +✓ 47 tests passed +``` + +### Metadata +- Related Files: .nvmrc, package.json +- See Also: (none) +- Pattern-Key: env.node_version_mismatch +- Recurrence-Count: 1 +- First-Seen: 2026-05-24 +- Last-Seen: 2026-05-24 + +--- +``` + +`Active-Context: verify-gate` because that's the workflow phase the agent was in when the test step blew up. An upstream context loader could surface this entry next time `verify-gate` runs in a node project. If you don't have an analogous concept in your pipeline, omit the field. + +--- + +## Example 4 — External service workaround + +```markdown +## [HEAL-20260524-004] gh_api_rate_limit_backoff + +**Logged**: 2026-05-24T17:33:08Z +**Status**: verified +**Trigger**: external-change +**Area**: ci +**Priority**: high + +### Failure +Looping `gh api repos/.../issues` over 200 issues started returning `403 rate limit exceeded` after ~60 calls. Unauthenticated burst limit (abuse detection on rapid successive calls). + +### Diagnosis +Script was using `gh api` REST without batching. `gh` is authenticated but the secondary rate limit fires on rapid successive calls — not the primary 5000/hour limit. Switching to a single paginated GraphQL query bypasses the secondary limit entirely. + +### Fix +```bash +gh api graphql -f query=' + query($owner:String!,$repo:String!,$cursor:String) { + repository(owner:$owner,name:$repo) { + issues(first:100,after:$cursor) { ... } + } + }' -F owner=... -F repo=... +``` +Took ~3 calls total instead of 200. + +### Verification +Full run completed in 4.8s, no 403s, all 200 issues retrieved. Compared output against a sample of the original per-issue calls — fields match. + +### Artifacts +- `.learnings/heals/HEAL-20260524-004/fetch-issues.sh` + +### Metadata +- Related Files: (none — ad-hoc query) +- See Also: (none) +- Pattern-Key: api.gh.rate_limit +- Recurrence-Count: 1 +- First-Seen: 2026-05-24 +- Last-Seen: 2026-05-24 + +--- +``` + +--- + +## Example 5 — Abandoned heal (diagnosis was wrong) + +```markdown +## [HEAL-20260524-005] vitest_flaky_snapshot + +**Logged**: 2026-05-24T18:14:22Z +**Status**: abandoned +**Trigger**: tool-failure +**Active-Context**: verify-gate +**Area**: tests +**Priority**: medium + +### Failure +`vitest` snapshot test `Card > renders default` flaked twice in three runs. Diff showed a timestamp string differing by ~3 seconds. + +### Diagnosis (initial — wrong) +Assumed flake was timezone drift in the snapshot fixture. Patched the fixture to use a fixed `Date.now()` stub. + +### Diagnosis (current — correct) +The snapshot depends on multiple non-deterministic values: timestamp AND a `crypto.randomUUID()`. The clock stub addressed only one of them. The UUID is still random per render, so the snapshot keeps drifting on subsequent runs. + +### Fix (attempted) +Added `vi.useFakeTimers({ now: 1700000000000 })` to the test setup. + +### Verification +Test passed twice, then flaked again on the third run — same `Card > renders default`, different diff (this time the UUID changed). Original diagnosis was incomplete. + +### Abandonment notes +The right fix is to make the component deterministic via dependency injection (pass a `clock` and `idGen` prop), not to stub globally. That's a real change to the component contract — out of scope for a heal. Filed `FEAT-20260524-001` via self-improvement; surfaced to the user. + +### Metadata +- Related Files: src/components/Card.tsx, src/components/Card.test.tsx +- See Also: FEAT-20260524-001 +- Pattern-Key: tests.flaky_snapshot_multi_nondeterminism +- Recurrence-Count: 1 +- First-Seen: 2026-05-24 +- Last-Seen: 2026-05-24 + +--- +``` + +Abandoned heals are first-class. They document a dead end so the next agent doesn't re-walk it. The handoff to a `FEAT-` entry via self-improvement is the right next step when the real fix is a feature, not a heal. + +No Artifacts section — the attempted patch was reverted; nothing reusable was generated. diff --git a/data/skills/boocode/self-healing/scripts/detect-failure.sh b/data/skills/boocode/self-healing/scripts/detect-failure.sh new file mode 100755 index 0000000..1ea10a3 --- /dev/null +++ b/data/skills/boocode/self-healing/scripts/detect-failure.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# detect-failure.sh — PostToolUse hook for Bash invocations. +# Reads the tool result JSON on stdin (per Claude Code hook spec); if exit_code != 0, +# emits a system reminder pointing the agent at self-healing. +# +# Wire up in .claude/settings.json: +# "hooks": { +# "PostToolUse": [{ "matcher": "Bash", +# "hooks": [{ "type": "command", +# "command": "./data/skills/boocode/self-healing/scripts/detect-failure.sh" }] }] +# } + +set -euo pipefail + +# Hook payload arrives on stdin. We tolerate either jq-style JSON or raw text. +PAYLOAD="$(cat || true)" + +# Try to parse exit_code; fall through silently on parse failure. +EXIT_CODE=$(printf '%s' "$PAYLOAD" | python3 -c ' +import json, sys +try: + data = json.loads(sys.stdin.read() or "{}") + # Common shapes: {"tool_result": {"exit_code": N}}, {"exit_code": N}, {"output": "...", "exit_code": N} + for path in (("tool_result","exit_code"), ("exit_code",), ("result","exit_code")): + d = data + ok = True + for k in path: + if isinstance(d, dict) and k in d: + d = d[k] + else: + ok = False + break + if ok and isinstance(d, int): + print(d) + sys.exit(0) +except Exception: + pass +print(0) +' 2>/dev/null || echo 0) + +if [[ "$EXIT_CODE" != "0" ]]; then + cat <<'EOF' + +A Bash command just exited non-zero. This is a heal opportunity. + +Before retrying the same command verbatim: + 1. DIAGNOSE — read the error; identify the root cause (env? missing dep? wrong tool?) + 2. Search .learnings/HEALS.md for a matching Pattern-Key (don't re-solve a solved problem) + 3. PATCH — write the fix (or apply a known one) + 4. VERIFY — re-run the command; require exit 0 + 5. FILE — append a HEAL entry to .learnings/HEALS.md via data/skills/boocode/self-healing/scripts/new-heal.sh + +EOF +fi diff --git a/data/skills/boocode/self-healing/scripts/find-similar-heals.sh b/data/skills/boocode/self-healing/scripts/find-similar-heals.sh new file mode 100755 index 0000000..9b23024 --- /dev/null +++ b/data/skills/boocode/self-healing/scripts/find-similar-heals.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# find-similar-heals.sh — Search existing heals before generating a new fix. +# Usage: ./find-similar-heals.sh +# +# Prints matching HEAL entries with their Pattern-Key, Status, and Recurrence-Count +# so the agent can decide whether to re-apply an existing fix or write a new one. + +set -euo pipefail + +QUERY="${1:-}" +HEALS_FILE="$(pwd)/.learnings/HEALS.md" + +if [[ -z "$QUERY" ]]; then + echo "usage: $0 " >&2 + exit 2 +fi + +if [[ ! -f "$HEALS_FILE" ]]; then + echo "(no .learnings/HEALS.md yet — no prior heals to consult)" + exit 0 +fi + +# Find HEAL section headers that contain the query in their body (Pattern-Key, name, or text). +python3 - <- entry skeleton. +# Usage: ./new-heal.sh [trigger] +# trigger: tool-failure | missing-capability | env-issue | external-change | +# +# Appends a templated HEAL entry to .learnings/HEALS.md and prints the HEAL-ID. +# Does NOT create .learnings/heals// — that folder is lazy, created +# only when artifacts are written. + +set -euo pipefail + +NAME="${1:-}" +TRIGGER="${2:-tool-failure}" + +if [[ -z "$NAME" ]]; then + echo "usage: $0 [trigger]" >&2 + exit 2 +fi + +LEARNINGS_DIR="$(pwd)/.learnings" +HEALS_FILE="$LEARNINGS_DIR/HEALS.md" +mkdir -p "$LEARNINGS_DIR" + +DATE="$(date +%Y%m%d)" +SEQ=$(grep -c "^## \[HEAL-${DATE}-" "$HEALS_FILE" 2>/dev/null || echo 0) +NEXT=$(printf "%03d" $((SEQ + 1))) +HEAL_ID="HEAL-${DATE}-${NEXT}" + +# Active-Context is optional. The agent / harness can set ACTIVE_CONTEXT in env. +ACTIVE_CONTEXT="${ACTIVE_CONTEXT:-}" +ACTIVE_LINE="" +if [[ -n "$ACTIVE_CONTEXT" ]]; then + ACTIVE_LINE="**Active-Context**: $ACTIVE_CONTEXT +" +fi + +cat >> "$HEALS_FILE" <&2 +echo "(create .learnings/heals/$HEAL_ID/ only if you generate artifacts to put there)" >&2 diff --git a/data/skills/boocode/verify-gate/SKILL.md b/data/skills/boocode/verify-gate/SKILL.md new file mode 100644 index 0000000..5d5580c --- /dev/null +++ b/data/skills/boocode/verify-gate/SKILL.md @@ -0,0 +1,178 @@ +--- +name: verify-gate +description: "Runs project compile, test, and lint commands between implementation and quality review. Gates simplify-and-harden behind machine verification. If checks fail, enters a fix loop with diagnostics. If checks pass, signals ready for quality pass. Use after any implementation work completes and before signaling done. Essential for the inner loop's verify step." +--- + +# Verify Gate + +Machine verification gate between implementation and quality review. Runs the project's compile, test, and lint commands. If any fail, enters a fix loop. If all pass, unblocks the quality pass. + +This is the inner loop's **verify** step. Without it, the agent hands off code with zero machine signal about whether it actually works. + +## When to Use + +- After any implementation work completes, before signaling "done" +- Before running simplify-and-harden or quality review +- After fixing audit findings from code review +- Any time you want a machine-verified green signal + +## Pipeline Position + +``` +[implementation] → verify-gate → [quality review / simplify-and-harden] + ↻ fix loop — on failure, diagnose and retry +``` + +## Step 1: Discover Project Commands + +Read the project's configuration to find verification commands. Check these sources in order: + +1. **Project instruction files** (`CLAUDE.md`, `data/AGENTS.md`) — look for a `## Verification` or `## Test Commands` section +2. **package.json** — `scripts.test`, `scripts.lint`, `scripts.typecheck`, `scripts.build`. BooCode uses pnpm, so prefer `pnpm run