From 2b3d776403aca47ec522601df573bb3e6480dc75 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Mon, 30 Mar 2026 03:04:43 -0400 Subject: [PATCH] Matrix: ignore stale history after room eviction --- .../src/matrix/monitor/room-history.test.ts | 25 +++++++++++++++++++ .../matrix/src/matrix/monitor/room-history.ts | 8 +++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/extensions/matrix/src/matrix/monitor/room-history.test.ts b/extensions/matrix/src/matrix/monitor/room-history.test.ts index c8bf4303143..cb0f0243021 100644 --- a/extensions/matrix/src/matrix/monitor/room-history.test.ts +++ b/extensions/matrix/src/matrix/monitor/room-history.test.ts @@ -125,4 +125,29 @@ describe("createRoomHistoryTracker — roomQueues eviction", () => { expect(history).toHaveLength(1); expect(history[0]?.body).toBe("new msg in room1"); }); + + it("ignores late consumeHistory calls after the room queue was evicted", () => { + const tracker = createRoomHistoryTracker(200, 1); + const room1 = "!room1:test"; + const room2 = "!room2:test"; + + tracker.recordPending(room1, entry("old msg in room1")); + const prepared = tracker.prepareTrigger(AGENT, room1, 100, { + sender: "user", + body: "trigger in room1", + messageId: "$trigger", + }); + + // room2 creation evicts room1 (maxRoomQueues=1) while the trigger is still in flight. + tracker.recordPending(room2, entry("msg in room2")); + + // Late completion for the evicted room must not recreate a stale watermark. + tracker.consumeHistory(AGENT, room1, prepared.snapshotIdx, "$trigger"); + + // Recreate room1 and add fresh content. + tracker.recordPending(room1, entry("new msg in room1")); + const history = tracker.getPendingHistory(AGENT, room1, 100); + expect(history).toHaveLength(1); + expect(history[0]?.body).toBe("new msg in room1"); + }); }); diff --git a/extensions/matrix/src/matrix/monitor/room-history.ts b/extensions/matrix/src/matrix/monitor/room-history.ts index ecf12ded142..80c77a3a116 100644 --- a/extensions/matrix/src/matrix/monitor/room-history.ts +++ b/extensions/matrix/src/matrix/monitor/room-history.ts @@ -198,11 +198,17 @@ export function createRoomHistoryTracker( consumeHistory(agentId, roomId, snapshotIdx, messageId) { const key = wmKey(agentId, roomId); + const queue = roomQueues.get(roomId); + if (!queue) { + // The room was evicted while this trigger was in flight. Keep eviction authoritative + // so a late completion cannot recreate a stale watermark against a fresh queue. + agentWatermarks.delete(key); + return; + } // Monotone write: never regress an already-advanced watermark. // Guards against out-of-order completion when two triggers for the same // (agentId, roomId) are in-flight concurrently. agentWatermarks.set(key, Math.max(agentWatermarks.get(key) ?? 0, snapshotIdx)); - const queue = roomQueues.get(roomId); const retryKey = preparedTriggerKey(agentId, messageId); if (queue && retryKey) { queue.preparedTriggers.delete(retryKey);