Queued input silently executes before an earlier-queued /reload-plugins — the turn then runs against stale plugin state

Status Open
Reported on v2.1.222
Maintainer reply None cached
Activity 0 comments · opened Aug 5, 2026

What happened

Typed, in this order, while a turn was active:

  1. /reload-plugins (immediately after /plugin update) — shown as queued
  2. A prompt: "reloaded, test the new plugin version"

Claude Code delivered the prompt first. A new turn started and made MCP tool calls against the old, pre-update plugin server, while the conversation reads as if the reload had already applied. The reload ran later, after losing the idle slot to the very turn that was queued behind it.

Why this is a trust violation, not a scheduling nuance

The input box is a sequence. Two things submitted in order carry the most basic contract an interface can make: their effects apply in that order. Claude Code breaks that contract silently — no warning at bypass time, no marker in the transcript, nothing the user or the model can see.

Queue-and-walk-away is a core Claude Code workflow. Turns run for minutes; the whole point of the input queue is to line up next steps and go do something else. That is precisely the situation where a bypass warning (if one existed) would go unseen. For an absent user, out-of-order execution isn't a papercut — it's the harness editing their instructions behind their back.

The inversion is invisible after the fact. Reading the transcript later, everything looks fine: reload requested, prompt processed, reload completed. The one fact that matters — that the prompt executed against pre-reload state — appears nowhere.

The damage is correctness, not cosmetics. update → reload → test is the plugin development loop. This race makes the session confidently "verify" a new plugin version while exercising the old one. The model has no way to know either: it is handed a prompt asserting "reloaded" by a harness that knows the reload is still pending and says nothing. That's the harness setting up both parties to produce false results.

The design tension is real — the resolution chosen is the wrong one

I understand why /reload-plugins is idle-gated: it tears down plugin MCP processes, and running it mid-turn or mid-background-task would kill in-flight work. And I understand why strict FIFO is scary: a reload waiting on a 30-minute background task would freeze the entire input pipeline behind it.

But "ordered execution might block" cannot resolve to "execute out of order and tell no one." That trades a visible, explainable delay for an invisible correctness bug.

Any of these would restore trust

  1. Ordered by default. A queued gated command holds subsequently queued input. Show the state honestly: ⏸ 1 message held behind /reload-plugins (waiting on 1 background task) — with a way to reorder or cancel. Blocking that is visible and controllable is fine; reordering that is silent is not.
  2. If bypass must stay, make it loud on both sides. A prominent transcript banner at bypass time, and a system note injected into the bypassing turn — "/reload-plugins is still pending; plugin state is pre-reload" — so the model cannot claim post-reload behavior. The model is the other victim of this race; inform it.
  3. At absolute minimum: record actual execution order in the transcript, so a user who walked away can reconstruct what really happened instead of being shown a fiction.

Environment

  • Claude Code 2.1.222
  • macOS (darwin 25.5.0)
  • Plugin-provided MCP servers (stdio); reproduced with a plugin under active development

View original on GitHub ↗