Repository navigation
Make the Codex desktop-app restart cross-platform and fold it into --restart-codex #4510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
c42a346
docs(devlog): roadmap for the cross-platform Codex desktop-app restart
lidge-jun 6579078
docs(devlog): fold the A-phase audit blockers into the restart roadmap
lidge-jun 0ee21db
docs(devlog): transfer the restart lock to the handoff helper instead…
lidge-jun aaeceee
docs(devlog): give restart_in_flight a contract home and stop the ser…
lidge-jun 2bec0b8
docs(devlog): name the lock reentrancy rule and the test-only evidenc…
lidge-jun d8c503a
docs(devlog): record the roadmap unit's resume state and wp2 direction
lidge-jun b24a6b4
feat(codex): make the desktop-app restart a cross-platform shared sur…
lidge-jun 6a99a69
fix(codex): close three fail-open defects in the desktop-restart surface
lidge-jun a834c2b
fix(codex): keep the tree compiling after the reason-union rename
lidge-jun a067cf5
fix(codex): stop a corrupt restart lock from wedging every future res…
lidge-jun d4cd285
test(codex): give every desktop-restart case its own lock path
lidge-jun f26bc18
test(codex): cover the macOS and Linux halves of the desktop restart
lidge-jun f3289b4
test(codex): make the restart_in_flight case independent of pid roulette
lidge-jun 5fef439
docs(devlog): record the wp2 outcome and the direction for wp5
lidge-jun b029f24
feat(codex): restart the Codex app you are running inside
lidge-jun 5806ec5
fix(codex): close two handoff defects an audit found
lidge-jun 333beb2
docs(devlog): record wp5 built and the open cycle's work-phase binding
lidge-jun 482a3be
feat(cli): give --restart-codex one meaning across every command
lidge-jun ab4f53a
feat(codex): restart the desktop app from the management path too, an…
lidge-jun 1980013
docs: describe the merged restart contract in English and every locale
lidge-jun 2deb736
fix(codex): actually implement the desktop-tree app-server exclusion
lidge-jun 5962491
fix(cli): emit the desktop half of a catalog pull, and invert the con…
lidge-jun 20e870f
docs(cli): name the Windows exclusion limitation where the code makes…
lidge-jun 9cc1422
fix(cli): stop the desktop failure being clobbered, and finish invert…
lidge-jun 867d464
docs(devlog): close wp3 with its two reviewed residuals
lidge-jun 84488f0
fix(codex): never claim a stop the process list contradicts
lidge-jun c8c1c1a
fix(codex): confirm a stop by polling the process list, not by asking…
lidge-jun 2d22d97
fix: make the macOS restart cases hermetic and redact a foreign home …
lidge-jun 156e107
test: make discovery deterministic in the failed-probe case
lidge-jun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
199 changes: 199 additions & 0 deletions
199
devlog/_plan/260913_cross_platform_desktop_app_restart/000_plan.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| # Cross-platform Codex desktop-app restart, folded into `--restart-codex` | ||
|
|
||
| Status: OPEN. Opened 2026-09-13. Class C4 (public CLI contract change, process | ||
| termination on three operating systems, management-API contract change). | ||
|
|
||
| ## 1. Objective | ||
|
|
||
| `ocx sync --restart-codex` must fully quit and relaunch the Codex desktop app on | ||
| macOS, Linux and Windows, not merely send SIGTERM to `codex app-server` children. | ||
| The Windows-only `--restart-desktop-app` capability becomes one cross-platform | ||
| shared surface that every caller reads, and `ocx system codex-restart` restarts the | ||
| desktop app through that same surface instead of being app-server-only. | ||
|
|
||
| The maintainer report that opened this unit: `ocx sync --restart-codex` stopped | ||
| having any observable effect. The measured cause is in §3. | ||
|
|
||
| ## 2. Constraints | ||
|
|
||
| - **No local product suite.** `bun run test`, `bun run typecheck`, `bun run build:gui` | ||
| and installs are NOT RUN for this unit. Proof is hosted CI at the exact final head. | ||
| Focused local reads are for debugging only and are never quoted as a gate. | ||
| - Everything fails **closed**. A failed discovery, a failed enumeration, an | ||
| unreadable process identity or an unreadable ancestry chain must never be read as | ||
| "nothing to do" and must never authorise a kill. This is the existing doctrine in | ||
| `src/codex/desktop-app-restart.ts` and `src/codex/app-server-processes.ts`; the | ||
| cross-platform rewrite inherits it unchanged. | ||
| - Only the **current user's** processes are ever signalled, on every platform. | ||
| - Executables are resolved from trusted absolute system locations, never from PATH. | ||
| - The relaunch never creates a **second** instance. If anything survived | ||
| termination, nothing is relaunched and the operator is told. | ||
| - Out of scope: the proxy's own restart (`system-restart-contract.ts`), Claude | ||
| Desktop, Cursor, any provider or routing behaviour, and the GUI's visual design. | ||
|
|
||
| ## 3. Measured cause of "`--restart-codex` does nothing" | ||
|
|
||
| Measured live on 2026-09-13; full evidence in `001_platform_topology.md`. | ||
|
|
||
| The macOS Codex desktop app runs its app-server as a bundle-internal child: | ||
|
|
||
| ``` | ||
| 72687 1 /Applications/ChatGPT.app/Contents/MacOS/ChatGPT | ||
| 73511 72687 /Applications/ChatGPT.app/Contents/Resources/codex -c features.code_mode_host=true app-server ... | ||
| 76297 73511 /Applications/ChatGPT.app/Contents/Resources/codex-code-mode-host | ||
| ``` | ||
|
|
||
| `isCodexAppServerCommandLine` **does** match pid 73511, so `--restart-codex` is not | ||
| failing to find a target. It signals the app's own child, the app immediately | ||
| respawns it, and the renderer keeps the model list it built at app start. The | ||
| result an operator sees is "the command ran and nothing changed" — the same | ||
| symptom #2292 recorded on Windows, now on macOS too, because the picker's cache | ||
| lives in the shell rather than in the app-server. | ||
|
|
||
| So the fix is not a better matcher. The only thing that reliably refreshes the | ||
| picker is restarting the shell that owns it, which is exactly what | ||
| `--restart-desktop-app` already does on Windows and what no platform other than | ||
| Windows can currently do at all. | ||
|
|
||
| ## 4. The consent question, decided | ||
|
|
||
| `--restart-desktop-app` was deliberately kept separate from `--restart-codex` | ||
| (see the module header of `src/codex/desktop-app-restart.ts`): quitting the desktop | ||
| app ends live conversations, which is a larger consent than restarting a background | ||
| helper. That reasoning was sound and is now **superseded by an explicit maintainer | ||
| decision**: `--restart-codex` must mean "the Codex app is fully stopped and started | ||
| again". The narrow behaviour does not disappear — it moves to an explicit | ||
| `--restart-app-server-only` flag — so no caller loses the ability to ask for it. | ||
|
|
||
| `--restart-desktop-app` keeps working as a deprecated alias so existing scripts and | ||
| the published documentation do not break in the same release that changes the | ||
| meaning of the other flag. | ||
|
|
||
| ## 5. Work-phase map (dependency-ordered) | ||
|
|
||
| | # | Work-phase | Doc | Depends on | | ||
| |---|---|---|---| | ||
| | wp1 | Docs-first roadmap (this unit) | `000`, `001` | — | | ||
| | wp2 | Cross-platform shared restart surface | `010` | wp1 | | ||
| | wp5 | Detached self-handoff restart | `020` | wp2 | | ||
| | wp3 | CLI + management contract merge, docs and generated surfaces | `030` | wp2, wp5 | | ||
| | wp4 | Live three-host verification, hosted CI, PR, merge | `040` | wp3 | | ||
|
|
||
| `002` records the A-phase audit findings and their dispositions. It is part of wp1's | ||
| output: the roadmap was audited by three independent reviewers before any | ||
| implementation, two returned FAIL, and six blockers were folded back into `010`, | ||
| `020` and `030` before wp2 started. | ||
|
|
||
| Execution order is wp1 -> wp2 -> wp5 -> wp3 -> wp4. The goalplan ids are not | ||
| chronological because wp5 was appended after the first four were registered | ||
| (LOOP-UNIT-CHAIN-01); the dependency column above is authoritative. | ||
|
|
||
| ## 6. Why wp5 exists | ||
|
|
||
| The self-ancestry guard refuses to restart the desktop app when the command is | ||
| running **inside** it. On this maintainer's machine that is the normal case: the | ||
| shell that runs `ocx` is a descendant of `ChatGPT.app` through the app-server | ||
| (`10456 -> 73511 -> 72687 -> launchd`). Without wp5 the merged flag would refuse in | ||
| exactly the situation that produced the original complaint, and the feature would | ||
| still "not work". | ||
|
|
||
| wp5 replaces the refusal with a handoff: a fully detached helper outlives the | ||
| caller, waits for it to exit, re-enumerates, and then performs the restart from | ||
| outside the tree. The guard itself is kept — it is what decides that a handoff is | ||
| needed rather than a direct kill. | ||
|
|
||
| ## 7. Acceptance | ||
|
|
||
| - `ocx sync --restart-codex` fully quits and relaunches the Codex desktop app on | ||
| macOS, Linux and Windows, proven by a root-process identity change on a real host | ||
| of each platform. | ||
| - `ocx system codex-restart --yes` does the same through the same module. | ||
| - One module owns discovery, stop and relaunch; no caller carries a per-platform | ||
| branch. | ||
| - `--restart-desktop-app` still works and says it is deprecated. | ||
| - `--restart-app-server-only` reproduces the old `--restart-codex` behaviour. | ||
| - `ocx catalog pull --restart-codex` means the same thing as `ocx sync --restart-codex`, | ||
| so the flag has one meaning across the CLI (`002` §B4). | ||
| - The remote `POST /api/machine/sync` `restartCodex` field does **not** gain desktop | ||
| scope (`002` §B5). | ||
| - Hosted CI green at the exact final head; PR merged into `dev`. | ||
|
|
||
| ## 8. Terminal outcomes | ||
|
|
||
| - **DONE** — every item in §7 has fresh evidence recorded in `040`. | ||
| - **BLOCKED** — a host required for platform proof is unreachable and no equivalent | ||
| host of that platform exists. Record which platform lacks proof; do not claim it. | ||
| - **UNSAFE** — any design that could terminate a process outside the discovered, | ||
| current-user, package-owned tree. Stop and redesign. | ||
| - **NEEDS_HUMAN** — CI red at the final head for a reason outside this unit's scope. | ||
|
|
||
| ## 9. Resume state | ||
|
|
||
| Kept current so a later cycle, or a reader after a context loss, resumes from this | ||
| file rather than from a transcript. | ||
|
|
||
| | Work-phase | State | Artifact | | ||
| |---|---|---| | ||
| | wp1 roadmap | **done** | `000`, `001`, `002`, `010`, `020`, `030`, `040` on `codex/260913-cross-platform-desktop-restart` | | ||
| | wp2 shared surface | **done** | `010`; commits de44e6a6..49e36f1d | | ||
| | wp5 self-handoff | **built, cycle not yet closed** | `020`; commits d1efbebd, 75722903 | | ||
| | wp3 contract merge | **done** | `030`; commits 8ebbdc5c..7ac03181 | | ||
| | wp4 verification and delivery | not started | `040` | | ||
|
|
||
| **What wp1 concluded.** The inert `--restart-codex` is not a matcher defect — the | ||
| matcher finds the app-server correctly, and the app respawns it while the picker | ||
| keeps the roster the shell built at launch. Only restarting the shell fixes it, which | ||
| is why the Windows-only capability has to become cross-platform rather than the | ||
| matcher being widened. Three audit rounds moved the design from "quit the app" to | ||
| "quit the app, safely, from a process that will survive doing it", which is where the | ||
| handoff and the singleton lock came from. | ||
|
|
||
| **What wp2 concluded.** `010` was built as written. Two independent code audits found | ||
| three fail-open defects that the plan had specified correctly and the code had not | ||
| implemented: Linux ancestry returned a non-empty chain for an unreadable hop, so a | ||
| probe failure would have quit the shell hosting the caller's own session; macOS | ||
| classified every dead parent as unreadable, which would have made the wp5 helper | ||
| refuse forever; and the lock was not exclusive at all, because `wx` on a uniquely | ||
| named staging file always succeeds. All folded and re-audited to PASS. | ||
|
|
||
| The lesson for the remaining phases: a plan section saying "fails closed" is not | ||
| evidence that the code does. Each of the three defects reads as correct until the | ||
| error shape is checked against what the runtime actually throws. | ||
|
|
||
| **FSM note for whoever resumes.** The cycle currently open is bound to **wp3**, not | ||
| wp5: both became ready when wp2 closed and the orchestrator activated wp3. The wp5 | ||
| code is built, audited and committed regardless; the open cycle should now deliver | ||
| wp3 and close, and wp5's record lives in the goalplan task ledger. | ||
|
|
||
| **Direction for wp5.** Build `020` as written. The lock it depends on already exists | ||
| and is verified, including the transfer that lets the helper inherit ownership, so | ||
| wp5 adds `handoff.ts`, the hidden `internal` command, and the `startHandoff` seam the | ||
| ladder already accepts. `handoff_started` is in the union and confirmed unreachable | ||
| until that seam is supplied. | ||
|
|
||
| **What wp3 concluded.** The merged contract shipped across `sync`, `sync-cache`, | ||
| `catalog pull` and `ocx system codex-restart`, with docs in English and seven | ||
| locales. Three audit rounds were needed. The recurring failure was not the design | ||
| but the seams between the pieces: `excludePids` was passed to an option that did not | ||
| exist, `desktopAppRestarted` was computed and dropped, `restartIncomplete` was | ||
| assigned where it should only ever be set, and three source-oracle tests still | ||
| pinned the contract the change reverses. | ||
|
|
||
| **Two residuals accepted, both reviewed and recorded rather than hidden:** | ||
|
|
||
| - **Windows `excludePids` is a no-op for app-servers.** The CIM probe enumerates | ||
| `ChatGPT.exe` while Windows app-servers run as `codex.exe` / `codex-code-mode-host`, | ||
| so they still receive SIGTERM before the app quits. The restart is correct; the cost | ||
| is one extra interrupted turn on the platform that already had this feature. Closing | ||
| it means widening the query that decides what may be killed, which needs its own | ||
| verification. Documented at the decision point in `src/cli/restart-scope.ts`. | ||
| - **`desktopAppRestarted` appears on an unchanged-catalog pull.** Presence means the | ||
| restart was requested and `true` means it relaunched; a failed relaunch is | ||
| `ok: false` with `code: "restart_incomplete"`, so no caller can confuse them. | ||
|
|
||
| **Direction for wp4.** Execute `040` as written. The local macOS host cannot prove its | ||
| own direct restart and is reserved for the handoff proof, after the merge. | ||
|
|
||
| **Standing constraint.** No local product suite, build, typecheck or install at any | ||
| point in this unit (§2). Every completion claim rests on live host evidence plus | ||
| hosted CI at the exact final head. |
172 changes: 172 additions & 0 deletions
172
devlog/_plan/260913_cross_platform_desktop_app_restart/001_platform_topology.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| # Measured desktop-app topology on three platforms | ||
|
|
||
| Research document for `000_plan.md`. Measurements taken 2026-09-13 on live hosts. | ||
| No diffs here by LEXICO-SPLIT-01; the implementation designs are in `010`, `020`, `030`. | ||
|
|
||
| Every value below was read from a running installation. Nothing is inferred from | ||
| documentation or from the existing Windows implementation. | ||
|
|
||
| ## 1. macOS | ||
|
|
||
| Two hosts were measured: the maintainer's laptop (`local`) and `macmini-cf`. | ||
|
|
||
| | | local | macmini-cf | | ||
| |---|---|---| | ||
| | Bundle | `/Applications/ChatGPT.app` | `/Applications/ChatGPT.app` | | ||
| | `CFBundleIdentifier` | `com.openai.codex` | `com.openai.codex` | | ||
| | `CFBundleName` | `ChatGPT` | `ChatGPT` | | ||
| | `CFBundleShortVersionString` | 26.908.40834 | 26.901.51231 | | ||
| | Root process | pid 15901, ppid 1 | pid 25712, ppid 1 | | ||
| | Root argv[0] | `/Applications/ChatGPT.app/Contents/MacOS/ChatGPT` | same | | ||
| | bun | `~/.bun/bin/bun` 1.4.0 | `~/.bun/bin/bun` 1.3.14 | | ||
| | opencodex checkout | this worktree | `/Users/junny/Developer/opencodex` | | ||
|
|
||
| The bundle **name** is `ChatGPT` but the bundle **identifier** is `com.openai.codex`. | ||
| Discovery must key on the identifier: the display name is shared with a different | ||
| OpenAI product and is the wrong thing to match. | ||
|
|
||
| `mdfind "kMDItemCFBundleIdentifier == 'com.openai.codex'"` resolves to the bundle on | ||
| both hosts, and `osascript -e 'id of app "ChatGPT"'` returns `com.openai.codex`. | ||
|
|
||
| ### 1.1 Process shape | ||
|
|
||
| ``` | ||
| 15901 1 /Applications/ChatGPT.app/Contents/MacOS/ChatGPT | ||
| 16733 15901 /Applications/ChatGPT.app/Contents/Resources/codex ... app-server ... | ||
| 28300 16733 /Applications/ChatGPT.app/Contents/Resources/codex-code-mode-host | ||
| 16722 15901 .../bare-modifier-monitor --key DoubleShift | ||
| 15910/15911/15913/16916 15901 GPU, network, storage, audio services + renderers | ||
| ``` | ||
|
|
||
| Two findings that the implementation must respect: | ||
|
|
||
| **Crashpad handlers are launchd children, not app children.** pids 72689 and 72691 | ||
| were still alive under ppid 1 from an app instance that had already exited. A "root | ||
| process" rule of "parent is not in the tree" would classify a stale crashpad handler | ||
| as a restart target. Membership must therefore be decided by executable path inside | ||
| the bundle **and** liveness of the shell, and a surviving crashpad handler must not | ||
| block the relaunch. | ||
|
|
||
| **The app-server is inside the bundle.** `Contents/Resources/codex ... app-server` is | ||
| matched by `isCodexAppServerCommandLine`, so `--restart-codex` already signals it and | ||
| the app simply respawns it. This is the measured reason the flag appears to do | ||
| nothing (`000_plan.md` §3). | ||
|
|
||
| ### 1.2 Quit and relaunch primitives | ||
|
|
||
| - `osascript -e 'quit app id "com.openai.codex"'` delivers `kAEQuitApplication`. This | ||
| is the graceful path: the app runs its termination handlers and the helper tree | ||
| goes with the root. Delivery is synchronous, **termination is not** — the caller | ||
| must poll for the root pid to disappear. | ||
| - `kill -TERM <root>` bypasses `applicationShouldTerminate:`. Usable as the fallback | ||
| when the Apple event cannot be delivered, not as the first choice. | ||
| - `open -b com.openai.codex` starts the app through LaunchServices and **works when | ||
| the app is not running**. That is the relaunch primitive. | ||
| - `open -n` requests a second instance. An Electron app holding | ||
| `requestSingleInstanceLock()` hands the request to the existing instance instead, | ||
| so `-n` does not reliably produce a second instance — and we do not want one. | ||
| Relaunch uses plain `open -b`. | ||
|
|
||
| **Precondition, measured rather than assumed:** `open -b` launches into the invoking | ||
| user's **GUI session**. Issued over ssh to a Mac where that user has no logged-in | ||
| window session, it relaunches into nothing. Both macOS hosts here have an active | ||
| session, so the wp4 proof holds — but a headless macOS host would stop the app and | ||
| not visibly bring it back, and the operator-facing text must not promise otherwise. | ||
|
|
||
| An unknown bundle id makes `open` exit non-zero with | ||
| `LSCopyApplicationURLsForBundleIdentifier() failed`, which is a usable fail-closed | ||
| signal rather than a silent no-op. | ||
|
|
||
| ### 1.3 The local host cannot prove its own restart | ||
|
|
||
| The shell running `ocx` on the local host is a descendant of the app: | ||
|
|
||
| ``` | ||
| 31497 -> 16733 (bundled codex app-server) -> 15901 (ChatGPT) -> 1 (launchd) | ||
| ``` | ||
|
|
||
| Quitting the app kills the session issuing the command. This is not a corner case to | ||
| document away — it is the maintainer's normal working shape, and it is what wp5 | ||
| exists for. `macmini-cf` is the macOS host used for the destructive proof, because | ||
| the command there is issued over ssh and is not inside the app tree. | ||
|
|
||
| ## 2. Linux | ||
|
|
||
| Host `lidge`, Ubuntu 24.04.4 LTS, x86_64. | ||
|
|
||
| - Package: `chatgpt` 26.903.71938 amd64 ("ChatGPT by OpenAI"), installed via dpkg. | ||
| - Binary root: `/usr/lib/chatgpt/ChatGPT`. | ||
| - Launcher: `/usr/bin/chatgpt`. | ||
| - Desktop entry: `/usr/share/applications/chatgpt.desktop`, `Exec=chatgpt %U`, | ||
| registering `x-scheme-handler/codex` among its MIME types. | ||
| - User data directory: `~/.config/Codex` — note the directory is `Codex` even though | ||
| the package and binary are `chatgpt`. | ||
| - Codex CLI also present at `/usr/local/bin/codex` (codex-cli 0.154.0); the desktop | ||
| app and the CLI are separate installs on this host. | ||
|
|
||
| ### 2.1 Process shape | ||
|
|
||
| ``` | ||
| 3284901 /usr/lib/chatgpt/ChatGPT | ||
| 3284907 /usr/lib/chatgpt/browser_crashpad_handler --database=~/.config/Codex/Crash Reports ... | ||
| 3284913 /usr/lib/chatgpt/ChatGPT --type=zygote --user-data-dir=/home/lidgeai/.config/Codex ... | ||
| 3284951 /usr/lib/chatgpt/ChatGPT --type=gpu-process ... | ||
| 3284953 /usr/lib/chatgpt/ChatGPT --type=utility --utility-sub-type=network.mojom.NetworkService ... | ||
| ``` | ||
|
|
||
| Every process in the tree runs an executable under `/usr/lib/chatgpt/`, which is the | ||
| install root and therefore the membership test. Electron child processes are | ||
| distinguished only by `--type=`; the root is the one without it. | ||
|
|
||
| `~/.codex/app-server-control/` on this host contains `app-server-control.sock` and | ||
| `desktop-ssh-websocket-v0.sock`, which is how the desktop app is reached over SSH. | ||
|
|
||
| ### 2.2 Relaunch from a non-graphical session | ||
|
|
||
| This is the part with no Windows or macOS analogue. `open -b` and | ||
| `Start-Process shell:AppsFolder\...` both hand the launch to a session-aware | ||
| service. Linux has no such indirection: a process started from an ssh session has | ||
| no `DISPLAY`, no `WAYLAND_DISPLAY`, no `DBUS_SESSION_BUS_ADDRESS` and no | ||
| `XDG_RUNTIME_DIR`, and the relaunched app would fail to reach the user's compositor. | ||
|
|
||
| The environment must therefore be **inherited from the process being replaced**: | ||
| read `/proc/<root>/environ` before terminating it, carry forward only the graphical | ||
| session variables, and start the launcher detached with `setsid`. Nothing else in | ||
| that environment is copied — it is a process environment belonging to another | ||
| session and may contain credentials. | ||
|
|
||
| ## 3. Windows | ||
|
|
||
| Host `mini`, Windows (measured through MSYS/MINGW64). | ||
|
|
||
| - Package: `OpenAI.Codex_26.903.9818.0_x64__2p2nqsd0c76g0` (MSIX). | ||
| - Processes: `ChatGPT.exe` (pids 13860, 16944, 19696 at measurement time). | ||
| - `ocx` present at `/c/nvm4w/nodejs/ocx`, reporting opencodex 2.52.0. | ||
|
|
||
| This matches what `src/codex/desktop-app-restart.ts` already implements: runtime | ||
| package discovery through `Get-AppxPackage -Name OpenAI.Codex` with an | ||
| `OpenAI.CodexBeta` fallback, current-user scoping through `GetOwner`, graceful | ||
| `CloseMainWindow()`, forced `taskkill /PID <pid> /T /F`, and relaunch through | ||
| `Start-Process 'shell:AppsFolder\<PackageFamilyName>!App'`. | ||
|
|
||
| The Windows behaviour is the reference the other two platforms are being brought up | ||
| to, and it is not being changed except where the shared ladder replaces duplicated | ||
| logic. | ||
|
|
||
| ## 4. What generalises and what does not | ||
|
|
||
| | Step | macOS | Linux | Windows | | ||
| |---|---|---|---| | ||
| | Identity | bundle id `com.openai.codex` | install root `/usr/lib/chatgpt` | Appx package family | | ||
| | Discovery | LaunchServices / known bundle path | launcher + dpkg install root | `Get-AppxPackage` | | ||
| | Membership | exe under bundle, same uid | exe under install root, same uid | exe under `InstallLocation`, `GetOwner` = me | | ||
| | Graceful stop | `osascript` quit Apple event | `SIGTERM` to root | `CloseMainWindow()` | | ||
| | Forced stop | `SIGKILL` | `SIGKILL` | `taskkill /T /F` | | ||
| | Relaunch | `open -b <bundleId>` | `setsid <launcher>` + inherited session env | `Start-Process shell:AppsFolder\<aumid>` | | ||
| | PID-reuse guard | process start time | `/proc/<pid>/stat` start time | `CreationDate` | | ||
|
|
||
| The **ladder** — discover, enumerate, find roots, check self-ancestry, graceful, | ||
| wait, re-verify identity, force, wait, refuse-or-relaunch — is identical on all | ||
| three. Only the seven rows above differ, which is what makes a single shared | ||
| orchestrator with three small adapters the right shape rather than three parallel | ||
| implementations. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Align the Linux topology procedure with the implemented environment scan.
001_platform_topology.md:132-136requires reading/proc/<root>/environ, butsrc/codex/desktop-app/linux.tsscans enumerated processes oldest-first and selects the first environment containingXDG_RUNTIME_DIR, as specified in010_phase1_shared_restart_surface.md:340-347. The root-only procedure is stale and can mislead future implementation or verification work. Current production code already uses the child scan, so this does not currently causerelaunch_failed.Update the topology procedure to describe the oldest-first child scan and the five-key session-environment allowlist.
🤖 Prompt for AI Agents