#17812: fix: prune HEARTBEAT_OK turns from session transcript
size: S
trusted-contributor
Cluster:
HEARTBEAT_OK Suppression Fixes
## Summary
Prune HEARTBEAT_OK user and assistant messages from the session transcript when heartbeat results in shouldSkip: true. This prevents context pollution from accumulating zero-information heartbeat exchanges over time.
## Changes
- Added transcript cleanup for heartbeat-only responses
- Follows existing pattern of delivery suppression and updatedAt restoration
## Testing
- Tested that heartbeat still runs and detects alerts
- Verified silent acks are cleaned up from transcript
- No behavioral change for meaningful heartbeat content
Fixes openclaw/openclaw#17804
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR adds transcript pruning for HEARTBEAT_OK responses to prevent context pollution from zero-information heartbeat exchanges. The approach captures the session entry count before the heartbeat runs, then truncates entries back to that count if the heartbeat result is skippable.
- **Critical data loss bug**: The pre-counting guard requires `entry?.sessionFile` to be truthy (line 598), but the pruning function only checks `entry?.sessionId` (line 362). When `sessionFile` is undefined — a valid state per the `SessionEntry` type — `originalEntryCount` stays at `0` while pruning still proceeds, resulting in the entire session transcript being rewritten with only the header.
- **Race condition concern**: The raw `fs.promises.writeFile` in `pruneHeartbeatTranscript` can clobber concurrent writes from other processes (e.g., the Pi agent) that append to the session file between the read and write operations.
<h3>Confidence Score: 1/5</h3>
- This PR has a data loss bug that can destroy session transcripts when entry.sessionFile is undefined.
- The asymmetric guard between the pre-counting block (requires entry.sessionFile) and pruneHeartbeatTranscript (only requires entry.sessionId) creates a path where originalEntryCount is 0 but pruning proceeds, truncating the entire transcript to just the header. This is a silent data loss bug with no test coverage.
- src/infra/heartbeat-runner.ts — the pruneHeartbeatTranscript function and the pre-counting block both need the guard conditions aligned to prevent transcript data loss.
<sub>Last reviewed commit: c007e7e</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#15575: fix(heartbeat): suppress prefixed HEARTBEAT_OK ack replies (#15505)
by TsekaLuk · 2026-02-13
76.8%
#22277: fix: prevent heartbeat model override from bleeding into main session
by zhangjunmengyang · 2026-02-21
76.0%
#17371: fix(heartbeat): always strip HEARTBEAT_OK token from reply text
by BinHPdev · 2026-02-15
76.0%
#15050: fix: transcript corruption resilience — strip aborted tool_use bloc...
by yashchitneni · 2026-02-12
75.6%
#12786: fix: drop heartbeat runs that arrive while another run is active
by mcaxtr · 2026-02-09
75.1%
#11859: fix: filter HEARTBEAT_OK messages from chat.history when showOk is ...
by Zjianru · 2026-02-08
75.1%
#21014: fix(cron): suppress main-session summary for HEARTBEAT_OK responses
by nickjlamb · 2026-02-19
75.1%
#14241: fix(heartbeat): propagate originating session key for exec event qu...
by aldoeliacim · 2026-02-11
74.5%
#13574: fix(heartbeat): skip when session has active run
by 1kuna · 2026-02-10
74.5%
#15422: fix(auto-reply): keep cron systemEvent payloads that start with 'Re...
by liuxiaopai-ai · 2026-02-13
74.3%