Skip to content

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

Open

Description

@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.

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)
  2. 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)
  3. 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.

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    TUIIssues related to the terminal user interface: text input, menus and dialogs, and terminal display
    on Apr 2, 2026
  2. github-actions commented on Apr 2, 2026

    @github-actions
    Contributor
  3. isCopyman commented on Apr 2, 2026

    @isCopyman
    Author

    I reviewed the two potential duplicates from the bot, and they look related but not identical.

    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:

    • fresh session sees the new skill
    • resumed old thread does not expose it in the developer ## Skills context
    • explicit $skill can still work

    That split suggests the skill inventory visible to the model is not being re-injected for resumed threads, even though UI-side discovery has updated.

  4. added
    skillsIssues related to skills
    and removed
    TUIIssues related to the terminal user interface: text input, menus and dialogs, and terminal display
    CLIIssues related to the Codex CLI
    on Apr 2, 2026
  5. jyongchul commented on Aug 17, 2026

    @jyongchul

    Confirming that this is an operational blocker for a multi-host setup with custom skills and plugin-provided skills.

    Environment:

    • Codex Windows app 26.810.7004.0
    • bundled codex-cli 0.148.0-alpha.9
    • Windows 10.0.19045
    • additional Linux CLI workers

    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:

    • expose the loaded skill/plugin/MCP generation for each thread;
    • expose the currently discovered generation;
    • return a typed state such as current, refreshable-next-turn, or new-thread-required;
    • provide a supported “handoff to a new generation” action that carries a sanitized task checkpoint without copying auth/session databases;
    • never hot-rewrite a running thread, but do not let the existence of that thread block creation of a new lifecycle;
    • after resume, either re-inject the current allowed skill inventory or clearly report that the thread is intentionally pinned.

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingskillsIssues related to skills

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions