## 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.
## Context
This is the next step in the plugin auth-routing stack. The earlier PRs
make `PluginsManager` auth-aware and move the broad App/MCP surface
decision into that layer. This PR narrows the ChatGPT/SIWC behavior so
we only hide a plugin MCP server when it conflicts with an App
declaration of the same name.
In product terms: if a plugin exposes both an App route and MCP route
for `foo`, ChatGPT/SIWC sessions should use the App route for `foo`. If
the same plugin also exposes a separate MCP server like `foo2`, that MCP
server should remain available.
```json
// .app.json
{
"apps": {
"foo": {
"id": "connector_abc"
}
}
}
```
```json
// .mcp.json
{
"mcpServers": {
"foo": {
"url": "https://mcp.foo.com/mcp"
},
"foo2": {
"url": "https://mcp.foo2.com/mcp"
}
}
}
```
## Stack
- PR1: #27652 seed plugin manager auth at construction.
- PR2: #27459 route plugin surfaces by auth mode.
- PR3: #27607 dedupe plugin MCP servers by App declaration name.
- PR4: #27602 preserve plugin Apps in connector listings.
- PR5: #27461 skip install-time plugin MCP OAuth for matching App
routes.
## Summary
- Preserve App declaration names in loaded plugin metadata.
- Keep public effective App outputs as deduped connector IDs for
existing callers.
- For ChatGPT/SIWC, suppress only plugin MCP servers whose names match
declared App names.
## Validation
```bash
cargo fmt --all
cargo test -p codex-core-plugins plugin_auth_projection
cargo test -p codex-core-plugins effective_apps
cargo test -p codex-core-plugins read_plugin_for_config_installed_git_source_reads_from_cache_without_cloning
cargo test -p codex-core explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins
cargo test -p codex-core explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth
cargo test -p codex-app-server --test all plugin_install_filters_disallowed_apps_needing_auth
git diff --check
```
---------
Co-authored-by: Xin Lin <xl@openai.com>
## Why
CCA can select a capability root that lives in an executor environment,
but
Codex only had a host-filesystem plugin loader. Before selected executor
plugins can contribute MCP servers, we need a small package boundary
that can
answer:
> Does this selected root contain a plugin, and if so, what does its
manifest
> declare?
The answer must come from the selected environment's filesystem. A
failed
executor lookup must never fall back to the orchestrator filesystem.
## What this changes
This PR introduces:
```rust
PluginProvider::resolve(root)
-> Result<Option<ResolvedPlugin>, Error>
```
`ExecutorPluginProvider` resolves one `SelectedCapabilityRoot` through
its
exact `environment_id`. It checks the recognized manifest locations,
reads the
manifest through that environment's `ExecutorFileSystem`, and returns an
inert
`ResolvedPlugin` containing:
- the opaque selected-root ID;
- the environment-bound plugin root;
- the authority-bound manifest resource;
- parsed metadata and authority-bound component locators.
Descriptor construction rejects manifest or component paths outside the
selected package root, so consumers cannot accidentally lose the package
boundary when they receive a resolved plugin.
If the root has no plugin manifest, resolution returns `None`, allowing
the
caller to treat it as a standalone capability such as a skill.
```text
selected root: repo -> env-1:/workspace/repo
|
| env-1 filesystem only
v
.codex-plugin/plugin.json
|
v
ResolvedPlugin { authority, root, manifest }
```
The existing host loader and the new executor provider now share the
same
manifest parser. Existing `codex-core-plugins::manifest` type paths
remain
available through re-exports, so host behavior and callers are
unchanged.
## Scope
This is intentionally a non-user-visible package-resolution PR. It does
not:
- parse or register plugin MCP server configurations;
- activate skills, connectors, hooks, or MCP servers;
- change app-server wiring;
- introduce host fallback, caching, or lifecycle behavior.
#27670 has merged, and this PR is now based directly on `main`. Together
with
the resolved MCP catalog from #27634, it establishes the inputs needed
for the
executor stdio MCP vertical without changing the existing MCP runtime.
## Follow-up
The next PR will consume `ResolvedPlugin`, read its declared/default MCP
config
through the same executor filesystem, bind supported stdio servers to
that
environment, and feed those registrations into the resolved MCP catalog.
An
app-server E2E will prove that selecting an executor plugin exposes and
invokes
its tool on the owning executor.
Resume/fork semantics, dynamic environment replacement, and non-stdio
placement remain separate lifecycle decisions.
## Validation
- `just fmt`
- `cargo check --tests -p codex-plugin -p codex-core-plugins`
- `just bazel-lock-check`
- `git diff --check`
Test targets were compiled but not executed locally; CI will run the
test and
Clippy suites.
## 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
- emit `codex_plugin_installed` after a remote plugin install succeeds
- keep local installs unchanged, but let remote installs override the
analytics `plugin_id` with the backend remote plugin id
(`plugins~Plugin_...`)
- preserve the local/display identity in `plugin_name` and
`marketplace_name`, plus capability metadata from the installed bundle
- add regression coverage for local install analytics, remote install
analytics, and analytics id override serialization
## Testing
- `just fmt`
- `cargo test -p codex-analytics`
- `cargo test -p codex-app-server`
## Why
Plugins can bundle lifecycle hooks, but Codex previously only discovered
hooks from user, project, and managed config layers. This adds the
plugin discovery and runtime plumbing needed for plugin-bundled hooks
while keeping execution behind the `plugin_hooks` feature flag.
## What
- Discovers plugin hook sources from each plugin's default
`hooks/hooks.json`.
- Supports `plugin.json` manifest `hooks` entries as either relative
paths or inline hook objects.
- Plumbs discovered plugin hook sources through plugin loading into the
hook runtime when `plugin_hooks` is enabled.
- Marks plugin-originated hook runs as `HookSource::Plugin`.
- Injects `PLUGIN_ROOT` and `CLAUDE_PLUGIN_ROOT` into plugin hook
command environments.
- Updates generated schemas and hook source metadata for the plugin hook
source.
## Stack
1. This PR - openai/codex#19705
2. openai/codex#19778
3. openai/codex#19840
4. openai/codex#19882
## Reviewer Notes
- Core logic is in `codex-rs/core-plugins/src/loader.rs` and
`codex-rs/hooks/src/engine/discovery.rs`
- Moved existing / adding new tests to
`codex-rs/core-plugins/src/loader_tests.rs` hence the large diff there
- Otherwise mostly plumbing and minor schema updates
### Core Changes
The `codex-rs/core` changes are limited to wiring plugin hook support
into existing core flows:
- `core/src/session/session.rs` conditionally pulls effective plugin
hook sources and plugin hook load warnings from `PluginsManager` when
`plugin_hooks` is enabled, then passes them into `HooksConfig`.
- `core/src/hook_runtime.rs` adds the `plugin` metric tag for
`HookSource::Plugin`.
- `core/config.schema.json` picks up the new `plugin_hooks` feature
flag, and `core/src/plugins/manager_tests.rs` updates fixtures for the
added plugin hook fields.
---------
Co-authored-by: Codex <noreply@openai.com>
Load plugin manifests through a shared discoverable-path helper so
manifest reads, installs, and skill names all see the same alternate
manifest location.
## Summary
- move skill loading and management into codex-core-skills
- leave codex-core with the thin integration layer and shared wiring
## Testing
- CI
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
- extract plugin identifiers and load-outcome types into codex-plugin
- update codex-core to consume the new plugin crate
## Testing
- CI
---------
Co-authored-by: Codex <noreply@openai.com>