diff --git a/hermes_state_search.py b/hermes_state_search.py index 8f4ee0d4d4..f674e03d51 100644 --- a/hermes_state_search.py +++ b/hermes_state_search.py @@ -966,7 +966,7 @@ class SessionSearchMixin: batch = matches[start:start + 500] contexts = {match["id"]: [] for match in batch} try: - sql = _CONTEXT_WINDOW_SQL.format(ids=",".join("?" for _ in batch)) + sql = _CONTEXT_WINDOW_SQL.format(ids=",".join("?" for _ in contexts)) with self._read_ctx() as conn: rows = conn.execute(sql, list(contexts)).fetchall() for row in rows: diff --git a/tests/test_session_search_context_batch.py b/tests/test_session_search_context_batch.py index 0ea4e0efdf..9f7758bfda 100644 --- a/tests/test_session_search_context_batch.py +++ b/tests/test_session_search_context_batch.py @@ -25,3 +25,20 @@ def test_search_context_batches_hits_without_cross_session_neighbors(tmp_path): projected = db.search_messages("needleprobe", fields=["id"], limit=20) assert len(projected) == len(rows) assert not any("WITH target AS" in q for q in sql) + + +def test_context_batches_keep_tied_timestamp_order_and_duplicate_hits(tmp_path): + with SessionDB(db_path=tmp_path / "ties.db") as db: + db.create_session("ties", "cli") + db.append_messages_batch("ties", [ + {"role": "user", "content": f"needleprobe {i}", "timestamp": 10.0} + for i in range(502) + ]) + rows = db.search_messages("needleprobe", limit=502) + assert len(rows) == 502 + by_id = sorted(rows, key=lambda row: row["id"]) + for i, row in enumerate(by_id): + expected = [f"needleprobe {j}" for j in range(max(0, i - 1), min(502, i + 2))] + assert [r["content"] for r in row["context"]] == expected + duplicate = db._finalize_search_matches([dict(by_id[250]), dict(by_id[250])]) + assert duplicate[0]["context"] == duplicate[1]["context"] == by_id[250]["context"]