Skip to content

fix(webchat): hide heartbeat chat output using showAlerts#69084

Closed
Jmarzab wants to merge 2 commits into
openclaw:mainfrom
Jmarzab:fix/heartbeat-webchat-noise
Closed

fix(webchat): hide heartbeat chat output using showAlerts#69084
Jmarzab wants to merge 2 commits into
openclaw:mainfrom
Jmarzab:fix/heartbeat-webchat-noise

Conversation

@Jmarzab

@Jmarzab Jmarzab commented Apr 19, 2026

Copy link
Copy Markdown

Summary

Problem

Heartbeat-generated agent output could still appear in webchat even when channels.defaults.heartbeat.showAlerts = false.

Why it matters

It adds noisy system text to the visible chat transcript and makes it harder to distinguish real user/assistant messages from heartbeat output.

What changed

shouldHideHeartbeatChatOutput() now uses visibility.showAlerts instead of visibility.showOk when deciding whether to suppress heartbeat chat output in webchat.

What did NOT change (scope boundary)

No config schema, heartbeat execution, delivery logic, or non-webchat behavior was changed.


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 #
Related #
✔ This PR fixes a bug or regression


Root Cause (if applicable)

  • Root cause: Webchat filtering for heartbeat chat output used showOk instead of showAlerts, creating a semantic mismatch between backend heartbeat visibility and visible chat suppression.
  • Missing detection / guardrail: No focused test/guardrail covered the case where showAlerts=false should suppress heartbeat text in webchat.
  • Contributing context: Heartbeat output reaches webchat through the normal chat event path, so the wrong visibility flag caused user-visible transcript noise.

Regression Test Plan (if applicable)

Coverage level that should have caught this

  • ☐ Unit test
  • ✔ Seam / integration test
  • ☐ End-to-end test
  • ☐ Existing coverage already sufficient

Target test or file

Gateway/server-chat heartbeat visibility coverage around shouldHideHeartbeatChatOutput().

Scenario the test should lock in

When heartbeat output is produced and webchat visibility resolves with showAlerts=false, heartbeat text should be suppressed from webchat chat output.

Why this is the smallest reliable guardrail

The bug is caused by the interaction between heartbeat visibility resolution and gateway chat filtering, so a seam/integration test is the smallest level that exercises the real decision point.

Existing test that already covers this

None known.

If no new test is added, why not

This PR is intentionally minimal and focused on the source fix only; manual runtime verification was performed.


User-visible / Behavior Changes

Heartbeat text is no longer shown in webchat when channels.defaults.heartbeat.showAlerts = false.
Normal chat behavior is unchanged.


Diagram (if applicable)

Before:
[heartbeat run] -> [webchat filter checks showOk] -> [heartbeat text visible in chat]

After:
[heartbeat run] -> [webchat filter checks showAlerts] -> [heartbeat text suppressed] -> [cleaner chat transcript]

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: Debian/Linux
  • Runtime/container: local host install
  • Model/provider: openai-codex / gpt-5.4
  • Integration/channel: webchat / control-ui
  • Relevant config: channels.defaults.heartbeat.showAlerts = false

Steps

  1. Configure heartbeat visibility with showAlerts = false
  2. Trigger or wait for a heartbeat run
  3. Observe webchat transcript

Expected

  • Heartbeat runs internally
  • No heartbeat text appears in webchat

Actual

  • Before fix: heartbeat text appears in chat due to showOk being used

Evidence

  • ☐ Failing test/log before + passing after
  • ✔ Trace/log snippets
  • ☐ Screenshot/recording
  • ☐ Perf numbers

Human Verification (required)

Verified scenarios

  • Reproduced noisy heartbeat
  • Applied fix
  • Restarted gateway
  • Confirmed no heartbeat noise in chat
  • Normal chat unaffected

Edge cases checked

  • Normal chat flow intact

Not verified

  • Other channels beyond webchat
  • Automated test coverage

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.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

Risk

Users relying on previous showOk behavior may see different visibility.

Mitigation

  • Aligns behavior with intended showAlerts semantics
  • Scope limited strictly to webchat heartbeat filtering

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: XS labels Apr 19, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e822255c51

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const cfg = loadConfig();
const visibility = resolveHeartbeatVisibility({ cfg, channel: "webchat" });
return !visibility.showOk;
return !visibility.showAlerts;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep heartbeat ack suppression keyed to showOk

