fix(state): release archived Bot Chat title

This commit is contained in:
KoNit-K
2026-09-14 21:23:43 +08:00
committed by Teknium
parent a654de291a
commit b6207cb590
2 changed files with 28 additions and 1 deletions

View File

@@ -97,7 +97,7 @@ class SessionTitlesMixin:
return 0
if title:
conflict = conn.execute(
"SELECT id FROM sessions WHERE title = ? AND id != ?", (title, session_id),
"SELECT id, archived, hidden FROM sessions WHERE title = ? AND id != ?", (title, session_id),
).fetchone()
if conflict:
conflict_id = conflict["id"]
@@ -105,6 +105,16 @@ class SessionTitlesMixin:
# user, so transfer it onto the tip (uniqueness + lineage kept).
if self._is_compression_ancestor(conn, ancestor_id=conflict_id, descendant_id=session_id):
conn.execute("UPDATE sessions SET title = NULL WHERE id = ?", (conflict_id,))
# A deliberately archived hidden Bot Chat is a retired registry
# entry, not a live identity. Retire its name in the same title
# transaction so a replacement can become the sole canonical row;
# the old session remains archived and otherwise untouched.
elif (title == self.CANONICAL_BOT_CHAT_TITLE and bool(conflict["archived"])
and bool(conflict["hidden"])):
conn.execute(
"UPDATE sessions SET title = NULL, title_source = NULL WHERE id = ?",
(conflict_id,),
)
else:
raise ValueError(f"Title '{title}' is already in use by session {conflict_id}")
# CAS on the values just read (``IS`` is NULL-safe): a concurrent write between

View File

@@ -61,6 +61,23 @@ def test_visible_session_titled_bot_chat_stays_renameable(db):
assert db.get_session("ordinary")["title"] == "renamed away"
def test_deliberately_archived_canonical_chat_releases_name_for_replacement(db):
"""Retiring a Bot Chat must not leave its registry name permanently locked."""
retired = _make_canonical(db, "retired")
assert db.set_session_archived(retired, True)
db.create_session("replacement", source="desktop")
assert db.set_session_title("replacement", SessionDB.CANONICAL_BOT_CHAT_TITLE)
assert db.set_session_hidden("replacement", True)
# The retired row stays archived, while exact-title resolution reaches the
# replacement that Bot Mode will subsequently open.
assert db.get_session(retired)["archived"]
assert db.get_session(retired)["title"] is None
row = db.get_session_by_title(SessionDB.CANONICAL_BOT_CHAT_TITLE)
assert row and row["id"] == "replacement"
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