Skill read labels lose skill names for symlink and alias paths

Open 💬 0 comments Opened Jul 18, 2026 by ignatremizov

What version of Codex CLI is running?

Current upstream main at b8b61bc692517adcd18622df260f2ddd80635122 contains the affected exact-path annotation logic.

The behavior was observed and locally validated in a downstream 0.145.0 build based on the same upstream TUI implementation.

What subscription do you have?

Not subscription-specific.

Which model were you using?

Not model-specific. The observed reproduction used GPT-5.6 Luna, but the issue is in TUI rendering of parsed command metadata.

What platform is your computer?

Linux. The path-identity issue is not inherently Linux-specific; symlink or alternate-path aliases can occur on other supported platforms.

What terminal emulator and version are you using (if applicable)?

Not terminal-specific.

Codex doctor report

Not applicable. Skill loading and command execution succeed; only the TUI activity label loses the known skill name.

What issue are you seeing?

The fix for #16303 / #16813 annotates SKILL.md reads only when the parsed command path is byte-for-byte equal to a path in ChatWidget.skills_all.

If the same known skill is read through a symlink or another equivalent path alias, the lookup misses and the TUI falls back to the generic label:

• Explored
  └ Read SKILL.md

The skill is still loaded and read successfully. Only its semantic name is lost from the activity display.

A second fragile case is visible in the same function: if the asynchronous skills list has not populated skills_all yet, annotation returns immediately. An early skill read can therefore be rendered without a name even though its metadata becomes available later.

What steps can reproduce the bug?

  1. Make a user skill available through a symlinked skill directory, for example:

``text
~/.agents/skills/frontend-coding
-> /home/user/code/skills/frontend-coding
``

  1. Ensure Codex has loaded the skill with its frontmatter name frontend-coding.
  2. Have the agent read the discovered alias path:

``sh
cat ~/.agents/skills/frontend-coding/SKILL.md
``

  1. Observe the completed command in the TUI.
  2. The activity is rendered as Read SKILL.md rather than including frontend-coding.

In the locally captured failing rollout, the actual command was:

cat /home/ignat/.agents/skills/frontend-coding/SKILL.md

The symlink resolves to:

/home/ignat/code/skills/skills/frontend-coding/SKILL.md

What is the expected behavior?

Equivalent paths for a known SKILL.md should resolve to the loaded skill's authoritative frontmatter name:

• Explored
  └ Read SKILL.md (frontend-coding skill)

This should also remain reliable when the command starts before the initial skills-list response completes.

Directory names should not replace authoritative metadata when the two differ. A directory-derived name is useful only as a last-resort display fallback.

Additional information

Related issue and fix:

  • Original report: #16303
  • Original exact-path annotation fix: #16813

No separate open report for symlink/alias path misses was found before filing this follow-up.

Local validation:

  • Before: a real TUI run reading /home/ignat/.agents/skills/frontend-coding/SKILL.md rendered Read SKILL.md.
  • After: downstream commit https://github.com/ignatremizov/codex/commit/3f8017a12d208949ba92104718deedb0d83b9b88 rendered Read SKILL.md (frontend-coding skill) for the same symlink path.
  • The patched tree passed full workspace format, Clippy, and nextest verification, then a fresh installed 0.145.0 Linux binary reproduced the corrected TUI output.

Source analysis

The current TUI returns without annotation while skills_all is empty, then compares read paths using exact equality:

https://github.com/openai/codex/blob/b8b61bc692517adcd18622df260f2ddd80635122/codex-rs/tui/src/chatwidget/skills.rs#L160-L187

Skill loading canonicalizes roots for identity:

https://github.com/openai/codex/blob/b8b61bc692517adcd18622df260f2ddd80635122/codex-rs/core-skills/src/loader.rs#L522-L531

The resulting canonical path is retained in SkillMetadata:

https://github.com/openai/codex/blob/b8b61bc692517adcd18622df260f2ddd80635122/codex-rs/core-skills/src/loader.rs#L697-L707

That makes a valid alias path from ParsedCommand::Read fail the exact comparison even though both paths identify the same skill document.

Implementation / fix considerations

An upstream fix should preserve frontmatter metadata as authoritative while making path matching alias-aware. Possible approaches include:

  1. Build the TUI lookup from normalized/canonical skill identities plus known discovered aliases when the skills list is ingested.
  2. Normalize a parsed SKILL.md read path before lookup without adding blocking filesystem work to rendering.
  3. Re-annotate pending/running reads when skill metadata arrives, so startup ordering does not permanently lose the label.
  4. Use the parent directory only as a bounded last-resort display fallback when authoritative metadata is unavailable.

Acceptance criteria

  • Reading a known skill through its canonical path includes the skill name.
  • Reading the same skill through a symlink/discovered alias includes the same skill name.
  • A frontmatter name that differs from the directory name remains authoritative whenever metadata is available.
  • A skill read that begins before the initial skills-list response is not permanently rendered as generic Read SKILL.md.
  • Ordinary non-skill file-read labels remain unchanged.
  • TUI snapshot or rendering coverage includes canonical, symlink/alias, and metadata-not-yet-loaded cases.

View original on GitHub ↗