feat(agentMentionService): inline reply-mechanics cue (heartbeat-clobber mitigation) - #417
Closed
samxu01 wants to merge 1 commit into
Closed
feat(agentMentionService): inline reply-mechanics cue (heartbeat-clobber mitigation)#417samxu01 wants to merge 1 commit into
samxu01 wants to merge 1 commit into
Conversation
…ber mitigation) Smoke 2026-05-20 produced 3 reproductions of the openclaw heartbeat-clobbers-mention bug (Nova c3, Pixel c7, Ops c7): when an agent processes a chat.mention AND a heartbeat trigger fires in the same model session before the mention reply is committed, the openclaw run-loop's final-turn auto-post emits HEARTBEAT_OK (the LAST assistant turn) and CLOBBERS the in-flight mention answer. Upstream fix in the openclaw fork is a larger change. This inline cue is the kernel-side mitigation: tell the model NOT to rely on the implicit final-turn auto-post; instead, EXPLICITLY call commonly_post_message as soon as the mention answer is ready. Once committed via tool call, any subsequent heartbeat trigger is harmless. Cue added ONLY to chat.mention events — thread.mention replies post via a different path on the openclaw side, so the cue would be noise there. Follows the inline-cue manifest pattern (ADR-012 §9 + pod-context cue precedent). ~340 chars / ~80 tokens. Tradeoff is acceptable against silent-loss of mention replies. Note: textually overlaps with PR #416 (cross-runtime consultation cue, same function file). Merge order matters; whichever lands second needs a small rebase to compose both cues.
This was referenced May 21, 2026
Contributor
Author
samxu01
added a commit
that referenced
this pull request
May 22, 2026
…416 + #417) Per code-review feedback on PR #416 + #417: those two PRs touch the same content-build expression in enqueueMentions and have a mechanical merge conflict — whichever lands second hits it. Plus #416 didn't gate on eventType, so thread.mention events got the consultation cue erroneously. This PR composes both cues in one place with the correct 4-way matrix: chat.mention + non-specialist → [Pod] [Collab] [Reply] body chat.mention + specialist → [Pod] [Reply] body thread.mention + non-specialist→ [Pod] [Collab] body thread.mention + specialist → [Pod] body Rules: - Pod-context cue: ALWAYS (existing behavior, untouched) - Consultation cue: WHEN target agent is NOT a code specialist (codex / cloud-codex / claude-code). Both chat.mention AND thread.mention paths — code-help is a useful affordance in either context. - Reply-mechanics cue: ONLY for chat.mention. thread.mention replies post via a different openclaw path and don't have the heartbeat- clobber race shape. New helper: buildContentForTarget(podId, rawContent, eventType, targetAgentName) — single place to extend the cue composition in future iterations. Applied at all 3 enqueue paths (direct-match, agentName fallback, mention-autoJoin). Tests cover all 5 combinations (4 from the matrix + claude-code parity check for specialist gating). Closes #416 and #417 — both supersede here. Also incorporates reviewer's suggestion to soften the 403 case in the consultation cue: 'if you get a 403, they're not [a peer] — skip', so agents don't silently fail when a specialist isn't in any shared pod.
samxu01
added a commit
that referenced
this pull request
May 22, 2026
…416 + #417) (#420) Per code-review feedback on PR #416 + #417: those two PRs touch the same content-build expression in enqueueMentions and have a mechanical merge conflict — whichever lands second hits it. Plus #416 didn't gate on eventType, so thread.mention events got the consultation cue erroneously. This PR composes both cues in one place with the correct 4-way matrix: chat.mention + non-specialist → [Pod] [Collab] [Reply] body chat.mention + specialist → [Pod] [Reply] body thread.mention + non-specialist→ [Pod] [Collab] body thread.mention + specialist → [Pod] body Rules: - Pod-context cue: ALWAYS (existing behavior, untouched) - Consultation cue: WHEN target agent is NOT a code specialist (codex / cloud-codex / claude-code). Both chat.mention AND thread.mention paths — code-help is a useful affordance in either context. - Reply-mechanics cue: ONLY for chat.mention. thread.mention replies post via a different openclaw path and don't have the heartbeat- clobber race shape. New helper: buildContentForTarget(podId, rawContent, eventType, targetAgentName) — single place to extend the cue composition in future iterations. Applied at all 3 enqueue paths (direct-match, agentName fallback, mention-autoJoin). Tests cover all 5 combinations (4 from the matrix + claude-code parity check for specialist gating). Closes #416 and #417 — both supersede here. Also incorporates reviewer's suggestion to soften the 403 case in the consultation cue: 'if you get a 403, they're not [a peer] — skip', so agents don't silently fail when a specialist isn't in any shared pod.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Kernel-side mitigation for the openclaw heartbeat-clobbers-mention bug (3 reproductions in smoke 2026-05-20: Nova c3, Pixel c7, Ops c7). Adds a tight inline cue to every
chat.mentionpayload.content telling the model to commit its reply via explicitcommonly_post_messageBEFORE returning its final assistant turn.The bug
When an openclaw agent processes a
chat.mentionAND a heartbeat trigger fires in the same model session before the mention reply is committed, the openclaw run-loop's final-turn auto-post emits the heartbeat'sHEARTBEAT_OK(the LAST assistant turn) and clobbers the in-flight mention answer. Nova's session log proves it: she produced a 7-row table in assistant content, her next assistant turn wasHEARTBEAT_OK, and the table never landed in chat.The cue
Scoping
Added ONLY to
chat.mentionevents.thread.mentionreplies post via a different path on the openclaw side (thread comments aren't subject to the same final-turn auto-post race), so the cue would be noise there.Why not upstream fix
Upstream fix in the openclaw fork is a larger change (session-key per-event-type, OR a final-turn selector that prefers mention-reply over heartbeat-reply when both produced output). That's worth doing, but this kernel-side mitigation is independent — it works even if the model is the only thing that changes behavior. Once committed via tool call, the heartbeat trigger is harmless.
Token cost
~80 tokens per chat.mention. Tradeoff is acceptable against silent-loss of mention replies — the smoke had 3 cases where good answers vanished.
Test plan
agentMentionService.test.js:chat.mentionpayload includes both pod-context AND reply-mechanics cues, original message preservedthread.mentionpayload includes pod-context only (reply-mechanics OMITTED)@novatask; check her session log for the cue inline; verify she callscommonly_post_messageexplicitly (not relying on auto-post)Merge-order note
Textually overlaps with PR #416 (cross-runtime consultation cue, same function file). Both touch the content-build expression in
enqueueMentions. Whichever lands second needs a small rebase to compose both cues — they're independent in intent and can stack cleanly.🤖 Generated with Claude Code