# Review Pull Requests on a Gin API

Yes. An agent checks out the pull request, runs go vet and go test -race in its sandbox, and posts one review on errors, GORM queries and goroutines.

[Run this on Rush](https://cloud.getrush.ai/run/review-pull-requests/gin/), in a Project that has your repository.

## What the agent does

The agent reads how the API is wired before it reads the diff, because in a Gin service most review findings are about which group a route sits in and what the middleware guarantees. In gothinkster/golang-gin-realworld-example-app, `hello.go` registers `users.UsersRegister` on `/api/users` with no auth, then applies `users.AuthMiddleware(false)` for the anonymous article, tag and profile routes, then `users.AuthMiddleware(true)` for `/api/user`, profile follow and the authenticated article routes, and sets `RedirectTrailingSlash = false`. Handlers in `articles/routers.go` and `users/routers.go` read the user with `c.MustGet("my_user_model").(users.UserModel)`, which panics if a route is added to a group the middleware does not cover. `extractToken` in `users/middlewares.go` accepts a `Token` header or an `access_token` query parameter, and the signing key is the `JWTSecret` constant in `common/utils.go`, which `AGENTS.md` flags as not for production.

Then it reads the checks the repository already runs so the review quotes real output. `.github/workflows/ci.yml` tests on Go 1.21, 1.22 and 1.23, runs `scripts/gofmt.sh`, collects per-package coverage through `scripts/coverage.sh` with `-covermode=atomic`, runs golangci-lint with the repository's `.golangci.yml`, runs gosec with `-no-fail`, and then an API job that builds `hello.go`, starts it on port 8080, waits on `/api/ping` and runs the RealWorld Postman collection with Newman through `scripts/run-api-tests.sh`. `go.mod` pins `gin` v1.10.0, `gorm` v1.25.12 with the SQLite driver, `golang-jwt/jwt/v5` and `validator/v10`; the readme notes that gorm v1.30 and later has breaking changes and validator v10.30 needs Go 1.24. Tests are three `unit_test.go` files that use `common.TestDBInit` and `common.TestDBFree` around a SQLite file.

With the branch checked out the agent runs `go vet ./...` and `go test -race ./...`, which needs cgo for the SQLite driver and so is run in the sandbox rather than skipped. Then it reads the diff with the three questions the prompt names. Error handling: `ArticleUpdate` returns 404 for an unknown slug and 403 when `articleModel.AuthorID` differs from the caller, while `ArticleDelete` deletes whether or not the article exists; a new handler should match one of those contracts and `AGENTS.md` forbids discarding errors with `_`. GORM: the conventions in `AGENTS.md` are `Preload` for eager loads, `Association().Find()` for many-to-many, `Updates` for struct updates, pointers in `Delete`, and `int64` from `Count`; `FindManyArticle` in `articles/models.go` shows the house pattern of paging with `Offset` and `Limit` and then reloading with `Where("id IN ?", ids)`. Goroutine safety: `common.DB` is a package global, `UpdateContextUserModel` writes to the request context, and any new goroutine that touches either is a candidate for the race detector.

For each candidate finding the agent checks whether a test already pins the behavior and, where it can, writes a short test in the sandbox to confirm the problem before it posts, keeping that test out of the pull request. One GitHub review is posted per run: the summary quotes the `go vet` and `go test -race` results and the gofmt check, separates blocking issues from suggestions, and every finding cites file and line in `articles`, `users` or `common`. The prompt forbids pushing commits, so the agent pushes nothing.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Review this pull request on a Gin API. Check it out, run go vet ./..., go test -race ./... and bash scripts/gofmt.sh, and read the diff with the surrounding handlers, models and middleware. Look for handlers that read my_user_model from a route group without AuthMiddleware(true), errors discarded or returned with the wrong status compared with the existing handlers, GORM calls that break the conventions in AGENTS.md, and goroutines that touch common.DB or the request context. Confirm a finding with a short test in the sandbox where you can, but do not add it to the branch. Post one GitHub review that quotes the vet, race and gofmt results, separates blocking issues from suggestions, and cites file and line for every finding. Do not push commits.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app/pull/42"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a review on the pull request. The summary quotes the `go vet ./...` result, the `go test -race ./...` result with any race report the detector printed, and the gofmt check, then lists the findings with file and line in the package they belong to, marked as blocking or suggestion. Inline comments sit on the cited lines, and a finding about a GORM call names the convention from `AGENTS.md` it departs from.

The session's trajectory keeps the module download, every command and its output, the short tests the agent wrote to confirm a finding, and the files it opened while tracing a handler into its model. When the review says a route can panic on `MustGet`, the trajectory shows the request it sent to prove it.

Before merging, a reviewer reproduces the blocking findings with `go test -race ./...` on the branch, opens each cited line, and dismisses suggestions that do not hold. A race report is blocking by default; a style finding is not.

## Run it on every pull request opened

Create an automation in the Project with a `github` trigger on `pull_request.opened` and `pull_request.synchronize`, limited to the branch the API merges to, and this prompt. The trigger adds the pull request after the prompt, so every pull request gets its own sandbox, its own race run and its own review.

The review is the result: it appears on the pull request, and the run shows in the Project like one started from the console. The automation's max runs per hour, 1 to 30, caps a burst of small pull requests; a fire past it is skipped as `rate_limited` and that pull request waits for its next push, so set the cap to the busiest hour you expect.

## Why run it on Rush?

The findings that matter in a Gin API are the ones a diff does not show: a route in the wrong group, a status code that disagrees with its neighbors, a write to a shared `*gorm.DB` from a new goroutine. Catching them means checking out the branch, building with cgo for SQLite and running the race detector, which is why most reviews skip it. On Rush the agent does that in its own sandbox and keeps every command and its output, so the review rests on an executed suite rather than a reading.

One sandbox per pull request keeps reviews parallel when the API takes changes from several contributors at once, each with its own `go test -race` run and its own SQLite file. The author keeps their checkout, the reviewer gets findings with the evidence attached, and nothing is pushed to the branch.

## Frequently asked questions

### Which Go version does it use?

The CI matrix runs 1.21, 1.22 and 1.23 and `go.mod` says 1.21, so the agent uses the newest of those for the race run and notes if the pull request moves the `go` directive. A dependency that needs a newer Go than the matrix, like validator v10.30, is a finding.

### Does the race detector need anything special here?

cgo, because `gorm.io/driver/sqlite` builds on `mattn/go-sqlite3`. The sandbox has a C toolchain, so `go test -race ./...` runs as written. The tests share a SQLite file through `TEST_DB_PATH`, so the agent runs packages serially if a race report points at the test database rather than the code.

### Will it run the Newman API tests too?

Ask for it in the prompt and the agent runs `scripts/run-api-tests.sh`, which builds the server, starts it on port 8080 and runs the RealWorld Postman collection. The review then quotes the collection result alongside the unit tests. It is left out by default because it downloads the collection from the upstream repository at run time.

### What does it do with the hard-coded JWT secret?

If the pull request touches `common/utils.go`, `users/middlewares.go` or `common/test_helpers.go`, the review checks that the secret still comes from one place and that the tests still sign tokens with it. A change that reads the secret from an environment variable gets a finding if the test helper or CI was not updated to set it.
