Skip to content

Commit 6c70351

Browse files
Merge pull request #1300 from corbitsdev/fix/reconnect-stale-generation
fix(providers): suppress stale reconnect replay
2 parents 4f89423 + ea8fe3c commit 6c70351

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

‎src/tui/runner/reconnect-recovery.test.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,34 @@ describe("reconnect recovery selection", () => {
197197
expect(delivered).toHaveLength(1);
198198
});
199199

200+
test("does not replay when the accepted generation becomes stale", () => {
201+
const state = createReconnectRecoveryState();
202+
const attempt = state.begin(operatorMessage(), "xai");
203+
state.observe(attempt, credentialRetry());
204+
state.observe(attempt, credentialFailure("xai/default-2"));
205+
const pending = required(state.settle(attempt), "pending recovery");
206+
207+
let completeReconnect: ((connected: boolean) => void) | undefined;
208+
applyReconnectRecoverySelection({
209+
state,
210+
generation: pending.generation,
211+
selectedId: reconnectRecoveryItemId(pending.scope),
212+
reconnect: (_scope, onComplete) => {
213+
completeReconnect = onComplete;
214+
},
215+
armContinuation: () => {
216+
throw new Error("should not arm");
217+
},
218+
cancelContinuation: () => undefined,
219+
deliverContinuation: () => {
220+
throw new Error("should not deliver");
221+
},
222+
});
223+
224+
state.begin(operatorMessage(), "anthropic");
225+
completeReconnect?.(true);
226+
});
227+
200228
test("cancel, failure, and committed attempts never replay", () => {
201229
for (const connected of [false, true]) {
202230
const state = createReconnectRecoveryState();

‎src/tui/runner/reconnect-recovery.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ export function createReconnectRecoveryState() {
183183
replay: !claimed.committed,
184184
};
185185
},
186+
isCurrent(generation: number): boolean {
187+
return generation === nextGeneration;
188+
},
186189
clear(): void {
187190
pending = null;
188191
nextGeneration++;
@@ -208,7 +211,13 @@ export function applyReconnectRecoverySelection(args: {
208211
const onComplete = (connected: boolean): void => {
209212
if (completed) return;
210213
completed = true;
211-
if (!connected || !acceptance.replay) return;
214+
if (
215+
!connected ||
216+
!acceptance.replay ||
217+
!args.state.isCurrent(acceptance.generation)
218+
) {
219+
return;
220+
}
212221
args.armContinuation(acceptance.generation);
213222
try {
214223
args.deliverContinuation(

0 commit comments

Comments
 (0)