openclaw - ✅(Solved) Fix [Bug] Background task updates injected as user-role messages instead of system-role [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#72703Fetched 2026-04-28 06:33:15
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
cross-referenced ×2

Background task updates, deferred maintenance notifications, and heartbeat prompts are delivered as role: "user" messages instead of role: "system". This pollutes the user message stream and causes models to confuse system notifications with actual user input.

Root Cause

Background task updates, deferred maintenance notifications, and heartbeat prompts are delivered as role: "user" messages instead of role: "system". This pollutes the user message stream and causes models to confuse system notifications with actual user input.

Fix Action

Fixed

PR fix notes

PR #72761: Fix: Issue 72703 system events user role

Description (problem / solution / changelog)

Summary

Describe the problem and fix in 2–5 bullets:

If this PR fixes a plugin beta-release blocker, title it fix(<plugin-id>): beta blocker - <summary> and link the matching Beta blocker: <plugin-name> - <summary> issue labeled beta-blocker. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.

  • Problem: queued runtime system events, including background task and deferred maintenance notices, were prepended to user prompt text.
  • Why it matters: models could treat OpenClaw-generated maintenance/task notices as user-authored requests, causing confused replies or unnecessary follow-up turns.
  • What changed: drained system events now route through runtime system context, while tests assert they stay out of commandBody, followupRun.prompt, and transcript prompt text.
  • What did NOT change (scope boundary): heartbeat prompts remain documented user messages; task registry delivery policy, notification formatting, and direct channel delivery behavior are unchanged.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #72703
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause: queued system events were formatted as System: lines and passed into buildReplyPromptBodies, which prepended them to the user prompt path.
  • Missing detection / guardrail: tests asserted the old behavior instead of requiring runtime-generated events to stay out of user-authored prompt text.
  • Contributing context (if known): heartbeat prompts are intentionally user messages, which made the nearby system-event wake path easy to conflate with normal user prompt delivery.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
    • Target test or file: src/auto-reply/reply/get-reply-run.media-only.test.ts
    • Scenario the test should lock in: drained system events are routed to extraSystemPrompt and are absent from commandBody, followupRun.prompt, transcriptCommandBody, and followupRun.transcriptPrompt.
    • Why this is the smallest reliable guardrail: it covers the prompt assembly seam where queued system events are converted into model input without requiring live channel or model execution.
    • Existing test that already covers this (if any): updated existing prompt-placement tests in get-reply-run.media-only.test.ts.
    • If no new test is added, why not: N/A

User-visible / Behavior Changes

Runtime-generated queued system events are no longer presented to the model as user-authored prompt text. Heartbeat prompt behavior is unchanged.

Diagram (if applicable)

For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write N/A.

 Before:
  [background task update] -> [queued system event] -> [user prompt text] -> [model treats as user input]

  After:
  [background task update] -> [queued system event] -> [runtime system context] -> [model sees non-user runtime context]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local Node/pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

Steps

  1. Queue or mock a formatted runtime system event.
  2. Build a prepared reply.
  3. Inspect prompt fields passed to runReplyAgent.

Expected

  • Runtime system-event text is present in followupRun.run.extraSystemPrompt.
  • Runtime system-event text is absent from user prompt and transcript prompt fields.

Actual

  • Before this PR, runtime system-event text was included in commandBody and followupRun.prompt.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
    • pnpm test src/auto-reply/reply/get-reply-run.media-only.test.ts src/agents/pi-embedded-runner/run/runtime-context-prompt.test.ts src/agents/pi-embedded- runner/context-engine-maintenance.test.ts
    • pnpm check:changed
    • pnpm build
    • pnpm dlx codex review --base origin/main
  • Edge cases checked: untrusted event sender downgrade remains covered; trusted events preserve owner status; deferred follow-up prompts no longer replay system events as user text.
  • What you did not verify: live channel delivery with a real messaging integration.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: moving system events out of user prompt text could hide context from deferred turns if not forwarded elsewhere.
    • Mitigation: tests assert the event text is still present in extraSystemPrompt.
  • Risk: untrusted system events could regain owner authority.
    • Mitigation: existing ownership downgrade tests remain and continue to pass.

Built with Codex

Changed files

  • src/auto-reply/reply/get-reply-run.media-only.test.ts (modified, +24/-15)
  • src/auto-reply/reply/get-reply-run.ts (modified, +19/-7)
  • src/auto-reply/reply/prompt-prelude.ts (modified, +2/-6)
  • src/plugins/cli-registry-loader.ts (modified, +27/-1)
  • src/plugins/cli.test.ts (modified, +87/-0)
RAW_BUFFERClick to expand / collapse

Summary

Background task updates, deferred maintenance notifications, and heartbeat prompts are delivered as role: "user" messages instead of role: "system". This pollutes the user message stream and causes models to confuse system notifications with actual user input.

Observed Behavior

  • In Main/Shodan's recent session: 100% of user-role messages were system-generated — zero direct user text was present
  • Background task updates like "Context engine turn maintenance. Deferred maintenance is waiting for the session lane to go idle." appear as user messages
  • Models attempt to "respond" to system notifications as if they were user requests
  • This causes looping behavior where each notification triggers a model turn

Impact

  • Models can't distinguish real user messages from system noise
  • Trigger unnecessary response cycles (looping)
  • Weaker models (MiniMax fallback) completely lose track of actual user text

Environment

  • OpenClaw 2026.4.23
  • Multiple agents affected

Expected Behavior

Background task updates, maintenance notifications, and heartbeat prompts should be injected via role: "system" or a dedicated mechanism that doesn't pollute user-role messages.

extent analysis

TL;DR

Update the message role for background task updates, maintenance notifications, and heartbeat prompts to role: "system" to prevent pollution of the user message stream.

Guidance

  • Verify the current implementation of message role assignment for system-generated messages to identify where the incorrect role is being set.
  • Check the documentation for OpenClaw 2026.4.23 to see if there are any configuration options or APIs that can be used to set the correct message role for system-generated messages.
  • Consider adding a dedicated mechanism for injecting system notifications that does not use the role: "user" messages.
  • Test the updated implementation with weaker models like MiniMax fallback to ensure they can correctly distinguish between real user messages and system notifications.

Example

No code snippet is provided as the issue does not contain enough information about the implementation details.

Notes

The solution may require changes to the OpenClaw configuration or the custom code that generates system notifications. The exact steps may vary depending on the specific implementation.

Recommendation

Apply workaround: Update the message role for system-generated messages to role: "system" to prevent pollution of the user message stream, as this is a more targeted solution that can be implemented without waiting for a potential fix in a future version of OpenClaw.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING