diff --git a/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.test.ts b/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.test.ts index 43d16f79a2228..b97f2ba0fc639 100644 --- a/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.test.ts +++ b/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.test.ts @@ -475,7 +475,7 @@ describe('runRewindSubmit durable-address discipline (#87059)', () => { expect(submit?.params?.confirm_truncate).toBeUndefined() }) - it('leaves a bound durable rowId untouched (no extra history call)', async () => { + it('leaves a bound durable rowId untouched (no extra history call) but drops the client ordinal', async () => { const calls: Call[] = [] await runRewindSubmit(makeGateway(calls), 'sid', 'fixed prompt', 1, undefined, false, undefined, 13, 'typo prompt') @@ -485,7 +485,7 @@ describe('runRewindSubmit durable-address discipline (#87059)', () => { const submit = calls.find(call => call.method === 'prompt.submit') expect(submit?.params?.truncate_before_row_id).toBe(13) - expect(submit?.params?.truncate_before_user_ordinal).toBe(1) + expect(submit?.params?.truncate_before_user_ordinal).toBeUndefined() }) }) diff --git a/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.ts b/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.ts index aec6ad6ac60cd..170e348e3a28d 100644 --- a/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.ts +++ b/apps/desktop/src/app/session/hooks/use-prompt-actions/rewind.ts @@ -260,6 +260,15 @@ export async function runRewindSubmit( // gateway's 4030 cross-check. Unresolved: plain resubmit, no truncation. resolvedOrdinal = undefined resolvedMessageId = undefined + } else if (wantsTruncation && typeof truncateRowId === 'number') { + // The caller already bound a durable ROW id. Keep it as the SOLE address + // and drop the ordinal the same way: the two ordinal spaces can diverge + // (failed turns are skipped client-side while durable repair merges + // adjacent user rows), and the gateway refuses the ordinal+row_id + // combination with 4030 whenever they disagree (#82756). messageId-only + // restores keep their ordinal — there the ordinal is still part of the + // address (stale rendered ids fall back to it, #82462). + resolvedOrdinal = undefined } const interrupt = async () => {