How it works

How Shipmoor Code Review works

The local-first engine that reads a change the way a careful reviewer would, reports advisory findings, and never gates. Bring your own agent. The orchestration is deterministic, the judgment is not, and that is exactly why review advises while the scan stays the gate.

This is the reading layer. Where the deterministic scan answers structural questions with the same finding ids every time, Code Review asks the soft questions a reviewer asks out loud, and it answers them with an agent you bring. It is advisory by nature: it reports and exits zero. The scan is the only enforcer, and that split is the whole point.


1. The problem

The deterministic scan was built to answer questions that have one right answer: does this package exist, is this body a stub, is this a trust bypass. It is fast, reproducible, and correct because it never has an opinion.

A real reviewer asks a different kind of question, the kind that has no AST node and no registry lookup behind it:

  • is this the right SQL, or just SQL that runs
  • should that handle be closed before the function returns
  • is this the discount, or the last item in the list
  • does this except swallow the failure the caller needed to see
  • does the change actually do what the surrounding code implies it should

These are reading-level judgments. They depend on intent, on the rest of the file, on what the change was for. No deterministic rule reaches them, and a rule that tried would be wrong often enough to be noise. The honest answer is that this layer needs a reader, and a reader is a model.

So Code Review draws a clean line. The scan keeps the questions with one answer and is allowed to block. Code Review takes the questions with a judgment call and is never allowed to block. A reading pass that can be confidently wrong must not hold up a merge, so it advises and the deterministic gate stays in charge.


2. Where it runs

Code Review runs in the same window as the scan, after the agent finishes and before a human spends attention on the change. The difference is what does the reading: your own agent, on your own machine, under your own provider relationship. Shipmoor hosts no model and uploads no source.

AI agent
Developer
Shipmoor Review
Your agent
produces a change
review the change
drive one turn (stdin JSON)
comments (stdout JSON)
advisory findings, exit 0

The agent produces an edit. The developer runs shipmoor review --agent claude. Shipmoor resolves the change, builds a review request, drives your agent through a tool-use conversation, collects the agent’s comments, maps them to findings, and exits zero. The advisory findings feed the fix loop. They never become a verdict.

This is the deliberate contrast with the scan. The scan is a deterministic gate that can fail the build. Code Review is a second pair of eyes that cannot. Same moment, opposite authority.


3. The pipeline at a glance

A review is a one-shot pipeline, like the scan, but the reasoning step is delegated to a program you control. There is no daemon, no history database, and no Shipmoor-hosted model. Shipmoor owns the change selection, the prompt, and the mapping. Your agent owns the judgment.

Input working tree · staged · commit · range · whole files
Stage 1Acquire the change
Stage 2Build the review context knowledge rules and the diff
Stage 3Drive your agent through tool-use turns
Stage 4Collect comments
Stage 5Map to shipmoor.scan.v1 findings
Stage 6Render
Output Terminal · JSON · SARIF
Exit code always 0 (advisory)

The six stages:

  1. Acquire the change for the selected mode into a list of per-file diffs, with the introduced line ranges noted for later.
  2. Build the per-file review context from the bundled knowledge rules and the diff.
  3. Drive your bring-your-own-agent through a tool-use loop, one turn per subprocess call, so it can inspect the change and then report.
  4. Collect the agent’s comments from a thread-safe collector.
  5. Map each comment to a canonical shipmoor.scan.v1 finding stamped category: code_review and rule_id: shipmoor.review.llm.
  6. Render the result to the terminal, JSON, and SARIF, and exit zero.

The one nuance the scan page does not have lives in stages 3 and 4. The orchestration around the agent is fully deterministic: the acquisition, the prompts, the tool plumbing, the token budget, the mapping. The agent’s judgment inside it is not. That is the reason review advises and the deterministic scan gates.


4. Bring your own agent

Shipmoor ships no model. The reviewing intelligence is a command you pass with --agent, and Shipmoor talks to it across one small, neutral boundary: a single turn is a single subprocess call. Shipmoor writes a JSON ChatRequest to the command’s stdin and reads a JSON ChatResponse from its stdout. That is the entire contract.

One review turn
Build ChatRequest model · messages · tools · max_tokens
Spawn the —agent command request JSON on stdin
Outcome
missing / error / timeout / unparseable
finding-free completion never a fabricated finding
valid response
Parse ChatResponse read content and tool_calls only

The request side carries a model, the messages, the available tools, and a max_tokens ceiling. The response side is read for exactly two things: the assistant text and the tool calls. A finish_reason is parsed for fidelity but never consulted. Completion is signalled by a tool call, not by a stop reason, which keeps the contract unambiguous across providers.

