Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
*/

/**
* #1954 busy-race settlement: a sessions:send that raced a root turn another
* client opened can come back `steered` (the send owns no turn) or under a
* Host-chosen turnId. Both results must be interpreted identically by the
* #1954 busy-race settlement: a submitted Message that raced a root turn
* another client opened can come back `steered` (the send owns no turn) or
* under a Host-chosen turnId. Both results must be interpreted identically by the
* new-chat and existing-session branches, and a rebind must never overwrite
* an authoritative live projection that beat the IPC response.
*/
Expand Down
60 changes: 60 additions & 0 deletions apps/desktop/src/main/__tests__/quote-companion-retry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,66 @@ test('replays queued Side Conversation text after Host assigns the ticket to a s
assert.equal(probe.getAttribute('data-processing'), 'false');
});

test('binds an unproven Side Conversation send through the durable transcript', async () => {
let admissionId: string | undefined;
const pendingSend = deferred<{
ok: false;
reason: 'outcome_unknown';
messageId: string;
}>();
// The Host opened a root Turn under its own identity and the answer was lost.
// No `message_admission` event exists for a root Message, so the transcript is
// the only thing that can tie the sent identity back to the Turn.
const { container, emit, send } = await renderOwnershipProbe({
send: async (_sessionId, command) => {
admissionId = command.turnId;
return pendingSend.promise;
},
readSettledMessages: async () => ({
messages: admissionId
? [
{
type: 'user' as const,
id: admissionId,
turnId: 'unproven-root',
ts: 1,
text: 'reconcile me',
},
]
: [],
settled: true,
}),
});

let sendResult!: Promise<boolean>;
await act(async () => {
sendResult = send('reconcile me');
await Promise.resolve();
});
await act(async () => {
pendingSend.resolve({
ok: false,
reason: 'outcome_unknown',
messageId: admissionId as string,
});
assert.equal(await sendResult, true);
await Promise.resolve();
});
await act(async () => {
emit(textDeltaEvent('unproven-text', 'unproven-root', 1, 'answer from the lost send'));
await Promise.resolve();
});
await act(async () => {
await Promise.resolve();
});

const probe = container.firstElementChild;
assert.ok(probe);
assert.equal(probe.getAttribute('data-live-turn-id'), 'unproven-root');
assert.equal(probe.getAttribute('data-live-text'), 'answer from the lost send');
assert.equal(probe.getAttribute('data-processing'), 'false');
});

test('clears a stopped Side Conversation admission when its live retraction is lost', async () => {
let admissionId: string | undefined;
const pendingStop = deferred<{ kind: 'retracted'; messageId: string }>();
Expand Down
15 changes: 6 additions & 9 deletions apps/desktop/src/main/__tests__/runtime-host-client-uds.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,19 +345,16 @@ test('drives the renderer Session execution facade through real UDS framing', as
result: { kind: 'managed', access: 'read_only', revision: 2 },
};
},
'turn.start': async (input) => {
'turn.message.submit': async (input) => {
assert.equal(input.sessionId, projected.id);
assert.equal(input.messageId, 'turn-1');
assert.equal(input.placement, 'current_turn');
assert.equal(input.content.text, 'Run through the Host');
return {
ok: true,
result: {
kind: 'started',
turn: {
sessionId: input.sessionId,
turnId: input.turnId,
runId: 'run-1',
status: 'running',
},
disposition: 'turn_started',
turnId: 'turn-host-1',
skillInvocation: { loaded: [], failed: [], receipts: [] },
},
};
Expand Down Expand Up @@ -410,7 +407,7 @@ test('drives the renderer Session execution facade through real UDS framing', as
}),
{
ok: true,
turnId: 'turn-1',
turnId: 'turn-host-1',
attachments: [],
inlineReferences: [],
skillInvocation: { loaded: [], failed: [], receipts: [] },
Expand Down
Loading