Explicit-only skill activation is lost after compaction in long-running tasks

Open 💬 4 comments Opened Jul 10, 2026 by ignatremizov
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

codex-cli 0.144.0

Also visible from the current skills extension and compaction/context reconstruction paths on main.

What subscription do you have?

Not subscription-specific. Observed on Pro 20x.

Which model were you using?

Observed with GPT-5.5 and GPT-5.6-style model configurations.

This appears to be host/runtime state handling rather than model-specific behavior.

What platform is your computer?

Linux 6.17.0-35-generic x86_64

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

Ghostty (TERM=xterm-ghostty), no tmux/zellij.

Codex doctor report

Not applicable. The problem is visible in the skill activation/context reconstruction lifecycle.

What issue are you seeing?

An explicitly selected skill is injected for the current turn, but its activation is not durable across compaction/context reconstruction in a long-running task.

This is most visible for explicit-only skills whose agents/openai.yaml contains:

policy:
  allow_implicit_invocation: false

The user explicitly selects the skill through $<skill-name> and Codex initially reads and follows its SKILL.md. As the task runs through multiple compaction/context-reconstruction rounds, the selected workflow is reduced to whatever the compaction summaries happen to preserve instead of being rehydrated as active host-owned state.

Compaction can preserve a short instruction verbatim, so the failure is not reliably demonstrated by a one-line marker. It becomes reproducible with a substantive multi-step workflow whose skill promotes the main agent into a supervisor/validator role:

  • initially, the parent sequences phases, delegates bounded implementation, and validates child work;
  • after later compactions, the promotion/role contract disappears;
  • the parent silently becomes the executor and performs implementation itself;
  • it may say it no longer has an explicit delegation instruction or that the explicit-only skill is unavailable/not installed.

Because explicit-only skills are intentionally omitted from the implicit Available skills catalog, the model cannot recover the selected workflow through implicit routing.

The installed/configured state and the selected/active state are being conflated. A skill can remain installed and resolvable by the host while its explicit activation receipt disappears from model context.

This is especially disruptive for skills that promote the main session into a durable workflow role, such as a supervisor, reviewer, audit loop, or other multi-turn process. Repeating the skill token manually after every compaction is not reliable for autonomous or long-running work.

What steps can reproduce the bug?

  1. Create an explicit-only supervisor skill:

``yaml
# agents/openai.yaml
policy:
allow_implicit_invocation: false
``

  1. Give its SKILL.md a substantive workflow rather than a short marker. For example:

```markdown
# Supervisor workflow

The main agent is promoted to supervisor and validator for the duration of
the selected task.

  • Read the complete referenced plan before starting.
  • Preserve its phase order and completion gates.
  • Do not implement production changes in the parent.
  • Delegate each bounded implementation phase to a coder with a disjoint

write scope.

  • Validate each coder's diff and tests before advancing.
  • Use separate reviewers for cross-phase and final validation.
  • Keep this supervisor role until every phase and final gate is complete.

```

  1. Create a realistic plan document with at least four implementation phases, for example (pseudo-plan):

```markdown
# Plan

  1. Refactor the data model and migration.
  2. Update service/business logic.
  3. Update API contracts and clients.
  4. Add integration tests and compatibility coverage.
  5. Run a separate final review and fix validated findings.

```

Each phase should contain enough concrete work, validation, and tool output to make the session cross several automatic compaction boundaries.

  1. Start the task by explicitly selecting the skill:

``text
$supervisor-test execute docs/plan.md through completion
``

  1. Confirm the initial behavior:
  • Codex reads the complete SKILL.md and plan;
  • the parent announces/uses the supervisor role;
  • implementation is delegated;
  • the parent validates child work rather than editing production code itself.
  1. Allow the workflow to continue through several phases and multiple automatic compactions/context reconstructions.
  2. Observe behavior after a later compaction:
  • the parent starts implementing the next phase directly;
  • it no longer applies the skill's delegation and validation gates;
  • it does not reread the selected SKILL.md;
  • when challenged, it may say there is no current explicit instruction to remain a supervisor/use subagents, or may assume the explicit-only skill is unavailable.
  1. Repeat $supervisor-test.
  2. Observe that Codex reloads the still-installed skill, recognizes the lost promotion, and resumes the supervisor workflow.

