obversa-review reviews one of three diffs. Run it inside the repository,
or point it at one with --cwd:
Terminal
--range main..HEAD --staged, is a usage error whatever their order. The
working tree diff is git diff, so untracked files don’t appear. Other
flags:
--cwd <dir>runs against another repository directory.--no-opendoesn’t place the pane. The command prints the page URL on stderr, and you open it yourself.--app <name>overrides the surface app name, which sets the frame markers on stdout.
Read and comment
The page shows the diff with syntax highlighting by file type, and a file tree with two views: Changes lists the changed files with file-type icons,+/− line counts and a status letter; All files lists every
tracked file. Click a changed file to scroll to its diff. Collapsed
N unmodified lines bands sit between hunks; click one to see the code
around a hunk, or Expand full file on a file header to open every band
in that file. Full-file context is available in the working tree and staged
modes, not in range mode. For JavaScript files (.js, .mjs, .cjs),
click an identifier to jump to the line that defines it when that line is
in the diff; otherwise the tooltip names its line number.
A + button on every diff line writes a comment on that line, and you can
remove a comment before you return. Return ends the review with the
comments you wrote, or with none. Cancel ends it with no comments and a
cancelled decision.
The page is placed through OBVERSA_SURFACE_BIN, the absolute path a host
injects. The cmux workspace launcher sets it, so in cmux the
page opens in a split beside the terminal. With the variable unset, the
page opens in your default browser.
Read the result
The command writes one framed JSON object to stdout, so a caller captures it with a pipe, and the one-line summary goes to stderr. For the default app name the markers are<<<REVIEW_RESULT_V1>>> and
<<<END_REVIEW_RESULT_V1>>>; the marker name is the app name in upper
case, punctuation replaced by _. The payload is the surface result, the
one shape every Obversa review surface returns:
Example result
surfacenames the package that answered and the version that was resolved, on every status, so a consumer can always say which surface produced the frame.surfaceIdnames this review session.gateIdnames the callback gate that opened it, ornullwhen you ran the command directly.decisionagrees with the annotations:changes-requestedcarries at least one,approvednone. A submission that says otherwise is refused and the page stays open, so a decision is never rewritten on the reviewer’s behalf.cancelledandtimed-outare set only by the session’s own ending.- Each annotation has an
anchor:targetis the repository-relative path as it appears in the diff,sideisoldfor a deleted line ornewfor an added or unchanged line, andpositionis the line number on that side. An anchor is those three plain values and nothing else; one with an extra, hidden or computed field is refused.bodyis the comment text with surrounding whitespace removed,authorthe reviewer,createdAtwhen the comment was written. - A
threadis an ordered list of replies, each an author and a body, bounded like the annotation. A responder agent appends to it withappendToThread(annotation, entry)from@obversa/surface-diff, which returns a new annotation and never changes the one you pass in. A thread submitted with the review comes back in the framed result, so the next round sees the conversation so far.
Failure
- A conflicted diff is refused whole, with a message naming the conflicted path. Git describes such a path as a combined diff, which a review could only show as a missing file, so the command never opens a page that looks clean over a conflicted tree.
- A repository that changes during capture is retried. The diff, the
code around each hunk and the file list are captured twice, and the
review is accepted only when both captures agree on every line, every
file read and the file list. After three attempts the command stops with
The repository changed while the review was being captured; retry when it is quiet. A change made and undone the same way during both captures isn’t detected, and the command takes no lock on the repository. statusiscompletedwhen the reviewer returns.cancelled,timed_outandinterruptedcarry a shortdetailand a payload with the samesurfaceIdandgateId, whosedecisioniscancelledortimed-outwith no annotations, so a gate can still route the outcome. The exit code is 0 forcompleted, 1 for every other status, and 2 for a bad argument.
Limits
- 500 annotations at most per review. 4000 characters per body.
- An annotation must anchor to a line in the diff. The server drops any other.
- Comment bodies come back as written, apart from the trimming and the length cap. The secret redaction the surface server applies to other responses doesn’t apply to them, because a comment can quote code.
Security
The page runs on the surface package: one loopback server, one session token, one result. The static page names nothing under review: not the diff, not the ref, not even the highlight rules, which are built from the tokens the diff contains. The page fetches the diff, its label and those rules from the session API with the bearer token, so a local process that finds the port without the token sees a shell that’s the same for every review. The address the page fetches from is a path on the session’s own origin or an absolutehttp or https URL; a path that
would leave the origin once parsed is refused. Nothing in a diff line can
execute: the page has no inline script, a strict Content Security Policy
allows scripts and styles from the session origin only, and every line is
rendered as text.
Next steps
- Obversa in cmux: the launcher that places the page in a split.
- Surfaces: the session every review page runs on.
- Surface Diff: the package, its options and
appendToThread.