From c380918d96b085dd94c90d0fbcd702aeb5e9716e Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Mon, 10 Aug 2026 10:15:31 -0700 Subject: [PATCH] test(provenance): cover the enforced branch of the agent memory surface The agent's stored-memory read was the only durable-provenance check site with no test of its enforced path, and the only one whose control flow was restructured by hand. Pin both directions against the shape that failed in production: an unrecorded memory reads through and reports, and the same memory refuses once the memory surface is closed. Co-Authored-By: Claude Opus 5 (1M context) --- .../executor/handlers/agent/memory.test.ts | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/apps/sim/executor/handlers/agent/memory.test.ts b/apps/sim/executor/handlers/agent/memory.test.ts index a345073fc86..e59a4c260ba 100644 --- a/apps/sim/executor/handlers/agent/memory.test.ts +++ b/apps/sim/executor/handlers/agent/memory.test.ts @@ -1,9 +1,18 @@ import { loggerMock } from '@sim/testing' import { beforeEach, describe, expect, it, vi } from 'vitest' -const { mockDecryptSecret, mockRedactObjectStrings } = vi.hoisted(() => ({ - mockDecryptSecret: vi.fn(), - mockRedactObjectStrings: vi.fn(async (value: unknown) => value), +const { mockDecryptSecret, mockRedactObjectStrings, mockIsEnforced, mockReportUnrecorded } = + vi.hoisted(() => ({ + mockDecryptSecret: vi.fn(), + mockRedactObjectStrings: vi.fn(async (value: unknown) => value), + mockIsEnforced: vi.fn(() => false), + mockReportUnrecorded: vi.fn(), + })) + +vi.mock('@/lib/execution/durable-secret-provenance-enforcement', () => ({ + DURABLE_SECRET_PROVENANCE_SURFACES: ['memory', 'table-row', 'knowledge'], + isDurableSecretProvenanceEnforced: mockIsEnforced, + reportUnrecordedDurableProvenance: mockReportUnrecorded, })) vi.mock('@/lib/core/security/encryption', () => ({ @@ -35,6 +44,7 @@ describe('Memory', () => { beforeEach(() => { vi.clearAllMocks() + mockIsEnforced.mockReturnValue(false) mockDecryptSecret.mockImplementation(async (encryptedValue: string) => ({ decrypted: `decrypted:${encryptedValue}`, })) @@ -503,6 +513,48 @@ describe('Memory', () => { expect(messages).toEqual([retainedPublicMessage]) expect(mockDecryptSecret).not.toHaveBeenCalled() }) + + /** Trace 2's shape: a stored memory a previous run could not vouch for. */ + it('reads a memory with unrecorded provenance while the surface stays open', async () => { + const registry = new ResolvedSecretTraceRegistry([], { + userId: 'user-1', + workspaceId: 'workspace-1', + }) + vi.spyOn(memoryService as any, 'fetchMemory').mockResolvedValueOnce({ + messages: [{ role: 'user', content: 'how do i see my tickets?' }], + provenance: { status: 'unknown' }, + }) + + const messages = await memoryService.fetchMemoryMessages( + createContext(registry) as never, + inputs + ) + + expect(messages).toEqual([{ role: 'user', content: 'how do i see my tickets?' }]) + expect(registry.isPermanentlyIncomplete()).toBe(false) + expect(mockReportUnrecorded).toHaveBeenCalledWith({ + surface: 'memory', + cause: 'stored-memory-provenance-unknown', + workspaceId: 'workspace-1', + }) + }) + + it('refuses that same memory once the memory surface is closed', async () => { + mockIsEnforced.mockReturnValue(true) + const registry = new ResolvedSecretTraceRegistry([], { + userId: 'user-1', + workspaceId: 'workspace-1', + }) + vi.spyOn(memoryService as any, 'fetchMemory').mockResolvedValueOnce({ + messages: [{ role: 'user', content: 'how do i see my tickets?' }], + provenance: { status: 'unknown' }, + }) + + await expect( + memoryService.fetchMemoryMessages(createContext(registry) as never, inputs) + ).rejects.toThrow() + expect(mockReportUnrecorded).not.toHaveBeenCalled() + }) }) describe('secret-safe diagnostics', () => {