Bug fixer
SkillDev toolsImplements and validates the fix for a bug once a failing reproduction test exists. TRIGGER when: a bug has a failing reproduction test and you are ready to make it pass, implementing the root-cause fix, the final step of the bug-fixing pipeline. DO NOT TRIGGER when: no reproduction test exists yet → opsmill-dev-test-driving-bugs; still diagnosing, or asked to fix a bug with no analysis or reproduction test yet → opsmill-dev-analyzing-bugs.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Bug fixer skill
What this skill tells your AI
The instructions your AI receives, as published by opsmill/infrahub in .agents/skills/opsmill-dev-fixing-bugs/SKILL.md and read by ahel’s review.
User Input
$ARGUMENTS
Your role
You are a senior engineer implementing a bug fix. Two prior steps have already completed:
/opsmill-dev-analyzing-bugs identified the root cause, and /opsmill-dev-test-driving-bugs wrote a failing test. Your job is to
fix the root cause. The test is your validation criteria -- it must pass -- but the analyst's
root cause analysis is what drives your fix, not the test.
Tool usage
- Use the
Readtool to read files -- do NOT usecatorhead/tailin Bash. - Use the
Globtool to find files -- do NOT usefindorls -Rin Bash. - Use the
Greptool to search file contents -- do NOT usegreporrgin Bash. - Reserve Bash for git commands,
ghCLI, and commands that require shell execution. - Shell state (variables,
cd) does not persist across separate Bash calls -- re-derive shell values you reuse. The pipeline's logical flags likeHAS_PRare decisions you carry in your own reasoning, not shell variables, so they do persist across steps.
Input and setup
Start from the analysis artifact, not a reconstructed slug. Discover it with Glob for
.bug-analysis-*.md in the repo root:
- No match: inform the developer "Run
/opsmill-dev-analyzing-bugs <issue>first." and STOP. - Exactly one match: use it.
- Multiple matches: pick the one whose
<key>best matches$ARGUMENTS; if still ambiguous, list them and ask which to use.
Read it for the root cause and fix strategy, and take the canonical <key> and Branch: from
its header fields. (If those fields are absent -- an older analysis -- fall back to the key in the
filename and ai-bug-pipeline-<key>.) Using the persisted branch -- rather than re-deriving the
slug -- is what keeps this step from dead-ending when the slug would have drifted.
Find the draft PR opened by /opsmill-dev-test-driving-bugs on that branch:
gh pr list --head "<branch>" --json number,title,body,headRefName --jq '.[0]'
If a PR exists (/opsmill-dev-test-driving-bugs ran with pr), set HAS_PR=true and validate it:
- PR body must contain
AGENT_TEST_COMPLETE. If not, inform the developer: "NoAGENT_TEST_COMPLETEmarker found. Run/opsmill-dev-test-driving-bugsfirst." and STOP. - PR body must NOT contain
AGENT_FIX_COMPLETE. If it does, inform the developer: "Fix has already been applied (AGENT_FIX_COMPLETEpresent)." and STOP.
Bind <branch> once, here: set <branch> to the PR's headRefName. That is the branch the
PR tracks, and it is the single value every later step (checkout, verify, push) uses -- so you
never check out one branch and push another. It normally equals the persisted Branch:; if it
differs (a hand-edited PR, or an older analysis with no Branch:), headRefName wins -- note the
discrepancy to the developer.
git fetch origin
git checkout "<branch>" # <branch> is now the PR's headRefName
If no PR exists, /opsmill-dev-test-driving-bugs was run without pr (fully local). Don't dead-end -- check
whether the branch itself exists:
git rev-parse --verify "<branch>" 2>/dev/null || git rev-parse --verify "origin/<branch>" 2>/dev/null
- Branch exists: set
HAS_PR=false, check it out (git checkout "<branch>"), and read its diff against the default branch to find the test commit. Proceed -- there is no marker to validate in local mode. - Branch does not exist either: only now is the test genuinely missing. Inform the developer
"Run
/opsmill-dev-test-driving-bugs <issue>first." and STOP.
Implement the fix
Follow steps 1--9.
Step 1: Read fix strategy
Read the analyst's fix strategy. This is your starting point: follow the recommended approach, scope, and "Do NOT" guardrails. If you believe the strategy is wrong after reading the code, state your reasoning to the developer before implementing -- do not silently ignore it.
Step 2: Read failing test
Read the failing test in the PR diff. This is your validation criteria -- the fix must make it pass -- but design your fix based on the analyst's fix strategy and root cause, not on what the test checks.
Step 3: Reason about the fix
Before writing any code, reason explicitly about the fix and state it to the developer:
- Is the root cause a shallow symptom (null check, off-by-one) or a deeper design issue?
- If shallow: a targeted fix is appropriate.
- If deeper: a proper fix may require refactoring the affected component. Do it -- do NOT paper over a design flaw with a guard clause.
Step 4: Implement the fix
- Fix the actual root cause, not just the symptom.
- Do NOT change the test the test-writer wrote.
- Do NOT refactor code unrelated to the root cause.
- If the proper fix requires changing more than expected, that is fine: explain why so the reviewer understands the scope.
- Stage files by name (
git add path/to/file) -- nevergit add .orgit add -A. - Commit the fix with an explicit commit message.
Step 5: Verify replication test passes
Run the specific test the test-writer wrote, using the same runner they used (the PR body / test file tells you which).
- If the test still FAILS, revisit your fix. Do NOT proceed until it passes.
- Before continuing, verify
git diffshows no changes to the test file(s) from the test-writer's PR. If you accidentally modified a test file, revert those changes.
Gate (T2-verify · P1): paste the actual test-run output proving PASS. Do not write "the test passes" without it. See
../quality-gates/gates/primitives/evidence-before-done.md.
Step 6: Pre-CI checks
Run the project's pre-CI checks before pushing. Detect the commands from the project rather
than assuming a toolchain -- look in AGENTS.md, a Makefile/invoke/tasks file,
pyproject.toml, or package.json scripts. Apply them in this order, fixing and committing
issues as separate commits (do NOT amend previous commits):
- Auto-format (e.g.
uv run invoke format,ruff format,npx biome check --write .,prettier --write). If formatting changed source files, re-run the later phases. - Regenerate any generated artifacts the project maintains (schemas, GraphQL/OpenAPI codegen, docs) if such tasks exist.
- Lint (e.g.
ruff,mypy/ty,eslint/biome, markdown/yaml/prose linters) as the project defines. - Unit tests for the affected area (e.g.
uv run invoke backend.test-unit,npm run test). Run the broader suite the project expects for a change of this size.
Stage any files changed by generation by name -- never git add . / git add -A.
Changelog: if the project has a changelog mechanism, add an entry for this fix:
- towncrier (a
[tool.towncrier]config or achangelog.d/newsfragmentsdir): create a fragment named after the issue, e.g.uv run towncrier create -c "<user-facing description>" <issue_number>.fixed.md. When there is no issue number (free-text bug), towncrier has no number to anchor on -- use its issue-less form with a+prefix, e.g.+<key>.fixed.md(in the free-text case<key>is the slug, with no issue prefix). - a
dev/guidelines/changelog.mddescribing another process: follow it. - otherwise a top-level
CHANGELOG.md: add a line under the appropriate section.
Write changelog text from the user's perspective, past tense, one sentence, no jargon. Commit the generated/edited file. If the project has no changelog mechanism, skip this and note it.
Gate (T2-verify · P1): paste the output of each pre-CI command (format, regenerate, lint, unit). A claim of "clean" without output fails the gate.
Step 7: Scope check
If the fix requires changes to more than ~10 files, or fundamentally alters a public API contract, STOP and escalate (see below).
Step 8: Push (PR mode) or hand off (local mode)
If HAS_PR=true: push your fix commits to the PR branch before touching the PR body. The
AGENT_FIX_COMPLETE marker is the "done" signal, so the commits must already be on the branch
when it is stamped (Step 9) -- otherwise a failed push leaves the PR permanently flagged
fix-complete with no fix, and a re-run dead-ends at the "Fix has already been applied" STOP.
git push -u origin "<branch>"
<branch> is the value bound during setup (the PR's headRefName) -- the same branch you checked
out, so the push always lands on the branch the PR tracks.
If the push fails (protected branch, non-fast-forward, network), STOP and report it -- do not proceed to stamp the marker, so a re-run can retry cleanly. Otherwise continue to Step 9.
If HAS_PR=false (local mode): do NOT push. Leave the fix committed on the local branch
<branch> and tell the developer it is ready locally -- they can review and open a PR themselves
(or re-run /opsmill-dev-test-driving-bugs … pr first if they want the pipeline to manage one). You are done -- skip
Step 9.
Step 9: Update the PR and mark complete (only if HAS_PR=true)
Ship gate (T2 · P2 + P3) — run before any PR edit or marker stamp. Run the ship gate per
../quality-gates/gates/primitives/independent-judge.md(judge → on-FAIL STOP → R2 degrade → write receipt on PASS, all defined there). R1 criteria: the.bug-analysis-<key>.mdfile verbatim (the root cause + fix strategy — NOT your summary). Artifact:git diff <default-branch>...HEAD. Forbidden evasions: the test-gate and fix-gate evasions from../quality-gates/gates/primitives/anti-gaming.md.
With the commits already pushed, finalize the PR last:
- Update the PR title to:
fix: <short description> (closes #<issue number>)(omit theclosesclause if there is no issue). - Update the PR body: if
.github/pull_request_template.mdexists, read it and fill in every section using this task's context (write "N/A" for sections with nothing meaningful, e.g. Screenshots -- do not skip or invent). If there is no template, write a concise body covering the root cause, the fix, and how it was validated. - Ensure the hidden marker
<!-- AGENT_FIX_COMPLETE -->appears somewhere in the PR body; it is the signal downstream automation uses to detect a completed fix, so it is added here, last. - Use
gh pr editto apply the title and body. - If the work is tied to a GitHub issue, post a comment on the issue linking to the updated PR.
Escalation
If at any point you determine that:
- the analyst's root cause is incorrect and the real cause is substantially different,
- the test cannot be made to pass with a correct fix (i.e. it tests the wrong behavior), or
- the fix is beyond the scope an automated agent should handle (step 7),
then inform the developer explaining your findings and STOP. Do not stamp
AGENT_FIX_COMPLETE (Step 9): an unstamped PR -- even if fix commits were already pushed in
Step 8 -- correctly signals the fix is incomplete, and the developer can take it from there.
Quality gates
Gates for this skill follow ../quality-gates/gates/gate-model.md. fixing-bugs is Tier 2 — it
ships a fix and stamps a completion marker.
| Gate | Step / trigger | Tier | Primitives | Pass criteria | On-fail |
|---|---|---|---|---|---|
| Test-passes | Step 5 | T2-verify | P1 | The test-writer's test passes; git diff shows the test file unchanged. Paste the test run. | STOP; revisit fix |
| Pre-CI | Step 6 | T2-verify | P1 | Format/lint/unit all clean. Paste each command's output. | STOP; fix and re-run |
| Root-cause | before Step 9 stamp | T2-ship | P2 + P3 | A fresh judge, given the .bug-analysis-<key>.md verbatim (R1) and git diff <base>...HEAD, returns PASS: fix addresses the documented root cause (not a symptom), test untouched, scope respected. | STOP; do NOT stamp AGENT_FIX_COMPLETE; fix and re-judge |
Common mistakes
| 🚩 Red flag | Do instead |
|---|---|
| Designing the fix from what the test checks | The analyst's root cause drives the fix; the test is only the validation gate |
| Editing the test file to make it pass | Never touch the test-writer's test — fix the production code |
| Papering over a design flaw with a guard clause | If the root cause is structural, fix it properly even if that means a larger change |
| Refactoring code unrelated to the root cause | Keep the change scoped; escalate if it must exceed ~10 files or change a public API |
git add . / git add -A | Stage changed files by name |
Stamping AGENT_FIX_COMPLETE before the push lands | In PR mode, push in Step 8 before stamping; the marker is the "done" signal, written last in Step 9 |
Signals
- GitHub stars
- 516
- Forks
- 59
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
opsmill-dev-fixing-bugs- Source
- github.com/opsmill/infrahub