Resumed threads keep stale model-visible skills after new skills are added

Open 💬 2 comments Opened Apr 2, 2026 by isCopyman

What variant of Codex are you using?

CLI / TUI (also likely affects other app-server-backed resume paths)

What issue are you seeing?

Resumed threads keep a stale model-visible skills list after new skills are added.

More specifically:

  • a newly added skill can show up in the UI skill list
  • explicit $skill-name invocation can still work
  • but after codex resume <thread-id>, the model-visible ## Skills context remains stuck on the older snapshot
  • asking the resumed thread what skills are available omits the newly added skill
  • implicit skill triggering does not pick up the new skill either

This makes it look like skill hot reload only updates the display/explicit invocation layer, but not the developer-context skill section that the model actually sees on resumed threads.

What steps can reproduce the bug?

One reliable repro is:

  1. Start a thread with some initial skill set.
  2. Add a new skill afterward (either a custom skill or a plugin-provided skill).
  3. Confirm the new skill is discoverable in a fresh session.
  4. Resume the old thread with codex resume <thread-id>.
  5. Ask the resumed thread what skills are available, or rely on implicit skill triggering.

Observed result:

  • the resumed thread still reports the old skill set
  • the new skill is missing from the model-visible skill inventory
  • explicit $new-skill can still work
  • reinstalling/reloading the plugin does not fix the already-existing resumed thread

What is the expected behavior?

When a thread is resumed, the model-visible skill inventory should reflect the current discovered skills, not the stale set from when the thread was originally created.

At minimum, resumed threads should re-inject the ## Skills developer section whenever the available skills have changed.

Additional information

This appears to be a deeper issue than the previously closed skill hot-reload reports.

  • #12227 says live detection of new/updated skills was implemented
  • #10478 did add watcher-driven skill updates
  • however, the current behavior still appears broken specifically for resumed old threads

Relevant code path analysis:

  1. Each turn does recompute skills:
  • core/src/codex.rs
  • plugins_manager.plugins_for_config(&per_turn_config)
  • skills_load_input_from_config(&per_turn_config, effective_skill_roots)
  • skills_manager.skills_for_config(&skills_input)
  1. But model-visible skills are rendered only through build_initial_context():
  • core/src/codex.rs around build_initial_context
  • turn_context.turn_skills.outcome.allowed_skills_for_implicit_invocation()
  • render_skills_section(&implicit_skills)
  1. In the normal runtime path, record_context_updates_and_set_reference_context_item() does this:
let should_inject_full_context = reference_context_item.is_none();
let context_items = if should_inject_full_context {
    self.build_initial_context(turn_context).await
} else {
    self.build_settings_update_items(reference_context_item.as_ref(), turn_context).await
};

So if a resumed thread reconstructs a non-None reference_context_item, it skips build_initial_context() and only emits settings diffs.

  1. Meanwhile SkillsUpdateAvailable in the TUI appears to refresh the UI-side list only:
  • tui/src/chatwidget.rs
  • on EventMsg::SkillsUpdateAvailable it calls AppCommand::list_skills(..., force_reload = true)
  • explicit $skill then works because it goes through UserInput::Skill

That would explain the observed split:

  • UI skill list updates
  • explicit $skill works
  • resumed thread's developer ## Skills context stays stale
  • implicit invocation does not see the new skill

This seems related to, but stronger/more specific than, #14785.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