Loop Engineering with Goose: From Failed CI to a Verified Repair
A dependency upgrade, a general-purpose repair agent, and the checks that decide whether to keep going.
I opened a dependency-update pull request that moved Mock Service Worker from 1.3.2 to 2.0.0.
CI failed immediately:
Module '"msw"' has no exported member 'rest'.The fix would have taken a few minutes. The experiment I cared about was whether CI could hand the failure to a general-purpose agent, let it investigate, and keep going until the pull request turned green or the retry budget ran out.
I used Goose to build and run that loop.
Why Automate This?
For one broken dependency, this loop is overkill. I could read the error, update the API, run the tests, and push the fix.
But the same work keeps coming back across repositories: recover the context, read the logs, inspect the migration, make the change, run the checks, and return later to see what happened. The edit may be small. Recovering context and babysitting CI is the tedious part.
That made dependency updates a useful test case. The trigger is clear, the repair is bounded, and CI already knows what passing looks like.
What Makes This a Loop
Addy Osmani calls this Loop Engineering: observe what happened, let the agent act, evaluate the result, and decide whether to continue. In this demo, Goose acts and CI grades the result.
Green stops the loop. If Goose exhausts its retry budget, the workflow hands the job back to a person.
The Failure Became Goose’s Next Input
The demo repository uses two workflows. The first runs ordinary CI with npm run check. The second wakes up only after CI fails, captures the log, and starts a headless Goose recipe.
That separation matters. Goose can change the code, but it cannot change what passing means.
The recipe is deliberately generic. It reads the failed log and asks for the smallest correct change without naming a file, API, or migration:
prompt:
Read .goose-ci-failure.log, inspect the repository, and diagnose why CI failed. Make the smallest correct code change. You do not know the failure or required files in advance. Do not change GitHub workflows, this recipe, or weaken tests and build commands.What Goose Actually Did
The MSW 2 upgrade introduced a real breaking change. The old handler used rest, the req, res, ctx resolver arguments, and response composition helpers:
rest.get(url, (req, res, ctx) => {
return res(ctx.status(200), ctx.json(data));
});Goose inspected the repository and changed the handler to use the MSW 2 API:
http.get(url, ({ params }) => {
return HttpResponse.json(data);
});That matches the official MSW migration: rest moves to http, resolver arguments move into one information object, and mocked responses use HttpResponse.
The full repair changed one file: test/handlers.ts. Then the recipe ran npm run check, and both the type checker and tests passed.
The successful Goose repair workflow run is public. The pull request keeps the failed commit and repair separate, so you can inspect the transition directly.
Closing the Outer Loop
Within one repair run, Goose can retry twice and use up to 20 turns. Recipes support shell checks and bounded retries.
If the local check passes, GitHub Actions commits the repair and reruns CI. A fresh CI failure can start another repair run, but the workflow stops after three Goose-authored commits.
The Loop Needs Boundaries
Goose could change repository files because the exact repair wasn’t known ahead of time. It could not change the workflow or the checks grading its work.
Three constraints made that acceptable to me:
Keep the evaluator independent. The agent repairs the code; CI decides whether it passes.
Limit the blast radius. Restrict branches, secrets, files, runtime, retries, and write permissions.
Define the handoff. Stop on green, stop when the budget is exhausted, and leave product judgment to a person.
This was one run. It does not prove that Goose can repair every upgrade or that the economics work. It shows that failed CI can become the next agent input and that existing checks can drive the loop without a person copying errors between tools.
The complete repository, pull request, workflows, recipe, and run history are public. Before trying this on your next broken update, ask three questions: Can the failure trigger the loop? Can existing checks grade the repair? Is it obvious when a person takes over?



