Bisect
Something used to work. Your job is to hand back the exact commit that broke it, with an explanation of why it broke. git bisect does the search; your job is building a reliable oracle and driving the machine.
Phase 1 — Build the oracle
Translate the symptom into an executable check script that exits 0 when the behavior is good and non-zero when it's bad. This script is everything — a wrong oracle bisects to a wrong commit with total confidence.
- Prefer the narrowest observable signal: a failing test, a grep on output, an HTTP probe, an exit code — not "run the app and look."
- Validate the oracle before trusting it: it must fail at the suspected-bad ref (usually HEAD) and pass at some older ref. If the user can't name a good ref, probe backwards — last release tag, then earlier tags/dates — until the check passes. No verified good/bad pair, no bisect.
- If the symptom is flaky, make the oracle run the check N times and fail on any failure (or majority, if the flake is inverse). State the confidence tradeoff.
Phase 2 — Drive the machine
- Work in a separate worktree (
git worktree add) so the user's working directory is untouched.
git bisect start <bad> <good>, then git bisect run <oracle>.
- Handle history friction:
- Commit doesn't build for unrelated reasons →
git bisect skip.
- Build/setup steps changed across history → make the oracle detect and adapt (e.g. try both old and new install commands), or bisect in stages across the boundary.
- Dependencies must match each era — reinstall per checkout if lockfiles change.
- If
bisect run can't work (manual-only check), fall back to stepping manually and asking the user to observe only when unavoidable — automate everything else.
Phase 3 — The verdict
Never stop at the commit hash. Deliver:
- The guilty commit — hash, author, date, message.
- The mechanism — read the diff and explain how this change produces the observed symptom. If the connection isn't obvious, trace it until it is; a bisect result you can't explain is a suspect, not a conviction.
- The options — clean revert (test whether it reverts cleanly and whether the symptom disappears), or a forward fix sketch, with a recommendation.
Cleanup
Always git bisect reset and remove the worktree, even after failure. Leave no machinery behind.
Edge cases
- Merge-heavy history: if the guilty commit is a merge, bisect the merged branch's commits (
git bisect handles this, but explain the result carefully).
- The oracle passes everywhere / fails everywhere: the good/bad pair was wrong or the symptom is environmental (data, config, dependency drift — not the repo's history). Say so; that's a real answer.
- Very expensive checks: estimate steps first (
log --oneline good..bad | wc -l → log₂) and tell the user the cost before starting.
1---2name: bisect3description: Find the exact commit that introduced a bug or regression using automated git bisect. Use when something used to work and doesn't anymore, and the user wants the guilty commit — especially from a plain-English symptom like "scrolling got janky sometime last month."4---56# Bisect78Something used to work. Your job is to hand back the exact commit that broke it, with an explanation of *why* it broke. `git bisect` does the search; your job is building a reliable oracle and driving the machine.910## Phase 1 — Build the oracle1112Translate the symptom into an executable check script that exits `0` when the behavior is good and non-zero when it's bad. This script is everything — a wrong oracle bisects to a wrong commit with total confidence.1314- Prefer the narrowest observable signal: a failing test, a grep on output, an HTTP probe, an exit code — not "run the app and look."15- **Validate the oracle before trusting it:** it must fail at the suspected-bad ref (usually HEAD) and pass at some older ref. If the user can't name a good ref, probe backwards — last release tag, then earlier tags/dates — until the check passes. No verified good/bad pair, no bisect.16- If the symptom is flaky, make the oracle run the check N times and fail on any failure (or majority, if the flake is inverse). State the confidence tradeoff.1718## Phase 2 — Drive the machine1920- Work in a **separate worktree** (`git worktree add`) so the user's working directory is untouched.21- `git bisect start <bad> <good>`, then `git bisect run <oracle>`.22- Handle history friction:23 - Commit doesn't build for unrelated reasons → `git bisect skip`.24 - Build/setup steps changed across history → make the oracle detect and adapt (e.g. try both old and new install commands), or bisect in stages across the boundary.25 - Dependencies must match each era — reinstall per checkout if lockfiles change.26- If `bisect run` can't work (manual-only check), fall back to stepping manually and asking the user to observe only when unavoidable — automate everything else.2728## Phase 3 — The verdict2930Never stop at the commit hash. Deliver:31321. **The guilty commit** — hash, author, date, message.332. **The mechanism** — read the diff and explain *how* this change produces the observed symptom. If the connection isn't obvious, trace it until it is; a bisect result you can't explain is a suspect, not a conviction.343. **The options** — clean revert (test whether it reverts cleanly and whether the symptom disappears), or a forward fix sketch, with a recommendation.3536## Cleanup3738Always `git bisect reset` and remove the worktree, even after failure. Leave no machinery behind.3940## Edge cases4142- **Merge-heavy history:** if the guilty commit is a merge, bisect the merged branch's commits (`git bisect` handles this, but explain the result carefully).43- **The oracle passes everywhere / fails everywhere:** the good/bad pair was wrong or the symptom is environmental (data, config, dependency drift — not the repo's history). Say so; that's a real answer.44- **Very expensive checks:** estimate steps first (`log --oneline good..bad | wc -l` → log₂) and tell the user the cost before starting.