Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 95 additions & 104 deletions Docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,104 @@
# CodexReviewKit Architecture
# Architecture

CodexReviewKit provides ReviewMonitor, a native macOS app for running and
observing Codex review. The package has one observable review store, one Codex
app-server gateway, and an internal MCP adapter owned by the app.

The package is organized around four ownership boundaries:

- `CodexReview` owns review behavior and observable product state.
- `CodexReviewAppServer` owns all `codex app-server` JSON-RPC I/O.
- `CodexReviewMCPServer` converts app-managed MCP tool calls to review commands.
- `CodexReviewHost` assembles concrete live dependencies.

`ReviewUI` renders the monitor state and forwards user intent. It does not own
review rules, app-server protocol details, persistence, or process lifecycle.
ReviewMonitor's UI and MCP server share one `CodexReviewStore`. The store owns
review state and commands; its backend sends requests to `codex app-server`.
`CodexReviewHost` connects these components when the app starts.

## Targets

| Target | Responsibility |
| --- | --- |
| `CodexReview` | Review API, `CodexReviewStore`, observable state, and product invariants |
| `CodexReviewAppServer` | `codex app-server` JSON-RPC protocol, process transport, request serialization, notifications |
| `CodexReviewMCPServer` | Internal MCP tool request/response conversion and Streamable HTTP endpoint |
| `CodexReviewHost` | Runtime composition for ReviewMonitor |
| `CodexReviewTesting` | Deterministic fake backend, fake JSON-RPC transport, gates, manual clock |
| `ReviewUI` | Native monitor UI rendering and user-intent forwarding |

ReviewMonitor is the product entry point. Review behavior, Codex protocol
handling, MCP conversion, and UI rendering remain in their owning targets.

## Runtime Flow

ReviewMonitor composes `ReviewUI` and `CodexReviewMCPServer` over one
`CodexReviewStore`. Its live backend delegates Codex I/O through
`CodexReviewHost` to `CodexReviewAppServer`; dependencies do not point back
toward the app or UI.

### Codex Updates

`CodexReviewStore.updateCodex(when:install:)` owns the update operation, its
observable progress, review dispatch suspension, and runtime replacement. It
retains MCP sessions and accepted jobs, joins existing execution and cleanup,
then resumes dispatch after runtime publication. Repeated update requests join
the same operation. Runtime recovery confirms the owned process has closed
without treating a recorded close error as permanent evidence that it is live.

The app's `ReviewMonitorCodexUpdater` owns update checks and their results. The
sidebar receives its availability projection, and Settings uses the same
instance. A continuous clock anchors automatic checks to launch and eight-hour
boundaries; manual checks share in-flight work without moving that anchor.
Checks due during installation are covered by one post-update check.

Application termination stops read-only checking, shuts down the Store, and
joins startup before replying to AppKit. The Store can cancel a deferred update
or wait for an installation already in progress. Codex updates do not use an
application relaunch helper.

## CodexReview

`CodexReviewStore` is the single source of truth for review, runtime, auth,
settings, workspace, job, and log state. It is also the command owner for
`review_start`, `review_await`, `review_read`, `review_list`, `review_cancel`, session close,
auth actions, and settings updates. UI and MCP both use the same store API.

`CodexReviewStoreBackend` is the dependency boundary below the store. Live,
preview, and test backends all implement that boundary; product state remains in
the store.

## App-Server Gateway

`CodexReviewAppServer` treats raw JSON-RPC as the only I/O boundary.

- One live `codex app-server` process maps to one shared connection.
- `initialize` and `initialized` run once per connection.
- App-server operations are typed at the request boundary.
- Same-thread mutating requests are serialized.
- Different-thread requests may run concurrently.
- `turn/interrupt` is a control request and is not queued behind an in-flight
same-thread `turn/start`.
- Reviews run as a normal `turn/start` on the thread created for that review.
One adapter renders the typed review target and references the server-reported
`$review-agent` skill provisioned during app-server initialization; the
request carries the review working directory.
- Notifications are subscribed before `turn/start` so terminal events emitted
with the response are not lost.
- Cancellation is represented by typed control/cleanup requests, not by closing
the transport.
| `CodexReview` | Review API, observable store, authentication, settings, and history contracts |
| `CodexReviewAppServer` | JSON-RPC requests, notifications, and process transport for `codex app-server` |
| `CodexReviewPersistence` | SQLite history storage, migrations, and retention |
| `CodexReviewMCPServer` | MCP tool conversion and the Streamable HTTP endpoint |
| `CodexReviewHost` | Live backend, filesystem locations, and dependency assembly |
| `CodexReviewTesting` | Fake backend and transport, gates, and manual clock |
| `ReviewUI` | Monitor views and controllers |
| `TextTransitions` | Animated text rendering |

