| name | fix |
| description | Structured bug fix — reproduce (failing test), diagnose, fix (minimal change), verify, commit. Enforces test-first. Use when a bug needs a methodical fix. |
| effort | medium |
| model | sonnet |
| keywords | ["bug","fix","broken","error","crash","failing","regression","debug","diagnose","reproduce","root-cause"] |
| task_strategies | ["bug-fix"] |
| stream_affinity | ["bugs","tech-debt"] |
| argument-hint | [task-id or description of the bug] |
| group | execution |
| depends_on | ["backlog"] |
| allowed-tools | ["AskUserQuestion","Bash","Edit","Glob","Grep","Read","Write","Agent","mcp__ruflo__autopilot_learn","ToolSearch","TaskCreate","TaskList"] |
| status | stable |
| growth_stage | evergreen |
Fix
Structured bug fixing workflow. Five steps: REPRODUCE → DIAGNOSE → FIX → VERIFY → COMMIT → (HARDEN). Enforces test-first debugging — no source changes until a failing test exists. HARDEN is optional: fires when the same errata class has appeared 2+ times — offers to convert the pattern into a PreToolUse gate.
Usage
/brana:fix [task-id or bug description]
If a task ID is provided, load it via brana backlog get <id> and use the description as the bug statement.
Step Registry
On entry, create CC Tasks for each step (for compression resilience):
TaskCreate: "/brana:fix — REPRODUCE"
TaskCreate: "/brana:fix — DIAGNOSE" (blocked by REPRODUCE)
TaskCreate: "/brana:fix — FIX" (blocked by DIAGNOSE)
TaskCreate: "/brana:fix — VERIFY" (blocked by FIX)
TaskCreate: "/brana:fix — COMMIT" (blocked by VERIFY)
TaskCreate: "/brana:fix — HARDEN" (blocked by COMMIT, optional)
Mark each in_progress when starting, completed when done. Resume after compression by calling TaskList and finding the in_progress step.
After creating the step registry, call /goal and write active-goal.json unless --no-goal was passed.
If a task_id is known, first extract AC: lines from task context:
brana backlog get {task_id} | python3 -c "
import json, sys
t = json.load(sys.stdin)
lines = [l[3:].strip() for l in (t.get('context') or '').splitlines() if l.startswith('AC:')]
print('\n'.join(lines))
"
-
If AC: criteria found:
/goal "fix {task-id} — Done when: {criteria joined with ' AND '}"
Pin base_ref at goal start and initialize tests_required[] empty (hardened form —
ADR-061 §4, identical to build's LOAD step 0):
base_ref=$(git -C "{git_root}" rev-parse HEAD)
cat > ~/.claude/run-state/active-goal.json <<JSON
{"task_id": "{task_id}", "cwd": "{git_root}", "session_id": "$BRANA_SESSION_ID", "base_ref": "$base_ref", "criteria": ["{criterion1}", "{criterion2}"], "tests_required": []}
JSON
base_ref anchors the grader-immutability check; the REPRODUCE failing test is committed
red in its own commit (the REPRODUCE step, Step 1) so the red-verification pre-commit hook (t-2216) registers
it into tests_required[] — earning the exemption by observed redness. The Stop hook
(goal-completion.sh) auto-completes the task when all criteria pass, behind the presence +
immutability interlocks.
-
If no AC: criteria (or no task_id):
/goal "fix {task-id}: reproduce → diagnose → fix → verify → commit"
No active-goal.json write — the narrative goal is for session anchoring only.
/goal binding declaration (ADR-061 §3, Stage 3 — t-2206).
Span: REPRODUCE → DIAGNOSE → FIX → VERIFY (gate-free — no human approval inside it).
Done-signal: the reproduce failing test now passes (encoded as an AC: line, graded by
goal-completion.sh). C1 ✓ machine-verifiable (test exit code); C2 ✓ gate-free span;
C3 ✓ mutation-bounded to the failing path's files + the new test. Re-audit rule: if a
human gate is ever added inside this span, the same commit must re-scope or retire this binding.
COMMIT is the natural terminator for single fixes. HARDEN fires only when recurrence is detected — skip it otherwise and self-terminate after COMMIT.
ToolSearch("select:mcp__ruflo__autopilot_learn")
Procedure
Step 1: REPRODUCE
Goal: prove the bug is real with a failing test before touching any source.
- Understand the symptom. Read the task description or ask: "What's broken, when does it happen, what's the expected vs actual behavior?"
- Find the failing case. Read relevant code, check recent commits, identify the conditions that trigger the bug.
- Write a failing test that captures the bug:
- The test IS the spec — it documents what "fixed" looks like
- Run it: confirm it fails with current code
- State: "Test fails as expected:
{test name} — {actual output}"
- If no test framework exists: document reproduction steps as a numbered list. State "No test framework — manual verification required." Note this in the task.
If 3 or more previous fix attempts have already failed for this bug, pause and run /brana:challenge on your diagnosis before continuing to Step 2.
Checkpoint:
printf '{"step":"REPRODUCE","completed":"%s","task":"%s"}\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "{task_id}" >> ~/.claude/run-state/{task_id}.jsonl
Step 2: DIAGNOSE
Goal: state a specific hypothesis about root cause — not a symptom.
- Read the failing test output carefully. Note the exact error message, line, and stack.
- Trace the call path. Follow the code from the failing assertion back to where the bad value or wrong path originates.
- State the hypothesis:
"This bug fails because {component} does {wrong thing} when {condition}, producing {bad result} instead of {expected result}."
- Distinguish root cause from symptom. A symptom is "the test fails on line 42." A root cause is "the nil check is missing at the call site in
parse_config()."
- If the hypothesis is uncertain: run one targeted experiment (add a log, write a second test, read adjacent code). Revise the hypothesis once.
Checkpoint:
printf '{"step":"DIAGNOSE","completed":"%s","hypothesis":"%s"}\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "{hypothesis}" >> ~/.claude/run-state/{task_id}.jsonl
Step 3: FIX
Goal: make the minimal change that satisfies the hypothesis.
- Write only what the hypothesis requires. No scope creep — do not refactor adjacent code while fixing.
- Change the minimum number of files. If the fix touches more than 3 files, re-examine the hypothesis — it may be too broad.
- Do not add features while fixing. If a fix reveals a missing feature, file a separate task.
- Run the failing test after each edit. Stop when it goes green.
Step 4: VERIFY
Goal: confirm the fix works and introduced no regressions.
- Run the full test suite. State the result: "N/N tests pass."
- Confirm the original symptom is gone. Re-state the expected vs actual: "Expected
{X}, now getting {X} — fixed."
- Boundary check: think of 1-2 adjacent cases that could have been affected. If they're not covered by existing tests, add them now.
- If any tests fail that were passing before: the fix introduced a regression. Go back to Step 2 with the new failure.
Step 5: COMMIT
-
Commit message format:
fix({scope}): {what was wrong and what changed}
Examples:
fix(parse-config): nil check missing when optional key absent
fix(session-end): cd /tmp invalidates git root for subsequent brana calls
-
If a task ID exists: add it to notes and set status:
brana backlog set {task_id} status completed
brana backlog set {task_id} notes --append "Fixed {date}: {one-line summary}"
-
Feed the autopilot — after committing, call:
mcp__ruflo__autopilot_learn()
Seeds pattern registry from completed fix outcome. Skip silently if ruflo unavailable.
-
Close the session if this was the only task: invoke /brana:close.
Step 6: HARDEN (conditional)
Goal: convert a recurring errata class into a structural PreToolUse gate so it cannot recur.
Trigger check (run immediately after COMMIT):
KEYWORDS="{keyword1} {keyword2}"
grep -c "$KEYWORDS" ~/.claude/CLAUDE.md \
"$(git rev-parse --show-toplevel)/.claude/CLAUDE.md" 2>/dev/null | \
awk -F: '{s+=$2} END {print s}'
If count ≥ 2: proceed. Otherwise: skip HARDEN entirely — mark the CC Task completed and self-terminate.
Harden flow (only if triggered):
-
Identify the invariant. State in one sentence what structural condition would have prevented every instance:
"Every write to {path} must have {field} absent / {condition} true."
-
Identify the interception point. Which CC tool event catches the violation before it lands?
Write / Edit to a specific file or path pattern → PreToolUse on Write/Edit
Bash command matching a pattern (e.g., git commit, cargo build) → PreToolUse on Bash
- MCP tool call →
PreToolUse on mcp__*
-
Offer via AskUserQuestion — never proceed without explicit yes:
AskUserQuestion(
question: "This errata class ({class}) has appeared {N} times. Convert to a PreToolUse gate?\n\nProposed invariant: {invariant}\nInterception: PreToolUse on {tool} matching {pattern}",
options: ["Yes — draft the hook (Recommended)", "No — skip hardening"]
)
-
If yes — draft the hook script:
-
Write a test in system/scripts/tests/test-{errata-slug}-gate.sh:
- One test: input that SHOULD be blocked → assert exit 1
- One test: valid input → assert exit 0
-
Present the complete diff (hook script + hooks.json entry + test) to the user for review before writing any file.
-
After approval: write files, run chmod +x on the hook script, verify ./validate.sh passes.
Rules
- Test before source. Never touch implementation until a failing test exists. The test is the spec.
- Hypothesis must be specific. "Something is wrong with X" is not a hypothesis. "X does Y when Z, causing W" is.
- 3-strike rule. If 3 attempts have failed, stop and run
/brana:challenge before continuing.
- Minimal change. The smallest fix that makes the test pass is the right fix.
- No refactor during fix. File a separate task for cleanup discovered during the fix.
- HARDEN is offer-only. Never write hook files without an explicit "yes" from AskUserQuestion. Draft and present first.
Resume After Compression
TaskList — find the in_progress step
- Read
~/.claude/run-state/{task_id}.jsonl for the last checkpoint state
- Resume from the in-progress step using checkpoint context