Files
openclaw/test/scripts/check-session-accessor-boundary.test.ts
Josh Lehman 127e174c9e refactor: route auto-reply sessions through session seam (#89124)
* clawdbot-6f0: route agent runtime session writes through seam

* clawdbot-6f0: route command entry persistence through seam

* test: ratchet auto-reply session accessor writes

* refactor: scope embedded attempt quota reads

* test: ratchet session store save writer
2026-06-15 15:50:38 -07:00

190 lines
8.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
findSessionAccessorBoundaryViolations,
migratedBundledPluginSessionAccessorFiles,
findSessionAccessorWriteBoundaryViolations,
migratedSessionAccessorFiles,
migratedSessionAccessorWriteFiles,
} 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",
]),
);
});
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 the auto-reply 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/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",
]),
);
});
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("ignores comments and strings that describe legacy readers", () => {
expect(
findSessionAccessorBoundaryViolations(`
// loadSessionStore and readSessionEntries used to be called here.
const description = "loadSessionStore";
`),
).toEqual([]);
});
});