import { describe, expect, it } from "vitest"; import { findGatewaySessionCreateLifecycleViolations, findSessionAccessorBoundaryViolations, findSessionCompactManualTrimBoundaryViolations, findSessionAccessorWriteBoundaryViolations, findSessionLifecycleCleanupBoundaryViolations, findTranscriptWriterBoundaryViolations, migratedBundledPluginSessionAccessorFiles, migratedSessionLifecycleCleanupFiles, migratedSessionCompactManualTrimFiles, migratedSessionAccessorFiles, migratedSessionAccessorWriteFiles, migratedTranscriptWriterFiles, } from "../../scripts/check-session-accessor-boundary.mjs"; describe("session accessor boundary guard", () => { it("ratchets only the files migrated by the session accessor slices", () => { expect(migratedSessionAccessorFiles).toEqual( new Set([ "src/agents/embedded-agent-runner/compaction-successor-transcript.ts", "src/agents/embedded-agent-runner/run/attempt.ts", "src/agents/embedded-agent-runner/tool-result-truncation.ts", "src/agents/embedded-agent-runner/transcript-rewrite.ts", "src/agents/embedded-agent-runner/transcript-runtime-state.ts", "src/auto-reply/reply/agent-runner-helpers.ts", "src/auto-reply/reply/agent-runner.ts", "src/auto-reply/reply/commands-subagents/action-info.ts", "src/auto-reply/reply/followup-runner.ts", "src/auto-reply/reply/queue/drain.ts", "src/commands/export-trajectory.ts", "src/commands/health.ts", "src/commands/sandbox-explain.ts", "src/commands/sessions-tail.ts", "src/commands/sessions.ts", "src/commands/status.agent-local.ts", "src/commands/status.summary.ts", "src/config/sessions/combined-store-gateway.ts", "src/cron/isolated-agent/delivery-target.ts", "src/cron/service/timer.ts", "src/gateway/session-compaction-checkpoints.ts", "src/gateway/session-utils.ts", "src/gateway/sessions-resolve.ts", "src/gateway/server-methods/sessions.ts", "src/infra/outbound/message-action-tts.ts", "src/tui/embedded-backend.ts", ]), ); }); it("ratchets only the bundled plugin files migrated by this slice", () => { expect(migratedBundledPluginSessionAccessorFiles).toEqual( new Set([ "extensions/discord/src/monitor/native-command-model-picker-apply.ts", "extensions/discord/src/monitor/thread-session-close.ts", "extensions/telegram/src/bot-handlers.runtime.ts", ]), ); }); it("ratchets only files migrated to session accessor writes", () => { expect(migratedSessionAccessorWriteFiles).toEqual( new Set([ "src/agents/command/attempt-execution.shared.ts", "src/agents/command/session-store.ts", "src/agents/embedded-agent-runner/run.ts", "src/agents/embedded-agent-runner/run/attempt.ts", "src/agents/main-session-restart-recovery.ts", "src/auto-reply/reply/abort-cutoff.runtime.ts", "src/auto-reply/reply/agent-runner-cli-dispatch.ts", "src/auto-reply/reply/agent-runner-execution.ts", "src/auto-reply/reply/agent-runner-memory.ts", "src/auto-reply/reply/agent-runner.ts", "src/auto-reply/reply/body.ts", "src/auto-reply/reply/commands-acp/lifecycle.ts", "src/auto-reply/reply/commands-reset.ts", "src/auto-reply/reply/directive-handling.impl.ts", "src/auto-reply/reply/directive-handling.persist.ts", "src/auto-reply/reply/dispatch-from-config.runtime.ts", "src/auto-reply/reply/followup-runner.ts", "src/auto-reply/reply/get-reply.ts", "src/auto-reply/reply/model-selection.ts", "src/auto-reply/reply/session-reset-model.ts", "src/auto-reply/reply/session-updates.ts", "src/auto-reply/reply/session-usage.ts", "src/tui/embedded-backend.ts", "src/config/sessions/cleanup-service.ts", ]), ); }); it("ratchets only the files migrated by the transcript writer slice", () => { expect(migratedTranscriptWriterFiles).toEqual( new Set([ "src/agents/command/attempt-execution.ts", "src/agents/embedded-agent-runner/context-engine-maintenance.ts", "src/config/sessions/transcript.ts", "src/gateway/server-methods/chat.ts", "src/gateway/server-methods/chat-transcript-inject.ts", "src/sessions/user-turn-transcript.ts", ]), ); }); it("ratchets only compact manual trim gateway files", () => { expect(migratedSessionCompactManualTrimFiles).toEqual( new Set(["src/gateway/server-methods/sessions.ts"]), ); }); it("ratchets only the lifecycle cleanup files migrated to backend cleanup", () => { expect(migratedSessionLifecycleCleanupFiles).toEqual( new Set([ "src/config/sessions/cleanup-service.ts", "src/cron/session-reaper.ts", "src/infra/heartbeat-runner.ts", ]), ); }); it("flags legacy reader imports", () => { expect( findSessionAccessorBoundaryViolations(` import { loadSessionStore, readSessionEntries as readEntries } from "../config/sessions.js"; import { readSessionEntry, readSessionStoreReadOnly } from "../config/sessions/store-load.js"; `), ).toEqual([ { line: 2, reason: 'imports legacy session store access "loadSessionStore"' }, { line: 2, reason: 'imports legacy session store access "readSessionEntries"' }, { line: 3, reason: 'imports legacy session store access "readSessionEntry"' }, { line: 3, reason: 'imports legacy session store access "readSessionStoreReadOnly"' }, ]); }); it("flags direct and namespace legacy access calls", () => { expect( findSessionAccessorBoundaryViolations(` loadSessionStore(storePath); sessions.readSessionEntries(storePath); sessions["loadSessionStore"](storePath); readSessionStoreReadOnly(storePath); resolveSessionStoreEntry({ store, sessionKey }); `), ).toEqual([ { line: 2, reason: 'calls legacy session store access "loadSessionStore"' }, { line: 3, reason: 'references legacy session store access "readSessionEntries"' }, { line: 4, reason: 'references legacy session store access "loadSessionStore"' }, { line: 5, reason: 'calls legacy session store access "readSessionStoreReadOnly"' }, { line: 6, reason: 'calls legacy session store access "resolveSessionStoreEntry"' }, ]); }); it("flags aliased namespace reader references", () => { expect( findSessionAccessorBoundaryViolations(` const load = sessions.loadSessionStore; const { readSessionEntries: readEntries } = sessions; const { loadSessionStore } = sessions; `), ).toEqual([ { line: 2, reason: 'references legacy session store access "loadSessionStore"' }, { line: 3, reason: 'aliases legacy session store access "readSessionEntries"' }, { line: 4, reason: 'aliases legacy session store access "loadSessionStore"' }, ]); }); it("flags legacy whole-store writes", () => { expect( findSessionAccessorBoundaryViolations(` import { saveSessionStore, updateSessionStore } from "../config/sessions.js"; saveSessionStore(storePath, store); updateSessionStore(storePath, update); `), ).toEqual([ { line: 2, reason: 'imports legacy session store access "saveSessionStore"' }, { line: 2, reason: 'imports legacy session store access "updateSessionStore"' }, { line: 3, reason: 'calls legacy session store access "saveSessionStore"' }, { line: 4, reason: 'calls legacy session store access "updateSessionStore"' }, ]); }); it("allows migrated accessor reads", () => { expect( findSessionAccessorBoundaryViolations(` import { listSessionEntries } from "../config/sessions/session-accessor.js"; listSessionEntries({ storePath }); `), ).toEqual([]); }); it("flags legacy writer imports and calls", () => { expect( findSessionAccessorWriteBoundaryViolations(` import { applySessionStoreEntryPatch, saveSessionStore, updateSessionStore, updateSessionStoreEntry as updateEntry } from "../config/sessions.js"; saveSessionStore(storePath, store); updateSessionStore(storePath, () => undefined); sessions.updateSessionStoreEntry({ storePath, sessionKey, update }); applySessionStoreEntryPatch({ storePath, sessionKey, patch }); `), ).toEqual([ { line: 2, reason: 'imports legacy session store writer "applySessionStoreEntryPatch"' }, { line: 2, reason: 'imports legacy session store writer "saveSessionStore"' }, { line: 2, reason: 'imports legacy session store writer "updateSessionStore"' }, { line: 2, reason: 'imports legacy session store writer "updateSessionStoreEntry"' }, { line: 3, reason: 'calls legacy session store writer "saveSessionStore"' }, { line: 4, reason: 'calls legacy session store writer "updateSessionStore"' }, { line: 5, reason: 'references legacy session store writer "updateSessionStoreEntry"' }, { line: 6, reason: 'calls legacy session store writer "applySessionStoreEntryPatch"' }, ]); }); it("allows migrated accessor writes", () => { expect( findSessionAccessorWriteBoundaryViolations(` import { updateSessionEntry } from "../config/sessions/session-accessor.js"; updateSessionEntry({ storePath, sessionKey }, () => undefined); `), ).toEqual([]); }); it("flags legacy transcript writer imports", () => { expect( findTranscriptWriterBoundaryViolations(` import { appendSessionTranscriptMessage } from "../config/sessions/transcript-append.js"; import { emitSessionTranscriptUpdate as emitUpdate } from "../sessions/transcript-events.js"; import { rewriteTranscriptEntriesInSessionFile } from "../agents/embedded-agent-runner/transcript-rewrite.js"; `), ).toEqual([ { line: 2, reason: 'imports legacy transcript writer "appendSessionTranscriptMessage"' }, { line: 3, reason: 'imports legacy transcript writer "emitSessionTranscriptUpdate"' }, { line: 4, reason: 'imports legacy transcript writer "rewriteTranscriptEntriesInSessionFile"', }, ]); }); it("flags direct and namespace legacy transcript writer calls", () => { expect( findTranscriptWriterBoundaryViolations(` appendSessionTranscriptMessage({ transcriptPath, message }); transcriptEvents.emitSessionTranscriptUpdate({ sessionFile }); transcriptAppend["appendSessionTranscriptMessage"]({ transcriptPath, message }); transcriptRewrite.rewriteTranscriptEntriesInSessionFile({ sessionFile, request }); `), ).toEqual([ { line: 2, reason: 'calls legacy transcript writer "appendSessionTranscriptMessage"' }, { line: 3, reason: 'references legacy transcript writer "emitSessionTranscriptUpdate"' }, { line: 4, reason: 'references legacy transcript writer "appendSessionTranscriptMessage"' }, { line: 5, reason: 'references legacy transcript writer "rewriteTranscriptEntriesInSessionFile"', }, ]); }); it("allows migrated transcript writer helpers", () => { expect( findTranscriptWriterBoundaryViolations(` import { appendTranscriptMessage, publishTranscriptUpdate } from "../config/sessions/session-accessor.js"; appendTranscriptMessage(scope, { message }); publishTranscriptUpdate(scope, { messageId }); `), ).toEqual([]); }); it("flags legacy writers inside the gateway sessions.create lifecycle", () => { expect( findGatewaySessionCreateLifecycleViolations(` const handlers = { "sessions.create": async () => { await updateSessionStore(storePath, () => undefined); ensureSessionTranscriptFile(params); }, "sessions.patch": async () => { await updateSessionStore(storePath, () => undefined); }, }; `), ).toEqual([ { line: 4, reason: 'calls legacy sessions.create lifecycle writer "updateSessionStore"' }, { line: 5, reason: 'calls legacy sessions.create lifecycle writer "ensureSessionTranscriptFile"', }, ]); }); it("allows the gateway sessions.create lifecycle accessor seam", () => { expect( findGatewaySessionCreateLifecycleViolations(` const handlers = { "sessions.create": async () => { await createSessionEntryWithTranscript(scope, createEntry); }, }; `), ).toEqual([]); }); it("flags gateway manual compact trim file mutations", () => { expect( findSessionCompactManualTrimBoundaryViolations(` import { archiveFileOnDisk } from "../session-utils.js"; import { readRecentSessionTranscriptLines } from "../session-transcript-readers.js"; const tail = readRecentSessionTranscriptLines(scope); const archived = archiveFileOnDisk(filePath, "bak"); `), ).toEqual([ { line: 2, reason: 'imports legacy session store manual compact trim "archiveFileOnDisk"' }, { line: 3, reason: 'imports legacy session store manual compact trim "readRecentSessionTranscriptLines"', }, { line: 4, reason: 'calls legacy session store manual compact trim "readRecentSessionTranscriptLines"', }, { line: 5, reason: 'calls legacy session store manual compact trim "archiveFileOnDisk"' }, ]); }); it("flags direct lifecycle cleanup helper usage", () => { expect( findSessionLifecycleCleanupBoundaryViolations(` import { archiveRemovedSessionTranscripts } from "../config/sessions/store.js"; import { cleanupArchivedSessionTranscripts } from "../gateway/session-utils.fs.js"; archiveRemovedSessionTranscripts({ removedSessionFiles, referencedSessionIds, storePath, reason: "deleted" }); cleanupArchivedSessionTranscripts({ directories, rules }); `), ).toEqual([ { line: 2, reason: 'imports legacy session store lifecycle cleanup "archiveRemovedSessionTranscripts"', }, { line: 3, reason: 'imports legacy session store lifecycle cleanup "cleanupArchivedSessionTranscripts"', }, { line: 4, reason: 'calls legacy session store lifecycle cleanup "archiveRemovedSessionTranscripts"', }, { line: 5, reason: 'calls legacy session store lifecycle cleanup "cleanupArchivedSessionTranscripts"', }, ]); }); it("ignores comments and strings that describe legacy readers", () => { expect( findSessionAccessorBoundaryViolations(` // loadSessionStore and readSessionEntries used to be called here. const description = "loadSessionStore"; `), ).toEqual([]); }); });