# CodexMonitor source analysis: a command stand for Codex—how a Tauri desktop app orchestrates many agents over the official protocol

> **Subject**: CodexMonitor (`github.com/Dimillian/CodexMonitor`), MIT, this analysis is based on **main branch 2026-03-26** (commit dd61b9a, PR #584 "Refactor tray thread menu"). Author **Dimillian** (Thomas Ricouard)—a well-known developer in the SwiftUI open-source circle (ACarousel, MovieSwiftUI).
> **Code size**: 860 files / 28MB. Rust backend 92 files / 31,171 lines (`src-tauri/`); TS frontend `src/` 557 ts/tsx files / 112,428 lines (counting css, 603 files / 127,161 lines); also docs/, scripts/ (iOS build chain). Runtime depends on the official `codex` CLI (`codex app-server` protocol).
> **One-line position**: **a desktop command stand for the OpenAI Codex CLI**—a Tauri 2 app. Each workspace spawns a `codex app-server` subprocess and drives multiple Codex agent threads through the **official stdio JSON-RPC protocol** (not PTY screen scraping); it ships workspace/worktree management, streaming chat, diff/PR review, usage stats, dictation, and a full iOS remote daemon.
> **Audience**: readers who have already read at least one analysis in this series (especially the **OpenAI Codex source analysis** and the **Kanban source analysis**—this lecture is a “deeper companion” of the Codex analysis, and a contrast sample against kanban’s “PTY+hooks scheme”). The whole text cites `file:line` so you can check the source.

---

## Contents

1. [Project overview: a command stand for the Codex CLI](#ch1)
2. [Panorama: Tauri dual-process + app-server protocol + event stream](#ch2)
3. [app-server attach: one codex process per workspace, LSP-style handshake](#ch3)
4. [Protocol cross-check: CodexMonitor × Codex’s official app-server protocol](#ch4)
5. [Thread model and event-driven state machine](#ch5)
6. [Rust shared layer: domain core shared by app and daemon](#ch6)
7. [worktree and workspace: first-class objects](#ch7)
8. [git / GitHub: libgit2 read + CLI write + gh integration](#ch8)
9. [Remote daemon and iOS: one core, two shapes](#ch9)
10. [Frontend architecture: feature-sliced + zero state libraries](#ch10)
11. [Frontend features: Composer / dictation / diff / usage](#ch11)
12. [Config tripartite: settings.json / workspaces.json / config.toml](#ch12)
13. [Engineering and tests: AGENTS.md layered discipline + unit-test density](#ch13)
14. [Development taste: Dimillian’s ten choices](#ch14)
15. [Summary: three most distinctive designs and their trade-offs](#ch15)

---

<h2 id="ch1">Chapter 1 · Project overview: a command stand for the Codex CLI</h2>

**What is CodexMonitor?** The first sentence of the README: *A Tauri app for orchestrating multiple Codex agents across local workspaces*. It is not another coding agent—**the agent is codex; it is the agent’s command stand**:

- **Multi-workspace**: the sidebar manages many projects; each workspace spawns a `codex app-server`, with an independent `CODEX_HOME`.
- **Multi-thread**: many threads (chat sessions) inside one workspace; resume/fork/archive/copy, pin/rename, per-thread drafts, interrupt/steer an in-flight turn.
- **worktree isolation**: give the agent an independent git worktree (independent branch + independent codex home), many agents in parallel with zero conflict—the same spirit as kanban’s one worktree per card.
- **Composer**: image attachments, Queue vs Steer dual follow-up modes, `$` skills / `/prompts:` / `/review` / `@` file autocomplete, model selection, context ring.
- **git/GitHub full flow**: diff panel, stage/revert, commit log, branches, GitHub issues/PR (list/diff/comments), “Ask PR” sending PR context into a new agent thread.
- **Usage dashboard**: home usage snapshot (parse Codex’s rollout JSONL) + sidebar credits meter.
- **Dictation**: local Whisper (whisper-rs), hold-to-talk + waveform.
- **iOS remote mode**: the desktop runs a daemon (TCP JSON-RPC); the iPhone connects over Tailscale as a thin client.

### How it relates to codex / kanban (two neighbors in this series)

| | codex (official CLI) | kanban (orchestration board) | CodexMonitor (desktop command stand) |
|---|---|---|---|
| Who is the Agent | itself | 7 CLI agents | only codex |
| Integration | — | **PTY process + hooks reverse injection** | **official app-server protocol (stdio JSON-RPC)** |
| Shape | terminal CLI | browser board | Tauri desktop + iOS |
| Orchestration grain | single session | card (task) | workspace × thread |
| Relation to codex | the body | codex is one of 7 (PTY) | **codex is the only core, over the official socket** |

**The most interesting contrast**: kanban and CodexMonitor do the same job (multi-agent orchestration) but take two sharply different technical routes—**black-box adaptation** (PTY screen scrape + hooks) vs **official protocol** (app-server JSON-RPC). That is the through-line of this lecture.

### Digitized project shape

| Dimension | Number | Note |
|---|---|---|
| Backend | src-tauri/ 92 files / 31,171 lines | Tauri commands + shared domain core + daemon |
| Frontend | src/ 557 ts/tsx / 112,428 lines (with css, 603 files / 127,161 lines) | React + Vite + feature-sliced, 25 features |
| Tauri commands | `generate_handler!` registers 125 (`#[tauri::command]` appears 138 times in source; the gap is dictation/terminal/menu cfg dual implementations) | lib.rs invoke_handler registration; each command is dual-track “local or forward to daemon” |
| app-server methods | ~22 | called from codex_core.rs; all official protocol methods (no private/experimental) |
| Dependencies (frontend) | 19 runtime deps; UI libraries only 6 families | lucide / react-markdown(+remark-gfm) / prismjs / xterm / react-virtual / Sentry, plus @pierre/diffs, vscode-material-icons, and 6 @tauri-apps plugins—no Tailwind/Radix/state library |
| Platforms | macOS primary + Windows/iOS | tray macOS only; Windows opt-in; iOS remote-daemon mode |
| License | MIT | extremely permissive |

---

<h2 id="ch2">Chapter 2 · Panorama: Tauri dual-process + app-server protocol + event stream</h2>

### Layering convention (`AGENTS.md` hard rule)

```
┌──────────────────────────────────────────────────────────────┐
│  React frontend (feature-sliced, src/features/ 25 features)   │
│  Orchestration in hooks/* and bootstrap/*; event fanout only in services/ │
└───────────────┬──────────────────────────────────────────────┘
                │ Tauri IPC (125 #[tauri::command] registered)
┌───────────────▼──────────────────────────────────────────────┐
│  Rust backend (src-tauri/)                                    │
│  ┌──────────────┐  ┌─────────────┐  ┌──────────────────────┐ │
│  │ lib.rs cmd   │→ │ shared/*    │← │ bin/codex_monitor_  │ │
│  │ workspaces/  │  │ domain core │  │ daemon (remote mode) │ │
│  │ files/ ...   │  │ (source of  │  │ #[path] include same │ │
│  │ thin adapter │  │  truth)     │  │                      │ │
│  └──────┬───────┘  └──────┬──────┘  └──────────────────────┘ │
│         ▼                 ▼                                   │
│  codex app-server subprocess (one per workspace, stdio JSON-RPC) │
└──────────────────────────────────────────────────────────────┘
```

- **Domain logic lives in `src-tauri/src/shared/*`**—app and remote daemon compile into the same binary and share the same `*_core` functions, so logic cannot drift.
- `codex/`, `workspaces/`, `git/` etc. are **thin Tauri-command adapters**; frontend IPC only goes through `src/services/tauri.ts`.
- The daemon (`src-tauri/src/bin/codex_monitor_daemon.rs`, TCP newline-delimited JSON-RPC) **reuses the shared core** with the app; method names/payloads align.
- Frontend **feature-sliced**: `src/features/{app,threads,workspaces,...}`; event fanout only in `src/services/events.ts`.

### Command registry (`src-tauri/src/lib.rs`, 337 lines)

`invoke_handler` registers 125 commands (`#[tauri::command]` appears 138 times in source; the extras are dictation/terminal/menu dual desktop vs mobile implementations under `cfg`). **Every codex command has a uniform pattern**: first check `remote_backend::is_remote_mode`; if so, `call_remote` (forward to daemon); else go to `shared::codex_core::*_core`. State is managed via `state::AppState`:

- `workspaces: Mutex<HashMap<String, WorkspaceEntry>>`
- `sessions: Mutex<HashMap<String, Arc<WorkspaceSession>>>`—**one app-server process per workspace**
- `app_settings`, `codex_login_cancels`, `tcp_daemon`, etc.

### Event stream (the through-line of the whole lecture)

```
codex app-server stdout (JSON lines)
  → backend/app_server.rs classify (request response / thread/list cwd attribution / pure notification)
  → AppServerEvent{workspace_id, message}
  → event_sink.rs → app.emit("app-server-event")
  → frontend services/events.ts createEventHub lazy-subscribe fanout
  → useAppServerEvents (30+ method allowlist + approval suffix match)
  → useThreadEventHandlers → each reducer slice dispatch
  → React UI
```

---

<h2 id="ch3">Chapter 3 · app-server attach: one codex process per workspace, LSP-style handshake</h2>

### spawn (`src-tauri/src/backend/app_server.rs`, L749 `spawn_workspace_session`)

1. `check_codex_installation` (`codex --version`).
2. `build_codex_command_with_bin` builds the command: configurable `codexBin`/`codexArgs`; Windows special-cases cmd/bat.
3. `current_dir(entry.path)` sets the working directory, sets `CODEX_HOME`, args `["app-server"]`—**one independent codex process + independent codex home per workspace** (no mutual pollution; config/session/usage data are naturally isolated).
4. stdio fully piped.

### Lifecycle (`src-tauri/src/shared/workspaces_core/connect.rs`)

- `connect_workspace_core` holds a **global spawn lock**: if a live session already exists, reuse it (`take_live_shared_session` walks existing sessions with `try_wait()` to decide liveness; **many workspaces in the same process share one codex process**—`app-server` itself supports multiple clients).
- Delete: `kill_session_by_id` → `kill_child_process_tree`.
- **Reconnect is “lazy”**: no background auto-restart. On frontend start / window focus it calls `connect_workspace` per workspace (the README is explicit). Failed sessions are cleared from the map and reborn on the next call.
- Handshake: `initialize` (clientInfo + experimentalApi, 15s timeout then kill) → `initialized` notification → emit a synthetic `codex/connected` event—**LSP-style two-step handshake**.

### stdio read/write and JSON-RPC codec

- Write side: `write_message` (JSON single line `\n`-delimited).
- Read side two tasks: stdout uses `BufReader::lines()` then `serde_json::from_str` per line; stderr is forwarded as `codex/stderr` events.
- `send_request_for_workspace` (L494): increment `next_id` → register `pending: oneshot` and `request_context(workspace, method)` → **timeout 300s** → response fills back by id.
- `send_notification` / `send_response` (answers to server requests such as approval).

### Event classification (the key to intelligent routing)

Each stdout line is first judged as one of three classes:

1. **Has `id` + `result/error`** = request response → look up `request_context` to restore workspace/method, and `extract_related_thread_ids` backfills the `thread_workspace` map.
2. **`thread/list` response**: extra parse of cwd → `resolve_workspace_for_cwd` (**longest-prefix match**) establishes thread→workspace attribution; `memory_consolidation` sub-agent threads join `hidden_thread_ids` and instead emit a synthetic `codex/backgroundThread(hide)` event (**memory-consolidation sub-agents do not pollute the main UI**).
3. **Pure notification**: route workspace by `thread_workspace`; global notifications (`account/*`) broadcast to all workspaces under the session.

### Essential difference from kanban’s PTY+hooks scheme

This is the most important contrast point of the analysis:

| | kanban | CodexMonitor |
|---|---|---|
| Data source | PTY byte stream + hooks text callback | **structured JSON-RPC notifications** |
| Status semantics | parse hook payload / poll rollout jsonl | **typed events** (thread/status/changed, item/started/completed, delta streams) |
| Control direction | one-way (receive only) | **two-way** (send_response answers permissions, turn/steer interrupts) |
| Reliability | depends on each agent’s hook implementation; Codex even has to poll | guaranteed by the protocol itself |
| Cost | an adapter matrix for 7 agents | bound to Codex’s official protocol (single ecology) |

**One sentence**: kanban treats the agent as a “black box” (screen scrape + text stream); CodexMonitor treats codex as a “service” (official socket, direct). The former is wide; the latter is deep.

---

<h2 id="ch4">Chapter 4 · Protocol cross-check: CodexMonitor × Codex’s official app-server protocol</h2>

> Every Codex-side citation in this chapter comes from the official Codex source checked out in this repository (`参考项目/codex/codex-rs/`). This is a “contrast experiment” aligning CodexMonitor’s code with Codex itself.

### 4.1 Full protocol method surface (Codex side)

Codex-side `codex-rs/app-server-protocol/src/protocol/common.rs` (4072 lines) defines the full protocol surface (every `common.rs:` line number in this section refers to that file):

- **Client requests** (ClientRequestPayload, common.rs:482–1235): `initialize`:482, `thread/start`:491, `thread/resume`:497, `thread/fork`:503, `thread/archive`:509, `thread/list`:630 (cursor/limit/sortKey/sourceKinds), `turn/start`:836, `turn/steer`:842, `turn/interrupt`:848, `review/start`:889, `model/list`:895, `collaborationMode/list`:964, `skills/list`:684, `account/rateLimits/read`:1058, `account/read`:1192, etc.
- **Experimental methods** (`#[experimental]`): `thread/search`, `environment/*`, `process/spawn|kill`, `thread/increment_elicitation`, `memory/reset`.
- **Server→client requests** (ServerRequestPayload, i.e. approval requests, common.rs:1505–1554): `item/commandExecution/requestApproval`, `item/fileChange/requestApproval`, `item/tool/requestUserInput`, `item/permissions/requestApproval`, `item/tool/call`, `attestation/generate`.
- **Server notifications** (common.rs:1658–1731): `thread/started`, `thread/status/changed`, `turn/started`, `turn/completed`, `item/started`, `item/completed`, `item/agentMessage/delta`, `item/plan/delta`, `command/exec/outputDelta`, `account/updated`, `model/rerouted`, etc.

### 4.2 CodexMonitor’s coverage (`src-tauri/src/shared/codex_core.rs`, 1033 lines)

It calls about **22 methods, all official protocol methods, not one private/experimental method**. It does declare `experimentalApi: true`, but uses no experimental methods (a protocol-capability switch; see below).

- Approval requests use `send_response(request_id, result)` as a **generic forward** (codex_core.rs:848–856), not the typed `*ApprovalResponse` structured handling in official `codex-rs/app-server/src/bespoke_event_handling.rs:1872–2018`.
- **Not covered**: multi-agent orchestration, plugin/marketplace management (skills is read-only list), `fs/*` RPC, `command/exec` dual mode, `process/spawn`, config RPC (it instead reads/writes config.toml directly), `thread/realtime`, `remoteControl`.

**Conclusion**: CodexMonitor is not a “full-coverage protocol client”—it covers **one depth**: thread/turn lifecycle, queries, and account. The rest of the surface it deliberately does not touch. This is “enough is enough” taste: deep-integrate the core path; the periphery uses simpler direct means (write config.toml directly).

### 4.3 Official design intent of app-server (Codex-side AGENTS.md)

- `AGENTS.md:104–106`: lists **app-server APIs** as a breaking-change must-search surface for external integrations (clients like CodexMonitor must watch versions).
- `:270`: new APIs only go into v2; v1 is frozen; `:273` `<resource>/<method>` singular naming; `:274–276` on-the-wire camelCase; `:286–287` experimental surface gated by `#[experimental]`; `:294–296` list methods default to cursor/limit paging.
- Official README position: **app-server = the “socket” for editors/IDEs**—so an IDE does not have to parse terminal ANSI or hack hooks, and can drive Codex with an LSP-style protocol.

**CodexMonitor is walking the officially designated path**: `src-tauri/src/backend/app_server.rs:760–771` spawn args, `build_initialize_params` (:419–430) `clientInfo{name:"codex_monitor"}` + `capabilities.experimentalApi:true`, `initialized` notification (:1091)—**item-by-item match** with official Codex `codex-rs/app-server/src/request_processors/initialize_processor.rs:70–101` handshake contract.

### 4.4 Rollout JSONL usage-stats cross-check

- Official format: `~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl`, line structure `RolloutLine{timestamp, ordinal, item}` (Codex `codex-rs/protocol/src/protocol.rs:3380`); `item` is an 8-variant enum `RolloutItem`: `SessionMeta | ResponseItem | InterAgentCommunication | InterAgentCommunicationMetadata | Compacted | TurnContext | WorldState | EventMsg` (same file :3186–3199). `TokenCountEvent` contains `info` (input/cached/output of total/last_token_usage) + **`rate_limits`** (same file :2140–2143).
- CodexMonitor (`src-tauri/src/shared/local_usage_core.rs`, 840 lines): `spawn_blocking` parses JSONL—`session_meta`/`turn_context` cwd filters the workspace (:220–229), take model (:232–237), token_count delta-dedup counts input/cached/output (:281–380), `agent_message`/`agent_reasoning` count duration (:260–279), `response_item` assistant counts runs (:383–409).
- **Took**: token usage, cache, model, cwd, active duration/count. **Missed**: `rate_limits` (credits/plan_type—the most valuable field already on disk officially), originator/source (CLI vs VSCode vs appServer), sub-agent lineage, exec_command/file_change call counts.

**Taste observation**: using already-on-disk official data for local stats (no API call, no hooks) is the smart move of “same frequency as the official data format”; but 840 lines of handwritten parse also expose “enough is enough”—the credits stats that should actually be done were not read.

### 4.5 Three small cross-checks: config.toml / auth.json / rules

- `[features]` (Codex `codex-rs/config/src/config_toml.rs:455` → `FeaturesToml` in `codex-rs/features/src/lib.rs:656`): keys defined in the same file `steer`:1286 (always on), `collaboration_modes`:1340 (always on), `unified_exec`:856, `apps`:1106 (needs chatgpt auth). `personality` top-level enum `None|Friendly|Pragmatic` (Codex `codex-rs/protocol/src/config_types.rs:311–317`). **The 5 fields CodexMonitor syncs match official key names exactly**, and it correctly rejects the deprecated `collab` key (CodexMonitor `src-tauri/src/codex/config.rs:50–53`).
- `auth.json`: `{OPENAI_API_KEY, tokens:{id_token, access_token, refresh_token, account_id}, last_refresh}`; idToken JWT payload contains `chatgpt_plan_type`/`email`. CodexMonitor `src-tauri/src/shared/account.rs:63–99` URL_SAFE_NO_PAD decode → take `chatgpt_plan_type`/email **corresponds exactly**, and is the fallback when `account/read` fails (`account_read_core` in `src-tauri/src/shared/codex_core.rs:627–650`).
- `prefix_rule` is an execpolicy Starlark builtin (defined in `execpolicy/src/parser.rs`; `config/src/requirements_exec_policy.rs:51–58` is its mirrored structure in `requirements.toml`, and the source comments say so), `decision="allow"` lets the agent skip approval. CodexMonitor `src-tauri/src/rules.rs:97–100` appends `prefix_rule(pattern=[...], decision="allow")` to `~/.codex/rules/default.rules`—**semantics correct**.

**These four cross-checks say one thing**: when Dimillian wrote CodexMonitor he **aligned field-by-field with Codex’s official source and config formats**—protocol method names, handshake params, config keys, auth JWT, rules syntax, all aligned. This is the integration taste of “stay in contract-level sync with upstream.”

---

<h2 id="ch5">Chapter 5 · Thread model and event-driven state machine</h2>

### 5.1 Thread lifecycle (`src-tauri/src/shared/codex_core.rs`)

| Operation | Codex method | Key params |
|---|---|---|
| New | `thread/start` | `{cwd, approvalPolicy:"on-request"}` |
| List | `thread/list` | sourceKinds exclude generic subAgent (prevent memory-consolidation leak) paging |
| Resume | `thread/resume` | called as soon as the frontend selects; refresh messages from disk |
| fork/archive/compact | `thread/fork/archive/compact/start` | — |
| Rename | `thread/name/set` | — |
| Send | `turn/start` | input items (text/image/localImage/mention), accessMode→sandboxPolicy (dangerFullAccess/readOnly/workspaceWrite) + approvalPolicy |
| Interject | `turn/steer` | with expectedTurnId |
| Interrupt | `turn/interrupt` | — |

### 5.2 The backend does not maintain a state machine

**Key design**: CodexMonitor’s Rust side **does not maintain a thread state machine**—state is an **event-driven frontend reducer**. `thread/status/changed`, `turn/started/completed`, `item/started/completed`, approval requests (`*requestApproval` suffix) are routed by `METHODS_ROUTED_IN_USE_APP_SERVER_EVENTS` in `useAppServerEvents.ts` into `useThreadsReducer` slices; `threadNormalize.ts` is the camelCase/snake_case fallback. running/idle/needs_approval are entirely derived on the frontend from events.

**Taste observation**: this is the corollary of “the protocol is the truth”—since app-server will push authoritative events, the backend does not need a mirrored state (one less layer of state-sync bugs). Contrast kanban’s backend state machine (`session-state-machine.ts` 78 lines): CodexMonitor put state transition into the frontend reducer (1179 lines).

---

<h2 id="ch6">Chapter 6 · Rust shared layer: domain core shared by app and daemon</h2>

`src-tauri/src/shared/` has 17 modules / 31 files / 11,197 lines—**a pure-logic layer, no Tauri/IPC dependency**, only types, codex::config/home, backend::app_server::WorkspaceSession, storage. App and daemon compile into the same binary and each call the same `*_core` functions.

### 6.1 worktree core (`workspaces_core/worktree.rs`, 642 lines)

`add_worktree_core` is a **highly generic** function (9 closures injected: FSpawn/FGit/FSanitize etc.). The core is simply `git worktree add`, three branch cases: branch already exists → `worktree add <path> <branch>`; not local but has a remote tracking → `worktree add -b <branch> <path> <remote_ref>`; else `worktree add -b <branch> <path>`.

- Root-directory priority: workspace-level `worktrees_folder` > global `global_worktrees_folder/<parent_id>` > `data_dir/worktrees/<parent_id>`.
- After create, generate `WorkspaceEntry{kind: Worktree, parent_id, worktree:{branch}}`; can `copy_agents_md` inherit the parent repo’s agents.md; a marker file records whether a one-shot `worktree_setup_script` has already run.
- `remove_worktree_core`: kill session → `git worktree remove --force` → `git worktree prune --expire now`.
- `rename_worktree_core`: `git branch -m` + `git worktree move` (**with full rollback**).

### 6.2 git / GitHub (`git_ui_core/*`)

**Hybrid implementation** (a taste highlight of this analysis):
- **Reads go through libgit2 (git2 crate)**: status/diff (`diff.rs` StatusOptions/Status bit-enum mapping A/M/D/R/T, DiffOptions patch, 10MB image base64, 2MB text cap, `has_ignored_parent_directory`); log (`log.rs` revwalk + `graph_ahead_behind`). Ignored-file recognition is the only place that uses `git check-ignore --stdin -z` CLI.
- **Writes go through git CLI** (`commands.rs`, tokio_command): commit/push/pull (`--autostash`→`--no-rebase` fallback chain) / fetch/stage/revert/checkout/`git init`.
- **GitHub all through gh CLI** (`github.rs`): `gh issue list --json`, `gh pr list --json`, `gh pr diff --color never` (`parse_pr_diff` text parser splits files), `gh api /repos/.../issues/N/comments --jq`, `gh pr checkout`, `gh repo create`.

### 6.3 Account and usage

- `account.rs` (221 lines): `account/rateLimits/read` + `account/read`; on failure fall back to parsing `$CODEX_HOME/auth.json` → idToken → JWT → `chatgpt_plan_type`/email (Chapter 4 already cross-checked the official format).
- `local_usage_core.rs` (840 lines): scan `$CODEX_HOME/sessions/<YYYY-MM-DD>/*.jsonl` (including each workspace’s independent codex_home), aggregate daily input/cached/output tokens, agent_time_ms, agent_runs, compute last7/last30, cache hit rate, peak day, top4 models; `MAX_ACTIVITY_GAP_MS=2min` merges runs.

### 6.4 Multi-agent config (`agents_config_core.rs`, 1026 lines)

Read/write `[features] multi_agent`, `[agents] max_threads/max_depth` (defaults 6/1, clamped 1-12/1-4); agent definitions are `[agents.<name>]` tables; managed agent config lands in `$CODEX_HOME/agents/<name>.toml`. Full CRUD (create generates from a template, update rename also changes the filename, delete), including **path-traversal protection and config rollback**.

### 6.5 Design taste

- **Why shared is independent**: app and daemon dual-process share the same crate’s pure logic; all state is injected via `Mutex<HashMap>`; side effects (git/fs/RPC) via closures or type injection—**naturally unit-testable** (tests.rs 437 lines).
- **RPC reuse**: `git_rpc.rs`/`workspace_rpc.rs` only declare method-name constants + DTOs (`to_params/from_params`); implementation is all in `*_core`—the daemon’s handler and the app’s client share the same signatures; one thin map is enough.
- **Hybrid git strategy**: read with libgit2 (fast, in-memory, type-safe), write with CLI (behavior-faithful), worktree orchestration also with CLI—**pragmatic, not dogmatic**.

---

<h2 id="ch7">Chapter 7 · worktree and workspace: first-class objects</h2>

### 7.1 Workspace model (`workspaces_core`)

`WorkspaceEntry`: id/name/path/kind/parent_id/worktree/settings. kind has ordinary repo, worktree, clone. Persisted in `workspaces.json` (app data directory).

- **add_workspace**: local path or `git clone`.
- **add_worktree**: build a worktree from the parent repo (Chapter 6.1).
- **add_clone**: URL clone a new workspace.
- Each workspace can have independent settings (codexBin, codex_home, worktrees_folder).

### 7.2 worktree contrast with kanban

| | kanban | CodexMonitor |
|---|---|---|
| Grain | one worktree per card (task-level) | one workspace can build many worktrees (agent-level) |
| Identity | worktree is the card’s “shadow” | **worktree is a kind of workspace (first-class object)** |
| Lifecycle | trash the card and it is deleted | CRUD, rename (with rollback), change upstream |
| Purpose | resource isolation for parallel tasks | independent branch + independent codex home for the agent |

**Taste observation**: kanban treats worktree as a by-product of task execution; CodexMonitor treats worktree as a manageable entity (`WorktreeEntry` can rename/pin/archive)—**it is a “workspace-management product,” not a “task board”**; product intent decided the data model.

---

<h2 id="ch8">Chapter 8 · git / GitHub: libgit2 read + CLI write + gh integration</h2>

(The core mechanism is already covered in Chapter 6.2; this chapter supplements the frontend face.)

- **diff panel**: render with `@pierre/diffs` + homemade `diffsWorker` (WorkerPool) + `@tanstack/react-virtual` virtual list; theme in design-system/diffViewerTheme.
- **stage/unstage/revert**: `stage_git_file` / `unstage_git_file` / `revert_git_file` (useGitActions).
- **Inline comments**: GitHub PR review—`usePullRequestLineSelection` selects lines, `get_github_pull_request_comments` fetches, submit as an independent review thread.
- **“Ask PR”**: when a PR is selected, `prefillDraft` a PR summary into composer; `runPullRequestReview`: ensure the workspace is connected → reuse the current thread or create new → `buildPullRequestReviewPrompt` (PR metadata + diffs ≤6 files×40 lines + comments ≤8 + intent instruction, safety fence) → `sendUserMessageToThread` into a new agent thread and activate.

**Taste observation**: “Ask PR” is the clever stroke of “hand the GitHub flow to the agent”—the agent can read PR context and work by intent. That is CodexMonitor’s differentiated value against a traditional git GUI.

---

<h2 id="ch9">Chapter 9 · Remote daemon and iOS: one core, two shapes</h2>

`src-tauri/src/bin/` is 4,902 lines in all: daemon entry (1966) + daemonctl (1414) + rpc domain handling (codex 510 / workspace 273 / rpc.rs 201 / git 196 / prompts 149 / daemon 60) + transport (103) + dispatcher (30).

### 9.1 Daemon architecture

- **Protocol**: TCP + **newline-delimited JSON-RPC** (JSONL, one JSON object per line), not HTTP. Default `127.0.0.1:4732` (daemon internal) / `0.0.0.0:4732` (daemonctl external).
- **Frame format**: request `{"id":n,"method","params"}`; response `{"id":n,"result"|"error"}`; events have no id: `{"method":"app-server-event","params":...}` (`broadcast::channel(2048)` pushes to all authenticated connections).
- **Auth**: `auth` handshake. Token from `--token` or `CODEX_MONITOR_DAEMON_TOKEN`; when unauthenticated, non-auth requests all return `unauthorized`; `--insecure-no-auth` skips. Concurrency limit: `Semaphore(32)` per connection.

### 9.2 RPC dispatch and reuse

`dispatcher.rs` try_handle in order: daemon → workspace → codex → git → prompts. **Reuse is extremely high**: the daemon entry uses `#[path=]` to include the main-program modules (backend/shared/files/workspaces_core etc.) directly; `DaemonState` methods almost 1:1 wrap `xxx_core`—the same batch of core as local Tauri commands. The only difference is `AppState` → `DaemonState` + `DaemonEventSink`; `menu_set_accelerators` degrades to no-op (“remote parity”).

### 9.3 daemonctl lifecycle control (most of the 1414 lines)

- **status**: `probe_daemon` three-step state machine—`ping` (if unauthenticated, receive unauthorized) → `auth` → `daemon_info`; produce `NotReachable / Running{auth_ok} / NotDaemon`.
- **start**: first probe; if running but identity/version/mode mismatch (`should_restart_daemon`) → RPC `daemon_shutdown` graceful stop → poll 20×100ms → if still not gone, `lsof -iTCP:<port>` parse PID → SIGTERM → SIGKILL. **Force-kill is allowed only when auth passed and the process name is `codex-monitor-daemon`** (`can_force_stop_daemon`).
- **No log file, no PID file**—PID is entirely reverse-looked-up from the port (minimalism, but depends on lsof).
- **command-preview**: output a full start-command template (token placeholder `<remote-backend-token>`).

### 9.4 Why iOS needs remote mode

iOS has none of the desktop resources: cannot spawn a `codex app-server` subprocess, cannot run git, no local workspace filesystem—so it is a **thin client** connecting to the desktop daemon. `terminal_mobile.rs` and `dictation/stub.rs` all commands directly return `"Terminal/Dictation is not available on mobile builds."` (terminal needs a local PTY; dictation needs a local Whisper model).

### 9.5 Tailscale integration

`tailscale_status`: scan candidate binaries (PATH → homebrew → Tailscale.app bundle) → `tailscale version` verify (`looks_like_tailscale_version` against GUI error output) → `tailscale status --json`. macOS uses `launchctl asuser <uid>` to run as the GUI user. `suggested_remote_host` parses `DNSName/TailscaleIPs/CurrentTailnet` to produce `macbook.tailnet.ts.net:4732`. The iOS side fills the suggested host + token into `Settings > Server`.

### 9.6 Taste observation

~5000 lines looks like a lot; **the core logic is all a reused thin shell**. What is genuinely new is three layers of glue: `#[path]` module splicing, JSON param-parse boilerplate, daemonctl lifecycle management. **Why it is worth it**: mobile cannot run codex locally; without this you would rewrite the entire backend for iOS—“one core + one protocol” serving desktop and iOS at once, behavior naturally consistent. The cost: the desktop side gains a class of “process + port + version drift” ops complexity; TCP plaintext token depends on Tailscale’s private network rather than its own encryption.

---

<h2 id="ch10">Chapter 10 · Frontend architecture: feature-sliced + zero state libraries</h2>

### 10.1 Scale and organization

`src/` has 603 ts/tsx/css files, about 127K lines; `src/features/` 496 files / 103K lines, 25 features, using feature-sliced `components/hooks/utils` subdirectory convention:

| feature | files/lines | Duty |
|---|---|---|
| app | 141 / 27,205 | startup, layout, sidebar, controllers (heaviest) |
| threads | 70 / 20,636 | chat-thread state machine |
| settings | 42 / 12,111 | settings page |
| git | 59 / 9,099 | diff/log/branch/GitHub |
| workspaces | 36 / 6,931 | workspace management |
| composer | 32 / 6,270 | input box + autocomplete |

### 10.2 Entry and routing

- **No router library, purely state-driven**. `App.tsx` (64 lines) uses `useWindowLabel()` (read Tauri window label) to split—`about` window lazy-loads `<AboutView>`, else `<MainApp />`.
- `main.tsx`: Sentry init + `app_open` count + mobile gesture/viewport compatibility.
- `MainApp.tsx` (1882 lines) **giant composition root**: no Context; ~70 hooks’ return objects are passed as props layer by layer. View switching relies on `activeTab` state (`home|projects|codex|git|log`) + `centerMode` (chat/diff) + `showHome`.

### 10.3 State-management choice (the taste core)

**Zero external state libraries**: no redux/zustand/jotai/mobx (grep-verified). Pure `useReducer` + `useState` + `useRef`, explicit props drill-down.

- `threadReducer` (1179 lines, 6 slices, no third party): threadLifecycleSlice (493 lines) / threadItemsSlice (333 lines) / threadConfigSlice / threadQueueSlice / threadSnapshotSlice / common. About 40 actions (`ensureThread`/`upsertItem`/`appendAgentDelta`/`appendToolOutput`/`appendReasoningSummary`/`markProcessing`…).
- `useThreads.ts` (938 lines, main aggregator) internally uses **ref-mirrored state** (`itemsByThreadRef` etc.) for event callbacks to read synchronously—solving the classic “closure captured stale state” problem.
- Bridge from events to reducer: `useThreadEventHandlers` → `useThreadTurnEvents` / `useThreadItemEvents` / `useThreadApprovalEvents` → each callback dispatch.

**Why no external store** (taste judgment):
1. A single-workspace desktop app; the state graph converges to “one activeWorkspace + a pile of threads”; a reducer is enough;
2. Event-dense streaming updates (delta-level) need ref mirror + dispatch; an external store’s benefit is small;
3. feature-sliced itself provides module boundaries, avoiding global-store coupling;
4. The cost is MainApp’s giant props passing and a lot of useMemo/useCallback glue.

### 10.4 Component library and styles

**No Tailwind, no Radix, no shadcn**. Dependencies only `lucide-react` (icons), `react-markdown`+`remark-gfm`, `prismjs`, `@xterm/xterm`, `@tanstack/react-virtual`, Sentry. UI is all handwritten: CSS-variable tokens (`ds-tokens.css`) + semantic class names; components are ordinary function components assembled into `ModalShell/ToastShell/PanelShell`.

### 10.5 Frontend contrast with kanban

The architecture is isomorphic (backend process + Tauri/browser frontend + event-stream fanout + reducer-driven UI), but CodexMonitor’s **state management is clearly heavier**—reducer 1179 lines/40 actions, event-parse hooks thousands of lines, ref-mirror sync, multi-workspace keyed state. The root of the difference: kanban card state is a coarse-grained snapshot (fetch then wholesale replace); CodexMonitor has to carry **streaming delta rendering** (agentMessage append per delta, tool output stream, reasoning stream) + approval queue + token/rate-limit live panel + subagent cascade archive. But the architecture layer is actually lighter: no store middleware, no selector library, no async actions; complexity is suppressed by feature-sliced directories and a huge unit-test count.

---

<h2 id="ch11">Chapter 11 · Frontend features: Composer / dictation / diff / usage</h2>

### 11.1 Composer autocomplete (fully homemade)

`useComposerAutocompleteState.ts` assembles three trigger groups: `$` (skills+apps), `/` (8 hardcoded slash commands + prompts), `@` (files). **All data goes through RPC**: skills→`skills_list`, apps→`apps_list`, prompts→`prompts_list` (all forwarded via daemon to Codex app-server); files→`list_workspace_files` (Rust scans disk directly, cap 500). Trigger decision / subsequence scoring / highlight are **all handwritten**; UI is `ComposerSuggestionsPopover` positioned above the textarea; no third-party autocomplete library. Selecting `$` an app generates an `app://id` mention binding; `/prompts:` supports `findNextPromptArgCursor` Tab-jump of argument slots.

### 11.2 Queue vs Steer (a model of interaction polish)

`ComposerSendIntent = "default"|"queue"|"steer"`. `resolveSendMessageOptions`: `canSteerCurrentTurn = isProcessing && steerEnabled && activeTurnId`. steer → RPC `turn_steer`; queue/unsupported → `send_user_message` (turn/start; message enqueue is managed by app-side ComposerQueue). Default mode from setting `followUpMessageBehavior`; **Shift+Cmd/Ctrl+Enter sends the opposite mode** (`oppositeSubmitIntent`). Failures distinguish `steer_failed` and handle stale-steer errors; runtime button copy switches between Steer/Queue.

### 11.3 Image attachments

Three entries all land as a **local-path array** (`useComposerImages` stored by thread draftKey): picker uses tauri `open`; drag-drop uses window-level `onDragDropEvent` + local DOM drop (web side FileReader to data URL); paste by `useComposerImageDrop.handlePaste` reading clipboardData. **What is sent to Codex is a data-URL string array**: `normalizeImagesForRpc` in remote/mobile mode calls Rust `read_image_as_data_url` one by one.

### 11.4 Dictation (Whisper)

Rust side **whisper-rs** local inference; `real.rs` defines ggml-tiny/base/small/medium/large-v3 download from HuggingFace; download/cancel/remove/status go through daemon RPC; progress events push to the frontend. waveform is frontend-drawn 36 dynamic bars (`DictationWaveform`, subscribe level events).

### 11.5 Usage and notifications

- **Usage**: Home usage = `local_usage_snapshot(30 days)` (Rust reads Codex home data, `useLocalUsage` 5-minute poll); sidebar credits meter = `account/rateLimits/updated` AppServerEvent → RateLimitSnapshot → `getUsageLabels`.
- **Notifications**: purely frontend-driven, subscribe AppServerEvents (turn complete / approval request); system notifications use `@tauri-apps/plugin-notification`; Rust only `send_notification_fallback`—on macOS debug, **osascript AppleScript** (not notify_rust). Sound uses local mp3 assets + HTMLAudio. All have min-interval / window-focus suppression.

### 11.6 Taste observation

**Bias toward homemade**: autocomplete engine, prompt history, rank scoring, waveform, usage view model all handwritten; only the heavy work is given to libraries (`@pierre/diffs`, xterm, whisper-rs, tauri plugins, react-virtual). **Interaction polish**: stale-steer tolerance, drag-position devicePixelRatio normalize, code-fence expand, `/prompts:` Tab-jump args, hold-to-talk, follow-up hint bar dynamically explaining Steer availability, mobile touch layout branch, every RPC writes a DebugEntry for tracing.

---

<h2 id="ch12">Chapter 12 · Config tripartite: settings.json / workspaces.json / config.toml</h2>

| File | Contents | Ownership |
|---|---|---|
| `settings.json` (app data directory) | AppSettings: codexBin/codexArgs, backendMode, remote backend host/token, shortcuts, defaultAccessMode, uiScale/theme, global_worktrees_folder | CodexMonitor itself |
| `workspaces.json` | WorkspaceEntry list (id/name/path/kind/worktree/settings) | CodexMonitor itself |
| `config.toml` (`$CODEX_HOME`) | Codex itself: features (steer/collaboration_modes/unified_exec/apps), personality, model | **codex is the source of truth** |

**Sync strategy** (`settings_core.rs`, 70 lines + `codex/config.rs`): 5 fields synced with config.toml (collaboration_modes_enabled/steer_enabled/unified_exec_enabled/experimental_apps_enabled + personality) are **bidirectionally synced in both read and write**—`get_app_settings_core` **overwrites** in-memory values from config.toml (codex is authoritative); `update_app_settings_core` writes back to config.toml (via toml_edit in `config_toml_core.rs`) + writes local storage.

**Taste observation**: respect “Codex’s config belongs to Codex”—the App only syncs the few switches its own UI exposes, does not full-mirror, avoiding two configs fighting. Chapter 4 already confirmed key names match official exactly.

---

<h2 id="ch13">Chapter 13 · Engineering and tests: AGENTS.md layered discipline + unit-test density</h2>

### 13.1 AGENTS.md (6KB of tribal knowledge)

- Domain logic → shared/*; commands → thin adapter layer; frontend IPC only through services/tauri.ts; event fanout only in services/events.ts.
- Frontend feature-sliced: `src/features/{app,threads,...}`, orchestration in hooks/* and bootstrap/*.
- Daemon and app reuse shared core; method names/payloads align (remote parity).
- `docs/codebase-map.md`: task-oriented file finding (“if you need X, edit Y”)—**this is a navigation document written for agents**.

### 13.2 Test density

- Rust side: `git_ui_core/tests.rs` 437 lines, tray.rs 6 unit tests, files io.rs 6 symlink-escape tests, worktree/workspaces each have tests—**the pure-logic layer is testable because of closure injection**.
- Frontend: every hook has `*.test.ts(x)` (useSettingsDefaultModels, useThreads, composer, etc.).
- Integration: `main.test.tsx`, `smoke`, etc.

### 13.3 Engineering-culture contrast with Codex / kanban

- **codex**: bazel monorepo, 126 crates, AGENTS.md long and complete (including app-server API best practices).
- **kanban**: GritQL static checks + husky + dogfood (use kanban to develop kanban).
- **CodexMonitor**: layered discipline written into AGENTS.md (agent-readable, agent-executable), docs/codebase-map.md navigation, dual-platform (desktop + daemon) shared-core architecture tests, `.codex/` directory (the project itself is developed with Codex).

**Commonality**: all three projects are shaped by an **agent-first development paradigm**—AGENTS.md is not documentation but “an engineering contract for the agent”; the codebase structure has to serve the agent’s navigability.

---

<h2 id="ch14">Chapter 14 · Development taste: Dimillian’s ten choices</h2>

> This chapter is the center of gravity of the lecture. Dimillian (Thomas Ricouard) is known for SwiftUI open-source components (ACarousel etc.), yet for a “tool that orchestrates AI agents” he chose **Tauri (Rust + Web frontend)**. The choice itself leaks a lot of information. Each of the ten choices below is annotated with source evidence.

### Choice 1: connect over the official protocol, not black-box scrape

CodexMonitor did not take the “spawn Codex’s TUI then parse the screen” path (that is what kanban does to Codex). It uses `codex app-server` + stdio JSON-RPC. **This shows respect for the “upstream ecology”**: the official side provided a socket (the app-server protocol is designed for IDEs), so use the socket; rather bind to the Codex ecology than do a fragile screen adapter. Chapter 4 confirmed all 22 methods are official protocol methods, handshake params item-by-item match, config key names consistent, auth JWT parse corresponds, rules syntax correct—**this is “contract-level alignment” code**, behind it the patience of reading official source field by field.

### Choice 2: shared-core + thin commands + closure injection

Domain logic (git, worktree, Codex, settings, usage) is all collected into the `shared/*` pure-logic layer; the Tauri command layer is only a thin shell of “local or forward to daemon”; platform side effects (git commands, fs, spawn) are all injected as closures. **This is a very Rust architectural view**: typed dependency injection + pure functions that are testable. Effect: app and daemon dual-process share one core, zero logic drift; unit tests can construct in-memory state to test the core (git_ui_core/tests.rs 437 lines).

### Choice 3: read with libgit2, write with CLI, GitHub with gh

Not dogmatic—reads want “fast and type-safe” (libgit2 pure memory); writes want “behavior fully faithful to git” (CLI); GitHub directly reuses `gh` (do not rebuild an API client). **Pick the most suitable tool for each scene**, rather than a unified abstraction. This is seasoned engineering judgment: libgit2’s write path has many pits (config differences, hooks); the read path is stable.

### Choice 4: worktree is a first-class object, not a task by-product

Contrast kanban (worktree is the card’s shadow; trash and it is gone): CodexMonitor made worktree a complete entity of `WorkspaceEntry{kind: Worktree}`: CRUD, rename (with rollback), change upstream, inherit agents.md. **Product intent decides the data model**—it is a “workspace-management product”; worktree is an independent execution environment for the agent (independent branch + independent codex home).

### Choice 5: frontend zero state libraries + feature-sliced

No redux/zustand; pure useReducer + ref mirror + explicit props drill-down; no Tailwind/Radix; CSS-variable tokens + handwritten components; UI side uses only 6 families of libraries. **“Complexity is suppressed by directory structure and discipline, not by libraries.”** Streaming delta rendering needs ref mirror + dispatch (`useThreads.ts` 938 lines); an external store’s benefit here is small; feature-sliced’s 25 feature directories provide a strong enough module boundary.

### Choice 6: if you can write it yourself, write it yourself

Autocomplete engine (trigger decision / subsequence scoring / highlight), prompt history, rank scoring, waveform (36 dynamic bars), usage aggregation (840-line JSONL parse), frontmatter parse, rules dedup, lock files—**all handwritten**; only the heavy work is given to libraries (xterm, whisper-rs, @pierre/diffs, react-virtual). **Dependency minimalism**: 600+ frontend files, only 19 runtime deps, of which UI-related only 6 families.

### Choice 7: macOS first, but not only macOS

Tray macOS only (tray.rs 701 lines; non-macOS empty implementation), window appearance uses objc2-app-kit calling NSAppearance directly, icon resolve calls defaults/sips—**native platform APIs called directly, not abstracted**; meanwhile Windows opt-in (separate tauri config to avoid macOS-only window effects), iOS remote mode. This is the strategy of “take the primary platform to the extreme; other platforms follow pragmatically.”

### Choice 8: iOS remote daemon—one core, two shapes

iOS cannot run Codex locally, so start a desktop daemon (TCP JSONL JSON-RPC) and let the iPhone use Tailscale. `#[path]` include reuses main-program modules; daemonctl uses port reverse-lookup of PID + version compare + force-kill safety gate to manage lifecycle. **This is “architectural investment for multi-end shapes”**—one core + one protocol, desktop/iOS behavior naturally consistent; the cost is daemon process/port/version-drift ops complexity (most of daemonctl’s 1414 lines is preventing “killing the wrong process”).

### Choice 9: agent-first development

AGENTS.md is “an engineering contract for the agent” (layering rules, remote parity, feature-sliced conventions); docs/codebase-map.md is “if you need X, edit Y” task navigation; the repo ships `.codex/` config—**this project itself is developed with Codex** (isomorphic with kanban’s dogfood). Codebase structure serves the agent’s navigability; this is a common trait of the 2025-2026 generation of AI-native project clusters.

### Choice 10: defensive programming + interaction polish

canonicalize + symlink-escape checks (6 tests), 400KB read truncation, strict UTF-8, Arc::ptr_eq against races, stale-steer tolerance, disconnect DISCONNECTED_MESSAGE—**engineering conservatism**; meanwhile Shift+Cmd+Enter reverses Queue/Steer, hold-to-talk waveform, `/prompts:` Tab-jump args, follow-up hint bar dynamically explaining—**product aggressiveness**. Conservative engineering + aggressive interaction is the typical temperament of a mature independent developer.

### Taste in one sentence

> **With the fewest dependencies and the cleanest architectural layering, put the official protocol to work, and make a “command stand for Codex”—restrained and conservative in engineering, polished in product, and architecturally investing early for multi-end shapes.**

---

<h2 id="ch15">Chapter 15 · Summary: three most distinctive designs and their trade-offs</h2>

### Three things (if you remember only three designs)

**1. Official app-server protocol direct connect, not PTY screen scrape**
This is the most essential split between CodexMonitor and kanban. kanban has to be compatible with 7 agents, so it must black-box-adapt; CodexMonitor only serves Codex, so it takes the official socket (LSP-style handshake + typed events + two-way control). **Trade-off**: deep not wide—give up “any agent can plug in,” in exchange for “every layer of Codex capability is in hand” (steer interrupt, approval response, sandboxPolicy passed precisely, account/rateLimits live).

**2. shared-core one core, desktop/iOS two shapes**
Domain logic collected into a pure-logic layer (closures inject side effects); app and remote daemon use `#[path]` include to share the same batch of `*_core`. **This is architectural investment for iOS remote mode**—without it you would rewrite the entire backend for mobile. The cost is daemon ops complexity (process/port/version drift).

**3. Frontend zero state libraries + streaming-delta state machine**
No redux/zustand/Tailwind/Radix; pure useReducer + ref mirror + handwritten CSS tokens; 600+ frontend files, 19 runtime deps (UI libraries only 6 families). Event-driven state machine (app-server events → hub fanout → reducer slice) carries streaming render. **Trade-off**: MainApp’s giant props glue in exchange for zero middleware complexity—a wise choice for a single-workspace desktop app.

### One-page contrast with Codex / kanban

| Dimension | Codex (official) | kanban | CodexMonitor |
|---|---|---|---|
| Role | the Agent itself | multi-agent orchestration board | Codex-dedicated desktop command stand |
| Integration path | — | PTY + hooks (black box) | app-server protocol (official socket) |
| State model | rollout file assets | card 4-column state machine | workspace × thread event stream |
| worktree | none | one worktree per card | worktree as first-class workspace |
| Frontend | TUI | React board | React feature-sliced |
| Philosophy | local-first CLI | browser control plane | desktop + iOS remote |

### Who it is for / who it is not for

- **For**: heavy Codex users (desktop experience far better than the terminal), developers who want multi-agent parallel with worktree isolation, iOS + Tailscale mobile-office scenes, readers studying the “official-protocol integration” paradigm.
- **Not for**: non-Codex users (bound ecology), teams that need browser access (no web edition), light users (installing a Rust toolchain + Codex + gh is not a low bar).
- **Connection to this series**: it is a **“deeper practical confirmation” of the OpenAI Codex source analysis** (protocol, rollout, config, auth all match), and the **“official-protocol contrast group” of the Kanban source analysis**.

### 🔍 Source pointers (go check the source)

| Want to know | Look here |
|---|---|
| app-server spawn and handshake | `src-tauri/src/backend/app_server.rs` (L749 spawn, :419 handshake) |
| Full set of Codex method calls | `src-tauri/src/shared/codex_core.rs` (1033 lines) |
| Event classify-and-route | `src-tauri/src/backend/app_server.rs` (extract_related_thread_ids / resolve_workspace_for_cwd) |
| shared domain core | `src-tauri/src/shared/` (31 files 11,197 lines) |
| worktree core | `src-tauri/src/shared/workspaces_core/worktree.rs` (642 lines) |
| git hybrid strategy | `src-tauri/src/shared/git_ui_core/` (libgit2 read + CLI write) |
| rollout usage stats | `src-tauri/src/shared/local_usage_core.rs` (840 lines) |
| Remote daemon | `src-tauri/src/bin/codex_monitor_daemon.rs` + `codex_monitor_daemonctl.rs` |
| Frontend event hub | `src/services/events.ts` (createEventHub lazy subscribe) |
| Frontend state machine | `src/features/threads/hooks/threadReducer/` (1179 lines 6 slices) |
| Composer autocomplete | `src/features/composer/` (useComposerAutocompleteState.ts) |
| Codex protocol definition | `参考项目/codex/codex-rs/app-server-protocol/src/protocol/common.rs` |
| Codex handshake implementation | `参考项目/codex/codex-rs/app-server/src/request_processors/initialize_processor.rs` (:70–101) |
| Codex best practices | `参考项目/codex/AGENTS.md` (:104 app-server breaking-change surface) |