shouldHideHeartbeatChatOutput() now checks showAlerts, but the ack/noise filter in normalizeHeartbeatChatFinalText() is what removes HEARTBEAT_OK-style output when acknowledgements are disabled. With common visibility settings (showOk=false, showAlerts=true), this change makes shouldHideHeartbeatChatOutput() return false, so heartbeat acknowledgements are no longer stripped/suppressed in webchat and can leak into user-visible chat transcripts. This regresses the showOk contract for heartbeat ack visibility.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes shouldHideHeartbeatChatOutput to check visibility.showAlerts instead of visibility.showOk when deciding whether to suppress heartbeat chat output in webchat. The semantic intent is correct — showAlerts is the appropriate flag — but the change inverts the default suppression behavior because showAlerts defaults to true while showOk defaults to false.

  • Default behavior regression: Users with no explicit heartbeat visibility config will now see heartbeat text in webchat (previously suppressed by default via showOk=false).
  • Test failures: The existing test \"suppresses heartbeat ack-like chat output when showOk is false\" and the ackMaxChars alert test use a mock with showAlerts: true, which after this change causes shouldHideHeartbeatChatOutput to return false — both tests will fail.

Confidence Score: 3/5

Not safe to merge: the fix reverses default suppression behavior and breaks at least two existing tests in server-chat.agent-events.test.ts.

The one-line change is semantically motivated but produces a default-behavior regression and invalidates existing test coverage that specifically asserts suppression. Both are present defects on the changed path.

src/gateway/server-chat.ts line 61 and the corresponding test file src/gateway/server-chat.agent-events.test.ts (not in the diff but directly broken by this change).

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/server-chat.ts
Line: 61

Comment:
**Default behavior reversal breaks existing users and tests**

Swapping `showOk``showAlerts` flips the out-of-the-box suppression behavior. `showOk` defaults to `false`, so `!showOk` was `true` (suppress). `showAlerts` defaults to `true`, so `!showAlerts` is now `false` (show). Any user with no explicit heartbeat visibility config will suddenly see heartbeat text in webchat after this change.

This also breaks the existing test `"suppresses heartbeat ack-like chat output when showOk is false"` in `server-chat.agent-events.test.ts`: the global mock already sets `showAlerts: true`, so after this change `shouldHideHeartbeatChatOutput` returns `false` and both the delta and the final broadcasts happen — the test expects 0 delta calls and `message: undefined` on the final, so it will fail. The test at line 1435 is similarly affected (expects stripped text but receives the full `HEARTBEAT_OK` string because the stripping path is never reached when `shouldHideHeartbeatChatOutput` returns `false`).

To preserve backward compatibility and fix the tests, either default `showAlerts` to `false` in `heartbeat-visibility.ts`, or update the test mocks to `showAlerts: false` for the suppression scenarios.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(webchat): hide heartbeat chat output..." | Re-trigger Greptile

const cfg = loadConfig();
const visibility = resolveHeartbeatVisibility({ cfg, channel: "webchat" });
return !visibility.showOk;
return !visibility.showAlerts;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Default behavior reversal breaks existing users and tests

Swapping showOkshowAlerts flips the out-of-the-box suppression behavior. showOk defaults to false, so !showOk was true (suppress). showAlerts defaults to true, so !showAlerts is now false (show). Any user with no explicit heartbeat visibility config will suddenly see heartbeat text in webchat after this change.

This also breaks the existing test "suppresses heartbeat ack-like chat output when showOk is false" in server-chat.agent-events.test.ts: the global mock already sets showAlerts: true, so after this change shouldHideHeartbeatChatOutput returns false and both the delta and the final broadcasts happen — the test expects 0 delta calls and message: undefined on the final, so it will fail. The test at line 1435 is similarly affected (expects stripped text but receives the full HEARTBEAT_OK string because the stripping path is never reached when shouldHideHeartbeatChatOutput returns false).

To preserve backward compatibility and fix the tests, either default showAlerts to false in heartbeat-visibility.ts, or update the test mocks to showAlerts: false for the suppression scenarios.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-chat.ts
Line: 61

Comment:
**Default behavior reversal breaks existing users and tests**

Swapping `showOk``showAlerts` flips the out-of-the-box suppression behavior. `showOk` defaults to `false`, so `!showOk` was `true` (suppress). `showAlerts` defaults to `true`, so `!showAlerts` is now `false` (show). Any user with no explicit heartbeat visibility config will suddenly see heartbeat text in webchat after this change.

This also breaks the existing test `"suppresses heartbeat ack-like chat output when showOk is false"` in `server-chat.agent-events.test.ts`: the global mock already sets `showAlerts: true`, so after this change `shouldHideHeartbeatChatOutput` returns `false` and both the delta and the final broadcasts happen — the test expects 0 delta calls and `message: undefined` on the final, so it will fail. The test at line 1435 is similarly affected (expects stripped text but receives the full `HEARTBEAT_OK` string because the stripping path is never reached when `shouldHideHeartbeatChatOutput` returns `false`).

To preserve backward compatibility and fix the tests, either default `showAlerts` to `false` in `heartbeat-visibility.ts`, or update the test mocks to `showAlerts: false` for the suppression scenarios.

How can I resolve this? If you propose a fix, please make it concise.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a828b468ee

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const cfg = loadConfig();
const visibility = resolveHeartbeatVisibility({ cfg, channel: "webchat" });
return !visibility.showOk;
return !visibility.showAlerts;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve showOk-only heartbeat acknowledgments in webchat