The important signal is the role transition from supervisor/validator to executor after context reconstruction, not whether a short phrase survives in a summary.

What is the expected behavior?

Explicit skill selection should create a host-owned activation receipt that survives context reconstruction for the scope of the active task.

At minimum:

  • compaction during the same logical task must not remove a selected skill;
  • the complete selected SKILL.md must be rehydrated after compaction before work continues;
  • a supervisor/reviewer/other promoted role must remain active until its defined scope ends;
  • explicit-only skills must not be treated as missing merely because they are absent from the implicit catalog;
  • activation should be removed only at a defined scope boundary or by an explicit deactivate action.

A useful scope model would be:

  • normal implicit routing: turn-scoped, as today;
  • explicit $skill selection: active-task or session-scoped;
  • skill selected by an active /goal: goal-scoped;
  • explicit /skill drop <name> or equivalent: deactivate it.

The exact UX can vary, but compaction should not be an accidental skill deactivation or role-demotion boundary. Summary quality should not determine whether an explicit workflow remains active.

Additional information

Current source shape:

  • codex-rs/ext/skills/src/extension.rs
  • TurnInputContributor::contribute calls collect_explicit_skill_mentions(&input.user_input, &catalog);
  • the selected skill body is injected into that turn;
  • selection is written to SkillsTurnState.
  • codex-rs/ext/skills/src/state.rs
  • SkillsThreadState stores configuration and catalog caches;
  • it does not store active explicit skill selections.
  • codex-rs/core-skills/src/render.rs
  • the model-visible instructions currently say: Do not carry skills across turns unless re-mentioned.
  • compaction/context reconstruction reinjects general initial context, but there is no durable selected-skill state to rehydrate.

The model instruction is reasonable for implicit routing. It is too broad for an explicit user selection attached to a long-running task, and it makes host state loss look like intended behavior.

Related but distinct issues:

  • #32170: /goal recognizes $skill tokens but persists them as inert objective text.
  • #23454: explicit $skill invocation can fail immediately when an explicit-only skill is absent from the implicit list.
  • #19695: confirms $<skill-name> is the intended reliable explicit invocation contract.
  • #22644: active /goal contract can be lost after compaction.
  • #31659: general /goal drift after auto-compaction.
  • #29356: generic operational continuity loss after compaction; this report is specifically about host-known explicit skill activation.
  • #30578: proposes persistent installed capability state, but not persistent selected/active skill state.

This report is narrower than generic compaction summary loss: the host already knows which structured skill was selected and can preserve that fact independently of model summarization.

Short implementation plan

  1. Add an active-skill set to thread/task state with at least:

``text
name
authority/source
package/path
content/config fingerprint
activation scope
activation source
``

  1. Record explicit structured or $skill selections in that state after successful resolution.
  2. Re-read/reinject active skill instructions when a new context epoch is created by compaction or resume.
  3. Keep implicit matches turn-scoped; do not make every automatically matched skill durable.
  4. Invalidate/re-resolve an active entry when its source/config fingerprint changes, and emit a visible warning if it can no longer be loaded.
  5. Add deterministic prompt-assembly regression coverage:
  • select an explicit-only supervisor skill with a multi-step workflow;
  • retain its activation in thread/task state;
  • force multiple context replacement/compaction epochs;
  • assert that each rebuilt context contains a fresh SkillInstructions fragment for the selected skill;
  • assert that the implicit inventory still excludes the explicit-only skill;
  • assert that deactivation/scope completion removes it deterministically.

The regression test should verify host-owned activation and reinjection directly. It should not depend on whether a compaction model happens to preserve a short instruction in generated summary text.

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #31822

Powered by Codex Action

ignatremizov · 1 month ago

Additional note on why the existing hook lifecycle does not solve this as a user-level workaround:

Codex has enough hook primitives to approximate rehydration:

PreCompact -> write external checkpoint
compaction -> queue SessionStart(source="compact")
SessionStart matcher "compact" -> return additionalContext

