← Back to PRs

#22351: fix(cron): deduplicate event text in cron heartbeat prompt

by seheepeak open 2026-02-21 02:19 View on GitHub →
size: XS
## 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