# review

A proposal is a change waiting for a decision. `review` decides proposals: **accept** applies the change exactly as previewed, **reject** closes it, **defer** keeps it waiting.

Anyone whose role may review decides a proposal: a person, or an agent working for one, the proposal's author included. An agent acts with its person's role unless it has a membership of its own. Other roles are refused (`not_authorized: your role (editor) cannot review`). A context proposal is decided by its maintainer or an administrator; an agent with no membership of its own whose person maintains it decides it too (below). Background agents, such as an overnight crawler, only propose: they are set up with the editor role, so they never decide.

If your role may review, status lists what you may decide under "Needs your review". Fetch a proposal to see whether you may decide it.

## Find and read proposals

```
wirk query proposal=proposed,deferred
wirk query c4a1e902
```

Fetching a proposal shows exactly what it would change. This is what an editor sees:

```
c4a1e902 · proposal · proposed · r1 · by nightly-agents (agent) · changed 1h ago · read-only
Mark webhook retries completed

Reason
  The retry fix merged in 4f2a9c1 and the retry tests pass.

Diff
  ~ 5c1e7a90 r3 Retry failed webhooks
      status in_progress → completed
Can accept: no, your role (editor) cannot review
more: query c4a1e902 depth=all
```

A proposal's title is the first line of its reason, and the rest of the reason shows under Reason; a one-line reason has no Reason block. Lists and status show only that title, so fetch a proposal to see what it would change.

Anyone who may decide it sees `Can accept: yes` and a line `decide with: review c4a1e902@1 ACTION --reason REASON` instead.

`r1` is the proposal's revision. Every decision, including defer, makes a new one.

## Decide

```
wirk review c4a1e902@1 accept --reason 'Tests pass on main'
wirk review c4a1e902@1 e7b35d16@2 defer --reason 'Wait for the load test'
```

The command requires a real reason and refuses the placeholders `ACTION` and `REASON`, so a pasted `decide with:` line decides nothing until it is filled in.

In MCP, `wirk_review` takes the same decisions (Request, below).

## Request

Sent with your token:

```
POST /v2/review
Authorization: Bearer <your token>

{"request_id": "r-5d0e3a9c71",
 "decisions": [
  {"id": "c4a1e902",
   "revision": 1,
   "action": "accept",
   "reason": "Tests pass on main"}]}
```

| Key | Notes |
|---|---|
| `decisions` | Required. 1–32; each is decided on its own |
| `id` | A full (`proposal_…`) or short proposal ID |
| `revision` | The `rN` you read |
| `action` | `accept`, `reject` or `defer` |
| `reason` | Up to 2,048 characters. The CLI requires one |
| `request_id` | 1–200 characters. Required on the wire; the CLI makes one when you leave it out |
| `workspace_id`, `format` | As everywhere |

## Response

```
Reviewed r-5d0e3a9c71 · 1 decision: 1 decided
  accepted c4a1e902 r1 → r2 · Tests pass on main
```

```json
{"ok": true,
 "data": {"request_id": "r-5d0e3a9c71",
          "results": [{"id": "proposal_c4a1e902…", "action": "accept", "outcome": "accepted", "r": 2,
                       "reason": "Tests pass on main"}]},
 "errors": [], "notices": [], "page": {"complete": true, "next_cursor": null}, "timing_ms": {"total": 7.9}}
```

Each decision stands on its own. When some are decided and others are not, the answer is HTTP 207 and says which and why:

```
Reviewed r-9a4c2e6b18 · 3 decisions: 1 decided · 1 conflicted · 1 skipped
  accepted c4a1e902 r1 → r2 · Verified: tests pass on main
  conflicted e7b35d16 · basis_changed: the proposal is at r3 (deferred by alice); you read r2
    Fetch it again (query e7b35d16) and decide at the revision it shows, under a new request_id.
  skipped 3a1f9c20 · not_authorized: Acme context is maintained by alice
    Its maintainer or an administrator decides it.
```

Accepting can also conflict when an item the proposal changes has moved on since it was proposed. Nothing is half-applied: a proposal applies whole or not at all. A proposal that creates or changes context is decided by whoever maintains that context or an administrator; an agent with no membership of its own whose person maintains it decides it too.

## Retries

As with [write](/docs/write.md#retries): resend the identical body with the same `request_id` after an uncertain result, or look it up with `wirk query receipt=r-5d0e3a9c71`. After any refusal, send the corrected body under a new request ID.

All codes: [Errors](/docs/errors.md).