However, that does not provide durable explicit skill activation in practice:

  • PreCompact and PostCompact cannot return model additionalContext; rehydration requires a separate SessionStart hook matching compact.
  • Hook registration is loaded for the workspace/session configuration and currently requires restarting/reloading Codex when a bundle is added. The hook must therefore already be installed before the explicit skill is selected.
  • Installed project hooks apply to every session using the same cwd/worktree. There is no first-class per-thread hook enable/disable or hook profile selection.
  • Parallel sessions in one worktree may intentionally use different roles. Installing a supervisor rehydration hook for one session also exposes that hook to ordinary coder/reviewer sessions in the same cwd.
  • A hook script can gate its behavior using a transcript-derived session key and an external state file, but that only gates an already-registered hook. It does not solve live registration, per-session ownership, cleanup, or UI visibility.
  • Hook input does not contain the structured set of explicitly active skills. A generic hook would need every skill to maintain its own activation ledger or would need to infer activation from rollout <skill> entries, which cannot reliably establish current scope/deactivation.
  • /goal skill tokens still lose their structured selection before goal continuation, so a hook would need another parallel state mechanism for goal-scoped activation.

This also intersects with #17636 (hot-reload hook configuration), but hot reload alone would not solve per-session scoping.

SessionStart(source="compact") is a useful internal primitive for implementing native rehydration. It is not a replacement for host-owned active-skill state. The runtime should preserve the resolved skill activation itself and reinject it after context reconstruction without requiring users to install cwd-wide hooks or build a second session-state system.

ignatremizov · 1 month ago

Not a duplicate of #31822.

#31822 has since been diagnosed as an under-development feature configuration issue:

[features]
token_budget = true

In that path, token-budget compaction intentionally calls start_new_context_window() without producing a normal compaction summary. The replacement history drops prior user/assistant/tool state, so the instant reset and total task-context loss are explained by that feature being enabled. Disabling token_budget restores normal summarizing compaction.

This issue concerns a different layer and remains reproducible with normal compaction behavior: Codex may preserve the task/plan summary while losing the host-known fact that an explicit-only skill was selected and remains active. The observable failure is specifically role/workflow demotion, such as a supervisor parent becoming the executor after later compactions, because selected skills are turn-local and are not stored/reinjected as durable activation state.

The acceptance criteria are therefore also different:

  • #31822: avoid an unexpected destructive window reset or clearly gate/warn about the under-development token-budget behavior.
  • #32169: persist resolved explicit skill activation and reinject the selected SKILL.md across ordinary context reconstruction, independently of what the summary happens to retain.
dbxe · 26 days ago

Additional reproduction on codex-cli 0.146.0, macOS 26.5.2 arm64. A long-running thread explicitly selected an installed explicit-only skill named looped-gated-implementation-agent. Thread ID: 019fb88a-46fa-7b50-95a0-e8d09b10f8b9.

The skill remained present and readable at $CODEX_HOME/skills/looped-gated-implementation-agent/SKILL.md throughout the failure. Its agents/openai.yaml sets allow_implicit_invocation: false, and both files predated the thread. This was not a system-skill cache deletion or installation failure.

The rollout contains 35 recorded compaction events. The first user-visible loss occurred immediately after compaction # 19 at 2026-08-01T06:15:09Z. Ten seconds later the user had to challenge whether the explicitly selected workflow was still being followed, and shortly afterward the agent reported that the skill was absent from its available-skills catalog. Later explicit $looped-gated-implementation-agent resume prompts did not reliably restore the complete contract.

The failure had a concrete procedural consequence rather than only wording drift. The skill says the implementation agent must never impersonate, spawn, replace, or assume the independently run gate role, and must never merge. After later compactions, the implementation context spawned a gate substitute and explicitly instructed it to invoke gh pr merge. The agent later explained that it had reconstructed the workflow from a compressed summary instead of receiving the complete active SKILL.md.

When the user injected the complete skill body and local path again, the agent immediately recognized the lost contract. This appears to match this issue's host-owned activation-loss diagnosis and also intersects #23454: once the explicit-only activation disappeared, repeated $skill invocation did not reliably re-resolve the still-installed skill from local roots.