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
62 changes: 62 additions & 0 deletions devlog/_fin/261010_cli_path_crossos_probe/000_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# 000 — Cross-OS probe of the desktop-owned CLI path

## Objective

Find out whether the desktop-owned CLI path and install work on `dev` behaves on Linux and Windows the way it was
designed and tested on macOS, and turn every gap into a PR to `dev` or an issue.

The work under test, all squash-merged on `dev` by 2026-10-09:

| PR | Commit | Surface |
|---|---|---|
| #6802 | 01acdc8874 | `ocx status`/`doctor`/`resolve` treat a Desktop-supervised runtime as the authority (`src/service/desktop-supervision.mjs`) |
| #6807 | 32abcc363d | package launcher falls back to a validated PATH Bun; lifecycle version-skew notice (`bin/ocx.mjs`, `src/lib/bun-path-runtime.mjs`) |
| #6809 | 63d87f6370 | `service`/`update`/`start`/`stop` refuse to compete with a Desktop-supervised runtime |
| #6812 | 3b9fc865fa | Windows launchers preflight the selected Bun before baking it in |
| #6816 | b89bbfb083 | Desktop owns the `ocx` terminal command on PATH (`desktop/src-tauri/src/cli_command*.rs`) |
| #6818 | 3618baac41 | package launchers hand off to the Desktop `ocx`; `status`/`doctor` report PATH selection |

The macOS proof is recorded in `devlog/_plan/261009_desktop_owned_path_cli/049_outcome.md`. That note leaves four
items to release QA: packaged click-through of the CLI page, fish on a real login, the Linux deb path, and Windows
terminal pickup after a real registry write. It also records that #6818 deliberately does not hand off on Windows
and that `desktop-supervision.mjs` returns `unsupported` on `win32`, so the #6802/#6809 authority and guards are
inert there.

No release contains #6816 yet: v2.81.0 was published 2026-10-08, before the merge. On Linux the installer probe runs
a Desktop built from `dev` inside a disposable overlay root on lidge. On Windows a second Desktop cannot run beside
the user's live single-instance 2.77.0, so the installer is exercised at module level (`perform_in` with a real
HKCU write) and the packaged launch is recorded as unverified.

## Hosts (baseline 2026-10-10)

| Host | OS | Relevant state |
|---|---|---|
| `ssh lidge` | Ubuntu 24.04.4, x86_64, the login user, bash | `open-codex` 2.61.0 deb owns `/usr/bin/ocx`; Desktop not running; no `~/.opencodex-desktop`; no zsh/fish/docker/xvfb; passwordless sudo; cargo 1.95, webkit2gtk-4.1 2.52.6; bun `/usr/local/bin/bun`; GNOME session on seat0 |
| `ssh mini` | Windows 11 build 26200, the interactive user | OpenCodex Desktop 2.77.0 per-machine MSI at `C:\Program Files\OpenCodex`, running in console session 1 with its `ocx` sidecar; npm `ocx` via nvm4w at `C:\nvm4w\nodejs` which is in both Machine and User Path; bun in `~\.bun\bin`; cargo/rustc; Windows Terminal; no Visual Studio found by vswhere |

## Constraints

- All repository work happens in `.tmp/lanes/cli-path-crossos` (branch `codex/cli-path-crossos-probe`); the main
checkout is not touched. Fix branches are cut from `origin/dev` in `.tmp/lanes/<lane>` inside this worktree.
- Remote hosts: probe in temp directories and throwaway users where possible, never uninstall an existing tool,
back up before any persistent write, and log every mutation with its exact revert command in
`.tmp/host-changes.md` (worktree scratch, summarised in 040).
- Sol subagents do the host lanes and reviews; `create_thread` is not exposed in this session.
- Authorised: PRs to `dev` and issues. Not authorised: merge, release, version bump, deploy, messaging.

## Work-phase map (dependency order)

