89e2e4f572
Follow-up to the salvaged #99560 commit: restore the original docstring wording (user writes still always land everywhere else), name the llm-outranks-derived hole the guard now closes, and annotate the two new tests with what each pins.
112 lines
4.4 KiB
Python
112 lines
4.4 KiB
Python
"""The canonical Bot Chat's title is its identity — renames must be refused.
|
|
|
|
Bot Mode resolves a bot's forever-chat by exact-title lookup on
|
|
(profile, "Bot Chat") every time it opens; there is no session-id pointer.
|
|
A user rename therefore orphans the whole conversation: resolution misses,
|
|
the next click mints an empty replacement, and UNIQUE(title) then blocks
|
|
renaming the original back (#92473).
|
|
|
|
The guard lives in SessionDB._set_session_title — the single write path
|
|
every rename surface funnels through (gateway session.title RPC, /title,
|
|
CLI rename, REST) — and keys on hidden + exact canonical title so ordinary
|
|
sessions a user happens to call "Bot Chat" stay freely renameable.
|
|
"""
|
|
import pytest
|
|
|
|
from hermes_state import SessionDB
|
|
|
|
|
|
@pytest.fixture
|
|
def db(tmp_path):
|
|
return SessionDB(tmp_path / "state.db")
|
|
|
|
|
|
def _make_canonical(db, session_id="forever"):
|
|
db.create_session(session_id, source="desktop")
|
|
assert db.set_session_title(session_id, SessionDB.CANONICAL_BOT_CHAT_TITLE)
|
|
assert db.set_session_hidden(session_id, True)
|
|
return session_id
|
|
|
|
|
|
def test_user_rename_of_canonical_bot_chat_is_refused(db):
|
|
sid = _make_canonical(db)
|
|
with pytest.raises(ValueError, match="canonical Bot Chat"):
|
|
db.set_session_title(sid, "My cool chat")
|
|
# Identity intact: exact-title lookup still finds the forever chat.
|
|
row = db.get_session_by_title(SessionDB.CANONICAL_BOT_CHAT_TITLE)
|
|
assert row and row["id"] == sid
|
|
|
|
|
|
def test_clearing_the_canonical_title_is_refused(db):
|
|
sid = _make_canonical(db)
|
|
with pytest.raises(ValueError, match="canonical Bot Chat"):
|
|
db.set_session_title(sid, "")
|
|
row = db.get_session_by_title(SessionDB.CANONICAL_BOT_CHAT_TITLE)
|
|
assert row and row["id"] == sid
|
|
|
|
|
|
def test_rewriting_the_same_canonical_title_is_a_noop_not_an_error(db):
|
|
# The plugin's eager session.title write re-asserts the canonical title
|
|
# on creation paths; that must never start failing.
|
|
sid = _make_canonical(db)
|
|
assert db.set_session_title(sid, SessionDB.CANONICAL_BOT_CHAT_TITLE)
|
|
|
|
|
|
def test_visible_session_titled_bot_chat_stays_renameable(db):
|
|
# hidden discriminates the registry row: a normal visible session the
|
|
# user happened to name "Bot Chat" is not canonical and renames freely.
|
|
db.create_session("ordinary", source="cli")
|
|
assert db.set_session_title("ordinary", SessionDB.CANONICAL_BOT_CHAT_TITLE)
|
|
assert db.set_session_title("ordinary", "renamed away")
|
|
assert db.get_session("ordinary")["title"] == "renamed away"
|
|
|
|
|
|
def test_auto_titler_still_cannot_touch_the_canonical_row(db):
|
|
# Pre-existing provenance contract, re-pinned here: user-authority title
|
|
# outranks derived/llm, so the turn-start auto-titler can never displace
|
|
# the registry name.
|
|
sid = _make_canonical(db)
|
|
assert not db.set_auto_title(sid, "Chat about groceries", source=SessionDB.TITLE_SOURCE_LLM)
|
|
row = db.get_session_by_title(SessionDB.CANONICAL_BOT_CHAT_TITLE)
|
|
assert row and row["id"] == sid
|
|
|
|
|
|
def test_auto_titler_cannot_rename_derived_canonical_bot_chat(db):
|
|
# #99517: the guard must be provenance-blind. A derived (rank 0) canonical
|
|
# title loses to an llm (rank 1) auto-title on precedence alone, so the
|
|
# identity check — not precedence — has to stop the write.
|
|
db.create_session("derived", source="desktop")
|
|
assert db._set_session_title(
|
|
"derived",
|
|
SessionDB.CANONICAL_BOT_CHAT_TITLE,
|
|
source=SessionDB.TITLE_SOURCE_DERIVED,
|
|
)
|
|
assert db.set_session_hidden("derived", True)
|
|
|
|
assert not db.set_auto_title(
|
|
"derived",
|
|
"Renamed by titler",
|
|
source=SessionDB.TITLE_SOURCE_LLM,
|
|
)
|
|
row = db.get_session("derived")
|
|
assert row["title"] == SessionDB.CANONICAL_BOT_CHAT_TITLE
|
|
assert row["title_source"] == SessionDB.TITLE_SOURCE_DERIVED
|
|
|
|
|
|
def test_auto_titler_can_rename_visible_derived_bot_chat(db):
|
|
# Control: hidden is still the discriminator — a visible session that
|
|
# merely carries the text "Bot Chat" upgrades derived -> llm as usual.
|
|
db.create_session("visible", source="desktop")
|
|
assert db._set_session_title(
|
|
"visible",
|
|
SessionDB.CANONICAL_BOT_CHAT_TITLE,
|
|
source=SessionDB.TITLE_SOURCE_DERIVED,
|
|
)
|
|
|
|
assert db.set_auto_title(
|
|
"visible",
|
|
"Renamed by titler",
|
|
source=SessionDB.TITLE_SOURCE_LLM,
|
|
)
|
|
assert db.get_session("visible")["title"] == "Renamed by titler"
|