fix(gateway): a /branch child row carries the parent's system prompt

The gateway /branch copies the parent transcript byte-for-byte but created
the child row without a system prompt, so the branch's first turn rebuilt
the prompt (re-probing the workspace) and lost the warm prefix cache, and
logged the null-prompt warning on every branch. 2ec12952 fixed the CLI and
TUI branch writers; the gateway is the third. Read the parent row's prompt
and pass it to create_session.

(cherry picked from commit 4103a7c66c3b86659ee2cdbd7eef37d735aff237)
This commit is contained in:
AhmetArif0
2026-09-25 02:54:02 +03:00
committed by kshitij
parent 379ce42b0a
commit e1b483a6ed
2 changed files with 38 additions and 1 deletions

View File

@@ -1039,6 +1039,14 @@ class GatewaySessionCommandsMixin:
# ``_branched_from`` keeps the branch visible in /resume and /sessions after the parent is
# reopened and re-ended. ALL routing columns go in at CREATE time: a crash before
# switch_session() records the peer would otherwise leave the branch unroutable.
# The child sends the parent's exact system prompt: a row without one makes the branch's
# first turn rebuild (re-probing the workspace) and forfeits the warm cache the copied
# transcript buys.
parent_prompt = None
try:
parent_prompt = ((await self._session_db.get_session(parent_session_id)) or {}).get("system_prompt")
except Exception:
logger.debug("branch: parent system prompt read failed for %s", parent_session_id, exc_info=True)
try:
await self._session_db.create_session(
session_id=new_session_id,
@@ -1048,7 +1056,7 @@ class GatewaySessionCommandsMixin:
parent_session_id=parent_session_id, user_id=dest_source.user_id,
session_key=dest_key, chat_id=dest_source.chat_id, chat_type=dest_source.chat_type,
thread_id=dest_source.thread_id, origin_json=_branch_origin_json,
display_name=current_entry.display_name)
display_name=current_entry.display_name, system_prompt=parent_prompt or None)
except Exception as e:
logger.error("Failed to create branch session: %s", e)
return t("gateway.branch.create_failed", error=e)

View File

@@ -153,3 +153,32 @@ class TestBranchRoutingColumns:
_ = real_switch_session # silence unused
class TestBranchSystemPrompt:
@pytest.mark.asyncio
async def test_branched_session_carries_parent_system_prompt(self, store):
"""The branch copies the parent transcript byte-for-byte so its first
turn can hit the warm prefix cache, but create_session() at branch
time dropped the system prompt: the child row's system_prompt column
was None even though the parent row held one. Mirrors the CLI/TUI fix
in 2ec12952 for the third branch-creation site (the gateway)."""
source = _make_source()
parent_entry = store.get_or_create_session(source)
store._db.update_system_prompt(parent_entry.session_id, "PARENT PROMPT")
store._db.append_message(parent_entry.session_id, role="user", content="hello")
store._db.append_message(parent_entry.session_id, role="assistant", content="world")
runner = _make_branch_runner(store)
await runner._handle_branch_command(_make_event("/branch"))
new_entry = store.get_or_create_session(source)
assert new_entry.session_id != parent_entry.session_id
row = store._db.get_session(new_entry.session_id)
assert row is not None
assert row["system_prompt"] == "PARENT PROMPT", (
"branched session lost the parent's system prompt — its first "
"turn will re-probe the workspace and forfeit the warm cache "
"the copied transcript buys"
)