ai.oz0/pr-reviewer
For: tenant administrators and plugin users.
ai.oz0/pr-reviewer reads a pull request, runs its tests and returns findings with a verdict. Use it to gather review evidence before a person decides what to do with the pull request.
oz0-pr-reviewer
The agent takes {"repository": "owner/name", "pull_number": 42}. It reads the pull request, its diff, files and checks with ai.oz0/github's read functions (oz0_github.get_pull_request, get_pull_request_diff, list_pull_request_files, get_file, list_checks), downloads its head commit with oz0_github.download_archive, runs the tests with oz0_pr_reviewer.run_tests, and answers with a review in a fixed shape.
It has no function that writes, so a pull request whose text asks it to approve, label or merge has nothing to call. A judge holds the review to a rubric before the job ends (outcome checks); a review that fails goes back once with the reasons.
The rubric is plugins/pr-reviewer/rubrics/review.md in the private repository. Reading its source requires repository access; public source downloads are coming soon.
oz0_pr_reviewer.run_tests
This function takes the archive download_archive returned: archive: oz0-file:sha256:<hex>, at most 32 MiB. The runtime hands it to the function as a file of its own.
The function unpacks the archive with every member checked in a separate sandbox. That sandbox has no network and writes only to the call's directory. The function finds how the checkout runs its tests: Go, pytest, unittest or npm.
The tests run in their own sandbox tier 1. No secret, setting or node process is visible, including the tool server. Network access follows only the tenant's egress allowlist. Writes stay in the checkout, which is deleted afterwards.
Dependencies are installed there first: a Python project that declares any gets a virtual environment with the project and its test, tests, testing or dev extras and its requirements files (pip install), an npm project npm ci --ignore-scripts (npm install --ignore-scripts without a lockfile); a Python project without dependencies runs offline with the plugin's own pytest. It refuses on a node without tier 1 (it never runs a pull request's code at tier 0); the agent then reviews the diff and says the tests did not run, and why.
oz0-pr-review, the flow, runs the agent and then posts the review as a comment withoz0_github.comment, the only write anything in this plugin makes. A retried step does not comment twice.
Install
Its agent oz0-pr-reviewer asks for the tenant's default model alias with tools, never a vendor's model, so it runs on whichever provider the tenant set up first; map it elsewhere with oz0 model alias set default <provider>/<model>.
oz0 plugin install hub:ai.oz0/github
oz0 plugin install hub:ai.oz0/pr-reviewer
oz0 run oz0-pr-review '{"repository": "acme/shop", "pull_number": 42}'
The second install is refused once with what pr-reviewer asks of ai.oz0/github (its five read functions and
comment) and a digest; a tenant admin grants it by installing again with --accept-uses <digest>
(uses). Connect ai.oz0/github first (its page).
pr-reviewer has no settings and holds no secret: every call to GitHub, the archive's download included, is
ai.oz0/github's, with the tenant's GitHub App (or its GITHUB_TOKEN), so a private repository and a GitHub
Enterprise Server need nothing more than github's connection. Earlier builds read PR_REVIEWER_GITHUB_TOKEN and
PR_REVIEWER_GITHUB_API_URL; if you set them, delete them (oz0 secret delete PR_REVIEWER_GITHUB_TOKEN).
Tests that download dependencies need their registries on the tenant's allowlist, such as proxy.golang.org and
sum.golang.org, pypi.org and files.pythonhosted.org, or registry.npmjs.org; when one is missing, the result
says "not run: installing the dependencies failed" and names what the allowlist refused.
On a node with sandbox tier 1
oz0_pr_reviewer starts sandboxes of its own, and a sandbox cannot start another, so on a node with the default
--plugin-sandbox strict it does not start until the node's operator allows it on that node:
oz0-node service install --plugin-sandbox-exempt pr-reviewer # or: oz0-node run --plugin-sandbox-exempt pr-reviewer
Allow it before the install: an install's quality gate runs the plugin's evals on a test node, and a node that holds the tool back fails them, with this reason on the gate's line. Until then the plugin's status and the node's findings say so. The tests still run in their own sandbox; the server that starts them runs as the node's user, which is why that is the operator's choice.
Reviews a webhook starts
A review that a pull request's webhook or a routine starts runs its tests too. Such a job runs its tools in a sandbox of its own (outside input, sandboxes), and a sandbox cannot start another, so oz0_pr_reviewer declares sandbox: own: in such a job its calls run in pr-reviewer's own server on the job's tier-1 home node, outside the job's sandbox, and start the tests' sandbox from there.
That is allowed only because the plugin holds nothing (no secret, setting, connection or egress), which lint, the node and the edge each check; the edge records every such call in the audit trail as tool.own_sandbox, with its job, function and node. pr-reviewer's server never reads the pull request's bytes itself: it copies the archive into the call's directory and unpacks it in a sandbox.
Its server runs up to four reviews' tests at once. ai.oz0/github's functions in the same job still run in the job's sandbox, whose egress allows api.github.com and codeload.github.com, where GitHub sends the archive; on GitHub Enterprise Server add its host (and codeload.<host> with subdomain isolation) to the tenant's egress list. A copy of pr-reviewer sent to a cluster as a private candidate (oz0 dev --server) does not run tests: a candidate's tool servers always run in the node's sandbox (private candidates), so the review says the tests were not run.
A flow of your own team's plugin that reviews each new pull request (its plugin declares uses: {pr-reviewer: {agents: [oz0-pr-reviewer]}}):
name: review-prs
description: Reviews a pull request when it is opened
trigger:
event: {source: github, types: [pull_request], filter: "event.action == 'opened'"}
steps:
- id: review
agent: oz0-pr-reviewer
input:
repository: ${{ trigger.event.attributes.repo }}
pull_number: ${{ trigger.event.attributes.number }}
output: ${{ steps.review }}
Add a step with oz0_github.comment to post it, as oz0-pr-review does (flows).
The tests may run for 30 minutes (timeout_s, ten by default); one call of the tool may take 35 (timeout: 35m in
its manifest).
Run the agent on Linux nodes with bubblewrap, or Macs (oz0-node sandbox check shows a node's tier). A
job a person starts is not routed by tier, so in a fleet with tier-0 nodes, label the tier-1 nodes and require the
label in an overlay of the agent.