fix(pi): retain ownership for uncertain mutations

This commit is contained in:
2026-08-25 23:53:54 +08:00
parent 621ebb1781
commit 019cbb115a
21 changed files with 1113 additions and 67 deletions

View File

@@ -1,6 +1,6 @@
// @vitest-environment node
import { afterEach, describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
@@ -57,6 +57,7 @@ async function processAlive(pid: number): Promise<boolean> {
}
afterEach(async () => {
vi.useRealTimers();
await Promise.all(workers.splice(0).map((worker) => worker.stop('test_injection').catch(() => undefined)));
await Promise.all(scratchRoots.splice(0).map((root) => rm(root, {
recursive: true,
@@ -101,6 +102,49 @@ describe('strict Pi LF JSONL framing', () => {
});
describe('Pi RPC client', () => {
it('keeps a mutation correlated after the 10 second confirmation timeout', async () => {
vi.useFakeTimers();
let written = '';
const writable = new Writable({
write(chunk, _encoding, callback) {
written += chunk.toString();
callback();
},
});
const lateResults: Array<{ success: boolean; code?: string }> = [];
const client = new PiRpcClient(writable, { generation: 1, defaultTimeoutMs: 10_000 });
const requested = client.request(
{ type: 'prompt', message: 'continue after slow preflight' },
{
retainAfterTimeout: true,
onLateResult: (result: { response?: { success: boolean }; error?: PiProcessError }) => {
lateResults.push({
success: result.response?.success === true,
...(result.error ? { code: result.error.code } : {}),
});
},
} as Parameters<PiRpcClient['request']>[1] & {
retainAfterTimeout: true;
onLateResult(result: {
response?: { success: boolean };
error?: PiProcessError;
}): void;
},
);
void requested.catch(() => undefined);
await Promise.resolve();
await vi.advanceTimersByTimeAsync(10_000);
await expect(requested).rejects.toMatchObject({ code: 'PI_RPC_TIMEOUT' });
expect(client.pendingCount).toBe(1);
const command = JSON.parse(written) as { id: string };
client.accept({ type: 'response', id: command.id, success: true });
expect(client.pendingCount).toBe(0);
expect(lateResults).toEqual([{ success: true }]);
});
it('waits for writable completion when the stream applies backpressure', async () => {
let written = '';
let flush: (() => void) | undefined;