#13507: fix(session): keep daily resets from metadata updates
size: S
Noticed that my main telegram thread got reset daily, but the Discord channels my bot were in, kept their context. Difference was that only my main channel has heartbeats. Tasked Opus with finding the bug, creating a failing test, fixing the bug. Tested by setting expiration time to 17:00, and seeing a discord session expire.
Potentially related:
- #11224
- #11520
---
## Summary
- prevent metadata-only inbound updates from bumping session updatedAt, so daily resets can trigger for group/channel sessions
- add regression coverage for daily resets blocked by inbound metadata and for heartbeat runs after the boundary
## Investigation
- traced inbound flow (recordInboundSession -> recordSessionMetaFromInbound -> mergeSessionEntry) and found updatedAt was always bumped to Date.now()
- daily reset in initSessionState uses updatedAt, so the metadata write made sessions look fresh and blocked resets
## Testing (lightly tested)
- bunx vitest src/auto-reply/reply/session.test.ts src/infra/heartbeat-runner.respects-ackmaxchars-heartbeat-acks.test.ts
- Manual (user): set expiration to 17:00, message at 17:00 created new session
## AI-assisted
- Yes, AI-assisted
- I understand what this change does and why it fixes the issue
- Prompts/session logs available on request
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Modified `mergeSessionEntry` in `src/config/sessions/types.ts:120` to preserve existing `updatedAt` when no explicit timestamp is provided in the patch. This prevents metadata-only inbound updates (like `recordSessionMetaFromInbound`) from bumping session timestamps, which was blocking daily session resets for group/channel sessions that received heartbeats or other metadata updates.
The change works with the existing call sites:
- `recordSessionMetaFromInbound` (line 850 in store.ts) passes metadata patches without `updatedAt`, so sessions correctly retain their original timestamp
- `updateLastRoute` (line 919 in store.ts) explicitly provides `updatedAt: Math.max(existing?.updatedAt ?? 0, now)`, so routing updates correctly advance the timestamp
The test coverage validates all three paths: metadata-only merges preserve timestamps, explicit updates advance timestamps, and new entries default to `Date.now()`. The integration test confirms daily reset freshness evaluation works correctly after metadata merges.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The fix is surgically targeted (single-line change) with comprehensive test coverage. The logic correctly preserves timestamps for metadata-only updates while maintaining explicit timestamp updates. All three behavioral paths are tested, and the integration test validates the actual daily reset scenario.
- No files require special attention
<sub>Last reviewed commit: 9c061d9</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#22887: Preserve daily reset timestamps for inbound session metadata updates
by graysurf · 2026-02-21
87.7%
#16061: fix(sessions): tolerate invalid sessionFile metadata
by haoyifan · 2026-02-14
80.2%
#4664: fix: per-session metadata files to eliminate lock contention
by tsukhani · 2026-01-30
78.4%
#11613: fix: clear stale model metadata on /new and /reset
by mcaxtr · 2026-02-08
77.7%
#6653: fix: persist archived session entry on /new or /reset
by leicao-me · 2026-02-01
77.5%
#20770: fix: prevent stale session-entry overwrite during reset-model persi...
by coygeek · 2026-02-19
77.3%
#22433: Slack: fix thread context loss after session reset
by stgarrity · 2026-02-21
77.1%
#7537: fix(sessions): reset token counts on /new for channel sessions
by SoniAssist · 2026-02-02
76.9%
#4693: fix: keep main session displayName on outbound sends
by ManojINaik · 2026-01-30
76.7%
#14243: fix: fire session-memory hook on auto-resets + topic-aware memory p...
by TheDude135 · 2026-02-11
76.4%