Summary
When the agent is mid-turn (e.g. between tool calls, or while a long-running tool turn is in flight) and a new user message arrives in the same session, the assistant's final draft can land against the OLDER user input — ignoring or only partially addressing the newest user message. This includes the case where the newest user message is a complaint about a previous stale reply ("you didn't answer my question") which should immediately re-anchor the assistant.
This is a well-known operator pain point in the field, addressed downstream by a workspace-local plugin stale-reply-guard (described below). Filing upstream so that the host gateway provides a first-class re-anchoring contract instead of requiring each operator to bolt on detection plugins.
Environment
openclaw 2026.5.7
- Channel: telegram, direct chat
- Frequently coincides with: active-memory plugin timeouts, long event-loop delays (37 s observed), and the queue-state-divergence bug (filed separately).
Evidence
1. Operator-built stale-reply-guard plugin
A workspace-local plugin (/.openclaw/extensions/stale-reply-guard/) was built specifically to detect this. It hooks before_agent_finalize and runs a finalize_check with these detection signals:
- 8 regex patterns matching operator complaints (representative samples):
you(?:'re| are)? (?:answering|replying)? ?(?:to )?(?:the )?(?:wrong|stale|old|previous)\b
did(?:n['']t| not)? answer (?:my|the) (?:question|message|latest)\b
that(?:[']s|s)? (?:the )?(?:wrong|stale|old) (?:answer|question)\b
wrong[- ]question
stale[- ]reply
answer(?:ed)? (?:my )?previous
(?:answer|address) (?:my )?(?:latest|current|new) (?:question|message|ask)
recheck.*(stale|duplicat|previous)
- Plus
draft-misses-latest-question-tokens: token overlap heuristic between the latest user message and the draft.
When stale, the plugin injects a re-anchor instruction:
Stale-reply guard triggered. Before sending, re-read the visible transcript and re-anchor to the NEWEST user message. If the newest user message is a correction/complaint about staleness, your first sentence must acknowledge it and answer the corrected target — not continue the older task.
2. Real finalize_check events from the plugin
Anonymized sample from logs/stale-reply-guard/events.jsonl:
{"kind":"finalize_check","stale":true,"reason":"complaint-not-acknowledged-or-not-addressed",
"complaint":true,"addressed":false,
"latestUserPreview":"you didn't answer my question — answer …"}
{"kind":"finalize_check","stale":false,"reason":"complaint-handled",
"complaint":true,"addressed":true,
"latestUserPreview":"you didn't answer my question",
"draftPreview":"Sorry, that was stale …"}
{"kind":"finalize_check","stale":true,"reason":"draft-misses-latest-question-tokens",
"complaint":false,"addressed":false,
"latestUserPreview":"show me yesterday memory entries about the firewall conf …"}
Two stale: true events captured live, with two distinct failure modes:
- complaint-not-acknowledged-or-not-addressed: user explicitly complained the assistant ignored them; assistant's draft did not acknowledge or address the corrected target.
- draft-misses-latest-question-tokens: no explicit complaint, but the draft has minimal token overlap with the latest user message, suggesting the assistant is still answering the previous turn.
3. Operator runbook history
The Hermes deployment alongside this OpenClaw instance has authored multiple runbooks specifically about stale replies:
fresh-session-autoresume-isolation-stale-replies.md
runtime-stale-reply-and-codex-extension-audit.md
cron-session-contamination-stale-direct-replies.md
active-memory-stale-plugin-debugentries-after-ok-proof.md
stale-task-pressure-and-session-state.md
stale-claude-cli-backend-override.md
lossless-qmd-stale-summary-feedback-loop.md
The volume of dedicated runbooks indicates this is a recurring class of incident, not an isolated event.
Reproduction
Three independent triggers we've observed:
- Mid-tool-call new message — agent is between tool calls; user sends a follow-up; draft compiles using the pre-followup user message and ignores the new one.
- Cron-triggered overlap — a scheduled cron run is mid-flight when a real user message arrives; the cron's reply lands in the chat first, against context that doesn't include the user message.
- Queue-state divergence (filed separately) — when
queued_work_without_active_run fires, the new user message is queued behind an already-running turn. By the time the running turn finishes drafting, the newer message is in the queue but not in the assistant's view of the conversation.
Expected behavior
The host should provide a first-class contract for "agent must consult the latest user message at draft-finalize time" without requiring an operator-built guard:
- Latest-user-message snapshot at finalize time: just before
before_agent_finalize runs, re-read the inbound message buffer for the session. If the latest inbound is newer than the inbound the assistant started its turn with, surface a structured signal to the agent ("a newer user message arrived during this turn — your draft must address it or explicitly defer it").
- Built-in complaint detection: a small, conservative regex set (similar to the
stale-reply-guard patterns above) that, when matched on the newest user message, sets a flag visible to the model in its system prompt.
- Re-anchor instruction in the system prompt when either of the above fires, with no plugin required.
Suggested fix
- In
src/agents/.../finalize.ts (or equivalent), add a latest-user-snapshot step just before the model emits its final draft.
- Compare the snapshot to the input the agent saw at turn start. If different, inject a re-anchor system message and let the model re-draft once.
- Cap re-anchor passes at 1 per turn to avoid loops.
- Make the behavior config-driven:
agent.staleReplyRecheck: { enabled: true, maxPasses: 1, complaintPatterns: [...] }.
Suggested test coverage
- mid-turn-new-message: turn starts with user msg A, mid-turn new user msg B arrives, assert the final draft addresses B (or explicitly defers it).
- complaint-overrides-prior-task: agent is finishing task X; user sends "you didn't answer my question — what's the deploy command?"; assert draft acknowledges the complaint and answers the deploy question rather than continuing X.
- token-overlap-heuristic-warns: user asks about firewall config; draft is still about the prior topic; assert warning fires (configurable threshold).
- no-spurious-trigger: normal fluent multi-turn conversation; assert no false-positive re-anchor passes.
Behavioral guarantees
- Re-anchor pass is opt-in via config but should default to enabled with conservative thresholds.
- A single re-anchor pass per turn maximum (no loops).
- Re-anchor must be visible in the model fallback / decision observability stream so operators can see when it fires.
Impact
- Eliminates a major operator pain point that has been worked around for months via custom plugins.
- Makes the framework correct-by-default for the most common cause of "the agent ignored my question" complaints.
Filed by
OpenClaw operator instance running openclaw 2026.5.7, with sibling Hermes deployment that has accumulated 7+ dedicated stale-reply runbooks. The workspace-local stale-reply-guard plugin's source is available on request and could serve as a reference for the upstream implementation.
Summary
When the agent is mid-turn (e.g. between tool calls, or while a long-running tool turn is in flight) and a new user message arrives in the same session, the assistant's final draft can land against the OLDER user input — ignoring or only partially addressing the newest user message. This includes the case where the newest user message is a complaint about a previous stale reply ("you didn't answer my question") which should immediately re-anchor the assistant.
This is a well-known operator pain point in the field, addressed downstream by a workspace-local plugin
stale-reply-guard(described below). Filing upstream so that the host gateway provides a first-class re-anchoring contract instead of requiring each operator to bolt on detection plugins.Environment
openclaw2026.5.7Evidence
1. Operator-built
stale-reply-guardpluginA workspace-local plugin (
/.openclaw/extensions/stale-reply-guard/) was built specifically to detect this. It hooksbefore_agent_finalizeand runs afinalize_checkwith these detection signals:you(?:'re| are)? (?:answering|replying)? ?(?:to )?(?:the )?(?:wrong|stale|old|previous)\bdid(?:n['']t| not)? answer (?:my|the) (?:question|message|latest)\bthat(?:[']s|s)? (?:the )?(?:wrong|stale|old) (?:answer|question)\bwrong[- ]questionstale[- ]replyanswer(?:ed)? (?:my )?previous(?:answer|address) (?:my )?(?:latest|current|new) (?:question|message|ask)recheck.*(stale|duplicat|previous)draft-misses-latest-question-tokens: token overlap heuristic between the latest user message and the draft.When stale, the plugin injects a re-anchor instruction:
2. Real
finalize_checkevents from the pluginAnonymized sample from
logs/stale-reply-guard/events.jsonl:{"kind":"finalize_check","stale":true,"reason":"complaint-not-acknowledged-or-not-addressed", "complaint":true,"addressed":false, "latestUserPreview":"you didn't answer my question — answer …"} {"kind":"finalize_check","stale":false,"reason":"complaint-handled", "complaint":true,"addressed":true, "latestUserPreview":"you didn't answer my question", "draftPreview":"Sorry, that was stale …"} {"kind":"finalize_check","stale":true,"reason":"draft-misses-latest-question-tokens", "complaint":false,"addressed":false, "latestUserPreview":"show me yesterday memory entries about the firewall conf …"}Two
stale: trueevents captured live, with two distinct failure modes:3. Operator runbook history
The Hermes deployment alongside this OpenClaw instance has authored multiple runbooks specifically about stale replies:
fresh-session-autoresume-isolation-stale-replies.mdruntime-stale-reply-and-codex-extension-audit.mdcron-session-contamination-stale-direct-replies.mdactive-memory-stale-plugin-debugentries-after-ok-proof.mdstale-task-pressure-and-session-state.mdstale-claude-cli-backend-override.mdlossless-qmd-stale-summary-feedback-loop.mdThe volume of dedicated runbooks indicates this is a recurring class of incident, not an isolated event.
Reproduction
Three independent triggers we've observed:
queued_work_without_active_runfires, the new user message is queued behind an already-running turn. By the time the running turn finishes drafting, the newer message is in the queue but not in the assistant's view of the conversation.Expected behavior
The host should provide a first-class contract for "agent must consult the latest user message at draft-finalize time" without requiring an operator-built guard:
before_agent_finalizeruns, re-read the inbound message buffer for the session. If the latest inbound is newer than the inbound the assistant started its turn with, surface a structured signal to the agent ("a newer user message arrived during this turn — your draft must address it or explicitly defer it").stale-reply-guardpatterns above) that, when matched on the newest user message, sets a flag visible to the model in its system prompt.Suggested fix
src/agents/.../finalize.ts(or equivalent), add alatest-user-snapshotstep just before the model emits its final draft.agent.staleReplyRecheck: { enabled: true, maxPasses: 1, complaintPatterns: [...] }.Suggested test coverage
Behavioral guarantees
Impact
Filed by
OpenClaw operator instance running
openclaw 2026.5.7, with sibling Hermes deployment that has accumulated 7+ dedicated stale-reply runbooks. The workspace-localstale-reply-guardplugin's source is available on request and could serve as a reference for the upstream implementation.