---
name: geminiloop
description: >
  Iteratively satisfy Gemini Code Assist's review on a GitHub PR, but treat every Gemini comment
  SKEPTICALLY (Gemini Code Assist is materially more error-prone than Greptile): verify each finding
  against the real code first, fix ONLY the genuinely-correct ones, and rebut + resolve false
  positives WITHOUT changing correct code. Repeat until no unresolved Gemini comments remain. Use
  when the user wants to clear a PR's Gemini Code Assist review.
compatibility: GitHub only (Gemini Code Assist is a GitHub app). Requires git + gh (GitHub CLI) authenticated, and the Gemini Code Assist app installed on the repo.
metadata:
  version: "1.0"
allowed-tools: Bash(gh:*) Bash(git:*)
---

# Geminiloop

Drive a GitHub PR until Gemini Code Assist has no unresolved comments, **but do not cargo-cult
its suggestions.** Gemini is often confidently wrong. Every comment is a *claim to verify*, not an
instruction to obey. A wrong suggestion applied is worse than the comment itself.

## How Gemini differs from other review bots

- **No check-run, no score.** Gemini does not publish a `X/5` confidence or a `greptile` check. It
  posts a **PR review** (state `COMMENTED`) authored by `gemini-code-assist[bot]` with a
  `## Code Review` summary body plus inline review comments. Detection is by polling the *reviews*
  endpoint, not a check-run. "Satisfied" = zero unresolved comments (each either fixed or rebutted),
  since there is no numeric target.
- **Auto-reviews on push/open.** It reviews automatically when a PR opens or gets new commits;
  `/gemini review` forces a fresh pass.
- **Severity, not confidence.** Inline comments carry a priority badge (a `![critical]` /
  `![high]` / `![medium]` / `![low]` shields image) at the top of the body. Weight `critical`/`high`
  seriously; treat `medium`/`low` as usually-skippable nits unless clearly correct.
- **Higher false-positive rate.** This is the whole point of the skill: bias toward *rebut* over
  *change*.

## Not for

- GitLab / Perforce (Gemini Code Assist is GitHub-only). For other review bots this catalog ships
  `codexloop`, `coderabbitloop`, and `qodoloop`; for CI failures rather than review comments, use
  `ci-fix-loop`.

## 1. Identify the PR

```bash
gh pr view --json number,headRefName,headRefOid -q '{number,branch:.headRefName,head:.headRefOid}'
```

Switch to the PR branch if not already on it. Capture `OWNER`/`REPO` (`gh repo view --json owner,name`).

## 2. The loop (max 5 iterations)

Keep an explicit iteration counter and stop at 5: the cap is a real bound to enforce, not a
figure of speech. Each pass through A–G is one iteration; on hitting the cap, go straight to the
report and list what is still unresolved rather than starting a sixth.

### A. Ensure a fresh Gemini review on the current head

Gemini auto-reviews new commits, but force a deterministic pass and record the head SHA:

```bash
HEAD_SHA=$(gh pr view <PR> --json headRefOid -q .headRefOid)
# Only trigger if no gemini review already exists for this exact SHA:
HAVE=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate \
  --jq "[.[] | select(.user.login==\"gemini-code-assist[bot]\" and .commit_id==\"$HEAD_SHA\")] | length")
if [ "$HAVE" = "0" ]; then gh pr comment <PR> --body "/gemini review"; fi
```

Poll for the review of THIS head to land. No check-run exists, so poll the reviews endpoint, and
poll it on a **deadline**, never `while true`: a review that never arrives must end the skill with
an honest timeout, not hang it.

```bash
# 10-minute deadline, one retry, then give up.
wait_for_review() {                       # $1 = attempt label
  local deadline=$(( SECONDS + 600 ))
  while [ "$SECONDS" -lt "$deadline" ]; do
    R=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate \
      --jq "[.[] | select(.user.login==\"gemini-code-assist[bot]\" and .commit_id==\"$HEAD_SHA\")] | last")
    if [ -n "$R" ] && [ "$R" != "null" ]; then return 0; fi
    echo "waiting for Gemini review of $HEAD_SHA ($1)..."; sleep 15
  done
  return 1
}

if ! wait_for_review "first wait"; then
  echo "no Gemini review after 10m, retrying once"     # say the retry out loud
  gh pr comment <PR> --body "/gemini review"
  if ! wait_for_review "after retry"; then
    echo "Gemini did not review $HEAD_SHA after a retry; stopping and reporting."
    exit 1                                # honest timeout, never a success claim
  fi
fi
```

Report the retry in the final summary; two silent timeouts are the failure mode this guard exists
to prevent.

### B. Fetch the findings

- **Summary** (the `## Code Review` body): the review `.body` from the object above: read the
  overall take and the severity spread.
- **Unresolved inline comments** on the current head:

```bash
gh api repos/{owner}/{repo}/pulls/<PR>/comments --paginate \
  --jq '.[] | select(.user.login=="gemini-code-assist[bot]") | {id, path, line, body}'
```

Also pull the review threads + their resolved state via GraphQL (see step F) so you only act on
unresolved ones.

### C. Critically evaluate EACH comment (the core of this skill)