ReviewMonitor is the app entry point. The package exports `CodexReview`,
`CodexReviewHost`, `ReviewUI`, and `TextTransitions` as libraries; the other
production targets support those libraries internally.

## Review flow

Both UI actions and MCP tools call the store. `CodexReviewStoreBackend` supplies
runtime operations, with live, preview, and test implementations. Each uses the
same store to manage product state.

The live backend uses one shared connection to a long-lived `codex app-server`
process. The gateway initializes the connection once with `initialize` and
`initialized`, then sends typed requests:

- Mutating requests on the same thread run in order. Requests on different
threads can run concurrently.
- A review uses a normal `turn/start` on its review thread. The adapter includes
the target, working directory, and server-reported `$review-agent` skill
provisioned during initialization.
- The gateway subscribes to notifications before `turn/start` so it can receive
terminal events sent alongside the response.
- `turn/interrupt` can proceed while `turn/start` is pending. Cancellation uses
control and cleanup requests; it keeps the transport open.

Fake and live tests use the same transport protocol.

## MCP Boundary

`CodexReviewMCPServer` knows MCP tool names, request arguments, and response
shape. It calls `CodexReviewStore` commands and does not know Codex JSON-RPC
details.

ReviewMonitor owns the default Streamable HTTP endpoint at
`http://localhost:9417/mcp`. The HTTP boundary follows current MCP session
semantics: `initialize` creates an `MCP-Session-Id`, subsequent requests carry
that session header, responses are delivered as JSON or SSE as negotiated by the
client, and `DELETE` closes a session. Tool and response contracts live in the
[MCP reference](mcp.md).

## Monitor UI Boundary

`ReviewUI` observes `CodexReviewStore` directly.

- Views and view controllers render observable state.
- User actions call store methods.
- UI tests cover layout, selection, rendering, accessibility-facing text, and
user-intent forwarding.
- Review/auth/settings semantics are tested in `CodexReviewTests` and
`CodexReviewAppServerTests`.
## MCP sessions

The app hosts `http://localhost:9417/mcp`. An `initialize` request creates an
`MCP-Session-Id`; subsequent requests carry that header. The server returns JSON
or SSE according to client negotiation, and `DELETE` closes the session.

Jobs belong to the session that started them. The MCP adapter converts tool
arguments into store commands and converts their results into MCP responses.
The app-server gateway handles Codex's JSON-RPC separately. See the
[MCP reference](mcp.md) for tool and response fields.

## History and UI

The store loads and saves history through `CodexReviewPersistence`, which owns
the SQLite database. `CodexReviewHost` supplies its filesystem location. History
stores review metadata, final results, and findings; live transcripts stay in
memory. Restored reviews appear in the UI but are unavailable to new MCP
sessions.

`ReviewUI` observes the store and forwards user actions to it. UI tests cover
layout, selection, rendering, accessibility text, and action forwarding.
`CodexReviewTests` and `CodexReviewAppServerTests` cover review, authentication,
settings, and protocol behavior.

## Codex updates

`CodexReviewStore.updateCodex(when:install:)` pauses new review execution while
keeping accepted jobs and MCP sessions. It waits for current reviews or cancels
them according to the requested timing, finishes cleanup, stops the old runtime,
runs the installation closure, and starts the replacement runtime. Queued jobs
resume once that runtime is ready. Concurrent update calls wait for the same
operation, using the first call's installation closure.

Recovery checks whether the owned process has closed. A previously recorded
close error alone does not prevent recovery if the process is now closed.

The app's `ReviewMonitorCodexUpdater` owns update checks and their results.
Settings and the sidebar share that instance. Automatic checks run at launch
and at eight-hour intervals measured from launch. Manual checks join a check
already in progress and keep that schedule. A check due during installation
runs once after the update.

On application termination, the app stops checking, shuts down the store, and
waits for startup work before replying to AppKit. Store shutdown cancels an
update still waiting for reviews or waits for an installation already in
progress. Codex updates restart the runtime within the running app.

### Simulate an update

To inspect UI responsiveness, set `REVIEW_MONITOR_SIMULATE_CODEX_UPDATE=1` in the
Xcode scheme's **Run → Arguments → Environment Variables** and launch the app.
Use the live runtime with `REVIEW_MONITOR_MOCK_JOBS` and
`REVIEW_MONITOR_REVIEW_MODE` disabled.

After a one-second simulated check, the normal **Update** button appears. The
installation step waits ten seconds through the same subprocess runner as a
real update. The existing Codex runtime stops and restarts normally, while the
Codex and Homebrew packages stay unchanged. Settings then shows **Up to Date**.
Relaunch the app to repeat the simulation.
Loading
Loading