cujo
Skip to content
Contents

How a review runs

Webhook to sandbox to review, in the order it happens.

The path

Where the trust boundary fallsTrustedDisposableGitHubpull requestapps/cujoverifies, foldsharnessruns the agentSandboxthe PR executes heretestsprobessmokedetonationgithub-mcpholds the keycodereportsone review

GitHub delivers a webhook to the Cujo service, which starts one turn on the agent harness. The harness runs the pull request inside a disposable sandbox. Only the pull request’s code and its dependency names cross into that sandbox; only JSON reports come back. The review is posted through a separate MCP server, which is where the credentials live — on the trusted side, and never in the box that ran the code.

Step by step

  1. 01
    A pull request arrives.

    The App fires a pull_request webhook. The service verifies the signature before it reads anything else, applies the entry filters, and reacts on the pull request.

  2. 02
    The service picks the review, and one turn starts.

    There are two reviews. The sandbox review, steps 3 to 5 below, runs the pull request. The diff review reads it: the service compresses the diff, reads the repository’s own standards files — AGENTS.md, CLAUDE.md, CONTRIBUTING.md, .github/copilot-instructions.md — at the base commit, and hands both to a cheap model on a session with no sandbox and a token budget. It posts one advisory review with findings of at most warn, because a block needs evidence only execution can give. Which review a pull request gets is mode in .cujo.yml, read from base; a dependency-manifest change or a Bot author is always the sandbox.

    Either way the turn’s context is the repository, the number, base and head SHA, and the changed-file list. The service stays subscribed to the turn’s event stream and folds what it sees into a run you can watch while it is still going.

  3. 03
    Into the sandbox.

    The agent provisions a box and runs two commands. The first clones head, adds a worktree at base, and hands back the base branch’s policy together with head’s build files, so the commands are settled in one step. The second seeds a decoy secret and starts the logging proxy and the decoy watcher.

  4. 04
    Run the checks.

    Each is a subagent with fresh context and no access to the others’ reports. Detonation starts first, during setup, because it installs into its own environment and needs nothing the repository’s install produces; tests, probes and smoke go together once that install is done. Each returns one JSON report. What each measures.

  5. 05
    Decide.

    The reports become findings. Hard rules force critical on the dangerous cases and the agent cannot argue with them; the agent judges everything else against its rubric. Findings and severity.

  6. 06
    Post.

    With no critical, the review posts as a comment. Any critical posts as REQUEST_CHANGES at once, and the cujo/guard check run on the commit fails, which is what holds the merge under branch protection. Nobody is asked; a maintainer lifts the block on the pull request. Blocking and the unlock.

What a new push does

Only the newest head is reviewed.

A pull request has one session for its whole life, and each push runs a fresh turn in it. When a new head arrives while a run is still going, that run ends superseded and the new head gets its own. If the superseded run was waiting on a person, that wait is answered too — the question was about a commit that no longer exists.

The service will not review a head cujo-guard[bot] has already reviewed, so a redelivered webhook costs nothing. To review the current head again deliberately, comment /cujo review.