fix(gateway): ws-orphan reaper leaves the canonical Bot Chat resumable - #93077
fix(gateway): ws-orphan reaper leaves the canonical Bot Chat resumable#93077ClintonEmok wants to merge 1 commit into
Conversation
The reap guard itself is well done: exact-title + profile-scoped identity, explicit-boundary reasons still end the row, gateway-owned sources keep the #60609 guard, and the missing-row case stays legacy. The pure
|
|
Heads-up from #92687 triage: this PR and #93217 attack the same bug from complementary angles — this one prevents the reaper from end-writing the canonical Bot Chat (future protection), #93217 makes the canonical-session lookups resurrect rows already archived by recoverable reasons (heals existing damage + other accidental-archival sources). They compose cleanly; neither blocks the other. One reviewer note on this PR as it stands: the diff also carries a complete file-picker default-path feature (picker-state.ts + tests + main.ts wiring, ref #92925) that's unrelated to the reap fix — splitting that into its own PR would let each half be reviewed and land on its own timeline. |
Bot Mode's forever-chat is identified by (profile_name, title='Bot Chat'), not by stored session ids. When the desktop's WS stayed down past _WS_ORPHAN_REAP_GRACE_S, _finalize_session ended the row with end_reason='ws_orphan_reap', which archives it; on reopen findExistingCanonicalChat located the archived row but openSession rejected it, the recreate collided with idx_sessions_title_unique, and the bot fell back to auto-titled throwaway sessions — 511 messages of history invisible from every UI path (NousResearch#92687). ws_orphan_reap is already classified as an accidental, recoverable end elsewhere (promote_to_session_reset, find_latest_gateway_session_for_peer); extend that leniency to the canonical chat at the write site: when an accidental reap reason targets a profile-scoped 'Bot Chat' row, skip only the DB end-write and log why. In-process teardown still runs, explicit user boundaries (tui_close, session.close, /new) still end it, ordinary sessions are unaffected, and the NousResearch#60609 gateway-owner guard keeps precedence. Covered by unit tests mirroring test_gateway_owned_session_reap.py: canonical row spared on reap, explicit close still ends, non-canonical titles unaffected, gateway-owned rows keep their guard, missing rows keep legacy behavior. Fixes NousResearch#92687
|
Review points addressed: Scope tightening (point 2) — Point 1 (bundled scope) — fair. #93076 carries the picker half with its own description and fixes the CI lint failure this PR's head shared; once both are green the two can land independently. Points 3 & 4 — agreed: memory-follows-usage is now documented on #93076, and the frozenset stays as the single extension point for future transport-loss reasons. On kshitijk4poor's note re: #93217 — agreed they're complementary (this prevents future archive-writes; theirs heals already-archived rows). No conflict from my side; whichever shape maintainers prefer. |
e030d25 to
b02bd3d
Compare
|
Scope-tightening fix looks right ( |
|
Closing: the user-visible symptom (#92687, canonical Bot Chat archived by the reaper) was fixed read-side by #93217 (recoverable end-reason resurrection in hermes_state.py), and the consolidated session-recovery PR #93361 now also prevents most reap-time archives from happening at all (resume cancels pending reaps; superseded runtimes end quietly as a recoverable reason). Your write-side skip became belt-and-braces on top of two landed layers. Thanks @ClintonEmok — your diagnosis of the archive collision was right, it just got fixed from the other side first. |
What does this PR do?
Bot Mode's forever-chat is identified by
(profile_name, title='Bot Chat'), not by stored session ids. When the desktop's WebSocket stayed down past_WS_ORPHAN_REAP_GRACE_S,_finalize_session(end_reason='ws_orphan_reap')ended that durable row — which archives it. On reopen,findExistingCanonicalChatlocated the archived row correctly but the open path rejected it; the plugin's recreate then collided with the globalidx_sessions_title_uniqueindex and forked throwaway auto-titled sessions (Tell me about yourself #N, ...). The bot appeared to have lost its memory, with hundreds of messages of history invisible from every UI path (#92687).ws_orphan_reapis already classified as an accidental, recoverable end reason elsewhere in the codebase —promote_to_session_resetandfind_latest_gateway_session_for_peerboth treat it as recoverable. This PR extends that same leniency to the canonical Bot Chat at the write site: when an accidental reap reason targets a profile-scopedBot Chatrow, only the DB end-write is skipped (with an info log); in-process teardown still runs.Explicit user boundaries are untouched:
tui_close,session.close,/newstill end the row normally. Ordinary sessions are unaffected, and the #60609 gateway-owner guard keeps precedence (it runs first).Related Issue
Fixes #92687
Type of Change
Changes Made
tui_gateway/server.pyCANONICAL_BOT_CHAT_TITLE = 'Bot Chat'+_is_canonical_bot_chat_row()— identity check requiring a non-emptyprofile_nameand exact title match_ACCIDENTAL_END_REASONS = {'ws_orphan_reap'}— the accidental-reason set, kept deliberately narrow_finalize_session: when the TUI owns lifecycle, the end reason is accidental, and the row is a canonical Bot Chat, skipdb.end_session(...)and log whytests/tui_gateway/test_canonical_bot_chat_reap.py(new) — mirrors the existingtest_gateway_owned_session_reap.pyconventionsHow to Test
python3 -m pytest tests/tui_gateway/test_canonical_bot_chat_reap.py tests/tui_gateway/test_gateway_owned_session_reap.py -q→ 16 passedstate.db: canonical row getsended_at/end_reason='ws_orphan_reap'. With this branch the row stays open while teardown still proceeds.session.close/ app quit ends the row as before (covered by test).Checklist
Code
fix(scope):)tests/tui_gateway/: all green except one pre-existing unrelated failure also present on clean main)Documentation and Housekeeping