fix(sync): honor expectedRuntimeKey in archive actions

`useSessionUIStore.archiveSessions` declared an `options` parameter and
discarded it, so any caller passing a captured runtime key got a silent
no-op. The archive path also never rechecked the runtime, letting a
response produced by a previous runtime reconcile the live and global
session stores of the runtime the user had switched to.

Move the batch to a canonical `archiveSessions()` action, add an optional
`expectedRuntimeKey` to `archiveSession()`, `patchSessionMetadata()`, and
`cleanupReviewMetadataBeforeDelete()`, and recheck that key before every
store reconciliation. A guarded batch stops at the first observed runtime
change: server-confirmed sessions stay in `archivedIds` and every
unconfirmed ID is returned in `failedIds`, so existing partial-failure
feedback stays truthful. Callers that pass no key keep prior behavior.

Type the store option as `ArchiveSessionsOptions` instead of
`Record<string, unknown>`, since the loose type allowed the drop.
This commit is contained in:
Alexandre Reyes Martins
2026-08-02 11:55:02 +00:00
parent 7afec99f80
commit 2e8fc6e192
5 changed files with 207 additions and 15 deletions
@@ -454,3 +454,33 @@ describe('routeMessage skill invocation', () => {
expect(sendCommandCalls).toHaveLength(0);
});
});
describe('archiveSessions option forwarding', () => {
let originalUpdateSession;
let updateSessionCalls;
beforeEach(() => {
updateSessionCalls = [];
originalUpdateSession = opencodeClient.updateSession;
opencodeClient.updateSession = (sessionId) => {
updateSessionCalls.push(sessionId);
return Promise.resolve(null);
};
});
afterEach(() => {
opencodeClient.updateSession = originalUpdateSession;
});
// The store used to accept an options object and silently drop it, so a
// caller-supplied runtime key had no effect. Passing a key that cannot match
// the active runtime must abort the batch before any SDK call.
test('honors expectedRuntimeKey instead of discarding the options object', async () => {
const result = await useSessionUIStore.getState().archiveSessions(['session-x', 'session-y'], {
expectedRuntimeKey: 'runtime-that-is-not-active',
});
expect(result).toEqual({ archivedIds: [], failedIds: ['session-x', 'session-y'] });
expect(updateSessionCalls).toEqual([]);
});
});