diagnosing-bugs ยท diff
git:20260825.cd0e496 to git:20260826.c3e7584
16 added, 58 removed. Audit A to A.
---
name: diagnosing-bugs
- description: Diagnose a known bug or performance regression by building a command that reproduces it before forming any theory. Use when something is broken, throwing, failing, hanging, corrupting data, or slower than it was, when a test fails for reasons nobody understands, or when the user says debug this, diagnose this, or asks why something is happening.
+ description: Diagnose a reported bug, a failing CI or lint job, or a performance regression, and find its cause. Use when something is broken, failing, throwing, hanging, or slower than it was, when CI or a pipeline fails, or when the user asks why something is happening.
argument-hint: "[<what is broken>]"
---
# Diagnosing Bugs
- Diagnose the bug in `$ARGUMENTS`, or the failure the user just reported. Work the sections below in the order they appear, by whatever route reaches each done-state. Feedback Loop is not skippable, and skipping a later section means naming the done-state you are claiming without it.
-
- ## Redact
-
- Replace every secret with `<REDACTED>` before showing a command, its output, or a captured artifact. Quote only the signal-carrying lines of a captured trace.
-
- When redaction removes what you need to diagnose the bug, say so and ask the user.
-
- ## Feedback Loop
-
- Goal: one command that goes **red** on this bug.
-
- A loop is **red** when it drives the real code path and asserts the user's exact symptom. A command that checks only that nothing crashed is not red. A loop is **tight** when it is fast, deterministic, and runnable unattended.
-
- Build the loop before reading code to explain it.
-
- Read [references/loops.md](references/loops.md) to pick a rung from the ladder, or when the loop you built is slow, flaky, or impossible to construct.
-
- Done when you can name one command, have run it at least once, and have shown the invocation and its redacted output going red on this bug.
-
- ## Reproduction
-
- Goal: the smallest scenario that still goes red.
-
- Run the loop. Confirm the failure it produces is the one the user described rather than a nearby one, and that the verdict repeats across runs. When it does not, raise and record a reproduction rate before continuing. Capture the exact symptom: error text, wrong value, measured timing.
-
- Then cut the scenario down: inputs, callers, config, data, and steps, one at a time, re-running the loop after each cut. Keep a cut that leaves the loop red. Revert a cut that stops the failure.
-
- Done when every remaining element is required.
-
- ## Hypotheses
-
- Goal: a ranked set of falsifiable causes, written before any of them is tested.
-
- Write three to five hypotheses. Each states a prediction the loop can settle: if X is the cause, changing Y stops the loop failing, or changing Z raises the failure rate. Sharpen or drop a hypothesis that states no prediction.
-
- Show the ranked list to the user before testing. Proceed on your own ranking when they do not answer.
-
- ## Instrumentation
+ Diagnose the failure in `$ARGUMENTS`, or the one the user just reported.
- Goal: every hypothesis settled by a run of the loop.
+ ## Reproduce
- Change one variable per run. Aim each probe at a specific prediction.
+ Build one command that fails on this bug. Reading and grepping to work out how to invoke the failing path is how you build it, not a detour around it.
- Use a debugger or REPL where the runtime supports one. Otherwise log where the hypotheses predict different values. Tag every debug log with a unique prefix such as `[DEBUG-a4f2]`.
+ A command that shows only that nothing crashed does not count. It has to drive the real code path and produce the symptom that was reported.
- For a performance regression, measure rather than log. Establish a baseline with a timing harness, a profiler run, or a query plan, then bisect against it.
+ For a failing CI or pipeline job, that command already ran. Use `github:actions-monitor` or `gitlab:ci-monitor` to get the logs rather than reconstructing the job locally.
- Done when every hypothesis is marked eliminated or surviving, each by a named run of the loop. When none survives, return to Hypotheses with what these runs ruled out.
+ When nothing you try reaches the bug, say so and stop. Name what you tried and what each attempt could not reach, then ask for an environment where it reproduces, a captured artifact, or permission to add instrumentation.
- ## Fix and Regression Test
+ ## Find the Cause
- Goal: a fix the loop proves, and a test at a seam that catches the bug returning.
+ Change one thing per run and let the command settle it. A cause the command has not responded to is a guess, and saying which line is at fault is not the same as showing the command go green when you change it.
- Find the seam first. A correct seam exercises the real pattern the bug takes at the call site. A single-caller test for a bug that needs two callers is too shallow.
+ Report one cause. When the cause is not clear, say what you ruled out and how you ruled it out, then ask rather than presenting a list to choose from.
- Report a missing seam as a finding about the module's shape, and continue. Name the module and the part of its interface that blocks the test.
+ ## Hand Off
- With a seam: turn the minimized scenario into a test there, watch it fail, apply the fix, watch it pass. Then re-run the loop against the original, pre-minimization scenario.
+ With the cause established and the fix clear, enter plan mode and map out the implementation.
- Done when the regression test fails without the fix and passes with it, and the original scenario no longer reproduces under the loop. A missing seam substitutes for the test only when the report names it.
+ When the user asked you to diagnose without changing anything, stop at the cause and the plan.
- ## Cleanup
+ ## Confirm
- Done when all of these hold:
+ Once a fix is in, run the original reproduction again. Report it fixed only after that command passes, and quote the run.
- - Re-running the loop after cleanup still fails to reproduce the original scenario.
- - The regression test passes, or the report names the seam that does not exist.
- - A grep for the debug prefix returns nothing.
- - No throwaway harness remains outside a path with `debug` in its name.
- - The commit or PR message states which hypothesis was correct.
+ A fix that was never run against the thing that failed is not a fix, and neither is one verified against a smaller case you built along the way.