From 3deb51d27f97735dd67afa41045337e61c480978 Mon Sep 17 00:00:00 2001
From: metaclassing <1027733+metaclassing@users.noreply.github.com>
Date: Thu, 20 Aug 2026 14:51:02 +0000
Subject: [PATCH] fix(session): tolerate catalog churn during memory admission
---
tests/test_session.py | 48 +++++++++++++++++++++++++++++++++++++++
turnstone/core/session.py | 22 +++++++++++++++++-
2 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/tests/test_session.py b/tests/test_session.py
index 8b824a76..37b9df25 100644
--- a/tests/test_session.py
+++ b/tests/test_session.py
@@ -8041,6 +8041,54 @@ class TestMemoryIndexSnapshotLifecycle:
assert get_storage().get_memory_index_snapshot(session.ws_id) is not None
+ def test_first_capture_waits_through_catalog_prefix_churn(self, tmp_db):
+ """Cold MCP catalog callbacks may invalidate several capture plans."""
+ session = _make_registered_session(ws_id="catalog-churn-index", user_id="owner")
+ generation = session._claim_generation(principal_id="owner")
+ session._memory_index_admission_generation = generation
+ candidate = {
+ "content": '',
+ "principal_id": "owner",
+ "project_id": "",
+ "project_name": "",
+ "entry_count": 0,
+ "char_count": 66,
+ "invalid_description_count": 0,
+ }
+ attempts = 0
+
+ def acquire(
+ _ws_id: str,
+ _principal_id: str,
+ *,
+ commit_context: Any,
+ ) -> dict[str, Any]:
+ nonlocal attempts
+ attempts += 1
+ if attempts <= 4:
+ session._invalidate_system_prefix()
+ with commit_context(candidate):
+ pass
+ return candidate
+
+ with (
+ patch(
+ "turnstone.core.session.acquire_memory_index_snapshot",
+ side_effect=acquire,
+ ),
+ patch("turnstone.core.session.time.sleep") as sleep,
+ ):
+ session._admit_memory_index_request(
+ session._primary_lane(),
+ my_generation=generation,
+ principal_id="owner",
+ )
+
+ assert attempts == 5
+ assert [item.args[0] for item in sleep.call_args_list] == [0.025, 0.05, 0.1, 0.2]
+ assert session._memory_index_snapshot == candidate
+ assert "