Bug fixer
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 |