The handler returned handled: false and an explanation:
I diagnose a failure from its job log, and a comment does not tell me which run failed.
That was true when it was written. listWorkflowRuns landed in #1410, so the run can be found — the newest failing run at the pull request's current head.
Restricted to the head deliberately. A branch keeps its old runs, and diagnosing one from three pushes ago produces a confident report about a failure somebody already fixed. Where nothing at the head is red, it says so rather than reaching further back for something to diagnose.
The constraint that shaped this
The generated command job checks out with no ref: — so it sits on the default branch, not the pull request's:
command:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0 # ← no refFrom there, a lock-file rewrite would regenerate against the wrong manifests and commitAndPush would push them to the wrong branch. The repair agent would edit the wrong tree.
So attemptFix gains analysisOnly — distinct from dryRun, which is a choice, where this is a constraint — and the comment path passes it alongside a null AI client so the agent cannot run at all.
| From a comment | |
|---|---|
| Classify and post the diagnosis | ✅ |
| Say whether the base branch already fails the same way | ✅ |
| Re-run a flake | ✅ — an API call, no checkout needed |
| Regenerate a lock file | ❌ — names the fix and points at the fix-ci job |
| Agent repair | ❌ — same |
The report says which repair it could not apply and who will. A reply that stops at "I cannot fix this here" reads as a limitation; naming the fix and pointing at the job that will make it is the useful version.
One orchestration, not two
Rather than grow a second copy of the sequence, the CLI's inline implementation moves to runFixCi in src/ci/run.ts — alongside runReviewForPR, which exists for exactly this reason. Its own docstring:
Shared by the CLI, the
@buddy reviewcommand and the automatic trigger, so all three assemble context the same way rather than drifting apart.
bin/cli.ts loses ~100 lines, and both entry points now resolve the run, apply the anti-loop guard and report identically.
Test coverage
createHandlers had no test coverage at all — nothing in test/ imported it — which is part of why a handler that refused to do its job went unnoticed. It has some now, along with the orchestration.
1987 → 2010. Worth noting: my own stub caught a real behaviour while writing these. It answered listWorkflowRuns identically for every branch, so failsOnBaseBranch saw the same failure on main and correctly declined every repair. Making the stub branch-aware is what the base-branch check is supposed to do — the tests now cover both directions end to end.
Lint ✓ · typecheck ✓ · check:docs ✓ · 2010 pass / 0 fail
🤖 Generated with Claude Code