AI-Powered Stagehand + git bisect: Finding and Fixing the Commit That Broke Your Code
Learn how to use AI-powered Stagehand to write robust, natural-language tests that pinpoint bugs in your code—then combine them with `git bisect` to track down the exact commit where things went wrong, all while keeping your tests backward-compatible and easy to maintain.
Chris Wood
Founder of qckfx
When you discover a bug, one of the most powerful ways to trace it back to its source is:
- Write an end-to-end test that reproduces the bug.
- Use Git’s built-in
bisectto pinpoint the exact commit where it was introduced.
However, this approach often stalls if your test code or environment depends on features that didn’t exist in older commits. It’s also common for standard E2E tests (like those in Selenium, Cypress, Puppeteer, or even vanilla Playwright) to become so flaky or DOM-dependent that running them on older commits turns into a nightmare.
Enter Stagehand—the AI-powered successor to Playwright. With Stagehand, you can write more resilient end-to-end tests that use natural-language instructions to interact with the browser. And once you couple these robust tests with Git’s bisect, you’ll have a streamlined way to find and fix code regressions.
1. Why Use Stagehand?
1.1 Natural Language for More Durable Tests
Traditional tests rely on DOM selectors—like data-qa attributes or class names—that can break if someone refactors the UI. Stagehand instead interprets user-intent instructions such as:
await page.act("click the login button");
Behind the scenes, Stagehand does the heavy lifting by:
- Inspecting the DOM for elements matching your natural-language query.
- Automatically generating a robust locator (like an XPath).
- Executing the appropriate Playwright action.
Because it focuses on semantic meaning rather than fragile attributes, Stagehand tests are inherently more backwards-compatible when you roll back in your repository. A small DOM shuffle is less likely to break a well-written Stagehand instruction.
1.2 Lower Maintenance, Fewer Flakes
Stagehand’s AI-driven approach:
- Survives renaming of CSS classes or small changes in the DOM hierarchy.
- Can attempt multiple strategies (DOM scanning, vision-based analysis, etc.) to locate elements.
This means once you create a bug-reproducing test, it’s far more likely to remain valid across multiple commits—exactly what you need for a successful git bisect.
2. Ensuring Backward Compatibility for git bisect
2.1 The Challenge: Old Code, New Tests
git bisect checks out older revisions in your repo. If your newly-written test relies on code, APIs, or environment changes that don’t exist in those older commits, the test may fail for unrelated reasons.
Goal: Provide a stable environment so your Stagehand test remains valid for as many commits as possible in the bisect range.
2.2 Devbox & Ephemeral Environments
One major culprit for “backwards incompatibility” is the system environment itself (Node version, package versions, OS differences, etc.). You can solve this by using ephemeral environments—Devbox is a fantastic tool here, as it leverages Nix under the hood to create reproducible, lightweight dev environments.
- Devbox ensures that no matter which commit you’re on, you’ll spin up the exact same environment, with pinned versions of Node, your dependencies, and anything else you need.
- This approach lets you run your test suite from older commits without frantically installing older versions of Node or flipping through different dependencies.
If you want more details on ephemeral environments with Devbox, check out our previous post about
git bisectand ephemeral environments.
2.3 Bridging Commits (What, Why, & When)
A bridging commit is sometimes mentioned as a solution to ensure a test exists at the point in history you want to start bisecting. However, it can be tricky to implement:
- The Idea: You create a commit in the past (by branching or rewriting history) that just adds the test (and possibly some environment configs) without changing app logic. Now, every subsequent commit in that branch has your new test file, so you can reliably
git bisectfrom that bridging commit onward. - When It’s Useful:
- If your test suite didn’t exist at all prior to some point, or the test harness is drastically different, you might insert a bridging commit so you can run that test from older versions up to the new ones.
- If you’re building for future stability—i.e., you plan to keep using
git bisectfor regression testing—then having that bridging commit in place can pay off.
- When It’s Overkill:
- If you just found a bug on your current
mainbranch and know that 10 commits ago it was working, you can usually add the test on the current HEAD, then just dogit checkout HEAD~10and confirm that environment is stable. You don’t necessarily need to rewrite older commits. - If your environment dependencies or fundamental code structure changed drastically 50 commits ago, bridging might be too big a headache. You might just limit your bisect range to the commits for which the test can reliably run.
- If you just found a bug on your current
Because bridging commits can require rewriting or rebase merges in your Git history, they’re often an advanced technique. For a straightforward bug that appeared recently, you’ll typically rely on ephemeral environments + Stagehand’s robust test to go back just far enough.
3. A Sample Stagehand Test for a Bug Repro
Below is a simplified illustration of how Stagehand can make your tests more future- and past-proof.
import { stagehandPage } from "stagehand"; // Hypothetical import
test("Repro: should show error message instead of crashing on invalid password", async () => {
const page = await stagehandPage();
// Step 1: Navigate to /login
// (Use page.navigate instead of page.act, per Stagehand best practices for URL changes)
await page.navigate("/login");
// Step 2: Fill in credentials
await page.act("type 'testuser' into the username field");
await page.act("type 'wrongpassword' into the password field");
// Step 3: Click login
await page.act("click the login button");
// Step 4: Validate that the app doesn't crash, but shows an error
const errorText = await page.extract("what error message is shown on the screen");
expect(errorText).toMatch(/invalid password/i);
});
Notice how we’re not referencing specific selectors like .username-input or #pwdField. Stagehand automatically determines how to target those fields by analyzing the DOM. If an older commit has slightly different naming or structure for these fields (yet still has the same essential concept of “username” and “password” fields), the test has a fighting chance to remain valid.
4. Using Stagehand and git bisect in Practice
4.1 Basic Steps
- Add or Update Your Test: Write the Stagehand test that reproduces your newly discovered bug.
- Confirm It Fails on Current Commit:
npm test -- --testPathPattern="myStagehandBugTest" - Check Out an Older Commit: E.g.,
git checkout HEAD~10and run the test to confirm it was passing then. - Start
git bisect:git bisect start git bisect bad # The current HEAD is bad (the bug is present) git checkout HEAD~10 git bisect good # The older commit is good (bug not present) - Automate with Stagehand:
git bisect run npm test -- --testPathPattern="myStagehandBugTest" - Bisect Finds the Commit: Once
git bisectcompletes, it tells you exactly which commit introduced the bug.
4.2 Why This Combo Rocks
- Stagehand: Minimizes test flakiness and ensures your instructions adapt to slight DOM changes.
- Devbox: Eliminates environment drift across commits by spinning up ephemeral, Nix-based setups.
- Git bisect: Methodically narrows down which commit introduced the regression.
5. Taking It Further: qckfx’s Automated Dev Agent
If you like the idea of:
- AI reading bug reports and auto-creating Stagehand-based tests,
- Kicking off a
git bisectto home in on the offending commit, - Having an LLM propose a fix,
- And automatically opening a PR with the fix plus the new Stagehand regression test…
…then check out qckfx. We’re building exactly that. Sign Up for Beta Access if you’d like to see how an AI agent can supercharge your debugging and development workflows.
6. Conclusion
Using Stagehand tests with git bisect can be a game-changer for diagnosing regressions quickly and confidently. The key is ensuring your test is as backwards-compatible as possible:
- Keep environment differences in check using ephemeral setups like Devbox (Nix-based).
- Write semantic, natural-language instructions with Stagehand so minor UI changes don’t break your test.
- Consider advanced Git history strategies—like bridging commits—only if you truly need to test very old commits with no existing test harness.
Armed with these techniques, you’ll spend less time rewriting flakey tests for older commits, and more time actually fixing the bugs you find!
Happy debugging—and may your commits reveal their secrets with minimal fuss.