| name | fix-bug |
| description | End-to-end workflow for fixing a tracked bug. Issue-first, branch, implement, push, PR, then a critic-fix loop that does NOT stop until the critic surfaces literally nothing new AND tests pass. Use when the user says "start on |
/fix-bug
Follow the shared conventions in ../conventions.md.
The full bug-fix workflow. Each phase is a hard gate; do not advance until its checks pass.
Pre-flight: load the canon
Before touching anything:
- Read the shared github-discipline rule end to end.
- Read the project's
CLAUDE.md and AGENTS.md (the project may add rules on top of canon).
- Confirm the remote before pushing (see conventions.md § Remote policy). Refuse to proceed past local commits if the verified remote is not the intended one.
Phase 1: issue setup
The issue is the audit trail. The issue must exist BEFORE the fix work begins. No exceptions. (See conventions.md § Issue-first for the shared issue shape; this skill uses the fuller template below.)
If the bug already has a GH issue: open it; verify the body follows the canon. If it doesn't, edit it into shape now.
If no issue exists yet, file one via gh issue create with this body shape:
## Status (YYYY-MM-DD)
Filed. No work started. Catalog source (if any): `path/to/audit-doc.md`.
## Symptom
What goes wrong, in plain prose. One or two paragraphs.
## Repro
Concrete shell commands or steps that demonstrate the failure on the current repo state. If static-only finding (no runtime trigger), say so explicitly.
## Severity / blast radius
Low / Medium / High, with one sentence on real-world impact and one on the trigger conditions.
## Affected sites
File references by SYMBOL name, never `file.swift:142`. Lines drift on every PR; symbols don't. Write `Foo.swift (the `searchSymbols` function)` not `Foo.swift:142`.
## Fix
The plan, in numbered steps. Reference the canonical pattern if one exists in the repo.
## Acceptance
Concrete observable conditions that prove the fix shipped. Test additions, command output, schema state.
## Out of scope
What this issue does NOT cover. Punt-to-follow-up items go here, named.
## Provenance
Where the bug was found (audit doc, user report, CI failure). If from a catalog, name the catalog letters.
Labels: pick from the 5-label canon ONLY (bug, enhancement, epic, priority: high, good first issue). Anything else is label sprawl.
Apply via gh issue create -R <owner>/<repo> --label "<comma,separated>". If the repo doesn't have the canonical labels yet, create them first, matching the shared label palette.
Phase 2: branch
git fetch origin main
git checkout -b fix/<N>-<topic> origin/main
<topic> is two-to-four-word kebab. Issue-number-anchored prefix is mandatory when an issue exists.
Edit the issue's ## Status (YYYY-MM-DD) block in place:
## Status (YYYY-MM-DD)
In progress on `fix/<N>-<topic>`. Started YYYY-MM-DD. Scope: <one-line scope statement>.
Use gh issue edit <N> -R <owner>/<repo> --body-file <(...)> or the gh api variant.
Phase 3: implement
Make the mechanical edits. Project-specific build + test commands run here:
xcrun swift build
swift test
npm test
pytest
Plus live verification when applicable (run the actual binary against real data, exercise the failure mode, observe the failure is gone).
Commit with conventional format (see conventions.md § Branch + commits), anchored to the issue number:
<type>(<scope>): <summary under 70 chars> (#<N>)
<body wrapped at 80 chars; multiple paragraphs OK>
Allowed types: fix, feat, chore, docs, refactor, test, style. Scope is the package or area being changed.
Style rules at commit boundary: apply conventions.md § Output discipline to every commit message (human-authored, no em dashes). Plus, specific to this skill: if a pre-commit hook fires, fix the underlying issue and create a NEW commit. Don't --amend to dodge the hook; don't --no-verify.
Phase 4: push + PR
git push -u origin fix/<N>-<topic>
Open the PR with gh pr create. PR title under 70 chars. Body opens with ## Status (YYYY-MM-DD), mirrors the issue body shape (Symptom, Repro, Severity, Affected sites, Fix, Acceptance, Out of scope), and ends with Closes #<N>.
Phase 5: critic-fix loop, run until 100% converged
This is the heart of the workflow. Do not exit early. (See conventions.md § The critic / fix loop for the base mechanic; this phase adds the finding categories and the out-of-scope ladder.)
The loop:
-
Read the full PR diff (git diff main..HEAD) as if you were a reviewer who didn't write it.
-
List every concrete issue you can find. Categories:
- Correctness: SQL columns missing from
DO UPDATE SET; tests that assert the wrong thing; missing null-guards; missed edge cases.
- Style: em dashes that slipped in; AI tells; inconsistent naming; line-number references in PR body or commit messages.
- Scope completeness: other sites with the SAME defect class that the original issue body missed. If you find one, you have THREE options, in priority order:
- (a) Fix it in this PR if it's mechanically similar and small.
- (b) File a sub-issue right now (
gh issue create) and reference it in the PR body's "Out of scope" section.
- (c) Punt with a comment in the PR body. ONLY if (a) and (b) are both wrong.
- Tests: missing regression tests; assertions that don't pin the behaviour they claim to.
- Docs: stale comments, contracts not documented, doc-strings that lie about what the code does.
-
For each issue, fix it with a new commit on the same branch:
critic-fix(<scope>): <what was wrong>
-
Re-run build + tests + live verification. ALL must pass.
-
Go to step 1.
Exit condition (and only this exit condition): step 1 surfaces zero new findings AND step 4 is fully green.
Do NOT exit because:
- "I've been looping a while." Loop longer.
- "These remaining findings feel out of scope." Use the (a)/(b)/(c) ladder above; out-of-scope findings get filed as sub-issues, not ignored.
- "The diff is big enough." Diff size is not the convergence criterion.
- "I'm probably overthinking it." You're not. Keep going.
Two consecutive empty critic passes is the minimum signal of convergence. Three is comfort.
Phase 6: merge
gh pr merge <N> --squash --delete-branch
Verify the auto-close:
gh issue view <N> --json state
Local cleanup:
git checkout main
git pull --ff-only
git branch -D fix/<N>-<topic>
Cross-cutting verifications (re-check before merging)
The shared checks from conventions.md (output discipline and remote policy) still apply. Plus, specific to this skill:
- No file references like
Foo.swift:142 in any committed text (issue body, PR body, commit messages, code comments). Symbols only.
- The 5-label set is what the issue carries. No
question / wontfix / help wanted etc.
- Every backtick-quoted file path in the issue body exists in the repo at write time.
- Every cross-referenced
#NNN is OPEN at write time, OR the surrounding sentence describes the dep as already-shipped.
When to use
- User says "start on #N" / "fix issue #N" / "work on bug X" / "implement the fix" / "let's tackle this bug" / similar.
- User hands you a known bug without a tracked issue. Phase 1 files it first; do not skip.
- A code-review pass on someone else's PR uncovers a defect class; Phase 1 files the issue, then the rest of the workflow runs as normal.
When NOT to use
- The user wants a quick scratch experiment that won't be committed. No PR, no critic loop needed.
- The change is genuinely documentation-only with no behavioural impact. Lighter weight commit-and-push is fine; Phase 5 critic-fix loop still applies if the diff is non-trivial.
- The change is a release branch merging into main. Use
release/v<X.Y.Z> instead of fix/<N>-<topic>.
Failure modes to refuse
- Filing an issue AFTER the PR is open. The issue is the audit trail; it must precede the work.
- Skipping the critic-fix loop because "the change is small." Small changes can still ship style violations, missed columns in DO UPDATE SET clauses, and line-number references that drift.
- Force-pushing to main. Never.
- Pushing to an unverified remote. Never. Investigate the URL before any push (conventions.md § Remote policy).
- Amending a commit to dodge a pre-commit hook failure. The hook is correct; the commit is wrong. Make a new commit.