Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion hermes_state_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,11 @@ def _project_compression_tips(self, sessions: List[Dict[str, Any]], compact_rows
):
if key in tip_row:
merged[key] = tip_row[key]
if merged.get("title") is None:
# The title is carried root->tip AFTER the publish transaction; a rotation cut off in
# between leaves it on the ended root, and exact-title lookups (`hermes peer dm` ->
# canonical "Bot Chat") must still see the lineage under its name (#106165).
merged["title"] = s.get("title")
merged["_lineage_root_id"] = s["id"]
merged["_lineage_ids"] = chain
projected.append(merged)
Expand Down Expand Up @@ -1089,7 +1094,7 @@ def list_recent_sessions_bounded(
tip.id,
tip.source,
tip.model,
tip.title,
COALESCE(tip.title, s.title) AS title,
s.started_at AS started_at,
tip.ended_at,
tip.end_reason,
Expand Down
37 changes: 37 additions & 0 deletions tests/gateway/test_peer_dm_hidden_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,40 @@ def test_hidden_lookup_requires_title_filter_e2e(peer_gateway):
ids = [s["id"] for s in listing["data"]]
assert peer_gateway.hidden_id not in ids
assert "ordinary_1" in ids


@pytest.fixture()
def peer_gateway_compressed_hidden(peer_gateway):
"""Aged Bot Mode footprint: the hidden canonical Bot Chat has a live
compression tip (issue #106165). The tip is untitled and hidden via the
lineage; the title lives only on the ended root."""
db = peer_gateway.db
tip_id = db.create_session(
"botchat_tip_1", "gateway_botmode", parent_session_id=peer_gateway.hidden_id)
db.end_session(peer_gateway.hidden_id, "compression")
db.set_session_hidden(peer_gateway.hidden_id, True) # lineage-hide root+tip
peer_gateway.tip_id = tip_id
return peer_gateway


def test_peer_dm_reaches_compressed_hidden_bot_chat_e2e(
peer_gateway_compressed_hidden, monkeypatch, capsys):
"""Hidden canonical Bot Chat under compression: the peer lookup must still
resolve (to the live tip) instead of missing it and colliding with
UNIQUE(title) on create (HTTP 400, issue #106165)."""
gw = peer_gateway_compressed_hidden
monkeypatch.setattr(peer_cmd, "_load_peers", lambda: {"spark": {"url": gw.url}})
monkeypatch.setattr(peer_cmd, "_peer_secret", lambda name: API_KEY)

rc = peer_cmd.cmd_peer(
SimpleNamespace(peer_action="dm", target="spark", message="disk status?", json=True)
)

assert rc == 0
payload = json.loads(capsys.readouterr().out)
assert payload["reply"] == "e2e reply to: disk status?"
assert payload["session_id"] == gw.tip_id

# No duplicate "Bot Chat" row was minted; the title still lives on the root.
assert gw.db.get_session_by_title("Bot Chat")["id"] == gw.hidden_id

15 changes: 15 additions & 0 deletions tests/test_hermes_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1708,6 +1708,21 @@ def test_unrelated_session_still_conflicts(self, db):
# The unrelated holder keeps its title.
assert db.get_session("a")["title"] == "shared"

def test_projected_tip_inherits_root_title_when_untitled(self, db):
"""A rotation that ended the root before the title carry ran leaves the name on the
root only; the projected lineage row must still surface it (exact-title lookups such as
`hermes peer dm` -> canonical "Bot Chat", #106165). A titled tip keeps its own title."""
import time as _time
self._make_compression_chain(db, _time.time() - 3600)
db.set_session_title("root", "Bot Chat")

rows = db.list_sessions_rich(limit=50, order_by_last_active=True, search_query="Bot Chat")
assert [(r["id"], r["title"], r["_lineage_root_id"]) for r in rows] == [("tip", "Bot Chat", "root")]

db.set_session_title("tip", "renamed tip")
rows = db.list_sessions_rich(limit=50, order_by_last_active=True)
assert [(r["id"], r["title"]) for r in rows] == [("tip", "renamed tip")]



class TestSanitizeTitle:
Expand Down
Loading