| Phase | Doc | Depends on | Closes with |
|---|---|---|---|
| wp1 | this file + 010/020/030/040 | — | roadmap reviewed |
| wp2 Linux probe | 010 | wp1 | Linux matrix with lidge evidence |
| wp3 Windows probe | 020 | wp1 | Windows matrix with mini evidence |
| wp4 fixes, issues, closeout | 040 (030 is the release-QA checklist both probes fill) | wp2, wp3 | PRs with exact-head CI, issues, reverts |

wp2 and wp3 touch different hosts and no shared files, so they run in parallel lanes.

## Outcome vocabulary

Each probe row ends as **works** (observed on the host), **broken** (observed failure, with the command and output),
**by design** (behaviour matches a documented decision, e.g. Windows `windows-path-only`), or **unverified** (with
the reason it could not be observed).
41 changes: 41 additions & 0 deletions devlog/_fin/261010_cli_path_crossos_probe/001_host_baseline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 001 — Host baseline and change-log protocol (2026-10-10)

Captured read-only before any probe mutation. Later rows in 030 compare against this.

## lidge (Ubuntu 24.04.4, kernel 7.0.0-30, x86_64)

- `dpkg -l open-codex` → `ii open-codex 2.61.0 amd64 OpenCodex desktop shell`; `dpkg -S /usr/bin/ocx` → `open-codex`;
`ocx --version` → `opencodex 2.61.0`. Desktop not running; `~/.opencodex-desktop` absent.
- Shell `/bin/bash`; `~/.bashrc` and `~/.profile` exist, `~/.bash_profile` and `~/.zshrc` do not; no fish, zsh,
docker or Xvfb. `unshare` and `setpriv` present; passwordless sudo.
- Bun `/usr/local/bin/bun` 1.3.14 (the launcher pins 1.4.2); Node `/usr/bin/node`; cargo/rustc 1.95 under
`~/.cargo/bin` (not on the non-interactive SSH PATH); webkit2gtk-4.1 / javascriptcoregtk-4.1 2.52.6, libsoup-3.0 3.4.4.
- GNOME session for `<user>` on seat0/tty2.

## mini (Windows 11 build 26200, the interactive user)

- OpenCodex Desktop 2.77.0, per-machine MSI, `C:\Program Files\OpenCodex\`, running in console session 1
(`opencodex-desktop` PID 22680, `ocx` sidecar PID 33476); `~\.opencodex-desktop` absent.
- npm `ocx` 2.58.0 via nvm4w: `C:\nvm4w\nodejs\ocx{,.cmd,.ps1}`; `C:\nvm4w\nodejs` is in both Machine Path and
User Path; HKCU Path is `REG_EXPAND_SZ`.
- Bun `%USERPROFILE%\.bun\bin\bun.exe`; cargo/rustc stable `x86_64-pc-windows-msvc` (links despite `vswhere -latest`
listing nothing); Windows Terminal `wt.exe`; PowerShell 7 in Path.

## Temp clones

Both hosts carry a clone of `d17a9f2239` with CI's empty sidecar/resource stubs (ci.yml:1567-1573):
`/tmp/ocx-probe-261010/repo` (lidge) and `%TEMP%\ocx-probe-261010\repo` (mini).

## Change-log protocol

Every remote mutation is appended to the worktree scratch file `.tmp/host-changes.md` (gitignored) BEFORE it is
made: time (KST), host, exact command, exact revert command, and the check that proves the revert. 040's closeout
copies the final table with each row marked reverted or kept (with reason). Secrets, tokens and request bodies never
enter the log.

## First evidence

- lidge `cargo test --manifest-path desktop/src-tauri/Cargo.toml cli_command`: run 1 `45 passed; 1 failed; 2 ignored`
(`final_save_before_journal_deletion_is_cleaned_on_reopen`, `Err("lock-busy")` at `cli_command_record.rs:1114`);
runs 2-4 `46 passed; 0 failed; 2 ignored`; isolated test 5/5 pass.
- mini, same command: `27 passed; 0 failed; 0 ignored`.
67 changes: 67 additions & 0 deletions devlog/_fin/261010_cli_path_crossos_probe/010_linux_probe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 010 — wp2: Linux probe on `ssh lidge`

Source under test: a temp clone of `d17a9f2239` at `/tmp/ocx-probe-261010/repo` on lidge. Every host mutation is
appended to the worktree's `.tmp/host-changes.md` with its revert before it is made.

## Code facts the probe relies on (read 2026-10-10)

- Install kind: `desktop/src-tauri/src/cli_command_posix.rs:147` rejects debug builds (`development-launch`),
AppImage env or `/tmp/.mount_` paths (`temporary-bundle`) and any executable whose canonical parent is not
`/usr/bin` (`unpackaged-launch`); a qualifying launch needs an executable `/usr/bin/ocx` and records `linux-deb`.
dpkg is never consulted.
- Owned paths: `~/.opencodex-desktop/{bin/ocx (0700), path.sh (0600), cli.json, cli.lock, journal/, backups/}`.
- Managed blocks (`# >>> OpenCodex Desktop ocx PATH v1 >>>`): `.bashrc` plus the FIRST existing of
`.bash_profile`/`.bash_login`/`.profile` (none created; absent → `login-file-absent`, partial); `.zshrc` and
`.zlogin` under `ZDOTDIR` or HOME; `${XDG_CONFIG_HOME:-~/.config}/fish/config.fish` wrapped in
`if status is-interactive` (`cli_command_posix.rs:97-132`).
- The ignored `real_shell_selects_desktop_shim_on_temp_home` test covers bash and zsh only, never fish.
- Launcher handoff (`src/lib/desktop-cli-handoff.mjs`): escape hatch and recursion guard `OCX_NO_DESKTOP_HANDOFF=1`;
update/uninstall/remove/internal commands are excluded; record must be 0700 dir / 0600 file owned by the euid.
- PATH-Bun fallback (`src/lib/bun-path-runtime.mjs`) needs Bun ≥ the pinned 1.4.2; lidge's `/usr/local/bin/bun`
is 1.3.14 and must be rejected.

