Disabled repository skill is re-enabled in Codex-generated worktrees due to absolute-path identity
Codex version
Codex Desktop 26.721.31836 on macOS.
What issue are you seeing?
A repository skill disabled in Settings → Skills is still advertised to the model as available—and can be automatically invoked—when a task runs in a Codex-generated Git worktree.
The disable setting is stored against the skill’s absolute path in the original checkout:
[[skills.config]]
path = "/Users/<user>/Documents/GitHub/<repo>/.agents/skills/<skill>/SKILL.md"
enabled = false
The task runs from a generated worktree and discovers the same repository skill at a different absolute path:
/Users/<user>/.codex/worktrees/<id>/<repo>/.agents/skills/<skill>/SKILL.md
The task context then includes that worktree copy in Available skills and instructs the model to invoke it when the description matches. In the observed case, the model read the disabled skill before the user noticed and interrupted the task.
The Settings UI continued to show the skill as disabled throughout.
Steps to reproduce
- In a repository, add a skill at
.agents/skills/example/SKILL.md. - Open the repository in Codex Desktop and disable the skill in Settings → Skills.
- Confirm that
~/.codex/config.tomlcontains a[[skills.config]]entry with the original checkout’s absolute path andenabled = false. - Start a task that runs in a Codex-generated worktree for that repository.
- Ask for work matching the disabled skill’s description.
- Observe that the task’s generated
Available skillscontext contains the worktree path and the model treats the skill as enabled.
Expected behavior
Disabling a repository skill should apply to that logical skill across the original checkout and all Codex-generated worktrees. A disabled skill should not be included in the model’s available-skills context or invoked implicitly.
Skill enablement identity should be repository-relative or otherwise normalized across worktrees, rather than keyed only by the discovered absolute filesystem path.
Actual behavior
The worktree copy is treated as a separate enabled skill because its absolute path differs from the path stored by the Settings toggle.
Impact
This violates an explicit user safety/control setting: users can disable a workflow intentionally, yet matching tasks may still load and follow it without explicit invocation.
1 Comment
I traced this on current
main, and the report matches the config path end to end:skills_config_write_response_innerturns the Settings path toggle intoConfigEdit::SetSkillConfig { path, enabled }.skill_config_rules_from_stackcanonicalizes that into aSkillConfigRuleSelector::Path.resolve_disabled_skill_pathsonly inserts/removes the exact discoveredAbsolutePathBuf.That means the linked-worktree copy cannot match the original checkout entry even though it is the same logical repository skill. Falling back to the existing name selector would be risky because it is global and could disable an unrelated same-named skill.
A narrow direction would be to give repository skills an identity based on the canonical main Git project plus the path relative to the current worktree root, while retaining absolute-path identity for user/system skills. There is already worktree-aware normalization in
resolve_root_git_project_for_trustthat may be reusable conceptually. I would cover it with a real linked-worktree regression test, plus a same-named skill in another repository to guard against overmatching.I am happy to prepare the focused fix and tests if the team would like to invite a PR under the contribution policy.