Skip to content

Commit 12d3323

Browse files
fix(cloud-task): drop unsafe append in log-gap inconsistency path (#2102)
1 parent 0fb03bc commit 12d3323

2 files changed

Lines changed: 18 additions & 43 deletions

File tree

apps/code/src/renderer/features/sessions/service/service.test.ts

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,7 +1527,7 @@ describe("SessionService", () => {
15271527
expect(mockSessionStoreSetters.appendEvents).not.toHaveBeenCalled();
15281528
});
15291529

1530-
it("processes a pending cloud log gap after active reconciliation finishes", async () => {
1530+
it("queues a pending cloud log gap when stale fetches can't fill it, without appending", async () => {
15311531
const service = getSessionService();
15321532
let sessionState = createMockSession({
15331533
taskRunId: "run-123",
@@ -1628,38 +1628,18 @@ describe("SessionService", () => {
16281628
});
16291629
resolveFirstLocalLogs("");
16301630

1631+
// The pending request must drain after the in-flight one resolves —
1632+
// verify the second readLocalLogs call eventually happens.
16311633
await vi.waitFor(() => {
1632-
expect(mockSessionStoreSetters.appendEvents).toHaveBeenCalledTimes(2);
1633-
});
1634-
expect(mockSessionStoreSetters.appendEvents).toHaveBeenNthCalledWith(
1635-
1,
1636-
"run-123",
1637-
[
1638-
expect.objectContaining({
1639-
message: expect.objectContaining({
1640-
params: { entry: firstEntry },
1641-
}),
1642-
}),
1643-
],
1644-
6,
1645-
);
1646-
expect(mockSessionStoreSetters.appendEvents).toHaveBeenNthCalledWith(
1647-
2,
1648-
"run-123",
1649-
[
1650-
expect.objectContaining({
1651-
message: expect.objectContaining({
1652-
params: { entry: secondEntry },
1653-
}),
1654-
}),
1655-
expect.objectContaining({
1656-
message: expect.objectContaining({
1657-
params: { entry: thirdEntry },
1658-
}),
1659-
}),
1660-
],
1661-
8,
1662-
);
1634+
expect(mockTrpcLogs.readLocalLogs.query).toHaveBeenCalledTimes(2);
1635+
});
1636+
// Stale fetches can't fill the gap; we must NOT append the snapshot's
1637+
// tail slice (positions [expectedCount-N, expectedCount]) on top of an
1638+
// events array that's still at processedLineCount=5 — that path used
1639+
// to corrupt the array with duplicates/gaps and ratchet
1640+
// processedLineCount past entries we don't actually have, leading to
1641+
// unbounded growth on long-running cloud runs.
1642+
expect(mockSessionStoreSetters.appendEvents).not.toHaveBeenCalled();
16631643
});
16641644
it("flips status to connected on _posthog/run_started", async () => {
16651645
const service = getSessionService();

apps/code/src/renderer/features/sessions/service/service.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3560,23 +3560,18 @@ export class SessionService {
35603560
return;
35613561
}
35623562

3563+
// The fetched logs lag behind expectedCount and `newEntries` is the latest
3564+
// tail slice of the snapshot — appending it here would create duplicates
3565+
// and gaps in `session.events` (and bump processedLineCount past entries
3566+
// we don't actually have). Skip; the next snapshot/log update will retry
3567+
// once the source has caught up.
35633568
log.warn("Cloud task log count inconsistency", {
35643569
taskRunId,
35653570
currentCount,
35663571
expectedCount,
3572+
fetchedCount: rawEntries.length,
35673573
entriesReceived: newEntries.length,
35683574
});
3569-
let newEvents = convertStoredEntriesToEvents(newEntries);
3570-
newEvents = this.filterSkippedPromptEvents(taskRunId, session, newEvents);
3571-
if (hasSessionPromptEvent(newEvents)) {
3572-
sessionStoreSetters.clearTailOptimisticItems(taskRunId);
3573-
}
3574-
sessionStoreSetters.appendEvents(
3575-
taskRunId,
3576-
newEvents,
3577-
latestCount + newEntries.length,
3578-
);
3579-
this.updatePromptStateFromEvents(taskRunId, newEvents);
35803575
}
35813576

35823577
private createBaseSession(

0 commit comments

Comments
 (0)