Skip to content

fix(gateway): session recovery honors reset boundaries and real idle time (#68617 + #78618 salvage) - #82743

Merged
teknium1 merged 3 commits into
mainfrom
salvage/reset-boundary-recovery
Aug 9, 2026
Merged

fix(gateway): session recovery honors reset boundaries and real idle time (#68617 + #78618 salvage)#82743
teknium1 merged 3 commits into
mainfrom
salvage/reset-boundary-recovery

Conversation

@teknium1

@teknium1 teknium1 commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Gateway session recovery now honors /new reset boundaries and real idle time: a newer reset-ended row fences the finder from resurrecting older rows behind it, and recovered sessions keep their true last_activity_at instead of being stamped updated_at=now (which made every recovered session look zero-seconds idle to reset policy).

Combined salvage of #68617 (@Tranquil-Flow, Jul 21 — earliest submitter) and #78618 (@hillimited, Aug 4), cherry-picked with both authors preserved; the two PRs fix complementary halves of the same recovery flow and were composed onto #82633's rewritten finder.

Changes

Validation

Result
Both PRs' adapted tests + session-continuity regression file (incl. reset-boundary + recency tests) pass
tests/state/ + tests/test_hermes_state.py 259 passed
Full tests/gateway/ + tests/state/ via canonical runner green (9 plain-pytest failures reproduced identically on unmodified base — pre-existing cross-file isolation flakes, pass under the per-file runner)
Sabotage run (fence removed) both fence tests fail

Closes #68617 (credit @Tranquil-Flow) and #78618 (credit @hillimited). Part of #82616; also the surviving half of #71530's diagnosis (@FrendoWu).

Infographic

The past stays past

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 13fdecc — chore: map contributor email for hillimited

all good!

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 9, 2026
Tranquil-Flow and others added 3 commits August 9, 2026 14:58
find_latest_gateway_session_for_peer filtered non-recoverable rows out of
candidacy BEFORE ordering, so recovery could search behind a /new reset
boundary and resurrect an older still-open row for the same peer —
silently restoring the exact context the user reset.

Rebuilt against the #82633 finder (has-messages ranking +
COALESCE(last_activity_at, started_at) recency): the fence is expressed
as a NOT EXISTS guard inside both the exact-key and peer-fallback
queries — a candidate is rejected when an intentional boundary row
(session_reset / session_switch / idle / daily / suspended /
resume_pending_expired) for the same peer ended after the candidate's
last activity. If the conversation's most recent event is an intentional
reset, recovery returns nothing rather than reaching behind it.

Cherry-picked from #68617 and adapted to the rewritten finder.
(cherry picked from commit bb2c562)
Both session recovery paths (the startup stale-entry repoint and the
lazy in-message recovery) rebuilt the routing entry with updated_at=now
and never consulted _should_reset, so an opt-in idle/daily session_reset
policy was silently dead across any gateway restart: a recovered session
always looked freshly active, and since every subsequent message bumps
updated_at, a session recovered stale could then never age out at all.

Fix in three parts:

- _create_entry_from_recovered_row derives updated_at from the durable
  last_activity_at the finder already returns on the row (no extra DB
  round-trip; the original PR added SessionDB.get_last_activity for
  this, unnecessary post-#82633), falling back to created_at. An
  invalid or missing started_at now maps to epoch 0 instead of now — an
  invalid durable timestamp must look old, never freshly active.
  reset_had_activity is set from the row's durable activity/message
  signals so the continuity hint stays accurate.

- _recover_session_from_db evaluates _should_reset on the rebuilt entry:
  an overdue session is durably promoted to a reset boundary
  (promote_to_session_reset, falling back to end_session) and the stale
  mapping is dropped instead of repointed.

- _query_recoverable_session no longer reopens the row; the
  get_or_create_session recovery phase evaluates _should_reset first and
  either feeds the normal auto-reset create path (reset notice,
  prev_session_id continuity, durable promotion) or reopens and
  publishes the recovered entry exactly as before.

Behavior is unchanged under the default session_reset mode "none":
_should_reset returns None there, so recovery still resumes every
recoverable row — only users who opted into idle/daily resets see the
policy actually applied across restarts.

Cherry-picked from #78618 and adapted to the #82633 finder.
(cherry picked from commit 31c71f7)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants