Explicit-only skills need a visible, reliable explicit invocation contract
What version of Codex is running?
Current main after policy.allow_implicit_invocation and explicit skill
mentions landed.
What issue are you seeing?
policy.allow_implicit_invocation: false provides the important policy split
requested in #10585: a skill can be enabled without being eligible for broad
implicit task matching. That part appears to be in place.
The remaining gap is the end-to-end contract for explicit-only skills. Today,
explicit-only skills can be excluded from broad task matching, but they are not
discoverable or invokable enough to be reliable explicit targets.
Current upstream already supports useful substrate:
- structured
UserInput::Skillselections - linked skill paths
$skilltoken mentions- parsing
policy.allow_implicit_invocation
But the remaining behavior still looks split:
- the model-visible skill list is built from implicit-eligible skills, so an
explicit-only skill can be omitted from the visible instructions
- plain-text skill names are not explicit mentions unless the user uses the
$skill form or a structured selection
- namespaced plugin skills are hard to invoke naturally because users and other
model-visible instructions often refer to the base skill name, while the
installed skill name may be namespace-qualified
- plugin names have a similar issue: linked
plugin://...mentions work, but
plain-text plugin config/display names are not treated as explicit plugin
mentions
The result is that an explicit-only skill can be enabled and intentionally
excluded from automatic routing, while still being unreliable as an explicit
target in normal user text or model-visible routing instructions.
Minimal repro shape
- Define an enabled skill with
policy.allow_implicit_invocation: false. - Start a fresh Codex session where the skill is available.
- Ask for the skill by normal explicit text, for example
use review-helper
rather than a structured skill chip or $review-helper.
- For a plugin-contributed skill, use a base name such as
review-helperwhile
the installed skill name is namespace-qualified, for example
example-plugin:review-helper.
Expected: the skill remains excluded from broad implicit task matching, but is
visible as an explicit-only skill and can be selected by an unambiguous explicit
plain-text name or base-name reference.
Observed: the skill can be absent from the visible skill list and the plain-text
name/base-name reference does not reliably resolve to a skill injection.
Expected behavior
Explicit-only skills should remain excluded from broad implicit matching, but
should still be reliable explicit targets.
A workable contract would be:
- render explicit-only skills in a separate model-visible section with narrower
trigger guidance
- keep them out of automatic task matching
- allow explicit matching by structured selection, fully qualified name, and
unambiguous plain-text or plugin base-name references where safe
- preserve ambiguity checks so unrelated skills or connectors are not selected
by accident
Why this is separate from #10585
#10585 asked for the policy switch. That part appears to be partially
implemented now.
This issue is about the follow-through contract after that switch exists:
explicit-only skills need to be visible and invokable enough to function as
explicit tools without re-opening broad implicit routing.
Implementation direction
I would avoid replacing the current budgeted/aliased skill rendering design.
Instead, extend that surface so explicit-only skills can be rendered separately
from implicit-eligible skills, with separate trigger text.
For matching, keep the existing structured and linked path behavior, then add
safe plain-text matching for unambiguous skill names and namespaced plugin skill
base names.
The important constraint is that this should not re-enable broad implicit
matching. It should only make explicit-only skills discoverable and invokable
when the user or another active instruction names them directly.
3 Comments
There is a reliable, visible invocation contract. It requires the
$<skill-name>syntax. Just mentioning a skill by name without using this explicit syntax will not invoke a skill it if it is markedpolicy.allow_implicit_invocation: false.Thanks, that clarification helped a lot.
I had been treating unadorned skill names / fully qualified skill IDs as a possible explicit invocation path. Your comment made the intended contract much clearer: explicit skill invocation should use the
$<skill-name>form, a structured skill selection, or the corresponding plugin/skill picker path.I adjusted my local plugin and validation harness around that contract. The split that is working now is:
$<skill-name>or, for plugin-bundled skills,$<plugin-name>:<skill-name><skill-name>or<plugin-name>:<skill-name>without$in route blocks, transcripts, andActivated skillsWith that change, a fresh-session smoke passes for an explicit-only plugin skill, and I am no longer relying on bare-name or plugin base-name matching as the invocation mechanism.
So I think this resolves the bare-name part of my report. The remaining feedback is probably documentation/UX rather than a runtime bug: since
$...is the intended explicit-only contract, it would be useful for the skills/plugins docs to make that explicit forallow_implicit_invocation: false, including the namespaced plugin skill shape like$<plugin-name>:<skill-name>where applicable.Thanks again. This unblocked the local fix and helped narrow the issue substantially.
@etraut-openai That used to work, but it is broken now. Please see https://github.com/openai/codex/issues/23454