Summary
The bundled coding-agent skill instructs agents to run openclaw system event --text "Done: ..." --mode now when a background task finishes. This enqueues a system event and calls requestHeartbeatNow(), which triggers the heartbeat. The LLM processes the system event and generates a response — but that response is silently dropped because heartbeat.target defaults to "none".
The conflict
- Coding-agent skill (bundled) tells agents to notify via
openclaw system event --mode now on completion
- This triggers
requestHeartbeatNow() → heartbeat fires → LLM responds
resolveHeartbeatDeliveryTarget() checks heartbeat.target → defaults to "none" → buildNoHeartbeatDeliveryTarget({ reason: "target-none" })
- Response is discarded — never delivered to any channel
The same issue applies to the built-in maybeNotifyOnExit() handler for backgrounded exec processes, which also uses enqueueSystemEvent() + requestHeartbeatNow().
Impact
A new user who:
- Sets up OpenClaw with default config
- Uses the coding-agent skill to run a background Codex/Claude task
- Waits for a completion notification
...will never receive one. They must manually ask "is it done?" to discover the task completed. The notification mechanism in the skill is effectively dead code unless the user independently discovers they need heartbeat.target: "last".
Reproduction
- Fresh OpenClaw install, default config (no
heartbeat.target set)
- Spawn a coding agent in background with the system event notification
- Wait for it to finish
- Observe: no message delivered to any channel
Possible fixes
- Option A: Change the default
heartbeat.target to "last" (breaking change for users who rely on silent heartbeats)
- Option B: Add a note to the coding-agent skill that
heartbeat.target: "last" is required for notifications
- Option C: Use a different notification mechanism that doesn't depend on heartbeat delivery (e.g., direct channel send via the message tool)
- Option D: Separate system event delivery from heartbeat delivery — system events triggered by exec completion could have their own delivery target that defaults to the originating session's last channel
Environment
- OpenClaw installed via npm (Homebrew)
- macOS (Apple Silicon)
- Discovered while running Codex background task; notification worked only after manually adding
heartbeat.target: "last" to config
References
resolveHeartbeatDeliveryTarget() in reply-*.js (line ~26974 in dist)
maybeNotifyOnExit() in pi-embedded-*.js (line ~15413 in dist)
- Heartbeat docs:
docs/gateway/heartbeat.md — confirms target: "none" is default
- Coding-agent skill:
skills/coding-agent/SKILL.md — "Auto-Notify on Completion" section
Summary
The bundled
coding-agentskill instructs agents to runopenclaw system event --text "Done: ..." --mode nowwhen a background task finishes. This enqueues a system event and callsrequestHeartbeatNow(), which triggers the heartbeat. The LLM processes the system event and generates a response — but that response is silently dropped becauseheartbeat.targetdefaults to"none".The conflict
openclaw system event --mode nowon completionrequestHeartbeatNow()→ heartbeat fires → LLM respondsresolveHeartbeatDeliveryTarget()checksheartbeat.target→ defaults to"none"→buildNoHeartbeatDeliveryTarget({ reason: "target-none" })The same issue applies to the built-in
maybeNotifyOnExit()handler for backgrounded exec processes, which also usesenqueueSystemEvent()+requestHeartbeatNow().Impact
A new user who:
...will never receive one. They must manually ask "is it done?" to discover the task completed. The notification mechanism in the skill is effectively dead code unless the user independently discovers they need
heartbeat.target: "last".Reproduction
heartbeat.targetset)Possible fixes
heartbeat.targetto"last"(breaking change for users who rely on silent heartbeats)heartbeat.target: "last"is required for notificationsEnvironment
heartbeat.target: "last"to configReferences
resolveHeartbeatDeliveryTarget()inreply-*.js(line ~26974 in dist)maybeNotifyOnExit()inpi-embedded-*.js(line ~15413 in dist)docs/gateway/heartbeat.md— confirmstarget: "none"is defaultskills/coding-agent/SKILL.md— "Auto-Notify on Completion" section