#22887: Preserve daily reset timestamps for inbound session metadata updates
size: S
# Preserve daily reset timestamps for inbound session metadata updates
Closes #21161.
## Summary
Fixes a session reset regression where inbound metadata/route writes refreshed `updatedAt` before `initSessionState` freshness checks. This caused group/channel sessions to appear perpetually fresh and skip daily reset boundaries.
## Problem
- Expected: Group/channel sessions reset after the daily boundary (default 4am) when the previous activity was before the boundary.
- Actual: Pre-reply inbound metadata writes updated `updatedAt` to `Date.now()`, so freshness checks always saw a fresh session.
- Impact: Medium. Group/channel sessions could persist across daily reset boundaries, accumulating stale context.
## Reproduction
1. Use default session config (`session: null`).
2. Send messages in a group/channel session (for example Discord channel).
3. Wait until after the daily reset boundary (4am local).
4. Send a new inbound message in that same session.
- Expected result: A new session is created after the boundary.
- Actual result: The existing session ID is reused because `updatedAt` was refreshed by inbound metadata updates.
## Issues Found
Severity: medium
Confidence: high
Status: fixed
| ID | Severity | Confidence | Area | Summary | Evidence | Status |
| --- | --- | --- | --- | --- | --- | --- |
| PR-22887-BUG-01 | medium | high | `src/config/sessions/store.ts`, `src/channels/session.ts` | Inbound metadata/route updates advanced `updatedAt` before freshness evaluation, blocking daily reset. | `recordSessionMetaFromInbound` / `updateLastRoute` behavior before `initSessionState` | fixed |
## Fix Approach
- Preserve `updatedAt` for existing entries in `recordSessionMetaFromInbound` so metadata-only patches do not refresh freshness timestamps.
- Added `preserveUpdatedAt` option to `updateLastRoute` and used it in `recordInboundSession` (pre-reply inbound path).
- Added regression tests that verify both metadata and route updates can preserve `updatedAt` while still updating routing/meta fields.
## Testing
- `pnpm test -- src/config/sessions.test.ts` (pass)
- `pnpm test -- src/auto-reply/reply/session.test.ts` (pass)
## Risk / Notes
- Default `updateLastRoute` behavior remains unchanged; preservation is opt-in for inbound pre-reply writes.
- This change is scoped to timestamp handling for freshness and does not alter routing key resolution.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR correctly fixes a session reset regression where inbound metadata writes were refreshing `updatedAt` timestamps before freshness evaluation, preventing daily reset boundaries from working properly for group/channel sessions.
**Changes:**
- `recordSessionMetaFromInbound` now preserves existing `updatedAt` timestamps (src/config/sessions/store.ts:851-855)
- `updateLastRoute` accepts optional `preserveUpdatedAt` parameter for inbound pre-reply path (src/config/sessions/store.ts:873, 925-928, 941-943)
- `recordInboundSession` uses `preserveUpdatedAt: true` when updating routes (src/channels/session.ts:52)
- Added regression tests verifying timestamp preservation for both metadata and route updates (src/config/sessions.test.ts:244-322)
**How it works:**
The fix ensures metadata-only updates don't advance the freshness timestamp used by `evaluateSessionFreshness` (checks `updatedAt < dailyResetAt` at src/config/sessions/reset.ts:152). By preserving the original `updatedAt`, sessions correctly appear stale after crossing the daily reset boundary.
**Implementation note:**
Both functions use a post-merge timestamp correction (after `mergeSessionEntry` overwrites with `Date.now()`) rather than preventing the overwrite. This works correctly but involves redundant operations.
<h3>Confidence Score: 5/5</h3>
- Safe to merge - fix is well-tested with clear regression coverage and preserves backward compatibility
- Fix correctly addresses the root cause by preserving timestamps during metadata-only updates. Implementation is conservative (opt-in via `preserveUpdatedAt` flag), thoroughly tested with two new regression tests covering both code paths, and default behavior remains unchanged. Only minor style improvement suggested (unreachable fallback code).
- No files require special attention
<sub>Last reviewed commit: 4fb541f</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#13507: fix(session): keep daily resets from metadata updates
by marijnvdwerf · 2026-02-10
87.7%
#4693: fix: keep main session displayName on outbound sends
by ManojINaik · 2026-01-30
80.0%
#16061: fix(sessions): tolerate invalid sessionFile metadata
by haoyifan · 2026-02-14
78.3%
#11613: fix: clear stale model metadata on /new and /reset
by mcaxtr · 2026-02-08
77.3%
#20770: fix: prevent stale session-entry overwrite during reset-model persi...
by coygeek · 2026-02-19
77.1%
#6653: fix: persist archived session entry on /new or /reset
by leicao-me · 2026-02-01
76.9%
#17527: fix(gateway): allow WebChat to attach to main session regardless of...
by Glucksberg · 2026-02-15
76.8%
#20188: fix: Update sessionFile path when rolling to new session in cron jobs
by jriff · 2026-02-18
76.4%
#22469: fix(gateway): avoid stale whatsapp labels on direct sessions
by loganprit · 2026-02-21
75.7%
#4664: fix: per-session metadata files to eliminate lock contention
by tsukhani · 2026-01-30
75.4%