import { execFile } from "node:child_process"; import { chmodSync, existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync, } from "node:fs"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { promisify } from "node:util"; import { afterEach, expect, test, vi } from "vitest"; import { parse } from "yaml"; import { buildApp } from "../src/app.js"; import { loadConfig } from "../src/config.js"; import { ThtRunner } from "../src/tht/tht-runner.js"; import { WorkspaceRegistry } from "../src/workspaces/registry.js"; import type { WorkspaceRegistryConfig } from "../src/workspaces/types.js"; const runFile = promisify(execFile); const harnessDir = resolve("../harness"); const thtBin = join(harnessDir, ".venv", "bin", "tht"); const roots: string[] = []; const canonicalWorkspace = `workspace: schema_version: 2 id: psd-clinical name: Runtime handoff language: en dwh: engine: postgres database: analytics schema: mart supported_transports: [postgres_direct] semantic_index: vector_store: engine: pgvector database: analytics schema: vectors collection: documents dimensions: 768 distance: cosine supported_transports: [pgvector_direct] embedding: provider: ollama_compatible model: embed dimensions: 768 llm_policy: allowed: [zai/glm-5.2] `; afterEach(() => { vi.unstubAllEnvs(); roots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true })); }); async function git(cwd: string, args: string[]): Promise { return (await runFile("git", args, { cwd })).stdout.trim(); } async function fixture() { const root = mkdtempSync(join(tmpdir(), "tht-runtime-handoff-")); roots.push(root); const remote = join(root, "remote.git"); const source = join(root, "source"); const registryRoot = join(root, "registry"); const secretRoot = join(root, "secrets"); const dataRoot = join(root, "data"); await git(root, ["init", "--bare", "--initial-branch=main", remote]); mkdirSync(source); await git(source, ["init", "--initial-branch=main"]); await git(source, ["config", "user.name", "Runtime Handoff Test"]); await git(source, ["config", "user.email", "runtime-handoff@example.invalid"]); mkdirSync(join(source, "workspaces")); writeFileSync(join(source, "workspaces", "psd-clinical.yaml"), canonicalWorkspace); await git(source, ["add", "workspaces/psd-clinical.yaml"]); await git(source, ["commit", "-m", "Canonical workspace"]); await git(source, ["remote", "add", "origin", remote]); await git(source, ["push", "origin", "main"]); mkdirSync(secretRoot); for (const name of ["dwh-password", "vector-password"]) { const path = join(secretRoot, name); writeFileSync(path, `${name}-value`, { mode: 0o600 }); chmodSync(path, 0o600); } mkdirSync(dataRoot); const registryConfig: WorkspaceRegistryConfig = { root: registryRoot, remoteUrl: remote, branch: "main", gitAuthorName: "Runtime Handoff Test", gitAuthorEmail: "runtime-handoff@example.invalid", installationId: "test", secretRoots: [secretRoot], maxImportBytes: 1024 * 1024, maxImportEntries: 16, }; const registry = new WorkspaceRegistry(registryConfig); await registry.bootstrap(); const revision = (await registry.list())[0]; const environment = { THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "postgres_direct", THT_WS_PSD_CLINICAL_DWH_HOST: "dwh.invalid", THT_WS_PSD_CLINICAL_DWH_PORT: "5432", THT_WS_PSD_CLINICAL_DWH_USER: "reader", THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: join(secretRoot, "dwh-password"), THT_WS_PSD_CLINICAL_VECTOR_TRANSPORT: "pgvector_direct", THT_WS_PSD_CLINICAL_VECTOR_HOST: "vector.invalid", THT_WS_PSD_CLINICAL_VECTOR_PORT: "5432", THT_WS_PSD_CLINICAL_VECTOR_USER: "vector-reader", THT_WS_PSD_CLINICAL_VECTOR_PASSWORD_FILE: join(secretRoot, "vector-password"), THT_WS_PSD_CLINICAL_EMBEDDING_BASE_URL: "http://embedding.invalid", }; for (const [name, value] of Object.entries(environment)) vi.stubEnv(name, value); vi.stubEnv("THT_HOME", join(root, "home")); return { root, dataRoot, registry, registryConfig, revision }; } function runnerFor(f: Awaited>): ThtRunner { return new ThtRunner({ thtBin, harnessDir, configPath: "config/tht.yaml", dataRoot: f.dataRoot, runtimeSnapshotRoot: join(f.registryConfig.root, "snapshots", "runtime"), secretRoots: f.registryConfig.secretRoots, } as any); } test("real schema-v2 registry revision loads through ThtRunner and the harness contract", async () => { const f = await fixture(); const runner = runnerFor(f); expect(await runner.sessionList(f.revision.snapshotPath)).toEqual([]); const created = await runner.sessionNew({ question: "runtime handoff", workspaceConfigPath: f.revision.snapshotPath, workspaceId: f.revision.id, workspaceRevision: f.revision.commit, }); expect(await runner.sessionShow(created.id, f.revision.snapshotPath)).toMatchObject({ id: created.id, workspace_id: "psd-clinical", workspace_revision: f.revision.commit, }); expect(existsSync(join( f.dataRoot, "sessions", "psd-clinical", "sessions", created.id, "session_manifest.yaml", ))).toBe(true); expect(readdirSync(join(f.registryConfig.root, "snapshots", "runtime"))).toEqual([]); }); test("separate runtime leases hand off one stable logical workspace identity", async () => { const f = await fixture(); const runner = runnerFor(f); const first = runner.acquireWorkspaceRuntime(f.revision.snapshotPath); const second = runner.acquireWorkspaceRuntime(f.revision.snapshotPath); try { expect(first.path).not.toBe(second.path); expect(parse(readFileSync(first.path, "utf8")).runtime_identity).toEqual({ workspace_id: "psd-clinical", workspace_revision: f.revision.commit, source_identity: "workspace://psd-clinical", }); expect(parse(readFileSync(second.path, "utf8")).runtime_identity).toEqual({ workspace_id: "psd-clinical", workspace_revision: f.revision.commit, source_identity: "workspace://psd-clinical", }); } finally { first.release(); second.release(); } }); test("local GET sessions mine uses the real canonical handoff and returns an empty inventory", async () => { const f = await fixture(); const app = buildApp(loadConfig({ AUTH_MODE: "none", THT_HARNESS_DIR: harnessDir, THT_BIN: thtBin, THT_DATA_ROOT: f.dataRoot, THT_WORKSPACE_REGISTRY_ROOT: f.registryConfig.root, THT_WORKSPACE_GIT_REMOTE: f.registryConfig.remoteUrl, THT_WORKSPACE_SECRET_ROOTS: f.registryConfig.secretRoots.join(","), }), { thtRunner: runnerFor(f), workspaceRegistry: f.registry, mgr: { get: () => undefined } as any, }); try { const response = await app.inject({ method: "GET", url: "/sessions?scope=mine" }); expect(response.statusCode).toBe(200); expect(response.json()).toEqual([]); } finally { await app.close(); } });