Switching shouldHideHeartbeatChatOutput() to !visibility.showAlerts causes webchat to suppress heartbeat output whenever alerts are disabled, including HEARTBEAT_OK acknowledgments that should still be visible when showOk=true. With showOk=true and showAlerts=false, emitChatDelta() now bails out for heartbeat runs and final normalization suppresses the ack token, so users who intentionally configured “OK-only” heartbeat visibility no longer receive any chat acknowledgment.

Useful? React with 👍 / 👎.

@Jmarzab

Jmarzab commented Apr 19, 2026

Copy link
Copy Markdown
Author

Validated and completed this fix end-to-end.

What changed:

  • webchat heartbeat suppression now uses instead of
  • updated heartbeat visibility tests to match the intended semantics
  • adjusted one test to assert against the chat payload, since this flow legitimately emits both and events

Why:

  • backend behavior was already aligned with
  • webchat filtering was still keyed off , causing heartbeat text to leak into the user-visible transcript
  • CI failures were due to tests encoding the previous semantics and assuming a single chat event

Validation:

  • manually verified in webchat after gateway restart
  • confirmed heartbeat no longer pollutes chat when
  • normal chat flow unaffected
  • passes locally (40/40)

@clawsweeper

clawsweeper Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

This PR should stay open for maintainer/contributor follow-up, but it should not merge as-is: the patch conflates heartbeat OK acknowledgments with alert visibility, regressing both default webchat suppression and OK-only heartbeat configurations; the contributor’s later discussion also agrees the current branch should not merge unchanged.

Canonical path: Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

So I’m closing this here because the remaining work is already tracked in the canonical issue.

Review details

Best possible solution:

Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

Do we have a high-confidence way to reproduce the issue?

Yes, for the PR defect: source inspection shows the proposed gate makes the default showOk=false, showAlerts=true path visible instead of suppressed, and makes showOk=true, showAlerts=false suppress OK-only output. I did not run tests because this review was read-only.

Is this the best way to solve the issue?

No, this is not the best way to solve the issue because it replaces one visibility contract with another and bypasses current heartbeat token stripping for visible alerts. The better fix is a small Gateway decision that preserves both flags and tests the default and mixed configurations.

Security review:

Security review cleared: The diff only changes Gateway heartbeat visibility filtering and adjacent tests; no new permissions, dependencies, token handling, install hooks, network calls, or code-execution paths were introduced.

AGENTS.md: found and applied where relevant.

What I checked:

  • stale F-rated PR: PR was opened 2026-04-19T20:02:16Z, is older than 30 days, and the latest review rated it F.
  • proof blocker: real behavior proof is missing and proof tier is F, so this branch is not merge-ready without contributor follow-up.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • Shakker: Local git blame on the central shouldHideHeartbeatChatOutput() lines points to the grafted current-main snapshot commit; history is shallow, so this is a routing hint rather than original authorship. (role: current-main carrier in shallow blame; confidence: low; commits: d1fe0184b956; files: src/gateway/server-chat.ts, src/infra/heartbeat-visibility.ts, src/gateway/server-chat.agent-events.test.ts)
  • brokemac79: Authored the merged adjacent heartbeat webchat tool-event suppression work that touched the same Gateway event projection and test surface while leaving chat text visibility separate. (role: recent adjacent Gateway heartbeat contributor; confidence: medium; commits: 310ee3179b75, 58700db5ffa7; files: src/gateway/server-chat.ts, src/gateway/server-chat.agent-events.test.ts)
  • Jmarzab: Authored the proposed branch and later clarified in PR discussion that the current showAlerts-only patch should not merge without preserving separate heartbeat visibility contracts; not treated as current-main owner. (role: current PR proposer and discussion participant; confidence: medium; commits: e822255c51b1, a828b468ee29; files: src/gateway/server-chat.ts, src/gateway/server-chat.agent-events.test.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 9313471fa579.

@Jmarzab

Jmarzab commented May 1, 2026

Copy link
Copy Markdown
Author

I think clawsweeper is basically right here.

showOk and showAlerts have separate contracts in the current docs/defaults:

  • showOk controls OK-only heartbeat acknowledgments
  • showAlerts controls non-OK alert content

Swapping the webchat suppression gate from showOk to showAlerts changes behavior in the default case (showOk=false, showAlerts=true) and would let HEARTBEAT_OK-style ACK text leak into webchat again.

So I don’t think this patch should merge as-is. The narrow fix should preserve both flags separately and add explicit tests for:

  1. default (showOk=false, showAlerts=true)
  2. alerts off
  3. mixed configs where OKs are enabled but alerts are not (and vice versa)

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels May 20, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 20, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels May 22, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jun 6, 2026
@clawsweeper

clawsweeper Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS stale Marked as stale due to inactivity status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant