merge: integrate foreground conversation reconciliation

# Conflicts:
#	tests/e2e/pi-coding-first-chat.spec.ts
This commit is contained in:
inman
2026-09-03 10:47:56 +08:00
6 changed files with 439 additions and 22 deletions

View File

@@ -486,6 +486,61 @@ describe('coding Conversation store', () => {
expect(submitPrompt).toHaveBeenCalledTimes(1);
});
it('rehydrates a selected live Conversation without replaying an accepted mutation', async () => {
const source = new FakeEventSource();
const refresh = deferred<ConversationSnapshot>();
const staleRunning = {
...snapshot('conversation-a', 1, 4),
run: {
status: 'running',
runId: 'run-stale',
mode: 'prompt',
startedAt: 1_000,
} as const,
};
const persistedTerminal = {
...snapshot('conversation-a', 1, 5),
run: {
status: 'idle',
runId: 'run-stale',
mode: 'prompt',
startedAt: 1_000,
settledAt: 2_000,
terminalReason: 'aborted',
} as const,
};
const getSnapshot = vi.fn()
.mockResolvedValueOnce(staleRunning)
.mockImplementationOnce(() => refresh.promise);
const openEvents = vi.fn(async () => source as unknown as EventSource);
const submitPrompt = vi.fn();
const store = createCodingConversationStore({
getSnapshot,
openEvents,
submitPrompt,
createId: ids(),
});
await store.getState().selectConversation('conversation-a');
const reselection = store.getState().selectConversation('conversation-a');
expect(getSnapshot).toHaveBeenCalledTimes(2);
expect(openEvents).toHaveBeenCalledTimes(1);
expect(store.getState().entriesByConversationId['conversation-a']).toMatchObject({
loadState: 'live',
error: null,
});
expect(selectCodingConversationSnapshot('conversation-a')(store.getState())?.run.status)
.toBe('running');
refresh.resolve(persistedTerminal);
await reselection;
expect(selectCodingConversationSnapshot('conversation-a')(store.getState())?.run)
.toMatchObject({ status: 'idle', terminalReason: 'aborted' });
expect(submitPrompt).not.toHaveBeenCalled();
});
it('does not let a cancelled connection clear a newer connection flight', async () => {
const first = deferred<EventSource>();
const second = deferred<EventSource>();