From 2e1c0291b8810f604e1a7b72f3d51fcc42b03333 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 3 Nov 2026 01:39:40 -0800 Subject: [PATCH] fix(cron): scheduled posts reach a room the root gateway imported Greptile "Imported rooms miss scheduled posts": a standalone room the root gateway imported is fenced in shared-state.db and lives on in the ROOT home's state.db, but Room settings in a named-profile view advertises room:@research. The lookup checked only the research store and the frozen shared copy, so the post failed as "managed by another gateway". A shared row handed to the gateway store now resolves to the root gateway's store when that gateway serves it (the same handoff record tui_gateway/methods_groups_forward routes by). --- cron/scheduler_delivery_room.py | 19 ++++++++++++++++--- tests/cron/test_cron_room_delivery.py | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cron/scheduler_delivery_room.py b/cron/scheduler_delivery_room.py index 4d4af13b37..08b3700c7c 100644 --- a/cron/scheduler_delivery_room.py +++ b/cron/scheduler_delivery_room.py @@ -59,11 +59,24 @@ def _find_room_store(room_id: str, profile: Optional[str]) -> Optional[Path]: the room's owner entry: the shared-store import copies rooms out of ``state.db`` without deleting them, and nothing serves that leftover copy, so a post there would never reach the visible room.""" from gateway.hosted_rooms import probe_hosted_room - from gateway.session_hosted_service import probe_native_room_owner session_store, shared_store = _room_stores(profile) - if probe_hosted_room(session_store, room_id=room_id) and probe_native_room_owner(session_store, room_id=room_id): + if _serves(session_store, room_id): return session_store - return shared_store if probe_hosted_room(shared_store, room_id=room_id) else None + if not probe_hosted_room(shared_store, room_id=room_id): + return None + # A shared room the root gateway imported is fenced here and lives on in the ROOT home's state.db + # (only that gateway imports), whichever profile's view advertised the target. + from gateway.hosted_rooms_gateway_import import handed_room_ids + root_store = shared_store.with_name("state.db") + if room_id in handed_room_ids(shared_store) and _serves(root_store, room_id): + return root_store + return shared_store + + +def _serves(store: Path, room_id: str) -> bool: + from gateway.hosted_rooms import probe_hosted_room + from gateway.session_hosted_service import probe_native_room_owner + return probe_hosted_room(store, room_id=room_id) and probe_native_room_owner(store, room_id=room_id) def deliver_to_room(job: dict, content: str, address: str, *, for_failure: bool = False) -> Optional[str]: diff --git a/tests/cron/test_cron_room_delivery.py b/tests/cron/test_cron_room_delivery.py index ce05f4fbf0..0e1a5b71fd 100644 --- a/tests/cron/test_cron_room_delivery.py +++ b/tests/cron/test_cron_room_delivery.py @@ -81,6 +81,24 @@ def test_post_goes_to_the_session_store_when_its_gateway_owns_the_room(room_stor assert _posts(room_store) == [] +def test_named_profile_target_reaches_a_room_the_root_gateway_imported(room_store): + """The root gateway imported the standalone room (shared copy fenced, its own state.db owns it); + Room settings in the research view advertises room:@research, which must post to that store.""" + from gateway.hosted_rooms_gateway_import import import_standalone_rooms + root_store = hosted_rooms.default_db_path().with_name("state.db") + with closing(sqlite3.connect(root_store)) as conn, conn: # the root gateway's SessionDB owner table + conn.execute("CREATE TABLE state_meta (key TEXT PRIMARY KEY, value TEXT)") + assert import_standalone_rooms(root_store) == 1 + research = get_hermes_home() / "profiles" / "research" + research.mkdir(parents=True) + (research / "config.yaml").write_text("{}\n", encoding="utf-8") + + assert delivery._deliver_result(_job(deliver=f"room:{ROOM_ID}@research"), "Digest ready") is None + + assert [p["payload"]["text"] for p in _posts(root_store)] == ["Digest ready"] + assert _posts(room_store) == [] + + def test_room_a_profile_gateway_took_over_names_the_profile_remedy(room_store): from gateway.hosted_room_replicas import demote_room state = hosted_rooms.room_state(room_store, room_id=ROOM_ID)