77edf1b4df
A fresh chat created through $newChatRoute lost its owner the moment
session.create returned. The create RPC rode the captured route
(requestGatewayForAgent), but the optimistic row was stamped from
$activeGatewayProfile — still `default` in All-profiles / Bot routing —
and no owner hint was recorded. The first turn ran on the routed
backend (e.g. local::omar); every later session-scoped RPC resolved the
row as `default` and 4001'd "session not found", leaving the routed
runtime to be ws-orphan-reaped.
Make the ownership transition atomic with the create:
- record capturedRoute as the stored session's exact owner hint the
moment a routed create returns a stored id (main chat and tile paths);
- upsertOptimisticSession accepts an explicit owner and stamps
profile = targetProfile || profile plus the owning connection_id,
falling back to the ambient profile only for an unrouted create;
- contrib/wiring resolves session RPC owners as: persisted tile owner
route → exact unique owner hint → session-row profile → cross-profile
probe (resolveSessionRpcOwner, pure + unit-tested), so prompt.submit,
session.resume, attachments, interrupt, redirect and recovery all use
the exact owner; knownOwnerForSession follows the same ladder;
- the mid-create drift-abort session.close rides capturedRoute too.
Regressions: an integration test (ambient default, route local::omar,
two turns → both prompt.submit hit local::omar, no session-not-found,
no session.close) and a unit test asserting a routed fresh create never
yields { followupOwner: 'default', foregroundScope: 'conn:local::omar' }.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 95e2ef66becb641d3bdedf016dab6c104a777ae7)