From 59f1aa8bf6a3af93455fff47115f415a065b7c2f Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sun, 9 Aug 2026 14:08:49 -0700 Subject: [PATCH] fix(gateway): carry origin_json/display_name into /branch child sessions too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete the /branch routing-identity fix (salvaged from PR #62278 by @jcjc81): in addition to user_id/session_key/chat_id/chat_type/thread_id, forward origin_json and display_name at create_session() time, matching the reset-path db_create_kwargs pattern (#82633) so the branch row is born with full identity — no backfill gap for state.db consumers (mcp_serve, mirror, channel directory) if a crash lands before switch_session(). The obsolete compression-rotation half of #62278 was dropped: rotation now goes exclusively through publish_compression_child, which already copies all identity columns in-transaction. --- gateway/slash_commands.py | 23 +++++++++++++++++++- tests/gateway/test_branch_routing_columns.py | 13 +++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/gateway/slash_commands.py b/gateway/slash_commands.py index 312e1a3365..4533c66bef 100644 --- a/gateway/slash_commands.py +++ b/gateway/slash_commands.py @@ -4774,6 +4774,20 @@ class GatewaySlashCommandsMixin: parent_session_id = current_entry.session_id + # Serialize the parent's full origin (same shape as the reset path's + # db_create_kwargs in gateway/session.py, #82633) so the branch row + # carries complete identity from birth. Prefer the live entry's origin + # (it may hold richer metadata than the triggering event's source). + _branch_origin = current_entry.origin or source + _branch_origin_json = None + if _branch_origin is not None: + try: + import json as _json + + _branch_origin_json = _json.dumps(_branch_origin.to_dict()) + except Exception: + _branch_origin_json = None + # Create the new session with parent link. # Persist a stable ``_branched_from`` marker in model_config so # list_sessions_rich() keeps the branch visible in /resume and @@ -4798,12 +4812,19 @@ class GatewaySlashCommandsMixin: # IDOR guard too (which requires the row's chat_id/thread_id to # match the caller's). user_id is critical for the fallback lookup # path (hermes_state.py:1994-2009) that searches by the complete - # peer tuple when session_key doesn't match. + # peer tuple when session_key doesn't match. origin_json and + # display_name complete the identity (same shape as the reset + # path's db_create_kwargs in gateway/session.py, #82633) so + # consumers that read routing/presentation data from state.db + # (mcp_serve, mirror, channel directory) see the branch row + # fully formed with zero backfill gap. user_id=source.user_id, session_key=session_key, chat_id=source.chat_id, chat_type=source.chat_type, thread_id=source.thread_id, + origin_json=_branch_origin_json, + display_name=current_entry.display_name, ) except Exception as e: logger.error("Failed to create branch session: %s", e) diff --git a/tests/gateway/test_branch_routing_columns.py b/tests/gateway/test_branch_routing_columns.py index 5a47e238c6..17c78b91eb 100644 --- a/tests/gateway/test_branch_routing_columns.py +++ b/tests/gateway/test_branch_routing_columns.py @@ -137,6 +137,19 @@ class TestBranchRoutingColumns: assert row["session_key"] is not None, ( "branched session lost session_key — primary lookup path fails" ) + # origin_json completes the identity (#82633 reset-path pattern): + # consumers reading routing/presentation data from state.db + # (mcp_serve, mirror, channel directory) need the full origin on + # the branch row without waiting for any backfill. + assert row["origin_json"], ( + "branched session lost origin_json — state.db consumers see an " + "identity-less branch row until a peer refresh backfills it" + ) + import json as _json + + origin = _json.loads(row["origin_json"]) + assert origin.get("chat_id") == "170829464" + assert origin.get("thread_id") == "544520" _ = real_switch_session # silence unused