For every comment, **verify the claim against the actual code and repo conventions before touching
anything.** Read the file, the surrounding code, the types, and any call sites. Then classify:

1. **CORRECT + actionable**: the finding is real and the fix improves the code. → fix it (step D).
2. **FALSE POSITIVE / technically wrong**: the claim doesn't hold. → do **NOT** change code; write a
   specific, evidence-based reply (cite the exact code/line/behavior that disproves it), then resolve.
3. **Valid but out-of-scope / stylistic nit** that conflicts with repo convention or the PR's intent
   → briefly decline with a reason, then resolve. Do not expand the PR's scope to satisfy a nit.

**Hard rules:**
- **Never modify correct code just to silence Gemini.** Prefer a reasoned rebuttal.
- When uncertain whether a claim holds, **investigate** (read more code, run the type-checker / tests)
  rather than assume Gemini is right. Default to skepticism.
- If a suggested change would break other call sites, alter public behavior, or contradict a verified
  repo convention, it is a category-2 rebuttal, not a fix.
- Never fabricate identifiers to satisfy a comment (e.g. a Linear/ticket prefix). If Gemini asks for a
  ticket reference and none exists, say so; do not invent one.

**Gemini's common failure modes to watch for (default these to category 2):**
- Hallucinated APIs, options, or framework behavior stated as fact.
- "Add a null/undefined check" where the type already guarantees presence.
- Suggestions that compile-break or break other callers.
- Security/perf warnings with no actual exploit path or measurable cost.
- Restating library/framework semantics incorrectly.
- Style demands that contradict the repo's existing, consistent pattern.

### D. Apply fixes: category 1 only

Make the minimal correct change. Re-run the local gate if the repo has one (typecheck/tests) before
moving on.

### E. Commit and push FIRST, before resolving anything

Order matters. A resolved thread is a claim that the fix is on the branch, so the push has to
succeed before the claim is made: otherwise a failed commit or push leaves the PR unfixed with the
finding marked resolved, and nobody looks at it again.

If step D changed code:

```bash
# Stage ONLY the files your fixes touched: never `git add -A`, which sweeps up
# unrelated work and untracked secrets sitting in the worktree.
git status --short                     # look before you stage
git add <path> [<path>...]             # the files named in the findings you fixed
git commit -m "address gemini review feedback (geminiloop iteration N)"
git push
```

Author the commit per the repo's norms (e.g. the user's identity; no AI attribution if that is the
convention). Confirm the push actually landed before continuing:

```bash
git rev-parse HEAD
gh pr view <PR> --json headRefOid -q .headRefOid   # must match
```

If they differ, stop: the fix is not on the PR, so nothing may be resolved yet.

### F. Reply to and resolve every addressed thread

Only now, with the fixes pushed, reply and resolve. Fetch unresolved threads, **following
pagination**: a PR with more than 100 threads will otherwise look clean while unresolved findings
sit on page two:

```bash
# Loop until hasNextPage is false, passing endCursor back in as $cursor.
CURSOR=null
while : ; do
  PAGE=$(gh api graphql -F cursor="$CURSOR" -f query='
  query($cursor: String) {
    repository(owner: "OWNER", name: "REPO") {
      pullRequest(number: PR_NUMBER) {
        reviewThreads(first: 100, after: $cursor) {
          pageInfo { hasNextPage endCursor }
          nodes { id isResolved comments(first: 1) { nodes { databaseId author { login } path body } } }
        }
      }
    }
  }')
  echo "$PAGE"          # collect nodes from every page before deciding the PR is clean
  PI='.data.repository.pullRequest.reviewThreads.pageInfo'
  [ "$(echo "$PAGE" | jq -r "$PI.hasNextPage")" = "true" ] || break
  CURSOR=$(echo "$PAGE" | jq -r "$PI.endCursor")
done
```

Reply on a thread's comment via `gh api repos/{owner}/{repo}/pulls/<PR>/comments -f body="..." -F in_reply_to=<comment_id>`,
then resolve:

```bash
gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "THREAD_ID"}) { thread { isResolved } } }'
```

Resolve a thread only for comments authored by `gemini-code-assist[bot]` that you have fixed or
rebutted: never blanket-resolve, and never resolve a human reviewer's thread.

Threads you are **rebutting** need no push, so they may be replied to and resolved regardless of
whether step D changed code.

### G. Re-review

Pushing re-triggers Gemini automatically; go back to **A** with the new head SHA. If step D changed
nothing (all comments were rebutted), skip the push, ensure all threads are resolved, and exit.

## 3. Exit conditions

Stop when **any** is true:
- Zero unresolved `gemini-code-assist[bot]` comments remain, and every comment this round was fixed
  or rebutted+resolved. (There is no score to hit: this is "done".)
- Max iterations (5) reached: report what remains.

## 4. Report

```text
Geminiloop complete.
  PR:                 #<n>
  Iterations:         N
  Comments fixed:     N   (genuinely-correct findings)
  Comments rebutted:  N   (false positives / nits, resolved with rationale)
  Remaining:          0
```

If it stopped at max iterations, list the remaining threads with your current assessment
(fix-pending vs disputed) so a human can arbitrate.
