allow_implicit_invocation: false also disables explicit $skill invocation, contradicting docs
Open 💬 1 comment Opened Aug 25, 2026 by keybrdist
Docs (https://developers.openai.com/codex/skills → learn.chatgpt.com/docs/build-skills) state that with agents/openai.yaml:
policy:
allow_implicit_invocation: false
"the skill is not injected into the model context by default, but can still be invoked explicitly via $skill."
Measured on codex-cli 0.149.0 (macOS), the second half doesn't hold: $skill mentions of a policy-restricted skill are not expanded.
Repro:
- Create
~/.codex/skills/test-policy/SKILL.md(name/description frontmatter, body markerPOLICY-BODY) andagents/openai.yamlwith the policy above. - Create
~/.codex/skills/test-plain/SKILL.mdidentically, noopenai.yaml. - Run
codex debug prompt-input '$test-plain go'→ skill body is injected. - Run
codex debug prompt-input '$test-policy go'→ prompt contains only the literal text; no expansion, and the skill is absent from<skills_instructions>.
Same behavior through codex app-server threads. So a skill with the policy has no invocation path at all in non-TUI surfaces.
Expected: either $skill works for policy-restricted skills (docs behavior), or the docs state that allow_implicit_invocation: false disables explicit invocation outside the TUI picker.
1 Comment
Thanks for the detailed repro — I traced the full skill-invocation pipeline on current main and ran a verification test. Here's what I found:
1.
codex debug prompt-inputcannot demonstrate explicit$skillinvocation — it doesn't run the turn pipeline.build_prompt_input_from_session(codex-rs/core/src/prompt_debug.rs:88) explicitly notes: "Prompt debugging builds a standalone request without entering run_turn." The explicit-invocation machinery (build_skills_and_plugins→collect_explicit_skill_mentions→load_skill_prompts) only runs insiderun_turn(turn.rs:808–826). So indebug prompt-input, no host skill body is ever injected — visible or policy-restricted.What you observed for
test-plainvstest-policyis the<skills_instructions>catalog listing (name/description lines, not skill bodies):test-plainis model-visible so it appears;test-policyhasallow_implicit_invocation: falseso it's hidden from that list. That asymmetry is exactly the documented behavior — the docs' "not injected by default" half working as intended.2. On the real turn path, explicit
$skillinvocation of policy-restricted host skills does work (current main,c51e7b3736):catalog_entry_from_skill(ext/skills/src/provider/host.rs:141–146):!enabled→.disabled();!allows_implicit_invocation()→.hidden_from_prompt()— which only clearsprompt_visible, keepingenabled = true. Being "disabled" comes exclusively from user config rules (resolve_disabled_paths, config/src/skills_config.rs:94–124), never from the policy.enabled: ext selection.rs:72/89 and skills selection.rs:142/165 (is_skill_enabled= not indisabled_paths). Hidden skills are still selected by$name/skill://paths.load_skill_prompts(ext/skills/src/host_prompt.rs:56–96) has no visibility gate — it reads and injects whatever was selected.I verified this empirically with a quick unit test on current main (policy-restricted host skill →
enabledstays true,is_model_visible()false, andcollect_explicit_skill_mentionsselects it from$name): passes. This is the same design #36976 (Honor explicit-only orchestrator skills) established for orchestrator skills — hidden from the prompt catalog, still directly invocable.3. The genuine gap: the host path has no test coverage for this case.
#36976 added
explicit_only_orchestrator_skill_is_hidden_but_can_be_invoked(core/tests/suite/skills_extension.rs:821), and the executor path is covered too — but there is no equivalent host-side integration test. That's worth adding to lock in the documented behavior and catch regressions.4. Version note: 0.149.0 → 0.149.1 is only 5 commits (none touching skills), and the host-skill architecture you hit (#36309 world-state catalog, #37503 prompt injection move) predates 0.149.0 — so the code paths here behave the same on your version.
If you can reproduce non-expansion via the app-server v2 thread / TUI (the real
run_turnpath) on 0.149.x, that would point to a real bug — happy to dig in. Otherwise, my suggestion would be: add the missing host explicit-only integration test (I can prepare that PR), and optionally note in thecodex debug prompt-inputhelp text that it doesn't exercise turn-time skill/plugin injection.