openclaw - ✅(Solved) Fix Server-side rendered status footer (model can't lie about runtime state) [1 pull requests, 1 comments, 2 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#71984Fetched 2026-04-27 05:36:28
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Timeline (top)
closed ×1commented ×1cross-referenced ×1

Root Cause

This makes the footer truth-by-construction. The model literally cannot lie about it because it never authors the digits.

Fix Action

Fix / Workaround

Logged in our regressions file:

  • REG-023 (Apr 22): "Footer tokens MUST come from live session_status, never estimated"
  • REG-024 (Apr 23): "If session_status not called this turn, send NO footer rather than fabricated"
  • REG-028 (Apr 26): "Self-reported footer is structurally unreliable - kill it, don't patch it"

Workaround until shipped

PR fix notes

PR #72005: feat: server-rendered status footer and context threshold warnings

Description (problem / solution / changelog)

Summary

Server-rendered status footer and one-shot context-usage warnings for outbound chat messages. Fixes the "model lies about its own runtime state" failure mode in #71984 by moving footer authorship below the LLM.

Why

Asking a model to append a status footer like 📚 X% (Xk/200k) · 🧹 N compactions · 🧠 model to its own messages is structurally unreliable. Logged production regressions show the model writing 5% (10k/200k) while actual context was 134% (268k/200k). Three escalating prompt rules failed to prevent this. Any field the model writes about its own runtime is fabricable; the runtime knows the truth and should write it.

What ships

Feature A: footer renderer + stripper

  • New pure module src/utils/outbound-footer.ts exporting stripFabricatedFooter, renderFooter, evaluateContextWarning, and the composite processOutboundText hook.
  • messages.outboundFooter.enabled (bool) and messages.outboundFooter.template (string) on MessagesConfig.
  • The outbound pipeline in sendMessage always strips any model-written footer matching the canonical pattern, regardless of config. There is no opt-out for the strip step. When the renderer is enabled, a server-authored footer is appended in its place using live values from session state.
  • Supported placeholders: {context_pct}, {context_tokens}, {context_limit}, {compactions}, {model_alias}. Unknown placeholders are left as the literal {name} form so config typos stay visible.

Feature B: context threshold warning

  • messages.contextWarning.enabled (bool) and messages.contextWarning.thresholds (number array, default [70, 85, 95]).
  • When usage crosses the highest unwarned threshold for the session, the hook prepends a single line:
    ⚠️ Context 90% - consider /new
  • Warning state is tracked on SessionEntry.contextWarningThresholdsTriggered, so each threshold fires exactly once per session.

Pipeline placement

The hook lives in src/infra/outbound/footer-hook.ts and is wired into sendMessage before the outbound payload plan is built. It mirrors the lazy-loading pattern already used by the message gateway runtime so importing it does not pull a heavy session-store dependency tree into every consumer. Failures fall through to the original text: the hook never blocks delivery.

Tests

  • src/utils/outbound-footer.test.ts covers the stripper, the renderer, and the threshold-warning evaluator (22 tests).
  • Full unit-fast project: 6558 tests passing.
  • Full infra project: 2543 tests passing.
  • Full auto-reply project: 1598 tests passing.
  • pnpm tsgo:core and pnpm tsgo:extensions clean.
  • pnpm build clean.

Docs

New page at docs/concepts/outbound-footer.md covering setup, placeholders, and where the values come from.

Acceptance criteria from #71984

  • Config schema for footer.template with documented variables
  • Runtime renders/appends footer on outbound channel messages when configured
  • Variables sourced from same telemetry as session_status (context tokens, compactions, model alias)
  • Per-channel enable/disable: yes via the standard channels.<id> overrides on messages.outboundFooter
  • Optional [[footer]] directive variant for explicit placement: not implemented in this PR. The hook always appends at end-of-message when enabled. Happy to add the directive variant in a follow-up if reviewers want it.
  • Docs page covering setup + variable reference

Closes #71984.

Changed files

  • docs/concepts/outbound-footer.md (added, +108/-0)
  • src/config/schema.base.generated.ts (modified, +29/-0)
  • src/config/sessions/types.ts (modified, +6/-0)
  • src/config/types.messages.ts (modified, +35/-0)
  • src/config/zod-schema.session.ts (modified, +14/-0)
  • src/infra/outbound/footer-hook.ts (added, +209/-0)
  • src/infra/outbound/message.ts (modified, +11/-1)
  • src/utils/outbound-footer.test.ts (added, +225/-0)
  • src/utils/outbound-footer.ts (added, +234/-0)

Code Example

📚 {context_pct}% ({context_tokens}k/{context_limit}k) · 🧹 {compactions} compactions · 🧠 {model_alias}
RAW_BUFFERClick to expand / collapse

Problem

LLM agents asked to append a status footer to every chat message (e.g. 📚 X% (Xk/200k) · 🧹 N compactions · 🧠 model) cannot self-report runtime state reliably across long sessions. The model writes whatever number it last saw, last estimated, or last hallucinated. No prompt rule fixes this - we have three logged regressions in a single agent (REG-023, REG-024, REG-028) where a footer rule said "call session_status first or send no footer," and the model broke the rule anyway.

In our latest incident the message footer claimed 5% (10k/200k) while actual context was 134% (268k/200k). That's a 25x lie about runtime state, baked into a user-trusted UI element.

The root issue is structural: any field the model writes about its own runtime is fabricable. The runtime knows the truth. The runtime should write it.

Proposal

Add a server-side footer renderer in OpenClaw that:

  1. Lets a session/agent declare a footer.template in config (or via a new directive), e.g.:
    📚 {context_pct}% ({context_tokens}k/{context_limit}k) · 🧹 {compactions} compactions · 🧠 {model_alias}
  2. Lets the agent emit a placeholder token in its reply, e.g. [[footer]] on its own line, OR have the runtime auto-append the rendered footer to outbound messages on configured channels (Telegram, Discord, etc.)
  3. At send-time, OpenClaw substitutes the placeholder/appends the footer using live values from the same source session_status reads from. The model never writes the numbers.

This makes the footer truth-by-construction. The model literally cannot lie about it because it never authors the digits.

Variant: opt-in directive

Mirror the existing assistant output directives (MEDIA:, [[reply_to_current]], [[audio_as_voice]]):

  • Agent emits [[footer]] anywhere in the reply
  • OpenClaw strips the tag and appends the rendered footer at the end of the message before delivery
  • Channels that don't support the footer (or where the user disabled it) silently drop the tag

This keeps the agent in control of whether a footer appears, while the runtime owns what it says.

Why prompt rules don't work

Logged in our regressions file:

  • REG-023 (Apr 22): "Footer tokens MUST come from live session_status, never estimated"
  • REG-024 (Apr 23): "If session_status not called this turn, send NO footer rather than fabricated"
  • REG-028 (Apr 26): "Self-reported footer is structurally unreliable - kill it, don't patch it"

Three incidents, three escalating rules, same failure mode. The fix has to live below the model.

Acceptance criteria

  • Config schema for footer.template with documented variables
  • Runtime renders/appends footer on outbound channel messages when configured
  • Variables sourced from same telemetry as session_status (context tokens, limit, compactions, model alias, optional cost/elevated/thinking)
  • Per-channel enable/disable
  • Optional: [[footer]] directive variant for explicit placement
  • Docs page covering setup + variable reference

Workaround until shipped

We've killed our footer in SOUL.md and logged REG-028. The agent now sends no footer rather than a fabricated one. Users who want context status run /status (session_status equivalent) on demand.

extent analysis

TL;DR

Implement a server-side footer renderer in OpenClaw to ensure accurate runtime state reporting.

Guidance

  • Identify the footer.template configuration option and define a template with variables (e.g., context_pct, context_tokens, context_limit, compactions, model_alias) to be replaced with live values from session_status.
  • Develop a mechanism for the agent to emit a placeholder token (e.g., [[footer]]) or have the runtime auto-append the rendered footer to outbound messages on configured channels.
  • Ensure the footer is rendered using live values from the same source as session_status to prevent fabrication.
  • Consider implementing a directive (e.g., [[footer]]) to allow agents to opt-in to footer rendering.

Example

footer.template: "📚 {context_pct}% ({context_tokens}k/{context_limit}k) · 🧹 {compactions} compactions · 🧠 {model_alias}"

Notes

The proposed solution requires changes to OpenClaw and may involve updates to the agent configuration and runtime. The [[footer]] directive variant can provide flexibility in footer placement.

Recommendation

Apply the proposed workaround of killing the footer until the server-side footer renderer is implemented, as it ensures accuracy and prevents fabricated runtime state reporting.

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

openclaw - ✅(Solved) Fix Server-side rendered status footer (model can't lie about runtime state) [1 pull requests, 1 comments, 2 participants]