git:20261002.c185c74 to git:20261002.8aaf2b8

73 added, 43 removed. Audit A to A.

---
name: fix-github-issues
description: Verify, triage, fix, and close review-bot GitHub issues one finding at a time.
disable-model-invocation: true
argument-hint: "Optional issue numbers or a label (defaults to the project's review-bot label, else ai-review)"
- allowed-tools: "Bash(gh issue view *) Bash(gh issue list *) Bash(gh api *)"
+ allowed-tools: "Bash(gh issue view *) Bash(gh issue list *) Bash(gh issue comment *) Bash(gh issue close *) Bash(gh api repos/{owner}/{repo}/issues/*)"
---
# Fix GitHub Issues
Work the open issues that a review bot filed. Each issue is a **container of findings**:
some are legit, some stale, some wrong. Every finding earns its own verdict from the
- code on `origin/main`, never from the issue's headline. An issue closes only when no
- legit finding is left in it.
+ code on the default branch, never from the issue's headline. An issue closes only when
+ no legit finding is left in it, and a fix counts only once it reaches the default branch.
- **Project context.** Read `.agents/github-review.md` when the repo has one. It holds
- the bot label, the auto-dismiss and never-dismiss lists, and the stack facts the bots get wrong. Then
- follow the repo's `AGENTS.md` / `CLAUDE.md` rules for tests, lint, cache clears, and a
- dirty tree.
+ **Project context.** Read `.agents/github-review.md` when the repo has one. Its
+ sections `## Review bots` (with the bot's issue label), `## Auto-dismiss`,
+ `## Never dismiss`, and `## Stack facts` (what the bots get wrong) hold this repo's
+ facts; a missing section means the default here. Then follow the repo's `AGENTS.md` /
+ `CLAUDE.md` rules for tests, lint, cache clears, and a dirty tree.
## 1. Build the working set
- Run `git fetch origin`, and verify everything against `origin/main`, not local `main`.
- List the open issues with the bot label (or the ones the user named), newest first.
+ Find the default branch (written `<default>` below), run `git fetch origin`, and
+ verify everything against `origin/<default>`, not a local branch. List every open issue
+ with the bot label (or the ones the user named), newest first. The calls, and the
+ check that the list is not cut off at its limit, are in [`references/mechanics.md`](references/mechanics.md).
Walk order is newest to oldest, but let evidence reorder it. A newer diff that
introduced a defect an older issue already flagged makes the older finding LEGIT, and
you fix both in one commit chain. A newer commit that fixed an older finding makes that
finding STALE: cite the sha.
+ Completion criterion: every open issue in scope is listed, newest first, with fewer
+ issues than the limit.
+
## 2. Classify every finding
For each issue, read its body and every comment (note any "fixed in <sha>" or "false
positive" from earlier runs). Read the commits it reviewed. Then write one line per
finding:
```
Finding N: [STALE | AUTO-DISMISS | HALLUCINATION | LEGIT] — <one-line evidence>
```
- `Finding 1: STALE — fixed in bf707fde3, see app/Foo.php:42`
- `Finding 3: HALLUCINATION — rg 'BarPolicy::class' returns 0; symbol does not exist`
- `Finding 4: LEGIT — app/Auth.php:88 still uses < instead of <=, exploitable on token boundary`
Verification rules:
- - For a "class, method, or permission missing" claim, `rg` the symbol across the source
- and the tests. If it exists, the finding is HALLUCINATION.
+ - For a "class, method, or permission missing" claim, `rg` the symbol where the finding
+ says it is missing (that file, call site, or test). If it is there, the finding is
+ HALLUCINATION. That it exists somewhere else does not refute a missing check.
- For an "X may break Y" claim, trace the real call sites. If nothing calls it, the
finding is STALE or HALLUCINATION.
- - A finding on a file the commit did not touch is HALLUCINATION.
+ - A finding whose file:line does not exist at the reviewed commit, or does not hold the
+ code it describes, is HALLUCINATION. A finding on a file the commit did not touch
+ stands when the change reaches it (a changed caller that breaks an unchanged callee);
+ only when nothing in the commit leads there is it HALLUCINATION.
- A documented design choice in the commit body that contradicts the finding is
AUTO-DISMISS.
- A finding whose body retracts itself ("this technically works", "disregard") is
AUTO-DISMISS.
**Delegate the legwork on a large set.** With more than 5 findings, hand the first pass
- to read-only workers in batches of about 10. Each brief carries the batch (id, reviewed
- commit, file:line, body), the ref `origin/main`, the line format above, and the
- verification rules. In Claude Code with this plugin installed, dispatch
- `subagent_type=leek-skills:finding-verifier` for each batch in parallel. Elsewhere, use any
- read-only sub-agent on a cheaper model, or classify inline when the harness has none.
- Then re-verify yourself every line that came back LEGIT or UNSURE. You read that code
- for the fix anyway.
+ to read-only workers in batches of about 10. Each brief carries the batch (id, author,
+ reviewed commit, file:line, body), the ref `origin/<default>`, the line format above,
+ and the verification rules. In Claude Code with this plugin installed, dispatch
+ `subagent_type=leek-skills:finding-verifier` for each batch in parallel; it keeps the
+ bots' recurring false premises in project memory, under this repo's `.claude/`.
+ Elsewhere, use any read-only sub-agent on a cheaper model, or classify inline when the
+ harness has none. Then re-verify yourself every line that came back LEGIT or UNSURE.
+ You read that code for the fix anyway.
**Second pass.** Re-read every LEGIT against the project's auto-dismiss list and
downgrade any match. A category on the project's never-dismiss list stays LEGIT. Do this
before writing any code.
Completion criterion: every finding in the working set has a line, and the second pass is done.
## 3. Fix the legit ones
+ Work on a new branch `fix/issues-<run>` from `origin/<default>`, in a worktree when
+ the tree is dirty ([Branch or worktree](references/mechanics.md#branch-or-worktree)).
+
Per LEGIT finding: make the minimal change, with no drive-by refactors. Add a test for
logic, auth, or data changes and run it green. Commit it on its own (Conventional
Commits, explicit staging). Put `fixes #NNN` **only** on the commit that resolves the
issue's last legit finding, so the issue closes at the right moment.
- Run tests against the repo's configured test database when you can. When the run needs
- its own (a worktree `.env`, a throwaway test database), name it `<app db>_issues_<date>`,
- never a name the main checkout's `.env`, `.env.testing`, or `phpunit.xml` uses, and
- register it the moment you create it:
+ Run tests against the repo's configured test database when you can. A run that needs
+ its own database names and registers it by [Scratch databases](references/mechanics.md#scratch-databases).
- ```bash
- printf '%s\t%s\t%s\t%s\n' "$(date +%s)" <pgsql|mysql> <database> "<worktree path, or ->" \
- >> "$(git rev-parse --path-format=absolute --git-common-dir)/scratch-databases"
- ```
+ A LEGIT finding you cannot fix (a secret rotation, infra, a human decision) keeps the
+ issue OPEN; step 5 names the blocker.
- Remove nothing during the run. Before you report, list every worktree and database you
- created and ask one yes/no question: "Can I delete everything I created for these
- issues?" On yes, remove them all: `git worktree remove` (no `--force`), then each
- database and its parallel-testing copies (`<database>_test_<N>`) with the repo's client
- (`DROP DATABASE IF EXISTS`), deleting their lines from that file. On no, remove nothing;
- an answer that names items to keep keeps those. What stays stays registered, for
- `repository-cleanup`. Skip the question when you created nothing.
+ Completion criterion: every fixable LEGIT finding has a commit on `fix/issues-<run>`,
+ with a green test where it needs one, and every unfixable one is named.
- A LEGIT finding you cannot fix (a secret rotation, infra, a human decision) keeps the
- issue OPEN. Comment to dispose of the other findings, and name what is blocking.
+ ## 4. Land the fixes
- ## 4. Close with a disposition
+ An issue closes only when its `fixes #NNN` commit reaches `<default>`, so a local
+ commit fixes nothing yet. Show the user the commits and the issues each one closes,
+ then ask once: push to `<default>`, open a pull request, or keep them local. Act only on
+ the answer. Offer a pull request first when branch protection or the repo's rules
+ forbid direct pushes. The commands are in [Land](references/mechanics.md#land).
- When the issue closes, or you close it by hand because nothing was legit, post one
- comment with one bullet per finding:
+ Completion criterion: the commits are on `<default>`, in an open pull request, or kept
+ local by the user's choice.
+ ## 5. Post a disposition
+
+ Post one comment per issue in the working set, with one bullet per finding
+ (calls in [Disposition and close](references/mechanics.md#disposition-and-close)):
+
```
- Finding 1 (X): fixed in <sha>
- Finding 2 (Y): stale — superseded by <sha>
- Finding 3 (Z): auto-dismiss — <category>
- Finding 4 (W): hallucination — symbol does not exist at claimed location
```
+ - Fixed on `<default>`: the push closed it; post the comment after.
+ - Nothing legit: post the comment, then close the issue.
+ - Fix in an open pull request or kept local: the issue stays open; the comment says where
+ the fix is.
+ - A LEGIT finding you cannot fix: the issue stays open; the comment names the blocker.
+
If you later find a misclassified finding, reopen the issue with a correction note.
- Completion criterion: every issue in the working set is closed with a disposition, or
- stays open with a comment that names its blocker, and the user answered the clean-up
- question. Report the tally: fixed, stale,
- auto-dismiss, hallucination, and blocked, and name each worktree or database kept.
+ Completion criterion: every issue has its comment, and `gh issue view N --json state`
+ agrees with it.
+
+ ## 6. Ask once, then clean up
+
+ Remove nothing before this step. List every worktree and database you created and ask
+ one yes/no question: "Can I delete everything I created for these issues?" On yes,
+ remove them all ([Clean up](references/mechanics.md#clean-up)). On no, remove nothing;
+ an answer that names items to keep keeps those. A worktree whose fixes are not on
+ `<default>` yet is not on the list. What stays stays registered, for
+ `repository-cleanup`. Skip the question when you created nothing.
+
+ Completion criterion: the user answered, or you created nothing. Report the tally:
+ fixed, stale, auto-dismiss, hallucination, and blocked. Name each fix not yet on
+ `<default>`, and each worktree or database kept.