The built-in presets. A raw coding-agent CLI like claude -p does not speak this JSON wire. It takes a prompt and prints text. So Shipmoor ships an adapter inside the binary and exposes it as two bare presets, claude and codex:

  • shipmoor review --agent claude resolves the bare token claude to a re-exec of the very same Shipmoor executable as its own subprocess, through a hidden subcommand. That subprocess is the adapter.
  • The adapter reads the ChatRequest on stdin, flattens the review prompt and the diff into a single instruction, drives your local claude CLI with it, parses whatever the CLI prints back into comments, and writes a well-formed ChatResponse on stdout.

Because the adapter ships in the binary, the presets work for a plain curl | bash install with no external script. The underlying command is overridable through an environment variable when you want a different model or a wrapper.

Custom agents. Anything that can read the request on stdin and write the response on stdout works the same way. Point --agent at your own program or a command with arguments, and Shipmoor drives it exactly as it drives the presets. Your agent owns the provider relationship: the model, the key, the rate limits. Shipmoor owns only the bytes on the boundary.

Fail-safe is absolute. Every failure mode of the agent collapses to one safe outcome: a finding-free completion. A command that is missing, exits non-zero, times out, writes bytes that are not valid UTF-8, or returns output that cannot be parsed into findings ends the file cleanly with no comment. Shipmoor records zero advisory findings for that file and moves on. A broken reviewer can never invent an issue, and it can never fail your build, because review does not have that power in the first place.


5. The agentic loop

For each file in the change, Shipmoor runs a tool-use conversation with your agent. The agent can call tools to inspect the change, then reports each issue with a code_comment and ends the file with task_done. The loop keeps the conversation inside a token budget across turns by compressing its own memory.

Per-file context
Ask your agent one turn
Tool calls
inspect file_read · file_read_diff · code_search · file_find
report code_comment
task_doneend the file
execute tools · feed results back · repeat

How a turn runs:

  • Shipmoor sends the conversation with the tool definitions. The budget for a review file is a fixed number of turns, decremented before each call, so a budget of N means exactly N completions.
  • The response is read for tool calls. If there are none, Shipmoor appends a short nudge asking the agent to try again or call task_done, and continues. An empty nudge round does not count against the budget.
  • Each tool call is dispatched. An inspection tool returns its result back into the conversation as a tool result. A code_comment is parsed into comments and pushed to the collector. A task_done short-circuits the whole file immediately.
  • If several rounds in a row return no usable data, the loop stops on its own. So does budget exhaustion. Every normal ending is quiet: the file simply finishes.

Memory compression keeps the conversation in budget. A long inspection can grow the transcript past what fits. The loop manages this with three zones. The first two messages (the system prompt and the initial review prompt) are frozen and always kept. The most recent complete rounds that fit the budget form an active zone and are kept verbatim. The middle is summarized.

After a turn, measure the transcript
Over 60% of budget?
Yes
start background compression async, off the turn
Over 80% of budget?
Yes
compress now synchronous
under threshold
Summarize the middle zone
Fold the summary into the frozen prompt as a previous-review-summary block

Two thresholds drive it. Crossing 60% of the budget kicks off a background compression job that does not block the turn. Crossing 80% compresses immediately. The summary is produced by your agent through the same wire, then folded back into the second frozen message as a labeled summary block so the system message stays at the top.

The rule that makes this safe is that it never truncates. If the summary comes back empty, if the compression call errors, or if there is nothing meaningful to compress, the loop keeps the original conversation unchanged. Staying briefly over budget is always preferred to discarding context. Files in the change are dispatched concurrently under a bounded worker pool, and a per-file timeout keeps any single file from stalling the run.


6. The tools the agent gets

Inside the loop the agent has six tools. Four are for looking at the change, two are for reporting. Every one runs locally against the working tree or a git ref. Shipmoor uploads no source: every byte the agent learns about the code is produced here.

Tool registry
file_readread a file window, up to 500 lines
file_read_diffthe diff blocks for given paths
code_searchtext search across the tree
file_findlocate files by name
code_commentreport a finding
task_doneend the file
ToolWhat it does
file_readreads a line window of the new version of a file, capped at 500 lines, with a truncation note when the range is larger
file_read_diffreturns the parsed diff blocks for the requested paths, including related files that were changed but filtered out of review
code_searchtext search across the repository, backed by git grep with a non-git fallback, so the agent can check how a symbol is used elsewhere
file_findfinds files by name from the git file list, so the agent can pull in a definition it needs
code_commentsubmits one or more review comments, each with the offending lines and an optional suggested fix
task_donesignals the file is reviewed and ends the loop

The reporting tools have a few guardrails worth naming. task_done is recognized by the loop itself, before any registry lookup, so completion is never confused with a normal tool. On a code_comment, the loop force-overwrites the comment’s path with the file currently under review: cross-file comments are impossible, which closes the door on a model hallucinating a path. Tool arguments arrive as a JSON string and are decoded behind a guard that rejects non-finite numbers, so a malformed payload turns into an error result the agent can recover from rather than a crash. The loop survives bad input by design.

