Resumed threads keep stale model-visible skills after new skills are added
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-nameinvocation can still work - but after
codex resume <thread-id>, the model-visible## Skillscontext 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:
- Start a thread with some initial skill set.
- Add a new skill afterward (either a custom skill or a plugin-provided skill).
- Confirm the new skill is discoverable in a fresh session.
- Resume the old thread with
codex resume <thread-id>. - 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-skillcan 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:
- Each turn does recompute skills:
core/src/codex.rsplugins_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)
- But model-visible skills are rendered only through
build_initial_context():
core/src/codex.rsaroundbuild_initial_contextturn_context.turn_skills.outcome.allowed_skills_for_implicit_invocation()render_skills_section(&implicit_skills)
- 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.
- Meanwhile
SkillsUpdateAvailablein the TUI appears to refresh the UI-side list only:
tui/src/chatwidget.rs- on
EventMsg::SkillsUpdateAvailableit callsAppCommand::list_skills(..., force_reload = true) - explicit
$skillthen works because it goes throughUserInput::Skill
That would explain the observed split:
- UI skill list updates
- explicit
$skillworks - resumed thread's developer
## Skillscontext stays stale - implicit invocation does not see the new skill
This seems related to, but stronger/more specific than, #14785.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I reviewed the two potential duplicates from the bot, and they look related but not identical.
/resume/codex resume.$skillinvocation have refreshed.So there may be a shared resume/context-refresh root cause across all three issues, but this one is not just "config not loaded on resume" or "MCP disappears on resume".
The distinguishing symptom here is:
## Skillscontext$skillcan still workThat split suggests the skill inventory visible to the model is not being re-injected for resumed threads, even though UI-side discovery has updated.
Confirming that this is an operational blocker for a multi-host setup with custom skills and plugin-provided skills.
Environment:
The confusing part is not only that an old/resumed thread sees a stale model-visible skill inventory. There is also no clear lifecycle handoff UX. Operators need to preserve a healthy active thread, but they must still be able to start an independent thread bound to the newly discovered skill/plugin generation immediately.
Requested diagnostics and recovery:
current,refreshable-next-turn, ornew-thread-required;Without these signals, account rotation and runtime updates become entangled, leading to repeated turns spent diagnosing whether the issue is auth, config, discovery, or stale thread context. This is especially costly when several hosts are expected to converge on the same reviewed declarative configuration.