Prove continuation index completeness before trusting it

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Frank SongandPaperclip authored and nesquena-hermes committed 2026-06-26 16:27:40 +00:00
1 parent eda4c50cf5
commit d68760b288
2 files changed
+144 -15

No files matched your search

+19 -14
View File
@@ -6787,20 +6787,30 @@ def _pre_compression_continuation_session_id(session) -> str | None:
return None return None
if not isinstance(entries, list): if not isinstance(entries, list):
return None return None
try:
persisted_sidecar_ids = {
path.stem
for path in SESSION_DIR.glob("*.json")
if not path.name.startswith("_") and is_safe_session_id(path.stem)
}
except Exception:
return None
indexed_ids: set[str] = set()
row_seen_ids = set(seen_ids)
rows = [] rows = []
for entry in entries: for entry in entries:
if not isinstance(entry, dict): if not isinstance(entry, dict):
continue continue
child_sid = _safe_first(entry.get("session_id")) child_sid = _safe_first(entry.get("session_id"))
if ( if not child_sid or not is_safe_session_id(child_sid):
not child_sid
or child_sid in seen_ids
or not is_safe_session_id(child_sid)
or not _safe_first(entry.get("parent_session_id"))
):
continue continue
seen_ids.add(child_sid) indexed_ids.add(child_sid)
if child_sid in row_seen_ids or not _safe_first(entry.get("parent_session_id")):
continue
row_seen_ids.add(child_sid)
rows.append(entry) rows.append(entry)
if persisted_sidecar_ids - indexed_ids - seen_ids:
return None
return rows return rows
def _child_rows_from_sidecars(seen_ids: set[str]) -> list: def _child_rows_from_sidecars(seen_ids: set[str]) -> list:
@@ -6880,14 +6890,9 @@ def _pre_compression_continuation_session_id(session) -> str | None:
memory_seen_ids: set[str] = set() memory_seen_ids: set[str] = set()
rows = _child_rows_from_memory(memory_seen_ids) rows = _child_rows_from_memory(memory_seen_ids)
index_seen_ids = set(memory_seen_ids) index_rows = _child_rows_from_index(memory_seen_ids)
index_rows = _child_rows_from_index(index_seen_ids)
if index_rows is not None: if index_rows is not None:
indexed_result = _resolve_from_rows(rows + index_rows) return _resolve_from_rows(rows + index_rows)
if indexed_result:
return indexed_result
rows.extend(_child_rows_from_sidecars(set(memory_seen_ids)))
return _resolve_from_rows(rows)
rows.extend(_child_rows_from_sidecars(memory_seen_ids)) rows.extend(_child_rows_from_sidecars(memory_seen_ids))
return _resolve_from_rows(rows) return _resolve_from_rows(rows)
@@ -10,15 +10,16 @@ SESSIONS_JS = ROOT / "static" / "sessions.js"
def _write_sidecar(session_dir: Path, sid: str, **overrides): def _write_sidecar(session_dir: Path, sid: str, **overrides):
messages = overrides.pop("messages", [])
payload = { payload = {
"session_id": sid, "session_id": sid,
"title": sid, "title": sid,
"created_at": 100.0, "created_at": 100.0,
"updated_at": 100.0, "updated_at": 100.0,
"profile": "work", "profile": "work",
"messages": [],
} }
payload.update(overrides) payload.update(overrides)
payload["messages"] = messages
(session_dir / f"{sid}.json").write_text(json.dumps(payload), encoding="utf-8") (session_dir / f"{sid}.json").write_text(json.dumps(payload), encoding="utf-8")
@@ -128,6 +129,7 @@ def test_continuation_lookup_uses_index_without_scanning_sidecars(tmp_path, monk
"created_at": 200.0, "created_at": 200.0,
} }
] ]
+ [{"session_id": f"noise{idx:08d}", "profile": "work"} for idx in range(50)]
), ),
encoding="utf-8", encoding="utf-8",
) )
@@ -182,6 +184,128 @@ def test_empty_indexed_continuation_lookup_falls_back_to_sidecars(tmp_path, monk
assert loaded == ["childempty01"] assert loaded == ["childempty01"]
def test_stale_index_with_existing_candidate_falls_back_to_newer_sidecar(tmp_path, monkeypatch):
"""A complete-looking result is not trusted when another sidecar is absent from the index."""
from api import routes, config, models
class _S:
def __init__(self, sid, profile, snap=False):
self.session_id = sid
self.profile = profile
self.parent_session_id = None
self.pre_compression_snapshot = snap
self.updated_at = 100.0
self.created_at = 100.0
snapshot = _S("snapstale001", "work", snap=True)
index_file = tmp_path / "_index.json"
_write_sidecar(
tmp_path,
"oldstale001",
parent_session_id="snapstale001",
updated_at=200.0,
created_at=150.0,
)
_write_sidecar(
tmp_path,
"newstale001",
parent_session_id="snapstale001",
updated_at=400.0,
created_at=350.0,
)
index_file.write_text(
json.dumps(
[
{
"session_id": "oldstale001",
"profile": "work",
"parent_session_id": "snapstale001",
"updated_at": 200.0,
"created_at": 150.0,
}
]
),
encoding="utf-8",
)
monkeypatch.setattr(config, "SESSION_DIR", tmp_path, raising=False)
monkeypatch.setattr(models, "SESSION_DIR", tmp_path, raising=False)
monkeypatch.setattr(routes, "SESSION_DIR", tmp_path, raising=False)
monkeypatch.setattr(routes, "SESSION_INDEX_FILE", index_file, raising=False)
monkeypatch.setattr(routes, "SESSIONS", collections.OrderedDict(), raising=False)
assert routes._pre_compression_continuation_session_id(snapshot) == "newstale001"
def test_stale_index_multihop_falls_back_to_missing_descendant_sidecar(tmp_path, monkeypatch):
"""An indexed snapshot ancestor must not hide a newer descendant omitted from the index."""
from api import routes, config, models
class _S:
def __init__(self, sid, profile, snap=False):
self.session_id = sid
self.profile = profile
self.parent_session_id = None
self.pre_compression_snapshot = snap
self.updated_at = 100.0
self.created_at = 100.0
snapshot = _S("snapstale002", "work", snap=True)
index_file = tmp_path / "_index.json"
_write_sidecar(
tmp_path,
"oldstale002",
parent_session_id="snapstale002",
updated_at=200.0,
created_at=150.0,
)
_write_sidecar(
tmp_path,
"midstale002",
parent_session_id="snapstale002",
pre_compression_snapshot=True,
updated_at=300.0,
created_at=250.0,
)
_write_sidecar(
tmp_path,
"newstale002",
parent_session_id="midstale002",
updated_at=500.0,
created_at=450.0,
)
index_file.write_text(
json.dumps(
[
{
"session_id": "oldstale002",
"profile": "work",
"parent_session_id": "snapstale002",
"updated_at": 200.0,
"created_at": 150.0,
},
{
"session_id": "midstale002",
"profile": "work",
"parent_session_id": "snapstale002",
"pre_compression_snapshot": True,
"updated_at": 300.0,
"created_at": 250.0,
},
]
),
encoding="utf-8",
)
monkeypatch.setattr(config, "SESSION_DIR", tmp_path, raising=False)
monkeypatch.setattr(models, "SESSION_DIR", tmp_path, raising=False)
monkeypatch.setattr(routes, "SESSION_DIR", tmp_path, raising=False)
monkeypatch.setattr(routes, "SESSION_INDEX_FILE", index_file, raising=False)
monkeypatch.setattr(routes, "SESSIONS", collections.OrderedDict(), raising=False)
assert routes._pre_compression_continuation_session_id(snapshot) == "newstale002"
def test_indexed_continuation_lookup_follows_snapshot_hops_without_scanning(tmp_path, monkeypatch): def test_indexed_continuation_lookup_follows_snapshot_hops_without_scanning(tmp_path, monkeypatch):
"""Repeated compression can resolve through index-backed snapshot descendants.""" """Repeated compression can resolve through index-backed snapshot descendants."""
from api import routes, config from api import routes, config