After the main pass, an optional review filter does one more turn that asks the agent to drop any comment the diff plainly contradicts, and those are removed from the collector before mapping. It is a quiet precision step, and if it errors it is simply skipped.


7. Knowledge and rules

The prompts and the per-language review guidance are bundled with the engine, not fetched. A layered resolver picks the rule text for each file deterministically, in declaration order, so the same file always gets the same guidance.

File path
Matches a custom rule?
Yes
use it —rule flag
No
Project rule?
Yes
use it .shipmoor/rule.json
No
Global rule?
Yes
use it home-dir rule.json
No
Bundled per-language default first matching pattern wins, else the base default

Precedence runs from most specific to least: a custom rule passed on the flag, then a project rule at .shipmoor/rule.json, then a global rule in your home directory, then the bundled defaults. First match wins, and the order is the contract. The bundled defaults are keyed by path patterns, and the pattern that matches first supplies the review text for that file.

The language scope is wider than the scan, on purpose. The deterministic scan covers four languages, because deterministic rules are expensive to get right and the scan gates. Code Review carries a much broader language set, because a reading pass can advise across many more file types without the risk of a false block. The mapping that turns a comment into a finding is grounded in a 67-extension allowlist and a per-language taxonomy that names Java, Kotlin, Rust, C and C++, C#, Ruby, PHP, Swift, shell, SQL, and many more, each carried with its natural language label.

This is a deliberate per-feature carve-out, not a change to the scan. The four scanner languages still emit their exact canonical spelling, so a review finding on one of those files also satisfies the strict scan enum. Every other extension carries its own language label verbatim through the advisory code_review category, which is exempt from the four-language enum. The scan’s scope is untouched. The reading pass simply reaches further.


8. Change acquisition

A review sees the change through the same input modes the scan uses, so a review and a scan look at the same thing. One mode is active per run.

shipmoor review
Which mode
default
Working tree tracked and untracked
—staged
Staged the git index
—commit
Commit a commit vs its parent
—from —to
Range merge-base to head
—scan
Whole files read entire files
Per-file diffs with introduced line ranges
ModeWhat it reviews
shipmoor reviewthe working copy: tracked changes plus a synthetic diff per untracked file
shipmoor review --stagedthe git index only, for a pre-commit style review
shipmoor review --commit <ref>a single commit against its parent
shipmoor review --from <base> --to <head>a branch range, from the merge-base to the head ref
shipmoor review --scanwhole files rather than a diff, for a broader reading pass

Acquisition turns the selected change into the per-file diffs the loop consumes, and it binds the content reader to match the mode: the working tree for a workspace or staged review, and git show <ref>:<path> for a commit or range, so the agent reads the file as it exists at the reviewed point. The introduced line ranges are parsed from the diff hunks and carried forward, so a finding can later be marked as introduced by the change or pre-existing.

A small, honest detail: path-emitting git calls run with quoting turned off, so a file with non-ASCII bytes in its name is surfaced and reviewed rather than silently dropped. That path is then made safe at the mapping boundary so it cannot break the JSON or SARIF output.


9. From comment to finding

The seam between the engine and Shipmoor’s canonical contract is the mapper. It turns an internal model comment into an ordinary, advisory shipmoor.scan.v1 finding. No parallel serializer, no new schema: a review finding is a normal finding with a review stamp.

Findingadvisory · shipmoor.scan.v1
  • +idSHM-…
  • +rule_idshipmoor.review.llm
  • +categorycode_review
  • +languageverbatim label
  • +severitymedium, fixed
  • +confidencemedium, fixed
  • +path
  • +start_linemin 1
  • +end_linemin 1
  • +messagethe comment
  • +recommendationsuggested fix
  • +evidenceexisting_code, normalized
  • +change_status
  • +fingerprintSHA-256

The mapping pins a few things on purpose:

  • The review stamp. Every review finding carries category: code_review and rule_id: shipmoor.review.llm. The model emits no per-comment rule taxonomy, so all advisory findings share one Shipmoor-branded rule.
  • Severity and confidence are fixed at medium. The agent reports neither, and the category is excluded from the gate regardless of severity, so a fixed value is honest and deterministic instead of a fabricated per-comment score.
  • The positioning sentinel. When the engine cannot resolve a comment to a real line, it uses an internal (0,0) sentinel. A finding requires a position of at least 1, so an unresolved comment anchors to line 1 and carries evidence["position"] == "unresolved". That keeps a genuine line-1 finding distinguishable from a positioning failure.
  • Stable evidence. The existing_code the model copied can be rewritten in place by line relocation, so it is normalized before it enters the fingerprint. Two runs that relocate differently still produce the same SHM- id for the same issue.
  • Change status. In a diff review, a finding whose lines intersect an introduced range is marked introduced, otherwise existing. In whole-file mode there are no change ranges, so the status is unknown. An unresolved finding cannot be attributed to a change, so it is unknown too.

