#22351: fix(cron): deduplicate event text in cron heartbeat prompt
size: XS
Cluster:
Heartbeat Functionality Improvements
## Summary
- **Problem**: When a main-session cron job fires, event text appears **twice** in the model input — once via `prependSystemEvents` (`System:` line) and once via `buildCronEventPrompt` (prompt body embedding)
- **Why it matters**: The model sees redundant content, and the old wrapper prompt ("relay this reminder") misframes task-oriented cron jobs as simple reminders
- **What changed**: `buildCronEventPrompt` now returns a minimal context note (`---\nThis system message is triggered by a scheduled cron job.`) instead of embedding the event text
- **What did NOT change**: `prependSystemEvents` delivery path, noise event filtering (`isCronSystemEvent`), heartbeat prompt replacement logic, isolated-agent cron execution
## Change Type
Bug fix
## Scope
Gateway / orchestration
## Linked Issue/PR
- Related #20342 (reports the exact same duplication)
- Related #13317 (ghost reminder bug that led to the embedding workaround)
## User-visible / Behavior Changes
- Cron event text no longer appears twice in model input for main-session cron jobs
- The prescriptive "relay this reminder to the user in a helpful and friendly way" instruction is removed — cron payload text itself now defines expected behavior
## Security Impact
- 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
## Repro + Verification
### Environment
- OS: Linux
- Runtime/container: Node.js
- Integration/channel: Any channel with main-session cron jobs
### Steps
1. Create a cron job with `sessionTarget: "main"` and `wakeMode: "now"`
2. Wait for the cron job to fire
3. Observe the model input in logs or debug output
### Expected
- Event text appears once as `System: [timestamp] <event text>`
- Prompt body contains only the context note, not duplicated event text
### Actual (before fix)
- Event text appears twice: once as `System:` line, once embedded in prompt body
- Prompt body includes prescriptive "relay this reminder" instruction
## Evidence
- Code trace confirming dual delivery path: `peekSystemEventEntries` (heartbeat-runner.ts) reads events non-destructively, then `drainSystemEventEntries` (session-updates.ts) reads the same queue again during `getReplyFromConfig` pipeline
- Historical git blame showing the embedding was added as a workaround for #13317 (ghost reminders), but the actual fix was noise event filtering — making the embedding redundant
## Human Verification
- Verified scenarios: Confirmed cron event text appears only once in model input after the change
- Edge cases checked: Empty event text still returns HEARTBEAT_OK guidance; noise events (HEARTBEAT_OK, heartbeat poll/wake) still filtered correctly by `isCronSystemEvent`
- What was **not** verified: `wakeMode: "next-heartbeat"` path (uses same `buildCronEventPrompt`, should behave identically)
## Compatibility / Migration
- Backward compatible? Yes
- Config/env changes? No
- Migration needed? No
## Failure Recovery
- How to disable/revert this change quickly: Revert the single function change in `src/infra/heartbeat-events-filter.ts`
- Files/config to restore: `src/infra/heartbeat-events-filter.ts`
- Known bad symptoms reviewers should watch for: If cron events stop producing model responses, check that `prependSystemEvents` is still delivering the `System:` lines correctly
## Risks and Mitigations
- Risk: For information-only cron events where no response is needed, the model may not know to reply HEARTBEAT_OK since the prescriptive instruction is removed
- Mitigation: Users can include HEARTBEAT_OK guidance in their cron event text (e.g., "Reply HEARTBEAT_OK if no action needed"). This puts control in the hands of the job author.
---
Generated with [Claude Code](https://claude.com/claude-code) + approved by seheepeak
Most Similar PRs
#15422: fix(auto-reply): keep cron systemEvent payloads that start with 'Re...
by liuxiaopai-ai · 2026-02-13
74.8%
#21014: fix(cron): suppress main-session summary for HEARTBEAT_OK responses
by nickjlamb · 2026-02-19
74.3%
#11657: fix(cron): treat skipped heartbeat as ok for one-shot jobs
by DukeDeSouth · 2026-02-08
74.0%
#20521: feat(heartbeat): inject active cron job summary into heartbeat prompt
by maximalmargin · 2026-02-19
73.4%
#14677: fix: strengthen CRON_EVENT_PROMPT to ensure agents execute tasks
by botverse · 2026-02-12
73.4%
#6522: fix(cron): deliver original message when agent response is heartbea...
by sidmohan0 · 2026-02-01
70.7%
#5498: Cron: honor next-heartbeat
by sebslight · 2026-01-31
70.4%
#8418: fix: notify user after consecutive heartbeat/cron failures
by liaosvcaf · 2026-02-04
70.2%
#21279: Fix/sessions list cron model override
by altaywtf · 2026-02-19
70.1%
#19270: fix: retry event-driven heartbeats blocked by requests-in-flight
by deggertsen · 2026-02-17
69.9%