# Set up Code Terrier

You are helping the user connect their coding agent to Code Terrier. Code Terrier is a GitHub App that reviews pull requests with up to three model families in parallel, merges duplicate findings, and passes the result through an independent verification stage before publication. It posts findings as line-anchored comments on the pull request and sets a commit status on the head commit.

Follow these steps in order. Do not make global changes or handle credentials without the user's consent. Pause when a step fails and report the exact output.

---

## Step 1: Confirm the install

Code Terrier runs as a GitHub App. Confirm the App is installed on the repository's owner (the user does this once in a browser; there is nothing for you to install). You can verify with the GitHub CLI:

```bash
gh app list 2>/dev/null || true
```

If the App is not installed, stop and tell the user to install it from <https://github.com/apps/code-terrier>. You cannot review without an installation.

---

## Step 2: Open the pull request you want reviewed

Reviews are anchored to a pull request. Determine the target:

```bash
gh pr list --repo OWNER/REPO
PR=$(gh pr view PR_OR_URL --repo OWNER/REPO --json number,headRefOid -q '{number, head: .headRefOid}' | jq -r .number)
```

Code Terrier reviews the current head commit of the PR. When the user says "review this PR", pick the open PR for the current branch. If you are about to open one, open it first, then poll below.

If Code Terrier is in `manual` trigger mode (or you want a review now regardless of mode), comment on the PR with a line starting with `@code-terrier review`:

```bash
gh pr comment "$PR" --repo OWNER/REPO --body "@code-terrier review"
```

---

## Step 3: Poll for the review

Code Terrier reviews on its own schedule and posts **one `COMMENT` review** — it never `APPROVE`s, never `REQUEST_CHANGES`, never auto-merges, and never pushes or commits code. Wait for it by polling the **commit status under context `Code Terrier Review`** on the head SHA:

```bash
# Poll every 60s until the status is terminal (state is not empty or "pending").
# The status does not exist yet while the review is queued, so STATE is empty
# then — keep polling in that window instead of treating it as done.
while :; do
  # Re-resolve SHA every iteration: the status is attached to the head commit
  # the review ran on, so after a fix push the head moves and the old commit's
  # terminal status must not satisfy this loop.
  SHA=$(gh pr view "$PR" --repo OWNER/REPO --json headRefOid -q .headRefOid)
  # GitHub appends rather than updates statuses, so take only the LAST entry for
  # the context: a fresh "pending" after an earlier terminal status must not be
  # masked by the stale entry. Sort by created_at so the choice of "last" is
  # deterministic regardless of GitHub's array ordering.
  STATUS=$(gh api "repos/OWNER/REPO/commits/$SHA/status" --jq '[.statuses[] | select(.context == "Code Terrier Review")] | sort_by(.created_at) | last? // empty | {state, description}')
  # A failed gh api (expired auth, rate limit, closed PR) must surface, not loop
  # silently forever — the doc contract is "pause when a step fails and report".
  if [ $? -ne 0 ]; then
    echo "gh api failed — report to the user and stop polling." >&2
    exit 1
  fi
  STATE=$(printf '%s' "$STATUS" | jq -r '.state // ""')
  DESC=$(printf '%s' "$STATUS" | jq -r '.description // ""')
  echo "[$STATE] $DESC"
  [ -n "$STATE" ] && [ "$STATE" != "pending" ] && break
  sleep 60
done
```

The status lifecycle:

| State | Description | Meaning |
|---|---|---|
| `pending` | `Analyzing your changes…` | Review in progress. Keep polling. |
| `success` | `No issues found` | Review complete and clean. |
| `success` | `No substantive changes` | Review complete — nothing reviewable (formatting-only diff). Treat as clean. |
| `success` | `3 issues · 1 security, 2 bugs` | Review complete; findings exist. Read them (Step 4). |
| `success` | `Review unavailable - usage limit reached, see comment` | Quota exhausted; a comment explains. Report to the user. |
| `success` | `Review unavailable - infra failure, see comment` | Infrastructure problem; a comment explains. Report to the user. |
| `error` | `Review failed to complete` | The review failed. Report the state to the user. |

The status carries the bot's own account when it runs on the shared pool; `pending` may last 60–90 seconds or longer on large PRs. A status that never leaves `pending` means the review is still queued — keep polling, and surface it to the user only if it is stuck for many minutes.

A PR with a clean review also gets a review body of exactly:

```
**Code Terrier** reviewed the changes — no issues found. ✅
```

