1// @effect-diagnostics nodeBuiltinImport:off - Static architecture test scans source files.
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4
5import { assert, it } from "@effect/vitest";
6
7const sourceRoot = NodePath.resolve(import.meta.dirname, "..");
8const forbiddenImport =
9 /from\s+["'][^"']*(?:ProviderService|ProviderSessionDirectory|ProviderSessionReaper|ProviderCommandReactor|ProviderRuntimeIngestion)[^"']*["']/;
10// The V1 read model tables. `projection_projects` is not listed: it is the V2 project store.
11const legacyTable =
12 /\bprojection_(?:threads|thread_messages|thread_activities|thread_proposed_plans|thread_pull_requests|thread_sessions|turns|pending_approvals|state)\b/;
13/** Directories whose files may read the V1 tables: the importer and the schema history. */
14const legacyReaders = ["orchestration-v2/legacy/", "persistence/Migrations/"] as const;
15/**
16 * Individual files allowed to read the V1 tables, each with its reason. Keep this
17 * list short; new V1 reads belong in the importer.
18 */
19const legacyReaderFiles: Record<string, string> = {
20 // Provider history for settings migration reads V1 thread sessions once at load.
21 "serverSettings.ts": "one-time provider history for settings migration",
22};
23const retiredPaths = [
24 "orchestration",
25 "orchestration/Layers/ProviderCommandReactor.ts",
26 "orchestration/Layers/ProviderRuntimeIngestion.ts",
27 "orchestration/Services/ProviderCommandReactor.ts",
28 "orchestration/Services/ProviderRuntimeIngestion.ts",
29 "persistence/Services/ProjectionThreads.ts",
30 "persistence/Services/ProjectionProjects.ts",
31] as const;
32
33function productionTypeScriptFiles(directory: string): ReadonlyArray<string> {
34 return NodeFS.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
35 const path = NodePath.join(directory, entry.name);
36 if (entry.isDirectory()) return productionTypeScriptFiles(path);
37 return entry.isFile() && entry.name.endsWith(".ts") && !entry.name.includes(".test.")
38 ? [path]
39 : [];
40 });
41}
42
43const relativeSources = productionTypeScriptFiles(sourceRoot).map((path) => ({
44 path: NodePath.relative(sourceRoot, path).split(NodePath.sep).join("/"),
45 source: NodeFS.readFileSync(path, "utf8"),
46}));
47
48it("keeps the V1 agent runtime and engine deleted", () => {
49 for (const relativePath of retiredPaths) {
50 assert.isFalse(NodeFS.existsSync(NodePath.join(sourceRoot, relativePath)), relativePath);
51 }
52 const violations = relativeSources
53 .filter(({ path, source }) => !path.includes("/legacy/") && forbiddenImport.test(source))
54 .map(({ path }) => path);
55 assert.deepEqual(violations, []);
56});
57
58it("reads the V1 tables only from the legacy importer", () => {
59 const readers = relativeSources
60 .filter(({ source }) => legacyTable.test(source))
61 .map(({ path }) => path)
62 .filter(
63 (path) =>
64 !legacyReaders.some((directory) => path.startsWith(directory)) &&
65 legacyReaderFiles[path] === undefined,
66 );
67 assert.deepEqual(readers, []);
68 // The allowlist must not outlive the reads it excuses.
69 for (const path of Object.keys(legacyReaderFiles)) {
70 const file = relativeSources.find((candidate) => candidate.path === path);
71 assert.isTrue(file !== undefined && legacyTable.test(file.source), path);
72 }
73});
74
75it("keeps the legacy importer out of reach of new code", () => {
76 const importers = relativeSources
77 .filter(
78 ({ path, source }) =>
79 !path.startsWith("orchestration-v2/legacy/") && /from\s+["'][^"']*\/legacy\//.test(source),
80 )
81 .map(({ path }) => path)
82 .toSorted();
83 // Startup imports pending transcripts, the V2 runtime wires the importer, and
84 // thread and project services hydrate a V1 transcript before they act on it.
85 assert.deepEqual(importers, [
86 "orchestration-v2/ThreadManagementService.ts",
87 "orchestration-v2/runtimeLayer.ts",
88 "project/ProjectService.ts",
89 "serverRuntimeStartup.ts",
90 ]);
91});