The model’s private reasoning never escapes the engine. It is not part of the comment contract, it reaches no emitted field, and it is not in the fingerprint.

SARIF and JSON come from the scanner’s own emitters. The output bridge re-uses Shipmoor’s existing JSON and SARIF writers verbatim, so review output is byte-shape identical to scan output. SARIF carries a stable rule object for shipmoor.review.llm on every result, and the document serializes as a plain shipmoor.scan.v1 file, which keeps Community and IC parity and never bumps the schema version. Every consumer, the terminal, JSON, SARIF, the IDE, the harness, and the CI poster, reads one finding shape whether it came from a scan or a review.


10. It never gates

A review reports and exits zero. Always. The exit code reflects only how the run went, never what it found.

Review run
How did it go
ran, any findings
advisoryexit 0
bad flag / config
usage errorexit 2
agent ran but failed
run failedexit 3
Exit codeMeaning
0the review ran, with or without advisory findings
2a usage error: a bad flag, or an --agent command that does not resolve to a runnable program
3the agent ran but the run failed: a transport, timeout, or engine failure

There is no exit 1. The scan reserves exit 1 for a blocked gate, and review never returns it. The split is structural, not a setting you could flip. The code_review category is excluded from the --fail-on threshold by a single rule in the engine, and the scan gate imports that same rule (with a literal fallback when the review engine is not bundled). A code_review finding at any severity, even one the model calls critical, contributes zero to the gate. The advisory category used by the claim check is excluded the same way.

So the clean lane holds. The deterministic scan is the only enforcer. A review is a reading pass that can advise broadly precisely because it can never be coerced into blocking, and the agent-not-found check fails fast with a precise usage error rather than letting a misconfigured command collapse into a generic failure.


11. Local-first and private

A review is private by construction. Shipmoor hosts no model and uploads no source. The review request is piped into a subprocess you chose, and only that subprocess talks to a provider, over exactly the network path your agent already uses.

Your machine
the change stays on disk
Shipmoor
acquire · prompt · map no model, no upload
Your agent
the only thing that calls a provider
Advisory findings local

Shipmoor itself opens no network plane, hosts no model, and needs no API key of its own. The tools the agent uses to inspect the change all run locally against the working tree or a git ref. If you bring an offline or local agent, a review makes no network calls at all.

The trust boundary is the gate-versus-advisory split. The deterministic scan exit code is the only thing that can block a merge, and a review, being structurally gate-excluded, can never weaken it or be talked into blocking. In CI this is enforced by a split-workflow design so an untrusted fork pull request can never combine a source checkout with the credentials your agent uses.


12. Where it shows up

It is one engine. The same review, the same wire contract, and the same advisory findings appear everywhere Shipmoor runs.

One review engine
CLI shipmoor review
IDE advisory diagnostics
CI advisory PR comments
Agent Skills review then fix
Agent Harness a self-correcting loop
  • CLI. shipmoor review drives the review and prints advisory findings.
  • IDE. The editor renders review findings as information-level advisory diagnostics, kept visually and structurally separate from scan diagnostics.
  • CI. A review posts advisory comments on the pull request, under the split-workflow trust model, without ever touching the check status.
  • Agent Skills and Agent Harness. Both drive a review then fix loop: run the review, read the advisory findings, fix them, re-run. Because the findings are ordinary shipmoor.scan.v1 findings, the loop reads them the same way it reads scan findings.

One engine behind all of them. The reason it can sit in every surface without risk is the same reason it advises rather than gates: the orchestration is deterministic, the judgment is the agent you bring, and the deterministic scan stays the only gate.


13. In one sentence

Reading, not enforcement. Code Review reads the agent’s change the way a careful reviewer would, drives an agent you bring through one small JSON wire, maps its comments to ordinary shipmoor.scan.v1 findings, and exits zero every time. The orchestration around the agent is deterministic and local: the same acquisition, prompts, tools, and mapping, no hosted model and no source upload. The judgment inside it is the agent’s, which is exactly why a review advises and the deterministic scan stays the gate.

Try it

Code Review is a Shipmoor IC feature. Run it on your next agent-authored change with the agent you already use:

cd path/to/your/repo
shipmoor review --agent claude

It runs locally, hosts no model, and uploads no source. Read the Code Review overview for the product view, or see how the deterministic scan works for the gate it sits beside.

Contact sales

Our team can help with custom support, team rollouts, and self-hosted deployments. Or to get started now, explore our self-serve plans.