## Probe steps

| # | Step | Command sketch | Expected |
|---|---|---|---|
| L1 | Rust unit tests | `cargo test --manifest-path desktop/src-tauri/Cargo.toml cli_command` with CI's empty sidecar/resource stubs (ci.yml:1567-1573), repeated ×5 | all pass (first run: 1 `lock-busy` flake, see F1) |
| L2 | Ignored real-shell test | `... cli_command_posix::tests::real_shell_selects_desktop_shim_on_temp_home -- --ignored --exact --nocapture` | bash passes; zsh skipped (absent) |
| L3 | Build dev deb | `bun install` (root, gui, desktop) → `bun run build:gui` → `bun desktop/scripts/prepare-sidecar.ts --target x86_64-unknown-linux-gnu` → in `desktop/`: `bunx tauri build --ci --bundles deb --config '{"bundle":{"createUpdaterArtifacts":false}}'` | `.deb` under `src-tauri/target/release/bundle/deb/` |
| L4 | Throwaway user | `sudo useradd -m -s /opt/ocx-probe/fish/bin/fish ocxprobe` after unpacking `fish-4.9.3-linux-x86_64.tar.xz` into `/opt/ocx-probe/fish`; give it `.bashrc` and `.profile` copied from `/etc/skel` | user exists, `getent passwd ocxprobe` shows fish |
| L5 | Install dev deb | download v2.61.0 deb for revert first; `sudo apt install ./OpenCodex-*.deb` | `/usr/bin/opencodex-desktop` + `/usr/bin/ocx` from dev |
| L6 | Desktop reconcile as ocxprobe | run `/usr/bin/opencodex-desktop` as ocxprobe under a virtual display (`xvfb-run` installed and logged, or the user's Wayland/X session if Xvfb is refused) long enough for `reconcile_on_launch`, then stop it | `~ocxprobe/.opencodex-desktop/cli.json` enabled, kind `linux-deb`; blocks in `.bashrc`, `.profile`, `.zshrc`, `.zlogin`, `config.fish` |
| L7 | Real login shells | `sudo -iu ocxprobe` (fish login, interactive via `script -qc`), `fish -l -c 'command -v ocx'`, `bash -l -i -c`, `bash -lc`, `env -i ... bash --login`, `ssh ocxprobe@localhost` if keys allow | interactive fish/bash → `~/.opencodex-desktop/bin/ocx`; non-interactive fish → `/usr/bin/ocx` (guard) |
| L8 | Package launcher handoff | as ocxprobe: `npm i -g` of a `npm pack` tarball into `~/.npm-global`, `bun link` from the clone, and a linuxbrew-style `~/.linuxbrew/bin/ocx` symlink; run each `--version`/`status`; repeat with `OCX_NO_DESKTOP_HANDOFF=1` | handoff to the record target; escape hatch runs the package |
| L9 | Diagnostics | `ocx status`, `status --json`, `doctor`, `resolve --json` with and without the Desktop running | `ocx command:` line names Desktop first/not first; supervisor line only with Desktop live |
| L10 | #6809 guards | with dev Desktop running as ocxprobe: package `ocx start`, `stop`, `service install`, `update --check`-style dry paths | refusals naming Desktop |
| L11 | Bun fallback | temp package copy with bundled Bun removed; PATH first = Bun 1.4.2 in a temp dir; then 1.3.14 | 1.4.2: `using PATH Bun` notice; 1.3.14 rejected with Desktop CLI named |
| L12 | Opt-out and revert | Desktop "Remove" / disabled reconcile as ocxprobe; then `sudo userdel -r ocxprobe`, `sudo apt install ./OpenCodex-2.61.0-linux-amd64.deb` (allow downgrade), remove `/opt/ocx-probe`, `/tmp/ocx-probe-261010` | blocks removed except user-modified ones; host back at 2.61.0 |

## Outcomes

Each row is recorded in 030's matrix as works / broken / by design / unverified with the command and output tail.

## Reflection (same architect, Dewey 01a1225a-9e23): MISALIGNED → dispositions

These replace the matching rows above.

- **L1/F1:** the first-run `lock-busy` failure stays recorded as evidence. The forked-child cause is a hypothesis.
Before any fix, B reproduces under a loop (`for i in $(seq 30)`), captures `RUST_BACKTRACE=1` and the errno from
`flock`, and identifies the holder with `/proc/locks` plus `ls -l /proc/*/fd` matching the lock inode during a
failure (an instrumented copy of the test may sleep on failure to allow the snapshot). The fix shape is chosen
only from that evidence.
- **L7:** positive fish case is `fish -l -i -c 'command -v ocx'` and an interactive login via `script -qc 'sudo -iu ocxprobe' /dev/null`;
the negative case is `env -i HOME=/home/ocxprobe PATH=/usr/bin:/bin /opt/ocx-probe/fish/bin/fish -l -c 'command -v ocx'`.
Bash: `env -i HOME=/home/ocxprobe PATH=/usr/bin:/bin bash --login -c 'command -v ocx'` and with `-i`.
- **L10:** no live `update`. The guard probe runs `start`, `stop` and `service install` from the package as ocxprobe
only while the dev Desktop runs as ocxprobe with ocxprobe's own `~/.opencodex`, and the focused existing tests
(`tests/update/update-desktop-owner.test.ts`, `tests/service/service-ownership-handover.test.ts`,
`tests/cli/cli-stop-json.test.ts`) run on lidge for `update`.
- **L11:** set `OCX_NO_DESKTOP_HANDOFF=1`; remove the bundled Bun and the installer from the temp package copy; the
1.3.14 expectation is "rejected, no Desktop CLI named" because `findDesktopCli` returns null off macOS (candidate
finding F4).
- **L8/L12:** npm-global uses `npm_config_prefix=/home/ocxprobe/.npm-global`. Before L5, download the v2.61.0 deb to
`/opt/ocx-probe/revert/`, verify it against its `.sha256`, and record `dpkg-deb -f <deb> Package Version`; L12
restores with `sudo apt install --allow-downgrades /opt/ocx-probe/revert/OpenCodex-2.61.0-linux-amd64.deb`.

See the audit dispositions at the end of 020_windows_probe.md; items 1 and 5 replace L4, L5, L7 and L12 host steps.
Loading
Loading