If you see that body, the PR is done. If the status or body disagrees with the state, trust the **status context** — it is the machine-readable signal.

---

## Step 4: Read the findings

Findings are **inline review comments** from `code-terrier[bot]`, each anchored to a changed line. Every finding ends with a stable hidden anchor you can use to identify and dedupe it:

```html
<!-- codeterrier {"id":"…","category":"bug","severity":"high"} -->
```

Fetch them:

```bash
gh api "repos/OWNER/REPO/pulls/$PR/comments" \
  --jq '.[] | select(.user.login == "code-terrier[bot]") | {comment_id: .id, path, line, body}'
```

The stable finding identity is the `"id"` **inside the `<!-- codeterrier {…} -->` anchor in `body`** — not the `comment_id` GitHub assigns (that changes on every re-post). Parse the anchor id out of `body` to dedupe.

For each finding, read its category and severity from the anchor, and the fix it proposes:

- **Security** — a security hole. Treat seriously; do not apply a blind fix.
- **Bug** — a defect that breaks an existing caller, contract, or runtime path.
- **Flag** — a verification request, not an assertion. It names a latent contract risk; verify the premise.
- **Slop** — provably-removable dead code (pre-existing only; dead code new in the PR is a bug).

Findings on a changed line may carry a one-click `suggestion` block — a ```` ```suggestion ```` code block with the replacement. You may apply it directly to the file.

A finding's anchor `id` is stable across pushes (it is a hash of the file and title, not the line or the comment), so a finding that moved to a different line or was re-posted on a new head is the *same* finding. Dedupe by the anchor `id`, not by line number and not by `comment_id`.

---

## Step 5: Respond and push fixes

Code Terrier does not read chat replies. Instead, the **next review** (triggered by the next commit) reads your disposition on each finding, so settle each thread explicitly.

For each finding you decide to address:

1. Apply the fix to the changed line (or commit the `suggestion`).
2. Reply to the inline thread with the disposition word that tells the next review what you did:

   - Reply `Adopted` when you fixed it — the finding stays resolved.
   - Reply `Skipped` when you deliberately chose not to fix it — it is dismissed.
   - Reply `Escalate` when you want a human to decide — it stays visible for the human.

   ```bash
   gh api "repos/OWNER/REPO/pulls/$PR/comments/$COMMENT_ID/replies" \
     -f body="Adopted"
   ```

3. Commit and push. A new commit on the PR starts the **next** Code Terrier review automatically (in `auto` mode) and it re-reads your dispositions as bounded, untrusted context.

Repeat Step 3 (poll the `Code Terrier Review` status) after each push until the head SHA carries `success` with `No issues found`. This is the loop's close condition: **a clean review on the latest head commit.** You do not need to wait for a human.

---

## Optional: install the review loop as a skill

Instead of following the steps above each time, install the workflow as a reusable skill so future PRs pick it up automatically:

```bash
npx skills add https://codeterrier.com/skills/code-terrier.zip
```

This installs a `code-terrier` skill that encodes the poll-and-respond loop (wait for the `Code Terrier Review` status, read the anchored findings, reply `Adopted` / `Skipped` / `Escalate`, push, and re-poll until clean). The skill follows progressive disclosure: a lean `SKILL.md` for the common loop plus `references/` files (`poll`, `findings`, `disposition`, `troubleshooting`) loaded on demand. Prefer project scope so the change is limited to the current project; add `-g` for global scope only after the user explicitly agrees. The skill is served from codeterrier.com and does not require access to the source repository.

---

## Troubleshooting

- **No status appears after several minutes** — the App may not be installed on the owner, or the trigger mode is `manual`/`on-creation` for this change. Comment `@code-terrier review` to force a review.
- **Status `pending` for a long time** — the review is queued or running (large PRs take longer). Keep polling; do not assume failure.
- **Status `error`** — the review infrastructure failed. Report it; there is no agent-side fix.
- **Status `success` with an "unavailable" description** — quota or infra skip; the bot left a comment on the PR explaining. Report it to the user.
- **A finding's line number has changed** — match by the `id` in the anchor, not the line; the finding likely moved with the code.
- **You cannot apply a `suggestion` to a cross-file finding** — cross-file findings ride in the review body, not as inline comments, and carry no suggestion block. Address them in code as a normal bug fix.

Never paste the user's GitHub App token, API keys, or secrets into prompts, source files, or committed configuration. Code Terrier only reads; it never asks you to add credentials anywhere.
