c80003ff57
The gateway already ships the refusal as machine data: prompt.submit returns
JSON-RPC 4090 with error.data.reason = SESSION_NOT_OWNED. Matching the English
sentence in a new lib helper duplicated that contract and would silently miss a
reworded or localized message. submit.ts now classifies the rejection once
(isSessionNotOwnedError beside the sibling isSessionBusyError, reason code
first, prose only for pre-contract backends) and stamps the inline error bubble
with errorSurface {layer: gateway, code: SESSION_NOT_OWNED, retryable: false},
the same descriptor shape the gateway uses for turn failures. The error card
reads surface.code instead of re-sniffing text; retryable=false already hides
Retry through the existing gate.
Dropped from #106248: the stranded-resume overlay button. That overlay fires
when session.resume itself fails through every retry; reading is never fenced
by the lease, so the live-owner refusal never reaches it (the issue's screenshot
is the inline turn-error card). Also drops the resumeStartNewSession locale
keys and the separate detector module + its test; two invariant tests remain
(submit stamps the descriptor; the card offers Start new session / hides Retry).