fix(sessions): bulk archive never hides a live chat because its old history matched

`hermes sessions archive --older-than N` (SessionDB.archive_sessions) selected
every ENDED row matching the filters, which includes the compression ancestors
of a long conversation: they are ended (`end_reason='compression'`) and old by
construction. `set_session_archived` then flipped `archived` across the whole
lineage — including the OPEN, actively written, lease-holding live tip — and
the default `archived=exclude` listing (Desktop sidebar, `hermes sessions
list`, /resume) lost the chat while messages kept flowing.

A lineage is now matched through its tip only: `_prune_filter_where` gains
`lineage_tips_only`, which `archive_sessions` always sets and the CLI sets in
archive mode so the dry-run preview and the confirmation count show exactly
the rows the archive will touch. An idle, ended tip still archives its whole
chain (the lineage stays one unit — the listed row is the root, projected to
the tip, so sparing only the tip would leave the chat hidden anyway).
Prune is unchanged. Docs: the bulk-archive section says how compacted
conversations are matched.

Direction from #115500 by @whyyagswhy (automatic archives must not hide the
open live tip); the mechanism differs because the listing keys on the root.

Co-authored-by: whyyagswhy <166958865+whyyagswhy@users.noreply.github.com>
This commit is contained in:
teknium1
2026-09-19 23:49:22 -07:00
committed by Teknium
parent 2edbd4ed5c
commit ad1707e570
5 changed files with 54 additions and 7 deletions

View File

@@ -1651,8 +1651,11 @@ class SessionSessionsMixin:
self, older_than_days: Optional[float] = None, source: str = None, **filters,
) -> int:
"""Bulk soft-hide with prune_sessions' filter surface, via set_session_archived so each lineage
flips as a unit; idempotent. Returns matches."""
flips as a unit; idempotent. Returns matches. A lineage is matched through its TIP only: an
old compression ancestor never qualifies on its own age, or the fan-out would hide an open,
recently active continuation (#115489)."""
filters.setdefault("archived", False)
filters["lineage_tips_only"] = True
rows = self.list_prune_candidates(older_than_days=older_than_days, source=source, **filters)
for row in rows:
self.set_session_archived(row["id"], True)