## Summary
Replace Hatchling with uv's build backend for the Python SDK. The
backend infers the `src/openai_codex` module from the normalized project
name and standard source layout, so no uv-specific package configuration
is required.
This keeps Python packaging within the uv toolchain already used for
dependency management and release builds. A controlled before-and-after
PEP 517 comparison produced identical wheel package paths, bytes,
permissions, and semantic metadata. The sdist retains the SDK package
tree, root README, and project metadata while dropping the unrelated
examples README that Hatch included through its broad include matching.
## 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.
## Summary
We originally addressed startup prewarming holding the read side of
`RwLock<McpConnectionManager>` by snapshotting tool-list state. Review
feedback identified the broader ownership problem: the outer
synchronization should only publish or retrieve the current manager,
while MCP operations rely on the manager's internal synchronization. A
follow-up preserved operation retirement with a separate gate, but
further review questioned whether that synchronization was actually
required and whether we could support latest-wins replacement instead.
This PR now stores the current MCP manager in `ArcSwap`. Each operation
uses `load_full()` to obtain an owned `Arc<McpConnectionManager>`, then
performs MCP I/O without retaining the publication mechanism. Refresh
cancels obsolete startup work, constructs a replacement, and atomically
publishes it. New operations see the latest manager, while operations
that already loaded the previous manager retain a valid handle. Refresh
happens at a turn boundary, so there should be no active user tool calls
to drain.
Git history supports dropping the outer `RwLock`. It was introduced in
`03ffe4d595` on November 17, 2025 for non-blocking MCP startup: the
session published an empty manager, startup initialized that same object
while holding the write lock, and readers waited for initialization.
`7cd2e84026` on February 19, 2026 removed that two-phase initialization
in favor of constructing a fresh manager and swapping it in, explicitly
noting that `Option` or `OnceCell` could replace the placeholder design.
Hot reload later reused the existing lock to publish a replacement, but
I found no indication that the lock was introduced to guarantee
in-flight tool calls finish before refresh or shutdown.
Terminal shutdown remains separate from refresh: it aborts startup
prewarming and active tasks before shutting down the current manager, so
tool calls may be interrupted and no model WebSocket work continues
after shutdown. Focused regression coverage exercises pending tool-list
cancellation, deferred refresh, and startup-prewarm shutdown.
## Summary
TUI startup loads related plugin data from `hooks/list`, session MCP
initialization, and plugin skill warmup. These paths repeated filesystem
discovery and emitted the same plugin warnings, while `hooks/list` and
account/model bootstrap ran serially.
This change:
- Reuses one immutable plugin load outcome across startup consumers.
- Keys the cache only on plugin-relevant configuration.
- Single-flights concurrent plugin loads and prevents invalidated loads
from repopulating the cache.
- Runs hook discovery and account/model bootstrap concurrently.
- Preserves configuration-migration ordering, hook review behavior, and
accurate startup telemetry.
In 10 alternating release-build launches in the Ruff repository with the
existing `~/.codex` configuration, median time to the first editable
composer decreased from 833ms to 504ms. The branch was faster in 9 of 10
pairs, with a paired median improvement of 312ms.
## Summary
`codex resume --last` currently lists sessions by updated time using
scan-and-repair. Updated-time filesystem listing must stat every rollout
before applying the cwd, provider, and source filters, so startup scales
with the entire local session history...
This change queries the state DB first for the latest matching session.
For local workspaces, we only accept the indexed result when its rollout
path still exists; otherwise we retry with scan-and-repair. The same
lookup path is shared by `fork --last`.
I benchmarked the same `thread/list` request used by `resume --last` in
my local `ruff` checkout against a Codex home with 2,599 active rollouts
totaling 3.7 GiB, including 90 Ruff threads.
Across five fresh release app-server processes with warm filesystem
caches, the state-DB-only lookup had median latency of 0.37-0.44 ms,
while scan-and-repair had median latency of 139-162 ms. First-request
latency was 0.7-1.7 ms versus 142-185 ms.
So this **removes roughly 140-160 ms from the `resume --last` lookup**
on this machine, and makes that lookup over 300x faster.
The tradeoff is that this does leave two correctness gaps:
- If a newer matching rollout is missing from SQLite but an older
matching row exists, the fast path resumes the older thread and never
falls back to the filesystem scan.
- If an existing row has stale filter or ordering metadata, the fast
path can select a different thread from scan-and-repair. The rollout
tests already demonstrate this for stale cwd metadata: state-DB-only
returns the stale match, while scan-and-repair removes and repairs it.
So you could end up seeing the "wrong" result in cases like...
1. A crash or SQLite error occurs between Codex writing the conversation
file and updating SQLite, leaving the newer file unindexed.
2. An older Codex version, restore, or manual copy adds a conversation
file after SQLite’s one-time backfill completed.
These seem pretty rare though (and sessions can always be recovered via
other mechanisms -- `--last` is just a convenience feature), and I think
the tradeoffs are good here?
## Summary
`hooks/list` only consumes plugin hook declarations, but previously
loaded every enabled plugin's skills, MCP configuration, apps, and
capability summary before discarding them.
In a local benchmark, this reduced `hooks/list` latency by over 100ms
(e.g., from 594 to 467ms on startup, and 168 to 16ms when making a
`hooks/list` call later in the same TUI session). This is on the
critical path to rendering the TUI, so every 10s of ms should be eyed
skeptically (IMO).
This change adds a hook-specific plugin loading path that preserves
plugin enablement, remote/local conflict resolution, deterministic
ordering, manifest resolution, and hook-loading warnings while skipping
unrelated capabilities. (I think there's room for a more general design
here that allows you to project the capabilities you need at load-time,
but that seems unnecessary right now.)
## Summary
This PR adds `memchr` for some low-hanging performance improvements
(namely, in MCP stdio, Ollama streaming, and full message-history
newline counts).
Codex produced the following release benchmarks:
| Operation | Before | After | Speedup |
| --- | ---: | ---: | ---: |
| MCP 1 MiB chunked line | 2.172 s | 3.984 ms | 545x |
| Ollama 1 MiB chunked line | 1.673 s | 2.790 ms | 600x |
| Count newlines in 10 MiB history | 132.83 ms | 20.05 ms | 6.6x |
With a "real" MCP setup (`ExecutorStdioServerLauncher` started a Python
MCP server, completed `initialize`, requested `tools/list`, and
deserialized a 1 MiB tool description over newline-delimited stdio),
it's about 16x faster end-to-end:
| Branch | 50 calls | Per call |
| --- | ---: | ---: |
| `main` | 862.53 ms | 17.25 ms |
| this branch | 53.89 ms | 1.08 ms |
`memchr` is already in our dependency tree and extremely widely used for
this kind of optimized scanning.
## Summary
I frequently want to be able to paste into the searchable menu -- the
most common use-case here is when specifying an upstream for a
`/review`, where I copy the upstream from an open terminal.
## Summary
In https://github.com/openai/codex/pull/21584, we disabled doctests for
crates that lack any doctests. We can enforce that property via `cargo
shear --deny-warnings`: crates that lack doctests will be flagged if
doctests are enabled, and crates with doctests will be flagged if
doctests are disabled.
A few additional notes:
- By adding `--deny-warnings`, `cargo shear` also flagged a number of
modules that were not reachable at all. Some of those have been removed.
- This PR removes a usage of `windows_modules!` (since `cargo shear` and
`rustfmt` couldn't see through it) in favor of simple `#[cfg(target_os =
"windows")]` macros. As a consequence, many of these files exhibit churn
in this PR, since they weren't being formatted by `rustfmt` at all on
main.
- Again, to make the code more analyzable, this PR also removes some
usages of `#[path = "cwd_junction.rs"]` in favor of a more standard
module structure. The bin sidecar structure is still retained, but,
e.g., `windows-sandbox-rs/src/bin/command_runner.rs` was moved to
`windows-sandbox-rs/src/bin/command_runner/main.rs`, and so on.
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
`cargo test` has entails both running standard Rust tests and doctests.
It turns out that the doctest discovery is fairly slow, and it's a cost
you pay even for crates that don't include any doctests.
This PR disables doctests with `doctest = false` for crates that lack
any doctests.
For the collection of crates below, this speeds up test execution by
>4x.
E.g., before this PR:
```
Benchmark 1: cargo test -p codex-utils-absolute-path -p codex-utils-cache -p codex-utils-cli -p codex-utils-home-dir -p codex-utils-output-truncation -p codex-utils-path -p codex-utils-string -p codex-utils-template -p codex-utils-elapsed -p codex-utils-json-to-toml
Time (mean ± σ): 1.849 s ± 4.455 s [User: 0.752 s, System: 1.367 s]
Range (min … max): 0.418 s … 14.529 s 10 runs
```
And after:
```
Benchmark 1: cargo test -p codex-utils-absolute-path -p codex-utils-cache -p codex-utils-cli -p codex-utils-home-dir -p codex-utils-output-truncation -p codex-utils-path -p codex-utils-string -p codex-utils-template -p codex-utils-elapsed -p codex-utils-json-to-toml
Time (mean ± σ): 428.6 ms ± 6.9 ms [User: 187.7 ms, System: 219.7 ms]
Range (min … max): 418.0 ms … 436.8 ms 10 runs
```
For a single crate, with >2x speedup, before:
```
Benchmark 1: cargo test -p codex-utils-string
Time (mean ± σ): 491.1 ms ± 9.0 ms [User: 229.8 ms, System: 234.9 ms]
Range (min … max): 480.9 ms … 512.0 ms 10 runs
```
And after:
```
Benchmark 1: cargo test -p codex-utils-string
Time (mean ± σ): 213.9 ms ± 4.3 ms [User: 112.8 ms, System: 84.0 ms]
Range (min … max): 206.8 ms … 221.0 ms 13 runs
```
Co-authored-by: Codex <noreply@openai.com>