From 658fdaea5bbe4bb4ac926e25d2940d641477a2b4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 15 Jul 2026 00:30:16 -0700 Subject: [PATCH] refactor(audit): remove internal test exports (#108111) * refactor(audit): remove internal test exports * chore(deadcode): refresh export baseline --- scripts/deadcode-exports.baseline.mjs | 7 - src/audit/agent-event-audit.ts | 12 +- src/audit/audit-event-store.ts | 8 - src/audit/audit-event-writer.test.ts | 7 +- src/audit/audit-event-writer.ts | 6 - src/audit/audit-events.test.ts | 139 +++++++++--------- src/audit/audit-recorder.test.ts | 13 +- .../message-audit-events.test-support.ts | 28 ++++ src/audit/message-audit-events.test.ts | 27 +--- src/audit/message-audit-events.ts | 4 - .../deliver.queue-integration.test.ts | 9 +- src/infra/outbound/deliver.test.ts | 9 +- .../outbound/delivery-queue.recovery.test.ts | 8 +- src/infra/outbound/outbound-audit.test.ts | 12 +- 14 files changed, 121 insertions(+), 168 deletions(-) create mode 100644 src/audit/message-audit-events.test-support.ts diff --git a/scripts/deadcode-exports.baseline.mjs b/scripts/deadcode-exports.baseline.mjs index 6285c278d19a..2398c205d85d 100644 --- a/scripts/deadcode-exports.baseline.mjs +++ b/scripts/deadcode-exports.baseline.mjs @@ -134,13 +134,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/utils/tools-manager.ts: testing", "src/agents/workspace-templates.ts: resetWorkspaceTemplateDirCache", "src/agents/worktrees/run-lease.ts: testing", - "src/audit/agent-event-audit.ts: projectAgentEventToAudit", - "src/audit/agent-event-audit.ts: projectToolExecutionEventToAudit", - "src/audit/agent-event-audit.ts: resetAgentEventAuditForTest", - "src/audit/audit-event-store.ts: auditEventStoreLimits", - "src/audit/audit-event-store.ts: testApi", - "src/audit/audit-event-writer.ts: testApi", - "src/audit/message-audit-events.ts: resetMessageAuditEventsForTest", "src/auto-reply/reply/abort.ts: testing", "src/auto-reply/reply/acp-reset-target.ts: testing", "src/auto-reply/reply/agent-runner-context-recovery.ts: computeContextAwareReserveTokensFloor", diff --git a/src/audit/agent-event-audit.ts b/src/audit/agent-event-audit.ts index 750575302263..b91e3e2fd540 100644 --- a/src/audit/agent-event-audit.ts +++ b/src/audit/agent-event-audit.ts @@ -244,13 +244,8 @@ function projectAgentEvent(event: AgentEventPayload): AgentAuditProjection | und return undefined; } -/** Return a metadata-only audit input for supported run lifecycle events. */ -export function projectAgentEventToAudit(event: AgentEventPayload): AuditEventInput | undefined { - return projectAgentEvent(event)?.input; -} - /** Project the complete trusted tool-execution lifecycle without private diagnostic content. */ -export function projectToolExecutionEventToAudit( +function projectToolExecutionEventToAudit( event: TrustedToolExecutionEvent, ): ToolActionAuditEventInput | undefined { // Schema quarantine describes tool availability before invocation. Without @@ -481,8 +476,3 @@ export function createAgentEventAuditRecorder(options?: { }, }; } - -export function resetAgentEventAuditForTest(): void { - runProvenance.clear(); - persistenceFailureWarned = false; -} diff --git a/src/audit/audit-event-store.ts b/src/audit/audit-event-store.ts index f952f3f81743..8af3a1b29aaa 100644 --- a/src/audit/audit-event-store.ts +++ b/src/audit/audit-event-store.ts @@ -693,11 +693,3 @@ export function pruneExpiredAuditEvents( auditEventRowCounts.delete(db); }, params.database); } - -export const auditEventStoreLimits = { - maxRows: AUDIT_EVENT_MAX_ROWS, - pruneBatchRows: AUDIT_EVENT_PRUNE_BATCH_ROWS, - retentionMs: AUDIT_EVENT_RETENTION_MS, -} as const; - -export const testApi = { pruneAuditEventsAfterInsert }; diff --git a/src/audit/audit-event-writer.test.ts b/src/audit/audit-event-writer.test.ts index c968de5f5ad5..7eb0d25e476b 100644 --- a/src/audit/audit-event-writer.test.ts +++ b/src/audit/audit-event-writer.test.ts @@ -2,12 +2,11 @@ import { afterAll, afterEach, describe, expect, it } from "vitest"; import { cleanupTempDirs, makeTempDir } from "../../test/helpers/temp-dir.js"; import { closeOpenClawStateDatabaseForTest, - OPENCLAW_SQLITE_BUSY_TIMEOUT_MS, openOpenClawStateDatabase, } from "../state/openclaw-state-db.js"; import { listAuditEvents } from "./audit-event-store.js"; import type { AuditEventInput } from "./audit-event-types.js"; -import { createAuditEventWriter, testApi } from "./audit-event-writer.js"; +import { createAuditEventWriter } from "./audit-event-writer.js"; const tempDirs: string[] = []; @@ -35,10 +34,6 @@ afterAll(() => { }); describe("audit event worker", () => { - it("keeps shutdown beyond the supported SQLite contention window", () => { - expect(testApi.auditWriterShutdownTimeoutMs).toBeGreaterThan(OPENCLAW_SQLITE_BUSY_TIMEOUT_MS); - }); - it("returns immediately under SQLite contention and flushes before stop", async () => { const stateDir = makeTempDir(tempDirs, "openclaw-audit-writer-"); const database = { env: { OPENCLAW_STATE_DIR: stateDir } }; diff --git a/src/audit/audit-event-writer.ts b/src/audit/audit-event-writer.ts index 68e7beb38d4d..0c54dbca2b89 100644 --- a/src/audit/audit-event-writer.ts +++ b/src/audit/audit-event-writer.ts @@ -184,9 +184,3 @@ export function createAuditEventWriter( }, }; } - -export const testApi = { - auditWriterShutdownTimeoutMs: AUDIT_WRITER_SHUTDOWN_TIMEOUT_MS, - maxPendingAuditEvents: MAX_PENDING_AUDIT_EVENTS, - resolveAuditEventWriterUrl, -}; diff --git a/src/audit/audit-events.test.ts b/src/audit/audit-events.test.ts index 8845edf058a4..6c908b3b7a76 100644 --- a/src/audit/audit-events.test.ts +++ b/src/audit/audit-events.test.ts @@ -1,4 +1,4 @@ -import { afterAll, afterEach, describe, expect, it } from "vitest"; +import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest"; import { cleanupTempDirs, makeTempDir } from "../../test/helpers/temp-dir.js"; import type { AgentEventPayload } from "../infra/agent-events.js"; import { @@ -12,23 +12,17 @@ import { closeOpenClawStateDatabaseForTest, openOpenClawStateDatabase, } from "../state/openclaw-state-db.js"; -import { - createAgentEventAuditRecorder, - projectAgentEventToAudit, - projectToolExecutionEventToAudit, - resetAgentEventAuditForTest, -} from "./agent-event-audit.js"; -import { - auditEventStoreLimits, - listAuditEvents, - pruneExpiredAuditEvents, - recordAuditEvent, - testApi as auditStoreTestApi, -} from "./audit-event-store.js"; -import type { AuditEventInput } from "./audit-event-types.js"; +import { createAgentEventAuditRecorder } from "./agent-event-audit.js"; +import { listAuditEvents, pruneExpiredAuditEvents, recordAuditEvent } from "./audit-event-store.js"; +import type { AuditEventInput, ToolActionAuditEventInput } from "./audit-event-types.js"; import type { AuditEventWriter } from "./audit-event-writer.js"; const tempDirs: string[] = []; +const AUDIT_EVENT_MAX_ROWS_CONTRACT = 100_000; +const AUDIT_EVENT_PRUNE_BATCH_ROWS_CONTRACT = 1_024; +const AUDIT_EVENT_RETENTION_MS_CONTRACT = 30 * 24 * 60 * 60_000; +let auditTestRunSequence = 0; +let currentAuditTestRunId = "run-test-0"; function createDatabaseOptions() { return { env: { OPENCLAW_STATE_DIR: makeTempDir(tempDirs, "openclaw-audit-") } }; @@ -59,7 +53,7 @@ function auditInput(overrides: Partial = {}): AuditEventInput { function agentEvent(overrides: Partial): AgentEventPayload { return { - runId: "run-1", + runId: currentAuditTestRunId, seq: 1, stream: "lifecycle", ts: Date.now(), @@ -76,7 +70,7 @@ function toolEvent(overrides: Partial = {}): TrustedT type: "tool.execution.started", seq: 1, ts: Date.now(), - runId: "run-1", + runId: currentAuditTestRunId, sessionKey: "agent:coder:main", sessionId: "session-1", toolName: "exec", @@ -85,9 +79,44 @@ function toolEvent(overrides: Partial = {}): TrustedT } as TrustedToolExecutionEvent; } +function captureAuditWriter(inputs: AuditEventInput[]): AuditEventWriter { + return { + ready: Promise.resolve(), + record: (input) => { + inputs.push(input); + return true; + }, + stop: async () => {}, + }; +} + +function projectAgentEventToAudit(event: AgentEventPayload): AuditEventInput | undefined { + const inputs: AuditEventInput[] = []; + const recorder = createAgentEventAuditRecorder({ + writer: captureAuditWriter(inputs), + terminalSettleMs: 60_000, + }); + recorder.record(event); + void recorder.stop(); + return inputs.at(-1); +} + +function projectToolExecutionEventToAudit( + event: TrustedToolExecutionEvent, +): ToolActionAuditEventInput | undefined { + const inputs: AuditEventInput[] = []; + const recorder = createAgentEventAuditRecorder({ writer: captureAuditWriter(inputs) }); + recorder.recordTool(event); + void recorder.stop(); + return inputs.at(-1) as ToolActionAuditEventInput | undefined; +} + +beforeEach(() => { + currentAuditTestRunId = `run-test-${++auditTestRunSequence}`; +}); + afterEach(() => { closeOpenClawStateDatabaseForTest(); - resetAgentEventAuditForTest(); resetDiagnosticEventsForTest(); }); @@ -183,7 +212,7 @@ describe("audit event persistence", () => { recordAuditEvent(auditInput({ occurredAt }), database); const { db } = openOpenClawStateDatabase(database); db.prepare("UPDATE sqlite_sequence SET seq = ? WHERE name = 'audit_events'").run( - auditEventStoreLimits.maxRows + 1, + AUDIT_EVENT_MAX_ROWS_CONTRACT + 1, ); recordAuditEvent(auditInput({ occurredAt: occurredAt + 1, sourceSequence: 2 }), database); @@ -195,56 +224,34 @@ describe("audit event persistence", () => { const database = createDatabaseOptions(); const { db } = openOpenClawStateDatabase(database); const occurredAt = Date.now(); - const insert = db.prepare( - `INSERT INTO audit_events ( + db.prepare( + `WITH digits(d) AS (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)), + numbers(n) AS ( + SELECT 1 + a.d + 10*b.d + 100*c.d + 1000*d.d + 10000*e.d + 100000*f.d + FROM digits a, digits b, digits c, digits d, digits e, digits f + ) + INSERT INTO audit_events ( event_id, source_id, source_sequence, occurred_at, kind, action, status, actor_type, actor_id, agent_id, run_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - ); - for (let sequence = 1; sequence <= 4; sequence += 1) { - insert.run( - `event-${sequence}`, - `source-${sequence}`, - sequence, - occurredAt + sequence, - "agent_run", - "agent.run.started", - "started", - "agent", - "main", - "main", - `run-${sequence}`, - ); - } + ) + SELECT 'event-' || n, 'source-' || n, n, ? + n, 'agent_run', + 'agent.run.started', 'started', 'agent', 'main', 'main', 'run-' || n + FROM numbers + WHERE n <= ?`, + ).run(occurredAt, AUDIT_EVENT_MAX_ROWS_CONTRACT + 1); - auditStoreTestApi.pruneAuditEventsAfterInsert(db, occurredAt + 4, { - maxRows: 3, - pruneBatchRows: 1, + expect( + recordAuditEvent( + auditInput({ + sourceSequence: AUDIT_EVENT_MAX_ROWS_CONTRACT + 2, + occurredAt: occurredAt + AUDIT_EVENT_MAX_ROWS_CONTRACT + 2, + }), + database, + ), + ).toBeDefined(); + expect(db.prepare("SELECT COUNT(*) AS count FROM audit_events").get()).toEqual({ + count: AUDIT_EVENT_MAX_ROWS_CONTRACT - AUDIT_EVENT_PRUNE_BATCH_ROWS_CONTRACT, }); - expect(listAuditEvents({ database, limit: 10 }).events.map((event) => event.sequence)).toEqual([ - 4, 3, - ]); - - insert.run( - "event-5", - "source-5", - 5, - occurredAt + 5, - "agent_run", - "agent.run.started", - "started", - "agent", - "main", - "main", - "run-5", - ); - auditStoreTestApi.pruneAuditEventsAfterInsert(db, occurredAt + 5, { - maxRows: 3, - pruneBatchRows: 1, - }); - expect(listAuditEvents({ database, limit: 10 }).events.map((event) => event.sequence)).toEqual([ - 5, 4, 3, - ]); }); it("rolls back an insert whose sequence cannot be represented safely", () => { @@ -272,7 +279,7 @@ describe("audit event persistence", () => { const database = createDatabaseOptions(); const occurredAt = Date.now(); recordAuditEvent(auditInput({ occurredAt }), database); - const expiredAt = occurredAt + auditEventStoreLimits.retentionMs + 1; + const expiredAt = occurredAt + AUDIT_EVENT_RETENTION_MS_CONTRACT + 1; expect(listAuditEvents({ database, limit: 10, now: expiredAt }).events).toEqual([]); pruneExpiredAuditEvents({ database, now: expiredAt }); diff --git a/src/audit/audit-recorder.test.ts b/src/audit/audit-recorder.test.ts index 0a82a9490757..0a1681ab461d 100644 --- a/src/audit/audit-recorder.test.ts +++ b/src/audit/audit-recorder.test.ts @@ -1,12 +1,9 @@ -import { afterEach, describe, expect, it } from "vitest"; +import { describe, expect, it } from "vitest"; import type { AuditEventInput } from "./audit-event-types.js"; import type { AuditEventWriter } from "./audit-event-writer.js"; import { createAuditEventRecorder } from "./audit-recorder.js"; -import { - emitTrustedMessageAuditEvent, - onTrustedMessageAuditEvent, - resetMessageAuditEventsForTest, -} from "./message-audit-events.js"; +import { emitTrustedMessageAuditEvent } from "./message-audit-events.js"; +import { onTrustedMessageAuditEventForTest as onTrustedMessageAuditEvent } from "./message-audit-events.test-support.js"; function captureWriter(inputs: AuditEventInput[]): AuditEventWriter { return { @@ -34,10 +31,6 @@ function emitMessage(conversationKind: "direct" | "group") { }); } -afterEach(() => { - resetMessageAuditEventsForTest(); -}); - describe("message audit recorder", () => { it("keeps message events off by default policy", async () => { const inputs: AuditEventInput[] = []; diff --git a/src/audit/message-audit-events.test-support.ts b/src/audit/message-audit-events.test-support.ts new file mode 100644 index 000000000000..78adadc39e48 --- /dev/null +++ b/src/audit/message-audit-events.test-support.ts @@ -0,0 +1,28 @@ +import { afterEach } from "vitest"; +import { onTrustedMessageAuditEvent } from "./message-audit-events.js"; + +const activeSubscriptions = new Set<() => void>(); + +afterEach(() => { + for (const unsubscribe of activeSubscriptions) { + unsubscribe(); + } + activeSubscriptions.clear(); +}); + +export function onTrustedMessageAuditEventForTest( + listener: Parameters[0], +): () => void { + const unsubscribeListener = onTrustedMessageAuditEvent(listener); + let active = true; + const unsubscribe = () => { + if (!active) { + return; + } + active = false; + activeSubscriptions.delete(unsubscribe); + unsubscribeListener(); + }; + activeSubscriptions.add(unsubscribe); + return unsubscribe; +} diff --git a/src/audit/message-audit-events.test.ts b/src/audit/message-audit-events.test.ts index 51df85d09a83..5e72948069ba 100644 --- a/src/audit/message-audit-events.test.ts +++ b/src/audit/message-audit-events.test.ts @@ -1,10 +1,9 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { emitTrustedMessageAuditEvent, hasTrustedMessageAuditListeners, - onTrustedMessageAuditEvent, - resetMessageAuditEventsForTest, } from "./message-audit-events.js"; +import { onTrustedMessageAuditEventForTest as onTrustedMessageAuditEvent } from "./message-audit-events.test-support.js"; const event = { occurredAt: 1, @@ -20,19 +19,17 @@ const event = { } as const; describe("trusted message audit events", () => { - afterEach(() => { - resetMessageAuditEventsForTest(); - }); - it("isolates a throwing listener and continues notifying later listeners", () => { const laterListener = vi.fn(); - onTrustedMessageAuditEvent(() => { + const unsubscribeThrowing = onTrustedMessageAuditEvent(() => { throw new Error("listener failed"); }); - onTrustedMessageAuditEvent(laterListener); + const unsubscribeLater = onTrustedMessageAuditEvent(laterListener); expect(() => emitTrustedMessageAuditEvent(event)).not.toThrow(); expect(laterListener).toHaveBeenCalledOnce(); + unsubscribeThrowing(); + unsubscribeLater(); }); it("tracks listeners and forwards producer metadata without durable identity work", () => { @@ -51,16 +48,4 @@ describe("trusted message audit events", () => { unsubscribe(); expect(hasTrustedMessageAuditListeners()).toBe(false); }); - - it("reset clears listeners", () => { - const listener = vi.fn(); - onTrustedMessageAuditEvent(listener); - emitTrustedMessageAuditEvent(event); - resetMessageAuditEventsForTest(); - - expect(hasTrustedMessageAuditListeners()).toBe(false); - onTrustedMessageAuditEvent(listener); - emitTrustedMessageAuditEvent(event); - expect(listener).toHaveBeenCalledTimes(2); - }); }); diff --git a/src/audit/message-audit-events.ts b/src/audit/message-audit-events.ts index 3996e40805db..53c2ec5c7503 100644 --- a/src/audit/message-audit-events.ts +++ b/src/audit/message-audit-events.ts @@ -31,7 +31,3 @@ export function onTrustedMessageAuditEvent(listener: MessageAuditListener): () = export function hasTrustedMessageAuditListeners(): boolean { return listeners.size > 0; } - -export function resetMessageAuditEventsForTest(): void { - listeners.clear(); -} diff --git a/src/infra/outbound/deliver.queue-integration.test.ts b/src/infra/outbound/deliver.queue-integration.test.ts index 1c84c356f424..cb87797d8832 100644 --- a/src/infra/outbound/deliver.queue-integration.test.ts +++ b/src/infra/outbound/deliver.queue-integration.test.ts @@ -1,10 +1,7 @@ import { expectDefined } from "@openclaw/normalization-core"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { - onTrustedMessageAuditEvent, - resetMessageAuditEventsForTest, - type TrustedMessageAuditEvent, -} from "../../audit/message-audit-events.js"; +import type { TrustedMessageAuditEvent } from "../../audit/message-audit-events.js"; +import { onTrustedMessageAuditEventForTest as onTrustedMessageAuditEvent } from "../../audit/message-audit-events.test-support.js"; import type { ChannelOutboundAdapter } from "../../channels/plugins/types.public.js"; import type { OpenClawConfig } from "../../config/config.js"; import { createEmptyPluginRegistry } from "../../plugins/registry.js"; @@ -99,7 +96,6 @@ describe("deliverOutboundPayloads queue integration: mid-batch failure with send }); beforeEach(() => { - resetMessageAuditEventsForTest(); tmpDir = fixtures.tmpDir(); setActivePluginRegistry( createTestRegistry([ @@ -113,7 +109,6 @@ describe("deliverOutboundPayloads queue integration: mid-batch failure with send }); afterEach(() => { - resetMessageAuditEventsForTest(); releasePinnedPluginChannelRegistry(); setActivePluginRegistry(createEmptyPluginRegistry()); }); diff --git a/src/infra/outbound/deliver.test.ts b/src/infra/outbound/deliver.test.ts index 4e20ee280640..f55fa20ce95a 100644 --- a/src/infra/outbound/deliver.test.ts +++ b/src/infra/outbound/deliver.test.ts @@ -3,11 +3,8 @@ import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { - onTrustedMessageAuditEvent, - resetMessageAuditEventsForTest, - type TrustedMessageAuditEvent, -} from "../../audit/message-audit-events.js"; +import type { TrustedMessageAuditEvent } from "../../audit/message-audit-events.js"; +import { onTrustedMessageAuditEventForTest as onTrustedMessageAuditEvent } from "../../audit/message-audit-events.test-support.js"; import { chunkText } from "../../auto-reply/chunk.js"; import { createMessageReceiptFromOutboundResults } from "../../channels/message/receipt.js"; import type { @@ -315,7 +312,6 @@ describe("deliverOutboundPayloads", () => { beforeEach(() => { resetDiagnosticEventsForTest(); - resetMessageAuditEventsForTest(); releasePinnedPluginChannelRegistry(); setActivePluginRegistry(defaultRegistry); mocks.appendAssistantMessageToSessionTranscript.mockClear(); @@ -358,7 +354,6 @@ describe("deliverOutboundPayloads", () => { afterEach(() => { resetDiagnosticEventsForTest(); - resetMessageAuditEventsForTest(); releasePinnedPluginChannelRegistry(); setActivePluginRegistry(emptyRegistry); }); diff --git a/src/infra/outbound/delivery-queue.recovery.test.ts b/src/infra/outbound/delivery-queue.recovery.test.ts index 6246cb223057..e43839605199 100644 --- a/src/infra/outbound/delivery-queue.recovery.test.ts +++ b/src/infra/outbound/delivery-queue.recovery.test.ts @@ -2,11 +2,8 @@ // reconciliation, commit hooks, and retry budget deferral. import { MAX_DATE_TIMESTAMP_MS } from "@openclaw/normalization-core/number-coercion"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { - onTrustedMessageAuditEvent, - resetMessageAuditEventsForTest, - type TrustedMessageAuditEvent, -} from "../../audit/message-audit-events.js"; +import type { TrustedMessageAuditEvent } from "../../audit/message-audit-events.js"; +import { onTrustedMessageAuditEventForTest as onTrustedMessageAuditEvent } from "../../audit/message-audit-events.test-support.js"; import { openOpenClawStateDatabase } from "../../state/openclaw-state-db.js"; import { OutboundDeliveryError, @@ -66,7 +63,6 @@ describe("delivery-queue recovery", () => { const baseCfg = {}; beforeEach(() => { - resetMessageAuditEventsForTest(); resolveOutboundChannelMessageAdapterMock.mockReset(); }); diff --git a/src/infra/outbound/outbound-audit.test.ts b/src/infra/outbound/outbound-audit.test.ts index 55d5f6ae3250..55d8e384b09a 100644 --- a/src/infra/outbound/outbound-audit.test.ts +++ b/src/infra/outbound/outbound-audit.test.ts @@ -1,9 +1,6 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { - onTrustedMessageAuditEvent, - resetMessageAuditEventsForTest, - type TrustedMessageAuditEvent, -} from "../../audit/message-audit-events.js"; +import { describe, expect, it, vi } from "vitest"; +import type { TrustedMessageAuditEvent } from "../../audit/message-audit-events.js"; +import { onTrustedMessageAuditEventForTest as onTrustedMessageAuditEvent } from "../../audit/message-audit-events.test-support.js"; import { completedOutboundAuditTerminals, emitOutboundAuditTerminals, @@ -11,9 +8,6 @@ import { } from "./outbound-audit.js"; describe("outbound audit projection", () => { - beforeEach(() => resetMessageAuditEventsForTest()); - afterEach(() => resetMessageAuditEventsForTest()); - it("keeps mixed logical payloads distinct under one durable queue intent", () => { const events: TrustedMessageAuditEvent[] = []; const unsubscribe = onTrustedMessageAuditEvent((event) => events.push(event));