# gt-review (https://condensate.dev/apps/review/)

> Run the review hub, register checkouts and arm review rooms.

gt-review is the review hub. It serves each registered checkout through its own repository instance and runs review rooms, where agent reviewers and a fixer work on a branch as Condensate threads. The hub serves the review API and no review page. The review interface is [Dex](https://condensate.dev/apps/dex/), which reads the hub's `/api` routes through its `/hub/*` proxy. This page covers starting the hub, registering checkouts and running review rooms.

## Start the hub

From `apps/gt-review`:

```sh
bun run build
bun dist/cli.js hub --no-open
```

The hub listens on `http://127.0.0.1:4700` by default. Pass `--port` to choose another port. To reach the hub from your tailnet, keep the default bind and put `tailscale serve http://127.0.0.1:4700` in front of it. Do not bind the hub to all interfaces with `--host 0.0.0.0` unless you also turn on its admin-token gate. The gate is off by default, so such a hub accepts POST, PATCH and DELETE requests from any host that can reach it, with no authentication. To turn the gate on, start the hub with `GTR_REQUIRE_ADMIN_TOKEN=1` and a secret in `GTR_ADMIN_TOKEN`. The hub then refuses those requests unless they send the same secret in the `x-gtr-admin-token` header. Dex does not send that header, so writes through Dex fail while the gate is on. A request to `/` returns one line of plain text saying the port serves the review API. `GET /api/health` tells you the hub is up, and `GET /api/hub/repos` lists the checkouts it serves.

The hub runs reviewers and fixers as runtime threads. It reaches the runtime at `CONDENSATE_AGENTS_URL`, which defaults to `http://127.0.0.1:4741`. If you installed Condensate with the installer, start the hub with the ports the installer printed: `CONDENSATE_AGENTS_URL=http://127.0.0.1:<runtime port> bun dist/cli.js hub --no-open --port <hub port>`.

Reviews run only in review rooms. You arm a room yourself, or the hub arms it for you after a pull request sync when automatic arming is on, as described below.

## Register a checkout

The hub serves only checkouts in its registry. Register each one with a path to a git checkout:

```sh
bun dist/cli.js register /absolute/path/to/checkout
```

If a hub is running, `register` adds the checkout to it at once. If none is running, the checkout is saved and served the next time the hub starts. Registering the same checkout twice does nothing. If you installed Condensate with the installer, `register` reads `HUB_PORT` from the installation's `install.env`. It looks in `CONDENSATE_INSTALL_DIR`, or in `~/.local/state/condensate-fresh` when that is unset, so set `CONDENSATE_INSTALL_DIR` as the installer printed it. With no installation, `register` uses port 4700. The [quickstart](https://condensate.dev/start/quickstart/) shows the full local setup.

## Review a repository

Open Dex with the hub running. Choose the registered repository, inspect the changes and the discussion, and open the review thread you need. Choosing the repository matters. A hub can serve several repositories, each under its own `/<repo-name>/` path, and every request has to name the right one.

## Review rooms

A review room holds the review of one branch. When you arm a room, it gets:

| Part | What it does |
|---|---|
| Reviewer per engine | Reads the branch diff and raises findings. An arm can name up to two engines. |
| Standing fixer | Works on the findings the reviewers raise. |
| Rounds | Gives the reviewers and the fixer another turn each, so the reviewers can challenge each other's findings. Start rounds with `POST /api/review-rooms/<branch>/rounds`. One request runs one to three rounds. |

The reviewer engines are Claude Code (engine id `claude`), Condensate's own agent (`pi`) and Codex (`codex`). Condensate's own agent runs OpenAI models on a Codex login. An arm with no `engines` starts the reviewer that the `models.reviewAgent` setting names, which is Claude by default.

Arm one branch with `POST /api/pr-reviewer/arm`. Arm a whole stack, bottom to top, with `POST /api/pr-reviewer/arm-stack`. You can also let the hub arm rooms for you. After it syncs a repository's pull requests, it arms up to three rooms per pass. It considers only open, non-draft pull requests that target the trunk branch and come from the same repository, not a fork. It skips a branch a person disarmed at its current head, and a branch whose last automatic arm failed waits between one and 30 minutes before the next try. This is on by default for the `condensate` workspace. The variables below control it:

| Setting | Effect |
|---|---|
| `GTR_AUTO_REVIEW=0` | Turns automatic arming off for every workspace. |
| `GTR_AUTO_REVIEW_<WORKSPACE>=0` | Turns it off for one workspace, such as `GTR_AUTO_REVIEW_CONDENSATE=0`. |
| `GTR_AUTO_REVIEW_<WORKSPACE>=1` | Turns it on for a workspace other than `condensate`. |

Two pull request labels change what happens. `no-review` leaves the pull request out of automatic arming. `no-autofix` turns off automatic fixing for that pull request. Automatic arming never turns on auto-merge.

### Models

- The reviewer's model comes from the `models.reviewAgent` setting. The fixer's model comes from `models.gtFixer`. If that setting names no model, the fixer runs Claude Opus 5.5 with a 1M context window.
- The standing fixer reads `models.gtFixer` when it starts. A change reaches a running fixer only after the fixer is replaced. A per-finding fixer reads the setting each time it is dispatched.
- Re-arming a room keeps its existing fixer and its model, until the fixer is replaced or the room is pruned.

### Account limits

If every account for an engine is out of quota, the hub does not start a reviewer on that engine. It runs that reviewer's lens on Claude instead and tells the room why, with the time the quota resets. If Claude's accounts are out of quota too, no reviewer can run, and the runtime refuses the turn and raises an alert.

## Deployments

The installer does not start the hub. It prints the command to start it. To run the hub as a container, use the repository's `docker-compose.yml`, which binds the hub to `127.0.0.1:4700` on the host. It also picks up checkouts placed in its `./repos` directory. `deploy.sh` redeploys a container to a machine on your tailnet and keeps that machine's `.env` and cloned repositories.

Follow your deployment's update procedure, and keep the runtime's data directory. Local development commands do not update a deployed hub.

A hub restart does not drop a review round in flight. The round's progress is saved with the repository, and the reviewer and fixer agents run as runtime threads. When the hub starts again, it reattaches to the rounds that are still running. Rebuilding the hub is safe at any time.
