42eec4ab38
Review follow-up (StanleyStetson + egilewski on #83785/#83202): a successful rewind's replace_messages(archive_dropped=True) re-inserts the surviving prefix as NEW SQLite rows. Gateway memory picks up the fresh _row_id stamps via lastrowid, but the Desktop's surviving bubbles kept their pre-rewind ChatMessage.rowId — so a second rewind/edit/regenerate of an older surviving turn sent a stale truncate_before_row_id and was (correctly) refused with 4018 until a transcript reload. Fail-closed stays untouched, per both reviews; the fix is rebinding, not ordinal fallback. Server: prompt.submit now returns survivor_user_row_ids (fresh post-rewrite ids of surviving visible user turns, in visible-user-ordinal order) on both the inline and compute-host paths whenever a durable truncation committed. Desktop: runRewindSubmit surfaces the field; restore/edit/reload on both the primary chat and session tiles rebind surviving user bubbles positionally (same visible-user filter the ordinal math uses) and clear any rowId they cannot rebind — a cleared id degrades to the ordinal path instead of a 4018. Absent field (older gateway) leaves state untouched. Tests: consecutive-rewind regression on a real SessionDB (stale id 4018s, returned id succeeds; mutation-checked) + vitest for survivorRowIdsFrom / rebindSurvivorRowIds (rebind, null-clear, past-end clear, hidden skip, identity preservation).