Files
aiturk-hermes-ide/tests/hermes_state/test_86366_watermark_interplay.py
T

160 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Regression tests for #86366 × watermark interplay in archive_and_compact.
Two sibling sites of the superseded-duplicate class:
* carried-forward tail originals (tail_count) — the compressor's protected
tail rides inside compacted_messages verbatim; originals must take rewind
flags (active=0, compacted=0), not compacted=1.
* concurrent-tail originals (watermark clone, #75316) — rows appended during
the summary call are re-inserted byte-exact as live clones; their originals
are the SAME superseded-duplicate class and must take rewind flags too.
And the interaction bound: with both watermark and tail_count set, the
rewind-target LIMIT walk must not consume concurrent-append rows (above the
watermark) as if they were carried-forward tail.
"""
from __future__ import annotations
import sqlite3
from pathlib import Path
import pytest
from hermes_state import SessionDB
@pytest.fixture
def db(tmp_path: Path):
handle = SessionDB(db_path=tmp_path / "state.db")
try:
yield handle
finally:
handle.close()
def _flags(db: SessionDB, session_id: str):
conn = sqlite3.connect(db.db_path)
conn.row_factory = sqlite3.Row
try:
return [
dict(r)
for r in conn.execute(
"SELECT id, active, compacted, content FROM messages "
"WHERE session_id = ? ORDER BY id",
(session_id,),
).fetchall()
]
finally:
conn.close()
class TestWatermarkTailCountInterplay:
def test_rewind_walk_bounded_at_watermark(self, db: SessionDB) -> None:
"""A concurrent append above the watermark must not steal a rewind
LIMIT slot from a genuine carried-forward tail original."""
sid = "s-wm-bound"
db.create_session(sid, source="cli")
for i in range(4):
db.append_message(sid, "user", content=f"seen-{i}")
watermark = db.get_active_message_watermark(sid)
# Concurrent append AFTER the compressor snapshotted its input.
db.append_message(sid, "user", content="concurrent-append")
db.archive_and_compact(
sid,
[
{"role": "user", "content": "SUMMARY"},
{"role": "user", "content": "seen-2"},
{"role": "user", "content": "seen-3"},
],
watermark=watermark,
tail_count=2,
)
rows = _flags(db, sid)
by_content = {}
for r in rows:
by_content.setdefault(r["content"], []).append(r)
# Carried-forward tail originals: rewind flags (hidden from recall).
for content in ("seen-2", "seen-3"):
originals = [
r for r in by_content[content] if r["active"] == 0
]
assert originals, content
assert all(r["compacted"] == 0 for r in originals), (
f"{content} original stamped compacted=1 — recall duplicate"
)
live = [r for r in by_content[content] if r["active"] == 1]
assert len(live) == 1
# Summarized-away rows keep discoverability.
for content in ("seen-0", "seen-1"):
(row,) = by_content[content]
assert (row["active"], row["compacted"]) == (0, 1)
def test_concurrent_tail_original_takes_rewind_flags(
self, db: SessionDB
) -> None:
"""The watermark clone's original is a superseded byte-identical
duplicate — it must not satisfy recall next to its live clone."""
sid = "s-wm-clone"
db.create_session(sid, source="cli")
for i in range(3):
db.append_message(sid, "user", content=f"old-{i}")
watermark = db.get_active_message_watermark(sid)
db.append_message(sid, "user", content="mid-flight zqx-token")
db.archive_and_compact(
sid,
[{"role": "user", "content": "SUMMARY"}],
watermark=watermark,
)
rows = _flags(db, sid)
copies = [r for r in rows if "zqx-token" in r["content"]]
assert len(copies) == 2 # archived original + live clone
original = next(r for r in copies if r["active"] == 0)
clone = next(r for r in copies if r["active"] == 1)
assert original["compacted"] == 0, (
"concurrent-tail original stamped compacted=1 — it would be "
"recalled alongside its live clone (same class as #86366)"
)
assert clone["compacted"] == 0
# Recall filter surfaces exactly ONE copy.
hits = [
r
for r in db.search_messages("zqx-token")
if r.get("session_id") == sid
]
assert len(hits) == 1
def test_recall_stable_across_generations(self, db: SessionDB) -> None:
"""search_messages hit count for a carried-forward message must not
grow with compaction generations."""
sid = "s-gen"
db.create_session(sid, source="cli")
for i in range(5):
db.append_message(sid, "user", content=f"turn-{i} gentok-{i}")
payload = [
{"role": "user", "content": "SUMMARY"},
{"role": "user", "content": "turn-3 gentok-3"},
{"role": "user", "content": "turn-4 gentok-4"},
]
counts = []
for _ in range(3):
db.archive_and_compact(sid, list(payload), tail_count=2)
counts.append(
len(
[
r
for r in db.search_messages("gentok-3")
if r.get("session_id") == sid
]
)
)
assert counts == [1, 1, 1], counts