## Review Desk\:&#32;edit and decide locally

Imani prepares a fictional release note\.&#32;Her goal is to let a person review the wording before it is treated as accepted\.&#32;Her obstacle is that “accept” is often confused with “publish\.”

She makes a narrower decision\:&#32;acceptance will be&#32;**a local status change only**\.&#32;The extension contains no send\-to\-service or publish operation\.

Review Desk is one complete downloadable module set\.&#32;The milestones below explain its progression\;&#32;they are not separate downloadable versions\.

### The project

Files\:

- [Entry and runtime coordination](<https://present-sketch-tp94.here.now/examples/review-desk/index.ts>)
- [Domain transitions](<https://present-sketch-tp94.here.now/examples/review-desk/domain.ts>)
- [Native panel and bounded text](<https://present-sketch-tp94.here.now/examples/review-desk/panel.ts>)
- [UI and tool copy](<https://present-sketch-tp94.here.now/examples/review-desk/copy.json>)
- [Fictional release\-note fixture](<https://present-sketch-tp94.here.now/examples/review-desk/release-note.txt>)
- [Story and protocol notes](<https://present-sketch-tp94.here.now/examples/review-desk/story.json>)

**Terminal shell—launch the complete Review Desk\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/review-desk/index.ts" --session "$LAB/review-session.jsonl"
~~~

The compatible loader resolves the runtime TUI import\.&#32;If that fails\,&#32;investigate host\/package compatibility rather than replacing&#32;`panel.ts`&#32;with an inert mock\.

### Milestone\:&#32;inspect without changing the draft

**Starting state\:**&#32;fixture text\,&#32;revision&#32;`0`\,&#32;status&#32;`draft`\,&#32;no agent grant\.

**Human OMP slash command\:**

~~~text
/review-desk show
~~~

**Model tool arguments—call&#32;`review_desk`\:**

~~~json
{"op":"discover"}
~~~

~~~json
{"op":"inspect"}
~~~

~~~json
{"op":"query"}
~~~

`inspect`&#32;returns the complete draft\,&#32;status\,&#32;revision and authorization status\.

`query`&#32;is smaller\:&#32;revision\,&#32;status\,&#32;authorization\,&#32;whether a dialog is busy\,&#32;and the extension mode\.

**Expected query result in a fresh TUI session\:**

~~~json
{
  "revision": 0,
  "status": "draft",
  "authorized": false,
  "busy": false,
  "mode": "tui"
}
~~~

The tool duplicates its structured result as JSON text in&#32;`content`\,&#32;so its machine contract is also model\-visible\.

`show`&#32;refreshes the status\/widget and sends a custom review message\.&#32;It does not increment the draft revision\,&#32;but it is not completely journal\-inert\:&#32;the custom message is conversation data\.

**Observed\:**&#32;the loaded SDK tool exposed&#32;`discover`\,&#32;`inspect`\,&#32;`query`&#32;and&#32;`act`\,&#32;and refused an unauthorized mutation without changing the draft\.

**Exercise\:**&#32;is a status widget a substitute for&#32;`inspect`\?

**Answer\:**&#32;no\.&#32;The widget is a short human summary\.&#32;`inspect`&#32;is the supported way to retrieve the actual domain state\.

### Know the mode before opening a review

The same property can exist in every mode while doing useful work in only some of them\.

| Host | What Review Desk can use | Degraded behavior |
| --- | --- | --- |
| TUI | Standard editor\/selector dialogs\,&#32;status\,&#32;string widget\,&#32;custom message renderer and native overlay | Requires real terminal presentation and input |
| RPC | Editor\,&#32;confirm and labeled select requests answered by a connected client\;&#32;passive status\/widget frames | No native component factory or synchronous composer read |
| ACP | The supplied ACP adapter exposes semantic forms and initializes extensions with&#32;`mode: "rpc"` | Form support depends on negotiated&#32;`elicitation.form`\;&#32;terminal presentation remains unavailable |
| Default print\/JSON context | Read tools and explanatory custom messages | The example refuses its interactive review\,&#32;overlay and delegation paths |

**Source\-backed ACP nuance\:**&#32;the ACP extension context can report UI availability even when form elicitation is unsupported\.&#32;Its dialog methods then return defaults\.&#32;Because Review Desk accepts&#32;`mode: "rpc"`\,&#32;an ACP&#32;`review`&#32;with no form capability can receive&#32;`undefined`&#32;from&#32;`editor()`&#32;and record a&#32;**cancelled review outcome**\.&#32;Delegation receives false and grants nothing\.

That differs from the explicit print\-mode guard\,&#32;which changes neither draft nor authorization\.&#32;The recorded Review Desk protocol scenario exercised RPC\,&#32;not a Review Desk\-specific ACP client flow\.

### Milestone\:&#32;edit and accept locally

Imani asks for a review rather than directly replacing the draft\.

**Human OMP slash command\:**

~~~text
/review-desk review
~~~

The standard flow is\:

1. Open a multiline editor with sanitized draft text\.
2. Edit the note\.
3. Submit the editor\.
4. Choose&#32;**Accept locally**\,&#32;**Reject**\,&#32;or&#32;**Cancel**\.

In the TUI multiline editor\,&#32;Enter inserts a newline\.&#32;Submit with the editor’s configured follow\-up chord—by default Ctrl\+Q or Ctrl\+Enter\.&#32;The core editor also exposes its normal Ctrl\+G external\-editor shortcut\;&#32;this extension does not invoke an external editor programmatically\.

For the following checkpoint\,&#32;use this text\:

**Text to enter in the review editor\:**

~~~text
Release 1.4
A human-reviewed local note. Nothing is published.
~~~

Choose&#32;**Accept locally**\.

**Observed RPC checkpoint\:**&#32;the edited text became revision&#32;`1`\,&#32;status&#32;`accepted`\,&#32;with&#32;`authorized: false`\.&#32;The selector carried labels and aligned descriptions\.&#32;Status and string\-widget frames were emitted\.

The original&#32;`release-note.txt`&#32;fixture was not overwritten\.&#32;The draft is reconstructed from custom session entries named&#32;`workbook-review-desk-state`\.

### Milestone\:&#32;rejection and cancellation are distinct outcomes

Imani next tries an edit she does not like\.&#32;Rather than hiding what happened\,&#32;the domain records a review outcome while preserving the pre\-dialog text\.

| Human outcome | Stored text | Status | Revision |
| --- | --- | --- | --- |
| Accept locally | Edited text | `accepted` | Increases once |
| Reject | Original pre\-dialog text | `rejected` | Increases once |
| Escape from editor | Original pre\-dialog text | `cancelled` | Increases once |
| Cancel or dismiss selector | Original pre\-dialog text | `cancelled` | Increases once |

**Observed\:**&#32;after the accepted revision\,&#32;rejection produced revision&#32;`2`\,&#32;and editor cancellation produced revision&#32;`3`\.&#32;Both retained the accepted note’s text\.

This is an important vocabulary distinction\:

- **Cancel a review\:**&#32;record that the review was cancelled\;&#32;revision increases\.
- **Abort presentation because authority was invalidated or the session moved\:**&#32;do not record a review on a different branch\.
- **Revoke agent authority\:**&#32;leave draft text\,&#32;status and revision unchanged\.

Blank or over\-12\,000\-character replacement text fails domain validation\.&#32;The tool additionally constrains its&#32;`text`&#32;parameter\.&#32;Rejection and cancellation cannot be used to smuggle replacement text through the domain function\.

**Exercise\:**&#32;after accepting revision&#32;`1`\,&#32;open the editor\,&#32;replace all text\,&#32;submit\,&#32;then choose Reject\.&#32;Which text remains\?

**Answer\:**&#32;the pre\-dialog revision\-1 text\,&#32;now with status&#32;`rejected`&#32;and revision&#32;`2`\.

### Milestone\:&#32;authorize one agent action on one revision

Imani wants the agent to shorten the draft once\.&#32;She does not want a permanent permission recovered from the transcript\.

**Human OMP slash command\:**

~~~text
/review-desk delegate
~~~

Confirm the dialog\.&#32;The grant is\:

- in memory only\;
- associated with the current session ID\;
- associated with the inspected numeric revision\;
- consumed by a successful mutation\.

The tool cannot grant itself permission\.

If you followed the accepted\/rejected\/cancelled sequence and&#32;`inspect`&#32;reports revision&#32;`3`\,&#32;the following is an exact next action\.

**Model tool arguments—call&#32;`review_desk`&#32;only after checking that the current revision is&#32;`3`&#32;and the human grant is active\:**

~~~json
{
  "op": "act",
  "action": "revise",
  "expectedRevision": 3,
  "text": "Agent revision, still local."
}
~~~

**Observed result\:**

~~~json
{
  "revision": 4,
  "status": "draft",
  "text": "Agent revision, still local.",
  "authorized": false,
  "published": false
}
~~~

If your revision differs\,&#32;inspect and use that revision instead\.&#32;Never substitute a guessed counter\.

Other agent actions are&#32;`accept`\,&#32;`reject`&#32;and&#32;`cancel`\.&#32;They preserve the inspected text and do not accept a&#32;`text`&#32;argument\.&#32;To change text\,&#32;use&#32;`revise`\.

A stale action fails without mutation\.&#32;A failed stale attempt does not consume the valid grant\;&#32;a successful action does\.&#32;A second successful attempt requires another human grant\.

An open dialog blocks tool mutations with&#32;`Review dialog open. Nothing changed.`

### Milestone\:&#32;revocation defeats a late positive answer

The first implementation of revocation had a classic asynchronous bug\:&#32;clearing the current grant did not invalidate a confirmation already awaiting a response\.&#32;A late positive answer could restore authority\.

Imani’s decision changes from “clear the value” to “retire the pending decision\.”

**Exact excerpt—authority invalidation in&#32;[Review Desk’s entry](<https://present-sketch-tp94.here.now/examples/review-desk/index.ts>)\:**

~~~ts
function invalidateAuthority(): void {
  generation++;
  grant = undefined;
  pending?.abort();
}

pi.on("session_before_switch", invalidateAuthority);
pi.on("session_before_branch", invalidateAuthority);
pi.on("session_before_tree", invalidateAuthority);
~~~

The handler remembers the generation before awaiting the dialog\.

**Exact excerpt—the delegation result is accepted only in the same generation\:**

~~~ts
if (verb === "delegate") {
  const approved = await ctx.ui.confirm(copy.grantTitle, copy.grantMessage, { signal: controller.signal });
  if (epoch !== generation) return;
  grant = approved ? { sessionId: ctx.sessionManager.getSessionId(), revision: state.revision } : undefined;
  present(ctx, state);
  ctx.ui.notify(approved ? `One agent change authorized for revision ${state.revision}.` : "No agent change authorized. Draft unchanged.");
  return;
}
~~~

**Human OMP slash command\:**

~~~text
/review-desk revoke
~~~

**Observed correction\:**&#32;revoking during a real pending RPC confirmation emitted a matching cancel frame\.&#32;A delayed positive response to the retired request did not restore authority\.&#32;The draft was unchanged\,&#32;and&#32;`busy`&#32;became false after the pending handler settled\.

The same invalidation machinery handles session switching\,&#32;branching\,&#32;tree navigation and shutdown\.&#32;A same\-session reload that uses the switch path also invalidates authority\.

#### Honest limits

- The numeric revision is a domain counter\,&#32;not Seed Desk’s session\-plus\-entry\-ID token\.
- Navigation hooks invalidate the grant so sibling revisions cannot retain it through the supported flow\.
- This is not protection against arbitrary in\-process code rewriting domain entries\.
- `stateFor()`&#32;skips stored entries that fail&#32;`isReviewState()`\.&#32;Unlike Seed Desk\,&#32;it does not fail closed on every malformed matching snapshot\.&#32;Treat this as a small teaching fixture\,&#32;not a complete corruption\-recovery system\.

**Exercise\:**&#32;revoke while a delegate confirmation is pending\,&#32;then have the client answer the old request positively\.

**Checkpoint\:**&#32;no authorization\,&#32;no draft mutation\,&#32;and no revived dialog\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;linked Review Desk files\;&#32;`packages/coding-agent/src/modes/rpc/rpc-mode.ts`\,&#32;`requestRpcDialog`\,&#32;`requestRpcEditor`\;&#32;`packages/coding-agent/src/modes/acp/acp-agent.ts`\,&#32;`createAcpExtensionUiContext`\,&#32;`#configureExtensions`\.*
