Skip to content

fix(compression): preserve messages across session rotation (flush-before-rotate + /compress rewrite guard)#48584

Merged
teknium1 merged 2 commits into
mainfrom
fix/compression-flush-before-rotate
Jun 18, 2026
Merged

fix(compression): preserve messages across session rotation (flush-before-rotate + /compress rewrite guard)#48584
teknium1 merged 2 commits into
mainfrom
fix/compression-flush-before-rotate

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Compression and /compress no longer lose conversation messages from state.db when a session rotates. Two distinct data-loss paths in the same family are closed.

Changes

Validation

Before After
Mid-turn auto-compress turn messages dropped from old session all current-turn messages flushed before rotation
/compress with rotation skipped original transcript overwritten with summary original transcript preserved, warning logged
  • Real-SQLite E2E (temp HERMES_HOME, real SessionDB + real _flush_messages_to_session_db): 3 current-turn messages incl. final answer persist to the old session before end_session; 2 history messages correctly skipped by identity tracking.
  • Existing suite: tests/gateway/test_compress_command.py, test_compression_session_id_persistence.py, test_compression_failure_session_sync.py — 9/9 pass.

Issues

Fixes #47202, #46567, #44794, #39704.

Authorship: first commit is @kyssta-exe's (cherry-picked, #47215); rewrite-guard commit is ours.

Infographic

compression-flush-before-rotate

kyssta-exe and others added 2 commits June 18, 2026 11:34
compress_context() rotates the session (end_session -> create_session)
mid-turn when auto-compress triggers, but never called
_flush_messages_to_session_db() first. Messages generated during the
current turn that hadn't been persisted to state.db were silently lost.

The same bug existed in cli.py:new_session() (/new command). Both paths
now flush un-persisted messages before ending the old session.
… skipped

The manual /compress handler called rewrite_transcript() unconditionally on
the session id returned by _compress_context(). When rotation does not occur
(e.g. _session_db unavailable, or the DB split raised), session_id is unchanged
and rewrite_transcript() DELETEs the original messages and replaces them with
only the compressed summary — permanent data loss (#44794, #39704).

Guard the rewrite on actual rotation: only overwrite when _compress_context
produced a new session id. Otherwise leave the original transcript intact and
log a warning.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: fix/compression-flush-before-rotate vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11025 on HEAD, 11023 on base (🆕 +2)

🆕 New issues (2):

Rule Count
unresolved-attribute 2
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
run_agent.py:2941: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`

✅ Fixed issues (1):

Rule Count
invalid-assignment 1
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`

Unchanged: 5774 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

3 participants