Skip to content

fix(compression): tripped sessions recover across gateway agent rebuilds — durable recovery deadline column (salvage #100185) - #101192

Merged
teknium1 merged 1 commit into
mainfrom
p1/comp-recovery-deadline
Sep 2, 2026
Merged

fix(compression): tripped sessions recover across gateway agent rebuilds — durable recovery deadline column (salvage #100185)#101192
teknium1 merged 1 commit into
mainfrom
p1/comp-recovery-deadline

Conversation

@teknium1

@teknium1 teknium1 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Blocked (anti-thrash-tripped) sessions now earn their 300s recovery probe across gateway agent rebuilds: the recovery deadline is persisted on the session row as a wall-clock epoch instead of living in a process-local time.monotonic() value that every fresh ContextCompressor reset — so a long messaging conversation above the compression threshold no longer stays blocked forever.

Minimal salvage of #100185 by @Komzpa (credited via Co-authored-by). Per Teknium's decision, only the durable-deadline piece is carried: proper SessionDB column (not the model_config JSON blob), 300s window unchanged, no probe-lease/fencing state machine, no rerouted breaker test.

Changes

  • hermes_state_common.py: new sessions.compression_recovery_deadline REAL column in SCHEMA_SQL (declarative column reconciliation adds it to existing DBs); SCHEMA_VERSION 26 → 27.
  • hermes_state.py: SessionDB.get_compression_recovery_deadline() / set_compression_recovery_deadline() (0/None = disarmed), mirroring the compression_ineffective_count accessors.
  • agent/context_compressor.py:
    • _ANTI_THRASH clock now uses time.time() (wall clock) so it is meaningful across processes.
    • _load_anti_thrash_recovery_deadline() in bind_session_state(); _set_anti_thrash_recovery_deadline() persists on change only (arm / disarm / probe-granted).
    • No stored deadline → fresh compressor still starts a full window blocked (fix: preserve resumed compression hysteresis #54923 restart-must-not-disarm contract preserved). Stored deadline more than one window in the future (backward clock jump) → re-armed to now + 300s, so a wait can never exceed one window.
  • tests/agent/test_compression_anti_thrash_recovery.py: existing tests moved to the wall clock; new TestDurableDeadline (fresh compressors resume the same window, stay blocked inside it, backward-jump bound, disarm-on-clear, SessionDB round trip).
  • tests/state/test_session_git_metadata_generation.py: == SCHEMA_VERSION == 26 change-detector literal → == SCHEMA_VERSION.

Validation

Check Result
tests/agent/test_compression_anti_thrash_recovery.py + ..._anti_thrash_persistence.py + test_compaction_anti_thrash.py + ..._split_failure_cooldown.py + tests/state/test_session_git_metadata_generation.py 37 passed
Sabotage run (compressor fix stashed, new tests kept) 6 failed / 3 passed — the regression tests bite
tests/agent/test_compression_concurrent_fork.py + tests/test_hermes_state.py + test_context_compressor*.py (3 files) 457 passed, 1 failed (TestFTS5Search::test_search_projection_skips_context_enrichment_queries — fails identically on untouched origin/main, unrelated)
ruff check on touched files clean
scripts/audit_pr_attribution.py --fix all emails mapped

Live repro: real-import harness (SessionDB on a temp HERMES_HOME, set_compression_ineffective_count(sid, 2), three FRESH compressors bound to the same row with the wall clock advanced +20 min and +40 min between them) — before: B (fresh, +20min) blocked: True, C (fresh, +40min) blocked: True, db count stays 2 → REPRO_FIRES: fresh compressors never probe; deadline restarts per instance; after: B (fresh, +20min) blocked: False db count: 1, C ... blocked: FalseCLEAN.

Refs #100185 — salvaged from @Komzpa's PR (authorship credited via Co-authored-by); the broader lease/fencing design there is intentionally not carried.

Infographic

comp-recovery-deadline

…y agent rebuilds cannot block a session forever

The #14694 recovery clock (`_anti_thrash_recovery_deadline`) was a
process-local `time.monotonic()` value zeroed in `bind_session_state()`.
The gateway rebuilds the AIAgent (and its ContextCompressor) on every
cache eviction, so each fresh compressor bound to a durably tripped
session row (#69872) re-armed a full 300s window and the half-open probe
never fired — a long messaging conversation above the threshold stayed
blocked permanently.

Persist the deadline as a wall-clock epoch in a new
`sessions.compression_recovery_deadline REAL` column (declarative column
reconciliation; SCHEMA_VERSION 26 -> 27) with
`SessionDB.get/set_compression_recovery_deadline`. The compressor loads it
in `bind_session_state()` and writes it on change only via
`_set_anti_thrash_recovery_deadline()`. A fresh compressor with no stored
deadline still starts a full window blocked (#54923 restart contract); one
that loads an armed deadline resumes that window. Backward clock jumps are
bounded to one window. The 300s window is unchanged.

Minimal salvage of #100185 (the probe-lease/fencing state machine and
model_config-blob storage were not carried).

Refs #100185
Co-authored-by: Komzpa <me@komzpa.net>
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/compression Context compression and continuation sessions sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Sep 2, 2026
@teknium1
teknium1 merged commit 238b6c1 into main Sep 2, 2026
41 checks passed
@teknium1
teknium1 deleted the p1/comp-recovery-deadline branch September 2, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/compression Context compression and continuation sessions comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades 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.

2 participants