Local Plugin UI Containment Bug in Codex App
What version of the Codex App are you using (From “About Codex” dialog)?
Version 26.325.31654 (1272)
What subscription do you have?
Pro
What platform is your computer?
Darwin 24.6.0 arm64 arm
What issue are you seeing?
A local skill-bearing plugin installs cleanly and the CLI/runtime surface is
namespaced correctly, but the Codex Electron app still exposes plugin-owned
skills as directly invokable items in the picker and Skills UI. The same app
also renders local marketplace metadata differently from the curatedBuild iOS Apps plugin.
The core issue is that local plugin-owned skills are not visually contained
behind the plugin entrypoint in Electron, even though the runtime itself is
cleanly namespaced.
What is already proven clean:
- plugin-managed runtime install is proven through installed cache, enabled
config state, and fresh-session visibility
- local plugin source is a materialized directory at
~/plugins/apple-appdev-workflow
- plugin payload no longer ships
node_modules/ - CLI skill listing is namespaced and clean, for example:
apple-appdev-workflow:apple-product-surface-orchestratorapple-appdev-workflow:apple-persistence-orchestratorapple-appdev-workflow:apple-swiftdata-review
- explicit plugin-entry controls are green for both product-surface and
persistence brigades
Remaining Electron-only defects:
- plugin-owned skills still surface outside the plugin entrypoint in
picker/Skills UI
- plugin card metadata uses local marketplace
namein the Category row
instead of marketplace interface.displayName
- local plugin presentation differs from curated plugin presentation even when
plugin-level and skill-level metadata are aligned closely with
Build iOS Apps
Exact text error encountered during local marketplace metadata testing:
Failed to load plugin
invalid marketplace name: only ASCII letters, digits, '_', and '-' are allowed
Exact prompt examples used during validation:
Use [@apple-appdev-workflow](plugin://apple-appdev-workflow@LocalAppleWorkflow) to review the SwiftData model and migration approach for saved reference captures, and recommend what should change first to reduce rollout and data-loss risk.
Review the SwiftData model and migration approach for saved reference captures, and recommend what should change first to reduce rollout and data-loss risk.
Use apple-product-surface-orchestrator to review this SwiftUI app's main product surface and recommend what should change first so the navigation, empty states, and on-screen copy feel more coherent and production-ready.
Representative CLI validation commands:
python3 ./scripts/probe_plugin_managed_runtime.py --repo-root .
codex exec -C /tmp --skip-git-repo-check "List the available plugins in this session by name only."
codex exec -C /tmp --skip-git-repo-check "List the available skills in this session by name only."
Representative clean runtime conclusion:
plugin-managed runtime is proven for the target plugin through installed cache, enabled config state, and fresh-session visibility
Electron-specific text clues observed in app-side logging / Sentry scope:
Skills/list missing short_description ...
method=plugin/read
Why this appears platform-side rather than bundle-side:
- first-party
Build iOS Appsis also a real skills plugin - its installed cache includes
skills/and skill-levelagents/openai.yaml - CLI exposes those skills as namespaced runtime skills
- the local plugin runtime is also namespaced cleanly in CLI
- the exposed/duplicate behavior persists primarily in Electron UI surfaces
rather than in the runtime listing itself
What steps can reproduce the bug?
- Create or use a local plugin marketplace with a local skill-bearing plugin.
In my case:
- marketplace name:
LocalAppleWorkflow - marketplace display name:
Local Apple Workflow - plugin id:
apple-appdev-workflow
- Install the plugin from the local marketplace in the Codex Electron app.
- Confirm the runtime itself is healthy:
- run
python3 ./scripts/probe_plugin_managed_runtime.py --repo-root . - confirm the probe concludes:
plugin-managed runtime is proven for the target plugin through installed cache, enabled config state, and fresh-session visibility
- Confirm CLI runtime namespacing is clean:
- run
codex exec -C /tmp --skip-git-repo-check "List the available plugins in this session by name only." - run
codex exec -C /tmp --skip-git-repo-check "List the available skills in this session by name only." - confirm only namespaced skill entries appear for the local plugin
- Open the plugin card, picker, and Skills tab in the Electron app.
- Observe that plugin-owned skills are exposed as directly invokable UI items
outside the plugin entrypoint, and that the plugin card Category row uses the
marketplace slug-style name rather than the human-facing marketplace
interface.displayName.
What is the expected behavior?
For local skill-bearing plugins, Electron should either:
- preserve plugin containment rules consistently with curated plugins
- or document that local skill-bearing plugins are intentionally exposed as
standalone picker/Skills items
Expected metadata behavior:
- human-facing marketplace labels should use marketplace
interface.displayName
- slug-style marketplace
nameshould remain an internal identifier and not
leak into the plugin card Category row
Instead, I see:
- The local plugin runtime is clean in CLI, but Electron still exposes
plugin-owned skills as standalone invokable items in picker/Skills UI.
- The plugin card Category row uses the marketplace slug-style identifier
instead of the human-facing display name.
- Curated
Build iOS Appsis presented differently in Electron despite both
plugins being real skill-bearing plugins with runtime-visible skills/.
Representative local plugin runtime entries:
apple-appdev-workflow:apple-product-surface-orchestrator
apple-appdev-workflow:apple-persistence-orchestrator
apple-appdev-workflow:apple-swiftdata-review
Representative curated plugin cache root:
~/.codex/plugins/cache/openai-curated/build-ios-apps/...
Additional information
PII-redacted path examples:
/Users/<redacted>/.codex/plugins/cache/LocalAppleWorkflow/apple-appdev-workflow/local/skills/apple-product-surface-orchestrator/SKILL.md
/Users/<redacted>/.codex/plugins/cache/openai-curated/build-ios-apps/...
The local plugin and curated plugin both have:
skills/- plugin-level
agents/openai.yaml - skill-level
agents/openai.yaml
I did not find an explicit allow_implicit_invocation: false in the curated
plugin files I inspected, so the different Electron presentation does not look
like a simple manifest-field difference.
4 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I have a concrete runtime-level follow-up for this issue.
During local-plugin validation, I found that the containment problem is not only UI/state. There is also a runtime aliasing seam that makes plugin-owned skills usable through bare skill names even after they were loaded with a canonical plugin namespace.
What I found:
plugin_id:skill_id.apple-release-orchestratorinstead of onlyapple-appdev-workflow:apple-release-orchestratoror a structured chip/skill input.Why this matters for
#16663:The relevant runtime shape is:
That seems like the wrong default for plugin-contributed skills.
The narrow fix shape that worked well locally was:
$plugin:skilltext mentions workingImportant nuance: this is separate from
allow_implicit_invocation. We validated that the policy flag is not the right containment lever here. The real seam is the later plain-text alias matching.I think this issue is the right home for that observation because it explains why local plugin skills can escape strict plugin-managed routing even when the UI/plugin surface itself is the thing under investigation.
I can reproduce a closely related UI/source attribution issue on macOS.
In my Codex Desktop Skills UI, skills that are clearly provided by plugins are displayed under the Chinese label
个人/ Personal. That label reads as if the skill is user-created or locally installed, but the on-disk provenance shows otherwise.Environment observed:
codex-cli 0.128.0Darwin 25.4.0 arm64/ macOS 26.4.1On-disk skill provenance:
~/.codex/skills/*/SKILL.md— 24 observed~/.codex/skills/.system/*/SKILL.md— 5 observed~/.codex/plugins/cache/.../skills/*/SKILL.md— 58 observedExamples of plugin-owned skills that are nevertheless shown as Personal in the UI:
~/.codex/plugins/cache/openai-curated/build-web-apps/.../skills/frontend-app-builder/SKILL.md~/.codex/plugins/cache/openai-curated/github/.../skills/github/SKILL.md~/.codex/plugins/cache/openai-curated/superpowers/.../skills/using-superpowers/SKILL.md~/.codex/plugins/cache/openai-bundled/browser-use/.../skills/browser/SKILL.mdThe plugin-level metadata exists in
.codex-plugin/plugin.jsonand includes fields such asname,author,interface.displayName, andinterface.developerName. However, individualSKILL.mdfrontmatter only hasnameanddescription, so the app appears to flatten all available skills into the user's personal skill bucket.Expected behavior:
Personal / local,System, andPlugin: <plugin display name>.Observed behavior:
个人/ Personal is applied to plugin-provided skills, which makes source auditing confusing when dozens of skills are installed.This looks like the same broader containment/provenance problem described here, but specifically for the skill source label shown in Codex Desktop.
Current structured-provenance follow-up for this issue:
I tested a local app-server/runtime experiment that goes beyond the older flat
pluginIdapproach. The experiment adds structured skill provenance plus compact plugin metadata to theskills/listpayload.What the app-server returned in direct and packaged probes:
apple-appdev-workflowskillsprovenance = custom_marketplaceplugin.id = apple-appdev-workflow@LocalAppleWorkflowI then tested that patched app-server inside disposable Electron app bundles copied from newer hosts:
26.513.31313/ Electron42.0.126.519.31651/ Electron42.1.0For both disposable candidates:
skills/listprovenance probe passedResult: the Electron Skills UI still grouped those plugin-owned Apple workflow skills under
Personalon the latest tested host, even though the app-server wire payload contained enough plugin provenance to distinguish them from ordinary user skills.This narrows the remaining issue from “the app-server lacks plugin attribution” to “the Electron Skills page does not consume plugin provenance/plugin metadata for grouping or filtering.”
A source-level fix shape that would address the observed behavior:
skills/listentries.skill.plugin/skill.provenanceor equivalent plugin-owned metadata before falling back toscope = user.Personal; either keep them behind the plugin entrypoint or show them under a plugin-owned section labeled with the plugin display name.This also matches the separate community repro above where plugin cache paths clearly show plugin-owned skills but the desktop UI labels them as Personal.