70 Commits

  • release: consume standalone zsh artifacts (#30116)
    ## Why
    
    Once #30114 publishes zsh independently, regular Rust releases should
    reuse that protected, versioned artifact set instead of rebuilding
    identical zsh binaries for every Codex version. Keeping the zsh release
    tag explicit in the workflow also makes future artifact upgrades
    deliberate and easy to review.
    
    This PR assumes the first standalone artifact release will be published
    as `codex-zsh-v0.1.0` before this change lands.
    
    ## What changed
    
    - Added `CODEX_ZSH_RELEASE_TAG` near the top of
    `.github/workflows/rust-release.yml`, initially pinned to
    `codex-zsh-v0.1.0`.
    - Download the standalone release’s generated `codex-zsh` DotSlash
    manifest before assembling Linux and macOS Codex packages.
    - Added a `--zsh-manifest` package-builder override so release packaging
    fetches the matching target archive and verifies the size and SHA-256
    digest recorded in that manifest.
    - Removed the reusable zsh build job from regular Rust releases.
    - Stopped copying zsh archives into each Rust release and stopped
    regenerating a zsh DotSlash manifest there.
    
    Windows packaging remains unchanged because the patched zsh resource is
    only shipped for supported Unix targets.
    
    ## Testing
    
    - Added package-helper coverage that supplies a standalone manifest
    override and verifies the extracted zsh bytes.
    - Ran the `scripts/codex_package` unit test suite.
    - Validated `.github/scripts/build-codex-package-archive.sh` with `bash
    -n`.
  • Make formatter output quiet on success (#29467)
    ## Why
    
    `just fmt` is quite noisy even on successful runs.
    
    ## What
    
    Only print output when a formatter fails.
    
    - Buffer output from each formatter and print only a failed command and
    its diagnostics.
    - Prefix the `justfile` driver invocations with `@` so Just does not
    echo the command itself.
    - Retain rustfmt stderr on failure and cover silent-success and
    failure-reporting behavior.
    
    ## Validation
    
    - Confirmed `just fmt` and `just fmt-check` both exit successfully with
    empty stdout and stderr.
  • fix(install): support older awk checksum parsing (#28784)
    ## Why
    
    The standalone installer validates package checksums with an awk
    interval expression. Older mawk releases do not support that expression,
    so they reject valid 64-character digests and report that the release
    manifest is missing an entry. This affects both x64 and ARM64 systems on
    common Debian-derived environments.
    
    Fixes #24219.
    
    ## What Changed
    
    Replace the awk interval expression with an explicit length check plus
    rejection of non-hexadecimal characters. This preserves the existing
    SHA-256 validation and lowercase normalization while working with older
    awk implementations.
    
    ## How to Test
    
    1. Build and run the checksum predicate with mawk 1.3.4 20121129.
    2. Confirm the old interval predicate rejects a valid 64-character
    digest.
    3. Confirm the updated predicate accepts that digest.
    4. Put the old mawk binary first on PATH as awk and run
    scripts/install/install.sh with an isolated HOME, CODEX_HOME, and
    CODEX_INSTALL_DIR.
    5. Confirm Codex installs successfully and the installed binary reports
    version 0.140.0.
    6. Verify the predicate rejects wrong-length digests, non-hexadecimal
    digests, and entries for another asset while accepting uppercase
    hexadecimal digests.
  • Run core integration tests against a Wine-backed Windows executor (#28401)
    ## Why
    
    We want to exercise a linux app-server against a windows exec-server
    without having to repeat every test case. This approach has slight
    precedent in the remote docker test setup.
    
    ## What
    
    Run the shared `codex-core` integration suite against Windows
    exec-server behavior from Linux. This makes cross-OS path and shell
    regressions visible while keeping unsupported cases owned by individual
    tests.
    
    - Add `local`, `docker`, and `wine-exec` test environment selection with
    legacy Docker compatibility.
    - Extend `codex_rust_crate` to generate a sharded Wine-exec variant
    using a cross-built Windows server and pinned Bazel Wine/PowerShell
    runtimes.
    - Teach remote-aware helpers about Windows paths and track temporary
    incompatibilities with source-local `skip_if_wine_exec!` calls and
    follow-up reasons.
  • build: run buildifier from just fmt (#28125)
    ## Intent
    
    Keep Bazel and Starlark files consistently formatted without requiring
    contributors to install or version buildifier themselves.
    
    ## Implementation
    
    - Add a SHA-256-pinned, cross-platform DotSlash manifest for buildifier
    v8.5.1.
    - Run buildifier from the shared `just fmt` and `just fmt-check` driver,
    with Windows-safe explicit DotSlash invocation.
    - Provision DotSlash in formatting CI and contributor devcontainers, and
    document the source-build prerequisite.
    - Apply the initial mechanical buildifier formatting baseline.
  • [codex] Add hermetic Wine exec-server test (#27937)
    ## Why
    
    We want to make it possible for an app-server orchestrator on one OS to
    control an exec-server on another host running a different OS. In
    practice this kinda already works if you get lucky and the two hosts
    have the same path format, but we mangle quite a lot of operations if
    either end is Windows.
    
    This test starts exercising that interaction, although right now the
    initial bootstrap fails. Future changes will expand the test's
    assertions to match improved support.
    
    ## What
    
    Stacked on #27964. This adds a small Windows exec-server fixture and a
    Linux protocol smoke test using the reusable Wine harness, covering
    Windows environment discovery, non-TTY `cmd.exe` execution, output, exit
    status, and working directory.
    
    Once we've got the full codex binary cross-building under Bazel we could
    consider moving to the real binary instead of the stripped down
    exec-server-only binary used here.
  • [codex] stage npm packages concurrently (#27853)
    In the release job from
    
    https://github.com/openai/codex/actions/runs/27391514823
    
    staging the nine npm release tarballs serially took 104 seconds.
    
    Each package build writes to a separate staging directory, output path,
    and npm cache. Run them through the script's existing thread pool,
    bounded by the available CPU count. Delete each staging tree as its
    build finishes so concurrency does not retain all copies until the end.
    
    On ubuntu-24.04 in
    
    https://github.com/openai/codex/actions/runs/27397232050
    
    two serial trials took 103 and 101 seconds, while concurrent trials
    both took 41 seconds. Comparing every extracted file from the first
    serial and concurrent sets found no differences. This removes about one
    minute from every release.
  • Use dependency groups for Python SDK tooling (#27538)
    ## Summary
    
    `just fmt` previously used `uv run --with ruff` to make Ruff available.
    Because `--with` creates an ephemeral overlay outside the project
    lockfile, uv periodically re-resolved Ruff (by default every 10 minutes)
    instead of using the version recorded in `uv.lock`.
    
    Move the Python SDK tooling dependencies from the published `dev` extra
    into `format`, `test`, and composed `dev` dependency groups. The
    formatter now selects only the locked `format` group, contributor and CI
    setup explicitly sync the `dev` group, and CI and release commands reuse
    that environment with `--frozen --no-sync`. The scripts formatter also
    uses its project's locked Ruff dependency instead of an ephemeral
    overlay.
    
    Validated the Python 3.12 SDK suite (119 passed, 38 skipped) and the
    repository formatter.
  • [codex] reuse release artifacts for npm staging (#27312)
    The release job already downloads every workflow artifact into `dist`,
    but npm staging creates a new cache and downloads the six target
    artifacts again.
    
    Reuse `dist` as the staging script's artifact cache while preserving the
    existing download fallback for missing artifacts and standalone callers.
    The script retains ownership of temporary caches but does not delete a
    caller-provided directory.
    
    In https://github.com/openai/codex/actions/runs/27242495616, the
    duplicate
    download transferred 3.3 GiB and took 4 minutes 13 seconds. This should
    reduce total release time by about 4 minutes.
  • [codex] Keep Bazel startup options stable across commands (#26256)
    ## Why
    
    `just bazel-clippy` ran target discovery with
    `--noexperimental_remote_repo_contents_cache`, then ran the build with
    the workspace default `--experimental_remote_repo_contents_cache`. Bazel
    therefore killed and restarted its server on each transition, slowing
    repeated commands and discarding the in-memory analysis cache. An audit
    found the same class of startup-option variation in several CI command
    sequences.
    
    ## What changed
    
    - Keep local lint target-discovery queries on the workspace-default
    Bazel server, while making CI target discovery explicitly use the CI
    startup options.
    - Normalize GitHub Actions launches through the BuildBuddy wrapper to
    share `BAZEL_OUTPUT_USER_ROOT` and
    `--noexperimental_remote_repo_contents_cache`.
    - Route the CI lockfile check and Windows test-shard query through the
    same startup configuration.
    - Document the startup-option invariant and add wrapper regression
    coverage.
    
    ## Validation
    
    - Confirmed consecutive local clippy target-discovery runs retained the
    same Bazel server PID.
  • [codex] Fix Windows BuildBuddy Bazel wrapper execution (#25915)
    ## Why
    
    #25156 moved Bazel CI launches into a shared Python wrapper. On Windows,
    launching Bazel with `os.execvp` can split the spaced
    `--test_env=PATH=...` argument and fail to propagate the eventual Bazel
    exit status, allowing jobs to pass without running tests. This reapplies
    the wrapper after #25909 with a Windows-safe launch path.
    
    ## What changed
    
    Use a waited `subprocess.run` launch on Windows while preserving
    `os.execvp` on Unix. Add a process-level regression test for spaced
    arguments and child exit status, and run it on Windows Bazel shard 1.
    
    ## Experiment
    
    To confirm Bazel was actually invoking tests, patch `87b61d0be6`
    temporarily added an intentionally failing `codex-core` unit test. Bazel
    failed on that sentinel on all three major platforms:
    
    - [Linux Bazel
    test](https://github.com/openai/codex/actions/runs/26841132773/job/79151062486)
    - [macOS Bazel
    test](https://github.com/openai/codex/actions/runs/26841132773/job/79151062362)
    - [Windows Bazel test shard
    1/4](https://github.com/openai/codex/actions/runs/26841132773/job/79151062155)
    
    The sentinel was removed after collecting this evidence. Windows Bazel
    [clippy](https://github.com/openai/codex/actions/runs/26841132773/job/79151062914)
    and [release
    verification](https://github.com/openai/codex/actions/runs/26841132773/job/79151062739)
    also passed.
    
    ## Validation
    
    After removing the sentinel, `just test -p codex-core` no longer
    reported it. The local run retained two unrelated environment-specific
    failures.
  • [codex] Revert shared BuildBuddy Bazel wrapper (#25909)
    ## Why
    
    PR #25905 intentionally adds a failing `codex-core` unit test, but its
    [Bazel test on Windows
    check](https://github.com/openai/codex/actions/runs/26837526950/job/79135369259)
    passed. That shows the Bazel configuration introduced by #25156 is not
    behaving as expected, so revert it while the configuration can be
    investigated separately.
    
    ## What changed
    
    Revert #25156 in full, restoring the previous Bazel remote
    configuration, CI scripts, workflows, `rusty_v8` handling, and
    documentation. This removes the shared BuildBuddy wrapper and its tests.
    
    ## Validation
    
    Not run locally; this exact revert was prioritized for a fast rollback.
  • Route Bazel CI through shared BuildBuddy remote config wrapper (#25156)
    ## Why
    
    Bazel remote configuration was selected in several CI scripts and
    workflow steps. That made the BuildBuddy tenant policy easy to duplicate
    and harder to audit, especially for fork pull requests that must not use
    the OpenAI tenant.
    
    This builds on
    [sluongng/buildbuddy-ci-host-routing](https://github.com/openai/codex/compare/main...sluongng:codex:sluongng/buildbuddy-ci-host-routing)
    and consolidates the policy in one place.
    
    ## What to do if this breaks you
    
    See `codex-rs/docs/bazel.md` for details. TLDR:
    
    1. make a BuildBuddy API key and put it in `~/.bazelrc`
    2. if you're an OpenAI employee, add `common
    --config=buildbuddy-openai-rbe` to `user.bazelrc` in the repo root
    
    Run `just bazel-test` to ensure it works.
    
    Note that `just bazel-remote-test` no longer exists, you need to select
    a remote configuration as documented to use RBE.
    
    ## What changed
    
    - Add `.github/scripts/run_bazel_with_buildbuddy.py` as the shared Bazel
    wrapper and Python library. It selects the OpenAI host only for trusted
    upstream GitHub Actions runs, routes keyed fork runs to the generic
    host, and falls back to local Bazel execution when no key is available.
    - Move endpoint selection into explicit `.bazelrc` configurations and
    update Bazel CI, query helpers, and `rusty_v8` staging to use the shared
    policy. Loading-phase target-discovery queries remain local.
    - Add wrapper and `rusty_v8` unit coverage, plus `just test-scripts` for
    the `.github/scripts` Python tests.
    - Document local Bazel usage, `user.bazelrc` setup, BuildBuddy
    configurations, and CI behavior in `codex-rs/docs/bazel.md`.
    
    ## Validation
    
    - `just test-scripts`
    - `bash -n .github/scripts/run-bazel-ci.sh
    .github/scripts/run-bazel-query-ci.sh
    .github/scripts/run-argument-comment-lint-bazel.sh
    scripts/list-bazel-clippy-targets.sh`
    - `python3 -m py_compile .github/scripts/run_bazel_with_buildbuddy.py
    .github/scripts/test_run_bazel_with_buildbuddy.py
    .github/scripts/test_rusty_v8_bazel.py
    .github/scripts/rusty_v8_bazel.py`
    - `ruff check .github/scripts/run_bazel_with_buildbuddy.py
    .github/scripts/test_run_bazel_with_buildbuddy.py
    .github/scripts/test_rusty_v8_bazel.py
    .github/scripts/rusty_v8_bazel.py`
  • [codex] Add comprehensive root formatting check (#25683)
    ## Why
    
    The root formatting entrypoints could drift: `just fmt` did not format
    the Justfile itself, and the CI-facing check recipe only checked Python
    scripts instead of matching everything formatted by `just fmt`.
    
    ## What changed
    
    - Add a shared cross-platform Python formatter driver used by both `just
    fmt` and `just fmt-check`.
    - Run Justfile, Rust, Python SDK, and internal-script formatter groups
    concurrently while buffering each formatter group's output until it
    finishes.
    - Log formatter starts immediately, then print each formatter group's
    labeled output when it completes.
    - Keep the SDK lint-fix and Ruff formatting passes ordered, with source
    comments explaining their distinct roles and the check-mode equivalents.
    - Run Ruff through shared `uv run --no-sync --with ruff` overlays so
    formatting works on clean glibc Linux checkouts without installing the
    platform-specific SDK runtime wheel.
    - Show `fmt-check` help text in `just -l` and simplify CI to call the
    shared driver through `just fmt-check`.
    - Pin the general CI workflow to `just@1.51.0` so its formatter agrees
    with the checked-in Justfile.
    - Add regression coverage for the thin Just recipes and the driver's
    formatter graph.
    
    ## Validation
    
    - `just fmt`
    - `just fmt-check`
    - `python3 -m pytest
    sdk/python/tests/test_artifact_workflow_and_binaries.py -k 'root_fmt or
    root_format' -q`
    - `pnpm run format`
    - `git diff --check`
    - `just -l | rg -n '^    fmt|fmt-check'`
    - `uvx --from uv==0.7.22 uv run --frozen --project sdk/python --no-sync
    --with ruff ruff check --diff sdk/python`
  • Check root Python script formatting in CI (#25165)
    ## Why
    
    Python files under `scripts/` were not covered by the repository
    formatting recipe or the CI formatting job, so formatting drift could
    merge unnoticed.
    
    ## What
    
    - Add a dedicated `scripts/pyproject.toml` and `scripts/uv.lock` so
    root-script formatting uses a locked Ruff version.
    - Extend `just fmt` to format root Python scripts and add
    `fmt-scripts-check` for CI.
    - Run `just fmt-scripts-check` from `.github/workflows/ci.yml`,
    installing `uv` through SHA-pinned `astral-sh/setup-uv` while retaining
    the `uv` `0.11.3` pin.
    - Apply Ruff formatting to the root Python scripts, including
    `scripts/just-shell.py`, and extend
    `sdk/python/tests/test_artifact_workflow_and_binaries.py` to cover the
    root formatting recipe.
    - Update `AGENTS.md` so agents run `just fmt` after code changes
    anywhere in the repository.
    
    ## Validation
    
    - Extended the existing Python SDK workflow test to assert that `just
    fmt` includes root Python scripts.
  • [codex] Make justfile recipes Windows-aware (#24983)
    ## Summary
    
    Make the root `justfile` usable from Windows without maintaining a
    separate Windows copy of most recipes.
    
    The repo recipes previously assumed POSIX shell behavior for things like
    variadic argument forwarding (`"$@"`) and stderr redirection
    (`2>/dev/null`). That made common workflows such as `just fmt`, `just
    test`, and `just log` unreliable from Windows. This PR introduces a
    small cross-platform shell adapter so recipes can stay mostly unified
    while still expanding the few shell-specific constructs correctly on
    macOS/Linux and Windows.
    
    ## What Changed
    
    - Add `scripts/just-shell.py` as the configured `just` shell adapter.
      - On Unix it invokes `sh -cu`.
    - On Windows it invokes `pwsh -CommandWithArgs` so arguments containing
    spaces are preserved.
    - Add portable recipe placeholders:
    - `{args}` expands to `"$@"` on Unix and the equivalent PowerShell
    forwarded-args expression on Windows.
    - `{stderr-null}` expands to the platform-specific stderr suppression
    used by `fmt`.
    - Convert most variadic one-line recipes to the unified `{args}` form,
    including `codex`, `exec`, `file-search`, `app-server-test-client`,
    `fix`, `clippy`, `bench`, `mcp-server-run`, `write-app-server-schema`,
    and `argument-comment-lint-from-source`.
    - Keep genuinely shell-specific recipes split or Unix-only for now,
    including recipes backed by `.sh` scripts or recipes whose bodies are
    more than simple command forwarding.
    - Add a Windows `just install` path that installs PowerShell via
    `winget` when `pwsh` is not available, then runs the same basic Rust
    setup steps.
    - Update the SDK test that validates the root `fmt` recipe so it
    recognizes the new portable stderr placeholder.
    
    ## Validation
    
    - `just --summary`
    - `just --dry-run fmt`
    - `just --dry-run bench-smoke`
    - `just --dry-run codex foo "bar binky" baz`
    - `just --dry-run write-hooks-schema`
    - `just --dry-run bazel-lock-update`
    - `just --dry-run argument-comment-lint-from-source -- "foo bar"`
    - `git diff --check -- justfile scripts/just-shell.py
    sdk/python/tests/test_artifact_workflow_and_binaries.py`
    - Verified Windows argv preservation through `scripts/just-shell.py`
    with arguments containing spaces.
    - `uv run --frozen --project sdk/python --extra dev pytest
    sdk/python/tests/test_artifact_workflow_and_binaries.py::test_root_fmt_recipe_formats_rust_and_python_sdk`
  • fix: add noninteractive install script mode (#21567)
    # Summary
    
    The Codex standalone installers can pause after installation to ask
    about an older managed install or launching Codex. That makes unattended
    bootstrap and update flows hard to complete reliably.
    
    This PR adds noninteractive installer control on macOS/Linux and Windows
    through `CODEX_NON_INTERACTIVE=1`. Noninteractive operation is
    environment-only, which gives automated callers one stable way to
    suppress prompts. When a noninteractive install leaves an older npm,
    bun, or brew-managed Codex installed, the standalone bin is configured
    ahead of that command on `PATH` so the newly installed Codex is the one
    future launches select. It also supports `CODEX_RELEASE` for callers
    that select a release through environment variables while retaining the
    existing explicit release inputs. Release selection accepts `latest`,
    stable `x.y.z` versions, and Codex prereleases written as
    `rust-v0.134.0-alpha.3`, `v0.134.0-alpha.3`, or `0.134.0-alpha.3`; it
    validates that shape before constructing release requests.
    
    # Stack
    
    1. [#21567](https://github.com/openai/codex/pull/21567) - Adds release
    and noninteractive environment controls to the installers. (current)
    2. [#24637](https://github.com/openai/codex/pull/24637) - Runs
    standalone updater installs with `CODEX_NON_INTERACTIVE=1`.
    3. [#24639](https://github.com/openai/codex/pull/24639) - Removes
    explicit release argument inputs in favor of `CODEX_RELEASE`.
    
    # Evidence
    
    | Before | After |
    | --- | --- |
    | ![Interactive install
    prompts](https://github.com/user-attachments/assets/feecb45a-7087-4681-8775-ba57b07e97fa)
    | ![Noninteractive install completes without
    prompts](https://github.com/user-attachments/assets/53dcc791-383a-46e2-9a95-3b37b80ae053)
    |
    
    Environment-controlled macOS install with an existing npm-managed Codex
    on `PATH`:
    
    
    https://github.com/user-attachments/assets/442e0b5b-4a32-4bf5-996b-68784777380d
    
    # Design decisions
    
    Windows installs using the older standalone bin layout still require an
    interactive migration confirmation. Noninteractive mode does not
    auto-migrate that existing directory because replacing it is a
    destructive transition for an early, limited-use layout; unattended
    installs on that layout fail with an instruction to rerun interactively.
    
    # Testing
    
    Tests: installer syntax validation, release-selector acceptance and
    rejection coverage including PowerShell `Latest` compatibility, macOS
    live-terminal installer smoke testing with environment-controlled stable
    and prerelease installation and competing PATH precedence, shell
    rejection of the omitted noninteractive flag, and Windows ARM64
    PowerShell smoke testing with environment-only noninteractive behavior,
    retained release input, and competing PATH precedence through Parallels.
  • package: include zsh fork in Codex package (#23756)
    ## Why
    
    The package layout gives Codex a stable place for runtime helpers that
    should travel with the entrypoint. `shell_zsh_fork` still required users
    to configure `zsh_path` manually, even though we already publish
    prebuilt zsh fork artifacts.
    
    This PR builds on #24129 and uses the shared DotSlash artifact fetcher
    to include the zsh fork in Codex packages when a matching target
    artifact exists. Packaged Codex builds can then discover the bundled
    fork automatically; the user/profile `zsh_path` override is removed so
    the feature uses the package-managed artifact instead of a legacy path
    knob.
    
    ## What Changed
    
    - Added `scripts/codex_package/codex-zsh`, a checked-in DotSlash
    manifest for the current macOS arm64 and Linux zsh fork artifacts.
    - Taught `scripts/build_codex_package.py` to fetch the matching zsh fork
    artifact and install it at `codex-resources/zsh/bin/zsh` when available
    for the selected target.
    - Added package layout validation for the optional bundled zsh resource.
    - Added `InstallContext::bundled_zsh_path()` and
    `InstallContext::bundled_zsh_bin_dir()` for package-layout resource
    discovery.
    - Threaded the packaged zsh path through config loading as the runtime
    `zsh_path` for packaged installs, and removed the config/profile/CLI
    override path.
    - Kept the packaged default zsh override typed as `AbsolutePathBuf`
    until the existing runtime `Config::zsh_path` boundary.
    - Updated app-server zsh-fork integration tests to spawn
    `codex-app-server` from a temporary package layout with
    `codex-resources/zsh/bin/zsh`, matching the new packaged discovery path
    instead of setting `zsh_path` in config.
    - Switched package executable copying from metadata-preserving `copy2()`
    to `copyfile()` plus explicit executable bits, which avoids macOS
    file-flag failures when local smoke tests use system binaries as inputs.
    
    ## Testing
    
    To verify that the `zsh` executable from the Codex package is picked up
    correctly, first I ran:
    
    ```shell
    ./scripts/build_codex_package.py
    ```
    
    which created:
    
    ```
    /private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/
    ```
    
    so then I ran:
    
    ```
    /private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/bin/codex exec --enable shell_zsh_fork 'run `echo $0`'
    ```
    
    which reported the following, as expected:
    
    ```
    /private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/codex-resources/zsh/bin/zsh
    ```
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23756).
    * #23768
    * __->__ #23756
  • package: factor DotSlash executable fetching (#24129)
    ## Why
    
    The package builder already fetches `rg` from a checked-in DotSlash
    manifest. The zsh packaging work needs the same
    fetch/cache/size-check/SHA-256/extract path for another manifest, but
    keeping that refactor inside the zsh PR makes the review harder to
    follow.
    
    This PR factors the existing `rg`-specific implementation into a
    reusable helper with no intended behavior change for `rg` packaging.
    
    ## What Changed
    
    - Added `scripts/codex_package/dotslash.py` for checked-in DotSlash
    manifest parsing, archive download, cache reuse, size validation,
    SHA-256 validation, and member extraction.
    - Updated `scripts/codex_package/ripgrep.py` to delegate to the shared
    helper.
    - Preserved the existing `rg` manifest path, cache key, destination
    filename, and executable-bit behavior.
    
    ## Testing
    
    - `python3 -m py_compile scripts/codex_package/dotslash.py
    scripts/codex_package/ripgrep.py scripts/codex_package/cli.py
    scripts/codex_package/layout.py scripts/codex_package/zsh.py`
    - `python3 -m unittest discover scripts/codex_package`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/24129).
    * #23768
    * #23756
    * __->__ #24129
  • Prefer just test over cargo test in docs (#23910)
    `cargo test` for the core and other crates fails on a fresh macOS
    checkout without the right stack size variable. This change encourages
    using the just test command that sets the environment up correctly.
    
    As a bonus, this should encourage agents to get more benefit out of
    nextest's parallel execution.
  • ci: Use codex produced v8 artifacts for release builds (#23934)
    Updates our build script to pull down the artifacts like we do in CI for
    building v8 into our targets.
    
    This changes the flow so that we now pre-install rusty v8 assets for all
    of our release targets from pre-built in workflow.
    Secondarily if running it locally we now optionally pull the assets down
    on python run assuming the user hasn't set the proper values, it then
    provides them.
    
    Sorry for the miss here.
  • npm: remove legacy package artifact synthesis (#23836)
    ## Why
    
    `rust-release` now publishes `codex-package-<target>.tar.gz` as the
    canonical native package payload. npm staging should consume those
    archives directly instead of keeping legacy synthesis code that fetched
    `rg`, copied standalone binaries, and rebuilt an approximate package
    layout.
    
    That also means the package builder should not know the internal shape
    of `codex-package`. It should extract and copy the target payload
    wholesale so future layout changes stay localized to the archive
    producer.
    
    The release job stages `codex`, `codex-responses-api-proxy`, and
    `codex-sdk` together, so native artifact download should be filtered,
    observable, and shared across component installs. Since that native
    hydration is now only used by release staging, keeping a separate
    `install_native_deps.py` CLI adds an extra wrapper without a real
    caller.
    
    ## What Changed
    
    - Removed legacy `codex-package` synthesis and related compatibility
    flags from npm staging.
    - Folded the remaining native artifact hydration code into
    `scripts/stage_npm_packages.py` and deleted
    `codex-cli/scripts/install_native_deps.py`.
    - Made platform package staging copy the full extracted target directory
    instead of enumerating package entries.
    - Kept non-`codex-package` native components under their component
    directory name instead of using a legacy destination map.
    - Split native staging by component set while sharing one
    workflow-artifact cache across the invocation.
    - Changed workflow artifact download to select target artifacts by name,
    print sizes/progress, and reuse cached artifacts.
    - Removed the implicit `CI=true` default from `build_npm_package.py`;
    local CI-shaped runs should set that environment explicitly.
    - Kept `npm pack` cache/log output in its temporary directory so packing
    does not write to the user npm cache.
    
    ## Verification
    
    - `python3 -m py_compile scripts/stage_npm_packages.py
    codex-cli/scripts/build_npm_package.py`
    - `python3 -m unittest discover -s scripts/codex_package -p "test_*.py"`
    - `scripts/stage_npm_packages.py --help`
    - `codex-cli/scripts/build_npm_package.py --help`
    - Ran the release-shaped staging command from `rust-release.yml` against
    workflow run https://github.com/openai/codex/actions/runs/26240748758
    with `CI=true` set locally to match GitHub Actions:
    
    ```sh
    CI=true python3 ./scripts/stage_npm_packages.py \
      --release-version 0.133.0 \
      --workflow-url https://github.com/openai/codex/actions/runs/26240748758 \
      --package codex \
      --package codex-responses-api-proxy \
      --package codex-sdk
    ```
    
    That completed successfully, downloaded only the six target artifacts
    once, reused the cache for `codex-responses-api-proxy`, and produced all
    nine npm tarballs. Generated tarballs and staging/artifact temp dirs
    were cleaned afterward.
  • packaging: move rg manifest out of npm bin (#23833)
    ## Why
    
    Installing `@openai/codex` currently places a Dotslash `rg` manifest at
    `node_modules/@openai/codex/bin/rg`, even though the native optional
    dependency already ships the actual helper under
    `vendor/<target>/codex-path/rg`. The launcher prepends that `codex-path`
    directory, so the top-level `bin/rg` file is redundant in the npm
    install.
    
    The remaining direct consumers of the manifest are package-building
    paths: `scripts/codex_package/ripgrep.py` and
    `codex-cli/scripts/install_native_deps.py`. Keeping the manifest under
    `codex-cli/bin` makes it look like a shipped npm binary, so this moves
    it next to the package-builder code that owns it. The checked-in
    `@openai/codex` package metadata should likewise describe only the meta
    package payload; generated platform packages continue to publish
    `vendor`.
    
    ## What Changed
    
    - Moved the Dotslash ripgrep manifest from `codex-cli/bin/rg` to
    `scripts/codex_package/rg`.
    - Updated the package builder, npm native-artifact hydrator, README, and
    CLI help text to reference the new manifest location.
    - Stopped `codex-cli/scripts/build_npm_package.py` from copying `rg`
    into the `@openai/codex` meta package.
    - Narrowed the checked-in meta package `files` whitelist to
    `bin/codex.js`.
    
    ## Verification
    
    - `python3 -m unittest discover -s scripts/codex_package -p "test_*.py"`
    - `python3 -m unittest discover -s codex-cli/scripts -p "test_*.py"`
    - `python3 -m py_compile codex-cli/scripts/build_npm_package.py
    codex-cli/scripts/install_native_deps.py
    scripts/codex_package/ripgrep.py scripts/codex_package/cli.py
    scripts/stage_npm_packages.py`
    - `codex-cli/scripts/build_npm_package.py --package codex --version
    0.0.0-test --pack-output <tmp>/codex-meta-no-vendor.tgz`
    - `tar -tf <tmp>/codex-meta-no-vendor.tgz` showed only
    `package/bin/codex.js`, `package/package.json`, and `package/README.md`.
    - Direct staging check showed `codex` uses `files: ["bin/codex.js"]`
    while `codex-darwin-arm64` still uses `files: ["vendor"]`.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23833).
    * #23836
    * __->__ #23833
  • release: package prebuilt resource binaries (#23759)
    ## Why
    
    Release packaging should be a staging step once release binaries have
    already been built and signed. The Windows release job was downloading
    and signing `codex-command-runner.exe` and
    `codex-windows-sandbox-setup.exe`, but `scripts/build_codex_package.py`
    still rebuilt those helpers while creating the package archives.
    
    That makes the package step slower and, more importantly, risks putting
    helper binaries in the archive that were produced after the signing
    step. Linux had the same shape for package resources: `bwrap` could be
    rebuilt by the package builder instead of being passed in as a prebuilt
    release artifact.
    
    This builds on #23752, which fixes `.tar.zst` creation when Windows
    runners rely on the repository DotSlash `zstd` wrapper.
    
    ## What changed
    
    - Add explicit prebuilt resource inputs to the Codex package builder:
      - `--bwrap-bin`
      - `--codex-command-runner-bin`
      - `--codex-windows-sandbox-setup-bin`
    - Make `.github/scripts/build-codex-package-archive.sh` pass resource
    binaries from the release output directory when they are already
    present.
    - Build Linux `bwrap` for app-server release jobs too, so app-server
    package creation does not invoke Cargo just to supply the package
    resource.
    - Keep macOS package creation as a no-Cargo path when `--entrypoint-bin`
    is provided, since macOS packages have no resource binaries.
    - Add unit coverage showing prebuilt macOS, Linux, and Windows package
    inputs result in no source-built binaries.
    
    ## Verification
    
    - `python3 -m unittest discover -s scripts/codex_package -p 'test_*.py'`
    - `python3 -m py_compile scripts/codex_package/*.py`
    - `bash -n .github/scripts/build-codex-package-archive.sh`
    - Dry-ran Linux and Windows package builds with fake prebuilt resources
    and a nonexistent Cargo path to verify the package builder did not
    invoke Cargo.
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23759).
    * #23760
    * __->__ #23759
  • release: use DotSlash zstd for package archives (#23752)
    ## Why
    
    The Windows release job installed DotSlash successfully, but package
    archive creation still failed while writing `codex-package-*.tar.zst`.
    The Python archiver used `shutil.which("zstd")`, which does not reliably
    find the extensionless DotSlash manifest at `.github/workflows/zstd`
    from native Windows Python.
    
    That left release packaging dependent on a command named exactly `zstd`
    being discoverable on `PATH`, even though the repository already carries
    a DotSlash wrapper for Windows runners.
    
    ## What changed
    
    - Add `resolve_zstd_command()` to prefer a real `zstd` binary when
    present.
    - Fall back to invoking `dotslash .github/workflows/zstd` when `zstd` is
    not on `PATH`.
    - Keep the error explicit when neither `zstd` nor the DotSlash fallback
    is available.
    - Add unit coverage for direct `zstd`, DotSlash fallback, and
    missing-tool error paths.
    
    ## Verification
    
    - `python3 -m unittest discover -s scripts/codex_package -p 'test_*.py'`
    - `python3 -m py_compile scripts/codex_package/*.py`
  • dotslash: publish Codex entrypoints from package archives (#23638)
    ## Summary
    
    DotSlash should resolve the same canonical package archives used by
    standalone installers and npm platform packages, rather than continuing
    to point at single-binary zstd artifacts or the older Linux bundle
    archive.
    
    This updates the Codex CLI and `codex-app-server` DotSlash release
    config entries to match `codex-package-<target>.tar.gz` and
    `codex-app-server-package-<target>.tar.gz`, with paths that select
    `bin/codex` or `bin/codex-app-server` inside the extracted package. The
    other helper outputs stay on their existing per-binary artifacts for
    now.
    
    ## Test plan
    
    - `python3 -m json.tool .github/dotslash-config.json > /dev/null`
    - Ran a Python regex smoke test that checked every updated `codex` and
    `codex-app-server` platform entry against the archive names emitted by
    `.github/scripts/build-codex-package-archive.sh`.
  • npm: ship platform packages in Codex package layout (#23637)
    ## Summary
    
    The npm platform packages should stop carrying a bespoke native layout
    now that the release workflow builds canonical Codex package archives.
    Keeping npm on the same `bin/`, `codex-resources/`, and `codex-path/`
    structure lets the Rust package-layout detection behave consistently
    across standalone, npm, and future DotSlash installs.
    
    This changes platform npm packages to stage the `codex-package` artifact
    for each target under `vendor/<target>`. The Node launcher now resolves
    `bin/codex` and prepends `codex-path`, while retaining legacy
    `vendor/<target>/codex` and `vendor/<target>/path` fallback support for
    local development and migration. The npm staging helper downloads
    `codex-package` archives instead of rebuilding the CLI payload from
    individual `codex`, `rg`, `bwrap`, and sandbox helper artifacts.
    
    CI still needs to stage npm packages from historical rust-release
    workflow artifacts that predate package archives, so the staging scripts
    expose an explicit `--allow-legacy-codex-package` fallback. That
    fallback synthesizes the canonical package layout from legacy per-binary
    artifacts and is wired only into the CI smoke path; release staging
    remains strict and continues to require real package archives.
    
    For direct local use, `install_native_deps.py` now points its built-in
    default workflow at the same recent artifact run used by CI and
    automatically enables legacy package synthesis only when
    `--workflow-url` is omitted. Explicit workflow URLs remain strict unless
    callers opt in with `--allow-legacy-codex-package`.
    
    ## Test plan
    
    - `python3 -m py_compile codex-cli/scripts/build_npm_package.py
    codex-cli/scripts/install_native_deps.py scripts/stage_npm_packages.py
    scripts/codex_package/cli.py`
    - `node --check codex-cli/bin/codex.js`
    - `ruby -e 'require "yaml";
    YAML.load_file(".github/workflows/rust-release.yml");
    YAML.load_file(".github/workflows/ci.yml"); puts "ok"'`
    - Staged a synthetic `codex-linux-x64` platform package from a canonical
    vendor tree and verified it copied only `bin/`, `codex-path/`,
    `codex-resources/`, and `codex-package.json`.
    - Imported `install_native_deps.py` and extracted a synthetic
    `codex-package-x86_64-unknown-linux-musl.tar.gz` into `vendor/<target>`.
    - Ran legacy-layout conversion smokes for Linux, Windows, and unsigned
    macOS artifact naming.
    - Ran a synthetic `install_native_deps.py` default-workflow smoke that
    verifies legacy package synthesis is automatic only when
    `--workflow-url` is omitted.
    - `NPM_CONFIG_CACHE="$tmp_dir/npm-cache" python3
    ./scripts/stage_npm_packages.py --release-version 0.125.0 --workflow-url
    https://github.com/openai/codex/actions/runs/26131514935 --package codex
    --allow-legacy-codex-package --output-dir "$tmp_dir"`
    - `node codex-cli/bin/codex.js --version`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23637).
    * #23638
    * __->__ #23637
  • install: consume Codex package archives (#23636)
    ## Summary
    
    Standalone installs should exercise the same canonical package archive
    layout that release builds produce, rather than unpacking npm platform
    packages and reconstructing a parallel install tree.
    
    This updates `install.sh` and `install.ps1` to prefer
    `codex-package-<target>.tar.gz` plus `codex-package_SHA256SUMS`
    introduced in https://github.com/openai/codex/pull/23635, authenticate
    the checksum manifest against GitHub release metadata, verify the
    selected package archive against the authenticated manifest, and install
    the package archive directly.
    
    ## Compatibility Notes
    
    Package installs still leave a compatibility command at `current/codex`
    for managed daemon flows, while visible command shims point at
    `bin/codex` inside the package layout.
    
    Recent releases that predate package archives still publish per-platform
    npm artifacts, so both installers keep a legacy platform npm fallback
    for those versions and verify those archives against release metadata
    directly.
    
    Releases old enough to publish only the single root
    `codex-npm-<version>.tgz` archive are intentionally out of scope. The
    installers fail clearly when neither package archives nor per-platform
    npm archives are present.
    
    On Windows, the runtime helper lookups now recognize package-layout
    installs where `codex.exe` runs from `bin/`, so
    `codex-command-runner.exe` and `codex-windows-sandbox-setup.exe` resolve
    from the top-level `codex-resources/` directory. The direct-sibling and
    older sibling-resource fallbacks are preserved.
    
    ## Test plan
    
    - `sh -n scripts/install/install.sh`
    - `bash -n scripts/install/install.sh`
    - `pwsh -NoProfile -Command '$tokens=$null; $errors=$null; $null =
    [System.Management.Automation.Language.Parser]::ParseFile("scripts/install/install.ps1",
    [ref]$tokens, [ref]$errors); if ($errors.Count) { $errors | Format-List
    *; exit 1 }'`
    - `HOME="$home_dir" CODEX_HOME="$tmp_dir/codex-home"
    CODEX_INSTALL_DIR="$bin_dir" PATH="$bin_dir:$PATH" sh
    scripts/install/install.sh --release 0.125.0`
    - Verified the 0.125.0 isolated install leaves the visible command
    pointed at `current/codex` and includes the legacy `codex-resources/rg`
    payload.
    - `cargo test -p codex-windows-sandbox`
    - `just fix -p codex-windows-sandbox`
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23636).
    * #23638
    * #23637
    * __->__ #23636
  • build: package prebuilt Codex entrypoints (#23586)
    ## Why
    
    The package builder should describe the binaries it is actually
    packaging, not require callers to restate release metadata out of band.
    A caller-provided `--version` flag can drift from the workspace version,
    but running the target entrypoint to discover its version breaks
    cross-target packages when the produced binary cannot execute on the
    build host.
    
    This PR keeps package metadata tied to the repository source of truth by
    reading `[workspace.package].version` from `codex-rs/Cargo.toml`. It
    also prepares the package layout for `codex-app-server` packages: the
    same package structure can now represent either the CLI entrypoint or
    the app-server entrypoint while keeping shared sidecars such as `rg`,
    `bwrap`, and Windows sandbox helpers in the existing package
    directories.
    
    ## What changed
    
    - Removes the `--version` CLI flag from
    `scripts/build_codex_package.py`.
    - Adds Cargo.toml version discovery for `codex-package.json.version` via
    `codex-rs/Cargo.toml`.
    - Adds `--entrypoint-bin` so callers can package a prebuilt entrypoint
    instead of rebuilding it with Cargo.
    - Makes `--variant` an explicit choice between `codex` and
    `codex-app-server`, and uses it to select the cargo binary and packaged
    `bin/` entrypoint name.
    - Updates `scripts/codex_package/README.md` to document variants,
    prebuilt entrypoints, and Cargo.toml version detection.
    
    ## Verification
    
    - Compiled `scripts/build_codex_package.py` and
    `scripts/codex_package/*.py` with `PYTHONDONTWRITEBYTECODE=1`.
    - Ran `scripts/build_codex_package.py --help` and verified `--version`
    is gone while `--variant` and `--entrypoint-bin` are present.
    - Verified the package builder reads version `0.0.0` from
    `codex-rs/Cargo.toml`.
    - Built a fake cross-target `codex-app-server` package using a
    non-executable `--entrypoint-bin`; verified metadata records version
    `0.0.0`, variant `codex-app-server`, and `bin/codex-app-server` as the
    entrypoint.
  • build: default Codex package target and output (#23541)
    ## Why
    
    The package builder should be easy to run during local iteration.
    Requiring callers to provide both a target triple and an output
    directory every time makes the common host-package case more awkward
    than necessary.
    
    This PR keeps explicit overrides available, but makes the default
    invocation useful: build for the current host platform and place the
    package in a fresh temporary directory. Because a temp output path is
    otherwise easy to lose, the builder continues to print the final package
    directory path when it completes.
    
    ## What changed
    
    - Makes `--target` optional and maps the host OS/architecture to
    supported Codex package target triples.
    - Uses GNU Linux target triples for Linux host defaults, while keeping
    the musl targets available for release jobs that pass `--target`
    explicitly.
    - Makes `--package-dir` optional and creates a new `codex-package-*`
    temp directory when omitted.
    - Documents the new defaults in `scripts/codex_package/README.md`.
    
    ## Verification
    
    - Compiled `scripts/build_codex_package.py` and
    `scripts/codex_package/*.py` with `PYTHONDONTWRITEBYTECODE=1`.
    - Ran `scripts/build_codex_package.py --help` from outside the repo.
    - Verified Linux host detection maps `x86_64` and `aarch64` to GNU
    target triples.
    - Ran a fake-Cargo package build while omitting both `--target` and
    `--package-dir`; verified the generated metadata target, expected
    package files, and printed temp package path.
    - Ran a fake-Cargo package build for `x86_64-unknown-linux-gnu` and
    verified `codex`, `bwrap`, and `rg` are assembled into the package.
  • build: fetch rg for Codex packages (#23526)
    ## Why
    
    The Codex package builder should produce a complete package without
    requiring callers to pre-populate `rg` under `codex-cli/vendor` or have
    `dotslash` installed on `PATH`. The repo already tracks the
    authoritative DotSlash manifest in `codex-cli/bin/rg`, so the builder
    can read that metadata directly and fetch the correct ripgrep archive
    for the target it is packaging.
    
    ## What changed
    
    - Added `scripts/codex_package/ripgrep.py` to parse `codex-cli/bin/rg`
    after stripping the shebang, select the target platform entry, download
    the configured artifact, and verify the recorded size and SHA-256
    digest.
    - Added a cache under `$TMPDIR/codex-package/<target>-rg` so verified
    archives can be reused without fetching again.
    - Extracted `rg`/`rg.exe` from `tar.gz` and `zip` artifacts into the
    package-builder cache, then copied that into `codex-path` through the
    existing package layout flow.
    - Kept `--rg-bin` as an explicit local override for offline tests and
    unusual local workflows.
    - Documented the default `rg` fetch/cache behavior in
    `scripts/codex_package/README.md`.
    
    ## Verification
    
    - Ran wrapper/module syntax compilation.
    - Ran `scripts/build_codex_package.py --help` from `/private/tmp`.
    - Ran a local manifest fetch test covering shebang-stripped manifest
    parsing, `tar.gz` extraction, `zip` extraction, size/SHA-256
    verification, and cache reuse after deleting the original source
    archives.
    - Ran fake-cargo package/archive builds for macOS, Linux, and Windows
    target layouts with `--rg-bin`, including an assertion that generated
    tar archives contain no duplicate member names.
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23526).
    * #23541
    * __->__ #23526
  • build: add Codex package builder (#23513)
    ## Why
    
    Codex CLI packaging is currently split across npm staging, standalone
    installers, and release bundle creation, which makes it hard to define
    and validate a single valid package directory. This adds the first
    standalone package builder so later release paths can converge on the
    same canonical layout.
    
    ## What changed
    
    - Added `scripts/build_codex_package.py` as the stable executable
    wrapper around `scripts/codex_package`.
    - Added modules for CLI parsing, target metadata, grouped cargo builds,
    package layout validation, and archive writing.
    - The builder creates a package directory with `codex-package.json`,
    `bin/`, `codex-resources/`, and `codex-path`, and can serialize it as
    `.tar.gz`, `.tar.zst`, or `.zip`.
    - Source-built artifacts are built by one grouped `cargo build`: `codex`
    for all targets, `bwrap` for Linux, and the Windows sandbox helpers for
    Windows. `rg` remains an input because it is vendored from upstream
    rather than built from this repo.
    - Added `scripts/codex_package/README.md` to document the package
    layout, source-built artifacts, and cargo profile behavior.
    
    ## Verification
    
    - Ran wrapper/module syntax compilation.
    - Ran `scripts/build_codex_package.py --help` from `/private/tmp`.
    - Ran fake-cargo package/archive builds for macOS, Linux, and Windows
    target layouts, including an assertion that generated tar archives
    contain no duplicate member names.
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23513).
    * #23526
    * __->__ #23513
  • Fix rust-ci-full failures due to missing bwrap (#21604)
    Since https://github.com/openai/codex/pull/21255, `rust-ci-full` has
    been failing due to a missing `bwrap`.
    
    ```
    thread 'main' panicked at linux-sandbox/src/launcher.rs:43:13:
    bubblewrap is unavailable: no system bwrap was found on PATH and no bundled codex-resources/bwrap binary was found next to the Codex executable
    ```
    
    Since the happy path is now to use the system binary, let's ensure
    that's installed.
    
    
    https://github.com/openai/codex/pull/21604/commits/8d5182663158ee2d15965f39eed26ffa339ecb7d
    was necessary for the `bwrap` executable to be discoverable when the
    working directory is `/`.
    
    I ran `rust-ci-full` at
    https://github.com/openai/codex/actions/runs/25528074506
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • bazel: run sharded rust integration tests (#21057)
    ## Why
    
    Bazel CI was not actually exercising some sharded Rust integration-test
    targets on macOS. The `rules_rust` sharding wrapper expects a symlink
    runfiles tree, but this repo runs Bazel with `--noenable_runfiles`. In
    that configuration the wrapper could fail to find the generated test
    binary, produce an empty test list, and exit successfully. That made
    targets such as `//codex-rs/core:core-all-test` look green even when
    Cargo CI could still catch failures in the same Rust tests.
    
    The coverage gap appears to have been introduced by
    [#18082](https://github.com/openai/codex/pull/18082), which enabled
    rules_rust native sharding on `//codex-rs/core:core-all-test` and the
    other large Rust test labels. The manifest-runfiles setup itself
    predates that change in
    [#10098](https://github.com/openai/codex/pull/10098), but #18082 is
    where the affected integration tests started running through the
    incompatible rules_rust sharding wrapper.
    [#18913](https://github.com/openai/codex/pull/18913) fixed the same
    class of issue for wrapped unit-test shards, but integration-test shards
    were still going through the rules_rust wrapper until this PR.
    
    We still do not have the V8/code-mode pieces stable under the Bazel CI
    cross-compile setup, so this keeps those tests out of Bazel while
    restoring coverage for the rest of the sharded Rust integration suites.
    Cargo CI remains responsible for V8/code-mode coverage for now.
    
    This change did uncover a real failing core test on `main`:
    `approved_folder_write_request_permissions_unblocks_later_apply_patch`.
    That fix is split into
    [#21060](https://github.com/openai/codex/pull/21060), which enables the
    `apply_patch` tool in the test, teaches the aggregate core test binary
    to dispatch the sandboxed filesystem helper, canonicalizes the macOS
    temp patch target, and isolates the core test harness from managed
    local/enterprise config. Keeping that fix separate lets this PR stay
    focused on restoring Bazel coverage while documenting the first failure
    it exposed.
    
    ## What changed
    
    - Build sharded Rust integration tests as manual `*-bin` binaries and
    run them through the existing manifest-aware `workspace_root_test`
    launcher.
    - Keep Bazel sharding on the launcher target so Rust test cases are
    still distributed by stable test-name hashing.
    - Configure Bazel CI to skip Rust tests whose names contain
    `suite::code_mode::`.
    - Exclude the standalone `codex-rs/code-mode` and `codex-rs/v8-poc`
    unit-test targets from `bazel.yml`.
    
    ## Verification
    
    - `bazel query --output=build //codex-rs/core:core-all-test` now shows
    `workspace_root_test` wrapping `//codex-rs/core:core-all-test-bin`.
    - `bazel test --test_output=all --nocache_test_results
    --test_sharding_strategy=disabled //codex-rs/core:core-all-test
    --test_filter=suite::request_permissions_tool::approved_folder_write_request_permissions_unblocks_later_apply_patch`
    runs the actual Rust test body and passes.
    - `bazel test --test_output=errors --nocache_test_results
    --test_env=CODEX_BAZEL_TEST_SKIP_FILTERS=suite::code_mode::
    //codex-rs/core:core-all-test` runs the sharded target with code-mode
    skipped and passes overall locally, with one flaky attempt retried by
    the existing `flaky = True` setting.
  • ci: cross-compile Windows Bazel clippy (#20701)
    ## Why
    
    #20585 moved the Windows Bazel test job to the cross-compile path, but
    the Windows Bazel clippy and verify-release-build jobs were still using
    the native Windows/MSVC-host fallback. Those two jobs became the slowest
    Windows PR legs, even though both are build-only signal and do not need
    to execute the resulting binaries.
    
    ## What Changed
    
    - Switches the Windows Bazel clippy job from
    `--windows-msvc-host-platform` to `--windows-cross-compile`, so clippy
    build actions use Linux RBE while still targeting
    `x86_64-pc-windows-gnullvm`.
    - Switches the Windows Bazel verify-release-build job to
    `--windows-cross-compile` as well. This job only compiles
    `cfg(not(debug_assertions))` Rust code under `fastbuild`, so it does not
    need a native Windows build host.
    - Keeps the old `--skip_incompatible_explicit_targets` behavior only for
    fork/community PRs without `BUILDBUDDY_API_KEY`, where `run-bazel-ci.sh`
    falls back to the local Windows MSVC-host shape.
    - Adds `--windows-cross-compile` support to
    `.github/scripts/run-bazel-query-ci.sh`, so target-discovery queries
    select the same `ci-windows-cross` config as the subsequent build.
    - Threads that option through `scripts/list-bazel-clippy-targets.sh` so
    the Windows clippy job discovers targets under the same platform shape
    as the subsequent clippy build.
    
    ## Verification
    
    Local checks:
    
    ```shell
    bash -n .github/scripts/run-bazel-query-ci.sh
    bash -n scripts/list-bazel-clippy-targets.sh
    ruby -e 'require "yaml"; YAML.load_file(".github/workflows/bazel.yml"); puts "ok"'
    RUNNER_OS=Linux ./scripts/list-bazel-clippy-targets.sh | grep -c -- '-windows-cross-bin$'
    RUNNER_OS=Windows ./scripts/list-bazel-clippy-targets.sh --windows-cross-compile | grep -c -- '-windows-cross-bin$'
    ```
    
    The Linux target-list check reported `0` Windows-cross internal test
    binaries, while the Windows cross target-list check reported `47`,
    preserving the test-code clippy coverage shape from the existing Windows
    job.
  • ci: cross-compile Windows Bazel tests (#20585)
    ## Status
    
    This is the Bazel PR-CI cross-compilation follow-up to #20485. It is
    intentionally split from the Cargo/cargo-xwin release-build PoC so
    #20485 can stay as the historical release-build exploration. The
    unrelated async-utils test cleanup has been moved to #20686, so this PR
    is focused on the Windows Bazel CI path.
    
    The intended tradeoff is now explicit in `.github/workflows/bazel.yml`:
    pull requests get the fast Windows cross-compiled Bazel test leg, while
    post-merge pushes to `main` run both that fast cross leg and a fully
    native Windows Bazel test leg. The native main-only job keeps full
    V8/code-mode coverage and gets a 40-minute timeout because it is less
    latency-sensitive than PR CI. All other Bazel jobs remain at 30 minutes.
    
    ## Why
    
    Windows Bazel PR CI currently does the expensive part of the build on
    Windows. A native Windows Bazel test job on `main` completed in about
    28m12s, leaving very little headroom under the 30-minute job timeout and
    making Windows the slowest PR signal.
    
    #20485 showed that Windows cross-compilation can be materially faster
    for Cargo release builds, but PR CI needs Bazel because Bazel owns our
    test sharding, flaky-test retries, and integration-test layout. This PR
    applies the same high-level shape we already use for macOS Bazel CI:
    compile with remote Linux execution, then run platform-specific tests on
    the platform runner.
    
    The compromise is deliberately signal-aware: code-mode/V8 changes are
    rare enough that PR CI can accept losing the direct V8/code-mode
    smoke-test signal temporarily, while `main` still runs the native
    Windows job post-merge to catch that class of regression. A follow-up PR
    should investigate making the cross-built Windows gnullvm V8 archive
    pass the direct V8/code-mode tests so this tradeoff can eventually go
    away.
    
    ## What Changed
    
    - Adds a `ci-windows-cross` Bazel config that targets
    `x86_64-pc-windows-gnullvm`, uses Linux RBE for build actions, and keeps
    `TestRunner` actions local on the Windows runner.
    - Adds explicit Windows platform definitions for
    `windows_x86_64_gnullvm`, `windows_x86_64_msvc`, and a bridge toolchain
    that lets gnullvm test targets execute under the Windows MSVC host
    platform.
    - Updates the Windows Bazel PR test leg to opt into the cross-compile
    path via `--windows-cross-compile` and `--remote-download-toplevel`.
    - Adds a `test-windows-native-main` job that runs only for `push` events
    on `refs/heads/main`, uses the native Windows Bazel path, includes
    V8/code-mode smoke tests, and has `timeout-minutes: 40`.
    - Keeps fork/community PRs without `BUILDBUDDY_API_KEY` on the previous
    local Windows MSVC-host fallback, including
    `--host_platform=//:local_windows_msvc` and `--jobs=8`.
    - Preserves the existing integration-test shape on non-gnullvm
    platforms, while generating Windows-cross wrapper targets only for
    `windows_gnullvm`.
    - Resolves `CARGO_BIN_EXE_*` values from runfiles at test runtime,
    avoiding hard-coded Cargo paths and duplicate test runfiles.
    - Extends the V8 Bazel patches enough for the
    `x86_64-pc-windows-gnullvm` target and Linux remote execution path.
    - Makes the Windows sandbox test cwd derive from `INSTA_WORKSPACE_ROOT`
    at runtime when Bazel provides it, because cross-compiled binaries may
    contain Linux compile-time paths.
    - Keeps the direct V8/code-mode unit smoke tests out of the Windows
    cross PR path for now while native Windows CI continues to cover them
    post-merge.
    
    ## Command Shape
    
    The fast Windows PR test leg invokes the normal Bazel CI wrapper like
    this:
    
    ```shell
    ./.github/scripts/run-bazel-ci.sh \
      --print-failed-action-summary \
      --print-failed-test-logs \
      --windows-cross-compile \
      --remote-download-toplevel \
      -- \
      test \
      --test_tag_filters=-argument-comment-lint \
      --test_verbose_timeout_warnings \
      --build_metadata=COMMIT_SHA=${GITHUB_SHA} \
      -- \
      //... \
      -//third_party/v8:all \
      -//codex-rs/code-mode:code-mode-unit-tests \
      -//codex-rs/v8-poc:v8-poc-unit-tests
    ```
    
    With the BuildBuddy secret available on Windows, the wrapper selects
    `--config=ci-windows-cross` and appends the important Windows-cross
    overrides after rc expansion:
    
    ```shell
    --host_platform=//:rbe
    --shell_executable=/bin/bash
    --action_env=PATH=/usr/bin:/bin
    --host_action_env=PATH=/usr/bin:/bin
    --test_env=PATH=${CODEX_BAZEL_WINDOWS_PATH}
    ```
    
    The native post-merge Windows job intentionally omits
    `--windows-cross-compile` and does not exclude the V8/code-mode unit
    targets:
    
    ```shell
    ./.github/scripts/run-bazel-ci.sh \
      --print-failed-action-summary \
      --print-failed-test-logs \
      -- \
      test \
      --test_tag_filters=-argument-comment-lint \
      --test_verbose_timeout_warnings \
      --build_metadata=COMMIT_SHA=${GITHUB_SHA} \
      --build_metadata=TAG_windows_native_main=true \
      -- \
      //... \
      -//third_party/v8:all
    ```
    
    ## Research Notes
    
    The existing macOS Bazel CI config already uses the model we want here:
    build actions run remotely with `--strategy=remote`, but `TestRunner`
    actions execute on the macOS runner. This PR mirrors that pattern for
    Windows with `--strategy=TestRunner=local`.
    
    The important Bazel detail is that `rules_rs` is already targeting
    `x86_64-pc-windows-gnullvm` for Windows Bazel PR tests. This PR changes
    where the build actions execute; it does not switch the Bazel PR test
    target to Cargo, `cargo-nextest`, or the MSVC release target.
    
    Cargo release builds differ from this Bazel path for V8: the normal
    Windows Cargo release target is MSVC, and `rusty_v8` publishes prebuilt
    Windows MSVC `.lib.gz` archives. The Bazel PR path targets
    `windows-gnullvm`; `rusty_v8` does not publish a prebuilt Windows
    GNU/gnullvm archive, so this PR builds that archive in-tree. That
    Linux-RBE-built gnullvm archive currently crashes in direct V8/code-mode
    smoke tests, which is why the workflow keeps native Windows coverage on
    `main`.
    
    The less obvious Bazel detail is test wrapper selection. Bazel chooses
    the Windows test wrapper (`tw.exe`) from the test action execution
    platform, not merely from the Rust target triple. The outer
    `workspace_root_test` therefore declares the default test toolchain and
    uses the bridge toolchain above so the test action executes on Windows
    while its inner Rust binary is built for gnullvm.
    
    The V8 investigation exposed a Windows-client gotcha: even when an
    action execution platform is Linux RBE, Bazel can still derive the
    genrule shell path from the Windows client. That produced remote
    commands trying to run `C:\Program Files\Git\usr\bin\bash.exe` on Linux
    workers. The wrapper now passes `--shell_executable=/bin/bash` with
    `--host_platform=//:rbe` for the Windows cross path.
    
    The same Windows-client/Linux-RBE boundary also affected
    `third_party/v8:binding_cc`: a multiline genrule command can carry CRLF
    line endings into Linux remote bash, which failed as `$'\r'`. That
    genrule now keeps the `sed` command on one physical shell line while
    using an explicit Starlark join so the shell arguments stay readable.
    
    ## Verification
    
    Local checks included:
    
    ```shell
    bash -n .github/scripts/run-bazel-ci.sh
    bash -n workspace_root_test_launcher.sh.tpl
    ruby -e "require %q{yaml}; YAML.load_file(%q{.github/workflows/bazel.yml}); puts %q{ok}"
    RUNNER_OS=Linux ./scripts/list-bazel-clippy-targets.sh
    RUNNER_OS=Windows ./scripts/list-bazel-clippy-targets.sh
    RUNNER_OS=Linux ./tools/argument-comment-lint/list-bazel-targets.sh
    RUNNER_OS=Windows ./tools/argument-comment-lint/list-bazel-targets.sh
    ```
    
    The Linux clippy and argument-comment target lists contain zero
    `*-windows-cross-bin` labels, while the Windows lists still include 47
    Windows-cross internal test binaries.
    
    CI evidence:
    
    - Baseline native Windows Bazel test on `main`: success in about 28m12s,
    https://github.com/openai/codex/actions/runs/25206257208/job/73907325959
    - Green Windows-cross Bazel run on the split PR before adding the
    main-only native leg: Windows test 9m16s, Windows release verify 5m10s,
    Windows clippy 4m43s,
    https://github.com/openai/codex/actions/runs/25231890068
    - The latest SHA adds the explicit PR-vs-main tradeoff in `bazel.yml`;
    CI is rerunning on that focused diff.
    
    ## Follow-Up
    
    A subsequent PR should investigate making a cross-built Windows binary
    work with V8/code-mode enabled. Likely options are either making the
    Linux-RBE-built `windows-gnullvm` V8 archive correct at runtime, or
    evaluating whether a Bazel MSVC target/toolchain can reuse the same
    prebuilt MSVC `rusty_v8` archive shape that Cargo release builds already
    use.
  • ci: reuse Bazel CI startup for target-discovery queries (#19232)
    ## Why
    
    A rerun of the Windows Bazel clippy job after
    [#19161](https://github.com/openai/codex/pull/19161) had exactly the
    cache behavior we wanted in BuildBuddy: zero action-cache misses. Even
    so, the GitHub job still took a little over five minutes.
    
    The problem was that the job was paying for two separate Bazel startup
    paths:
    
    1. a `bazel query` to discover extra lint targets
    2. the real `bazel build --config=clippy ...` invocation
    
    On Windows, that query was bypassing the CI Bazel wrapper, so it did not
    reuse the same `--output_user_root`, CI config, or remote-cache setup as
    the real build. In practice that meant the rerun could still cold-start
    a separate Bazel server before the actual clippy build even began.
    
    ## What
    
    - add `.github/scripts/run-bazel-query-ci.sh` to run CI-side Bazel
    queries with the same startup and cache-related flags as the main Bazel
    command
    - switch `scripts/list-bazel-clippy-targets.sh` to use that helper for
    manual `rust_test` target discovery
    - switch `tools/argument-comment-lint/list-bazel-targets.sh` to use the
    same helper
    - simplify `.github/scripts/run-argument-comment-lint-bazel.sh` so its
    Windows-only query path also goes through the shared helper
    
    This keeps the target-discovery queries aligned with the later
    build/test invocation instead of treating them as a separate cold Bazel
    session.
    
    ## Verification
    
    - `bash -n .github/scripts/run-bazel-query-ci.sh`
    - `bash -n scripts/list-bazel-clippy-targets.sh`
    - `bash -n tools/argument-comment-lint/list-bazel-targets.sh`
    - `bash -n .github/scripts/run-argument-comment-lint-bazel.sh`
    - mocked a Windows invocation of `run-bazel-query-ci.sh` and verified it
    forwards `--output_user_root`, `--config=ci-windows`, the BuildBuddy
    auth header, and the repository cache flags
    
    ## Docs
    
    No documentation updates are needed.
  • Significantly improve standalone installer (#17022)
    ## Summary
    
    This PR significantly improves the standalone installer experience.
    
    The main changes are:
    
    1. We now install the codex binary and other dependencies in a
    subdirectory under CODEX_HOME.
    (`CODEX_HOME/packages/standalone/releases/...`)
    
    2. We replace the `codex.js` launcher that npm/bun rely on with logic in
    the Rust binary that automatically resolves its dependencies (like
    ripgrep)
    
    ## Motivation
    
    A few design constraints pushed this work.
    
    1. Currently, the entrypoint to codex is through `codex.js`, which
    forces a node dependency to kick off our rust app. We want to move away
    from this so that the entrypoint to codex does not rely on node or
    external package managers.
    2. Right now, the native script adds codex and its dependencies directly
    to user PATH. Given that codex is likely to add more binary dependencies
    than ripgrep, we want a solution which does not add arbitrary binaries
    to user PATH -- the only one we want to add is the `codex` command
    itself.
    3. We want upgrades to be atomic. We do not want scenarios where
    interrupting an upgrade command can move codex into undefined state (for
    example, having a new codex binary but an old ripgrep binary). This was
    ~possible with the old script.
    4. Currently, the Rust binary uses heuristics to determine which
    installer created it. These heuristics are flaky and are tied to the
    `codex.js` launcher. We need a more stable/deterministic way to
    determine how the binary was installed for standalone.
    5. We do not want conflicting codex installations on PATH. For example,
    the user installing via npm, then installing via brew, then installing
    via standalone would make it unclear which version of codex is being
    launched and make it tough for us to determine the right upgrade
    command.
    
    ## Design
    
    ### Standalone package layout
    
    Standalone installs now live under `CODEX_HOME/packages/standalone`:
    
    ```text
    $CODEX_HOME/
      packages/
        standalone/
          current -> releases/0.111.0-x86_64-unknown-linux-musl
          releases/
            0.111.0-x86_64-unknown-linux-musl/
              codex
              codex-resources/
                rg
    ```
    
    where `standalone/current` is a symlink to a release directory.
    
    On Windows, the release directory has the same shape, with `.exe` names
    and Windows helpers in `codex-resources`:
    
    ```text
    %CODEX_HOME%\
      packages\
        standalone\
          current -> releases\0.111.0-x86_64-pc-windows-msvc
          releases\
            0.111.0-x86_64-pc-windows-msvc\
              codex.exe
              codex-resources\
                rg.exe
                codex-command-runner.exe
                codex-windows-sandbox-setup.exe
    ```
    
    This gives us:
    - atomic upgrades because we can fully stage a release before switching
    `standalone/current`
    - a stable way for the binary to recognize a standalone install from its
    canonical `current_exe()` path under CODEX_HOME
    - a clean place for binary dependencies like `rg`, Windows sandbox
    helpers, and, in the future, our custom `zsh` etc
    
    ### Command location
    
    On Unix, we add a symlink at `~/.local/bin/codex` which points directly
    to the `$CODEX_HOME/packages/standalone/current/codex` binary. This
    becomes the main entrypoint for the CLI.
    
    On Windows, we store the link at
    `%LOCALAPPDATA%\Programs\OpenAI\Codex\bin`.
    
    ### PATH persistence
    
    This is a tricky part of the PR, as there's no ~super reliable way to
    ensure that we end up on PATH without significant tradeoffs.
    
    Most Unix variants will have `~/.local/bin` on PATH already, which means
    we *should* be fine simply registering the command there in most cases.
    However, there are cases where this is not the case. In these cases, we
    directly edit the profile depending on the shell we're in.
    
    - macOS zsh: `~/.zprofile`
    - macOS bash: `~/.bash_profile`
    - Linux zsh: `~/.zshrc`
    - Linux bash: `~/.bashrc`
    - fallback: `~/.profile`
    
    On Windows, we update the User `Path` environment variable directly and
    we don't need to worry about shell profiles.
    
    ### Standalone runtime detection
    
    This PR adds a new shared crate, `codex-install-context`, which computes
    install ownership once per process and caches it in a `OnceLock`.
    
    That context includes:
    - install manager (`Standalone`, `Npm`, `Bun`, `Brew`, `Other`)
    - the managed standalone release directory, when applicable
    - the managed standalone `codex-resources` directory, when present
    - the resolved `rg_command`
    
    The standalone path is detected by canonicalizing `current_exe()`,
    canonicalizing CODEX_HOME via `find_codex_home()`, and checking whether
    the binary is running from under
    `$CODEX_HOME/packages/standalone/releases`.
    
    We intentionally do not use a release metadata file. The binary path is
    the source of truth.
    
    ### Dependency resolution
    
    For standalone installs, `grep_files` now resolves bundled `rg` from
    `codex-resources` next to the Codex binary.
    
    For npm/bun/brew/other installs, `grep_files` falls back to resolving
    `rg` from PATH.
    
    For Windows standalone installs, Windows sandbox helpers are still found
    as direct siblings when present. If they are not direct siblings, the
    lookup also checks the sibling `codex-resources` directory.
    
    ### TUI update path
    
    The TUI now has `UpdateAction::StandaloneUnix` and
    `UpdateAction::StandaloneWindows`, which rerun the standalone install
    commands.
    
    Unix update command:
    
    ```sh
    sh -c "curl -fsSL https://chatgpt.com/codex/install.sh | sh"
    ```
    
    Windows update command:
    
    ```powershell
    powershell -c "irm https://chatgpt.com/codex/install.ps1|iex"
    ```
    
    The Windows updater runs PowerShell directly. We do this because `cmd
    /C` would parse the `|iex` as a cmd pipeline instead of passing it to
    PowerShell.
    
    ## Additional installer behavior
    
    - standalone installs now warn about conflicting npm/bun/brew-managed
    `codex` installs and offer to uninstall them
    - same-version reruns do not redownload the release if it is already
    staged locally
    
    ## Testing
    
    Installer smoke tests run:
    - macOS: fresh install into isolated `HOME` and `CODEX_HOME` with
    `scripts/install/install.sh --release latest`
    - macOS: reran the installer against the same isolated install to verify
    the same-version/update path and PATH block idempotence
    - macOS: verified the installed `codex --version` and bundled
    `codex-resources/rg --version`
    - Windows: parsed `scripts/install/install.ps1` with PowerShell via
    `[scriptblock]::Create(...)`
    - Windows: verified the standalone update action builds a direct
    PowerShell command and does not route the `irm ...|iex` command through
    `cmd /C`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Reuse remote exec-server in core tests (#17837)
    ## Summary
    - reuse a shared remote exec-server for remote-aware codex-core
    integration tests within a test binary process
    - keep per-test remote cwd creation and cleanup so tests retain
    workspace isolation
    - leave codex_self_exe, codex_linux_sandbox_exe, cwd_path(), and
    workspace_path() behavior unchanged
    
    ## Validation
    - rustfmt codex-rs/core/tests/common/test_codex.rs
    - git diff --check
    - CI is running on the updated branch
  • Add Bazel verify-release-build job (#17705)
    ## Why
    
    `main` recently needed
    [#17691](https://github.com/openai/codex/pull/17691) because code behind
    `cfg(not(debug_assertions))` was not being compiled by the Bazel PR
    workflow. Our existing CI only built the fast/debug configuration, so
    PRs could stay green while release-only Rust code still failed to
    compile. This PR adds a release-style compile check that is cheap enough
    to run on every PR.
    
    ## What Changed
    
    - Added a `verify-release-build` job to `.github/workflows/bazel.yml`.
    - Represented each supported OS once in that job's matrix: x64 Linux,
    arm64 macOS, and x64 Windows.
    - Kept the build close to fastbuild cost by using
    `--compilation_mode=fastbuild` while forcing Rust to compile with
    `-Cdebug-assertions=no`, which makes `cfg(not(debug_assertions))` true
    without also turning on release optimizations or debug-info generation.
    - Added comments in `.github/workflows/bazel.yml` and
    `scripts/list-bazel-release-targets.sh` to make the job's intent and
    target scope explicit.
    - Restored the Bazel repository cache save behavior to run after every
    non-cancelled job, matching
    [#16926](https://github.com/openai/codex/pull/16926), and removed the
    now-unused `repository-cache-hit` output from `prepare-bazel-ci`.
    - Reused the shared `prepare-bazel-ci` action from the parent PR so the
    new job does not duplicate Bazel setup boilerplate.
    
    ## Verification
    
    - Used `bazel aquery` on `//codex-rs/tui:codex-tui` to confirm the Rust
    compile still uses `opt-level=0` and `debuginfo=0` while passing
    `-Cdebug-assertions=no`.
    - Parsed `.github/workflows/bazel.yml` as YAML locally.
    - Ran `bash -n scripts/list-bazel-release-targets.sh`.
  • Stabilize exec-server filesystem tests in CI (#17671)
    ## Summary\n- add an exec-server package-local test helper binary that
    can run exec-server and fs-helper flows\n- route exec-server filesystem
    tests through that helper instead of cross-crate codex helper
    binaries\n- stop relying on Bazel-only extra binary wiring for these
    tests\n\n## Testing\n- not run (per repo guidance for codex changes)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Run exec-server fs operations through sandbox helper (#17294)
    ## Summary
    - run exec-server filesystem RPCs requiring sandboxing through a
    `codex-fs` arg0 helper over stdin/stdout
    - keep direct local filesystem execution for `DangerFullAccess` and
    external sandbox policies
    - remove the standalone exec-server binary path in favor of top-level
    arg0 dispatch/runtime paths
    - add sandbox escape regression coverage for local and remote filesystem
    paths
    
    ## Validation
    - `just fmt`
    - `git diff --check`
    - remote devbox: `cd codex-rs && bazel test --bes_backend=
    --bes_results_url= //codex-rs/exec-server:all` (6/6 passed)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Support remote exec cwd in TUI startup (#17142)
    When running with remote executor the cwd is the remote path. Today we
    check for existence of a local directory on startup and attempt to load
    config from it.
    
    For remote executors don't do that.
  • Add remote exec start script (#17059)
    Just pass an SSH host
    ```
    ./scripts/start-codex-exec.sh codex-remote
    ```
  • [codex] Make unified exec tests remote aware (#16977)
    ## Summary
    - Convert unified exec integration tests that can run against the remote
    executor to use the remote-aware test harness.
    - Create workspace directories through the executor filesystem for
    remote runs.
    - Install `python3` and `zsh` in the remote test container so restored
    Python/zsh-based test commands work in fresh Ubuntu containers.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-core --test all unified_exec_defaults_to_pipe`
    - `cargo test -p codex-core --test all unified_exec_can_enable_tty`
    - `cargo test -p codex-core --test all unified_exec`
    - Remote on `codex-remote`: `source scripts/test-remote-env.sh && cd
    codex-rs && cargo test -p codex-core --test all unified_exec`
    - `just fix -p codex-core`
  • ci: align Bazel repo cache and Windows clippy target handling (#16740)
    ## Why
    
    Bazel CI had two independent Windows issues:
    
    - The workflow saved/restored `~/.cache/bazel-repo-cache`, but
    `.bazelrc` configured `common:ci-windows
    --repository_cache=D:/a/.cache/bazel-repo-cache`, so `actions/cache` and
    Bazel could point at different directories.
    - The Windows `Bazel clippy` job passed the full explicit target list
    from `//codex-rs/...`, but some of those explicit targets are
    intentionally incompatible with `//:local_windows`.
    `run-argument-comment-lint-bazel.sh` already handles that with
    `--skip_incompatible_explicit_targets`; the clippy workflow path did
    not.
    
    I also tried switching the workflow cache path to
    `D:\a\.cache\bazel-repo-cache`, but the Windows clippy job repeatedly
    failed with `Failed to restore: Cache service responded with 400`, so
    the final change standardizes on `$HOME/.cache/bazel-repo-cache` and
    makes cache restore non-fatal.
    
    ## What Changed
    
    - Expose one repository-cache path from
    `.github/actions/setup-bazel-ci/action.yml` and export that path as
    `BAZEL_REPOSITORY_CACHE` so `run-bazel-ci.sh` passes it to Bazel after
    `--config=ci-*`.
    - Move `actions/cache/restore` out of the composite action into
    `.github/workflows/bazel.yml`, and make restore failures non-fatal
    there.
    - Save exactly the exported cache path in `.github/workflows/bazel.yml`.
    - Remove `common:ci-windows
    --repository_cache=D:/a/.cache/bazel-repo-cache` from `.bazelrc` so the
    Windows CI config no longer disagrees with the workflow cache path.
    - Pass `--skip_incompatible_explicit_targets` in the Windows `Bazel
    clippy` job so incompatible explicit targets do not fail analysis while
    the lint aspect still traverses compatible Rust dependencies.
    
    ## Verification
    
    - Parsed `.github/actions/setup-bazel-ci/action.yml` and
    `.github/workflows/bazel.yml` with Ruby's YAML loader.
    - Resubmitted PR `#16740`; CI is rerunning on the amended commit.