From 97ecfa0fc487322aa7d0dc38be323eb34fd070ef Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Fri, 29 May 2026 19:09:01 -0700 Subject: [PATCH] fix(session): extend no-FTS5 degradation to the trigram CJK index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The salvaged contributor commit guarded only messages_fts. Current main also creates a second virtual table, messages_fts_trigram (CJK substring search), whose CREATE VIRTUAL TABLE ... USING fts5 still raised "no such module: fts5" on builds without FTS5 — re-crashing SessionDB init. Wrap the trigram setup with the same guard, and broaden the test's no-fts5 mock to fail BOTH tables so the regression test actually exercises a faithful no-FTS5 build. --- hermes_state.py | 14 ++++++++++++-- tests/test_hermes_state.py | 14 +++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/hermes_state.py b/hermes_state.py index 7242e6b17..71a89a286 100644 --- a/hermes_state.py +++ b/hermes_state.py @@ -744,8 +744,18 @@ class SessionDB: # Trigram FTS5 for CJK/substring search try: cursor.execute("SELECT * FROM messages_fts_trigram LIMIT 0") - except sqlite3.OperationalError: - cursor.executescript(FTS_TRIGRAM_SQL) + except sqlite3.OperationalError as exc: + if "no such table" not in str(exc).lower(): + raise + try: + cursor.executescript(FTS_TRIGRAM_SQL) + except sqlite3.OperationalError as fts_exc: + err = str(fts_exc).lower() + if "fts5" not in err and "no such module" not in err: + raise + # Same FTS5-unavailable cause already warned about above for + # messages_fts; the trigram table is an additional CJK index, + # so just degrade silently here. CJK search falls back to LIKE. self._conn.commit() diff --git a/tests/test_hermes_state.py b/tests/test_hermes_state.py index d14f065ae..a6c33a5cb 100644 --- a/tests/test_hermes_state.py +++ b/tests/test_hermes_state.py @@ -11,12 +11,16 @@ class _NoFtsCursor(sqlite3.Cursor): """Simulate a SQLite build without the fts5 module.""" def execute(self, sql, parameters=()): - if sql.strip() == "SELECT * FROM messages_fts LIMIT 0": - raise sqlite3.OperationalError("no such table: messages_fts") + probe = sql.strip() + if probe in ( + "SELECT * FROM messages_fts LIMIT 0", + "SELECT * FROM messages_fts_trigram LIMIT 0", + ): + raise sqlite3.OperationalError("no such table: " + probe.split()[-3]) return super().execute(sql, parameters) def executescript(self, sql_script): - if "CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5" in sql_script: + if "USING fts5" in sql_script: raise sqlite3.OperationalError("no such module: fts5") return super().executescript(sql_script) @@ -167,6 +171,10 @@ class TestSessionLifecycle: db = SessionDB(db_path=tmp_path / "state.db") try: assert db._fts_enabled is False + # Neither FTS5 virtual table should have been created on a build + # that lacks the fts5 module — both init paths must degrade. + assert db._fts_table_exists("messages_fts") is False + assert db._fts_table_exists("messages_fts_trigram") is False db.create_session(session_id="s1", source="cli") db.append_message("s1", role="user", content="hello from sqlite without fts")