fix: harden workspace activation and snapshot retention

This commit is contained in:
2026-08-04 09:25:06 +02:00
parent 3b23cf3714
commit e4fdbed864
14 changed files with 474 additions and 103 deletions
+11
View File
@@ -18,6 +18,8 @@ import { ReadinessManager } from "./runtime/readiness-manager.js";
import { WorkspaceRegistry } from "./workspaces/registry.js";
import { createProductionWorkspaceDiagnoser } from "./workspaces/diagnostics.js";
import { workspaceRoutes, type WorkspaceDiagnoser } from "./routes/workspaces.js";
import { resolveRuntimeBindings, supportsSessionRuntime } from "./workspaces/bindings.js";
import type { WorkspaceDescriptor } from "./workspaces/schema.js";
export interface BuildAppDeps {
thtRunner?: ThtRunner;
@@ -29,6 +31,7 @@ export interface BuildAppDeps {
hub?: SseHub;
workspaceRegistry?: WorkspaceRegistry;
workspaceDiagnoser?: WorkspaceDiagnoser;
workspaceRuntimeSupport?: (workspace: WorkspaceDescriptor) => boolean;
}
export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstance {
@@ -55,6 +58,13 @@ export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstanc
const workspaceRegistry = deps?.workspaceRegistry ?? new WorkspaceRegistry(config.workspaceRegistry);
const workspaceDiagnoser = deps?.workspaceDiagnoser
?? createProductionWorkspaceDiagnoser(config.workspaceDiagnosticTimeoutMs);
const workspaceRuntimeSupport = deps?.workspaceRuntimeSupport ?? ((workspace: WorkspaceDescriptor) => (
supportsSessionRuntime(resolveRuntimeBindings(
workspace,
process.env,
config.workspaceRegistry.secretRoots,
))
));
const readiness = deps?.readiness ?? new ReadinessManager(
tht as ThtRunner,
Math.round(config.ollamaEnsureTimeoutMs / 1000),
@@ -88,6 +98,7 @@ export function buildApp(config: AppConfig, deps?: BuildAppDeps): FastifyInstanc
mgr, tht: tht as ThtRunner, hub, getSettings, readiness, listModels, workspaceRegistry,
dwhPrecheck: config.dwhPrecheck,
legacyWorkspaceMode: config.legacyWorkspaceMode,
workspaceRuntimeSupport,
});
sqlRoutes(app, { tht: tht as ThtRunner, getSettings });
metaRoutes(app, { harnessDir: config.harnessDir, listModels });
+127 -90
View File
@@ -8,6 +8,7 @@ import type { PrincipalContext } from "../auth/principal.js";
import type { ReadinessManager } from "../runtime/readiness-manager.js";
import type { ListModelsFn } from "./meta.js";
import type { WorkspaceRegistry } from "../workspaces/registry.js";
import type { WorkspaceDescriptor } from "../workspaces/schema.js";
const BOOTSTRAP_FAILURE_MESSAGE =
"Session startup failed. Check configuration and connectivity, then Resume the session.";
@@ -34,6 +35,8 @@ export function sessionRoutes(
dwhPrecheck?: boolean;
/** Explicit loopback-only compatibility path for old clients that send `workspace`. */
legacyWorkspaceMode?: boolean;
/** Fail-closed installation/runtime transport capability check. */
workspaceRuntimeSupport: (workspace: WorkspaceDescriptor) => boolean;
},
) {
const lifecycleTails = new Map<string, Promise<void>>();
@@ -289,110 +292,144 @@ export function sessionRoutes(
code: "workspace_revision_unavailable",
});
}
let workspaceConfigPath: string | undefined;
let workspaceId: string | undefined;
let workspaceRevision: string | undefined;
let allowedModels: readonly string[] | undefined;
if (requestedWorkspaceId) {
try {
const resolved = await d.workspaceRegistry.read(requestedWorkspaceId);
if (resolved.revision.state !== "operational") {
let revisionLease: Awaited<ReturnType<WorkspaceRegistry["acquireSessionRevision"]>> | undefined;
let manifestPersisted = false;
try {
let workspaceConfigPath: string | undefined;
let workspaceId: string | undefined;
let workspaceRevision: string | undefined;
let allowedModels: readonly string[] | undefined;
if (requestedWorkspaceId) {
try {
const registry = d.workspaceRegistry as Partial<WorkspaceRegistry>;
const resolved = typeof registry.acquireSessionRevision === "function"
? await registry.acquireSessionRevision.call(d.workspaceRegistry, requestedWorkspaceId)
: await d.workspaceRegistry.read(requestedWorkspaceId);
if ("markPersisted" in resolved && "abort" in resolved) {
revisionLease = resolved as Awaited<ReturnType<WorkspaceRegistry["acquireSessionRevision"]>>;
}
if (resolved.revision.state !== "operational") {
return reply.code(409).send({
error: WORKSPACE_REVISION_UNAVAILABLE_MESSAGE,
code: "workspace_revision_unavailable",
});
}
if (!d.workspaceRuntimeSupport(resolved.workspace)) {
return reply.code(409).send({
error: "This workspace transport is not available to runtime sessions.",
code: "workspace_not_activatable",
});
}
workspaceConfigPath = resolved.revision.snapshotPath;
workspaceId = resolved.revision.id;
workspaceRevision = resolved.revision.commit;
allowedModels = resolved.workspace.llm_policy.allowed;
} catch {
return reply.code(409).send({
error: WORKSPACE_REVISION_UNAVAILABLE_MESSAGE,
code: "workspace_revision_unavailable",
});
}
workspaceConfigPath = resolved.revision.snapshotPath;
workspaceId = resolved.revision.id;
workspaceRevision = resolved.revision.commit;
allowedModels = resolved.workspace.llm_policy.allowed;
} catch {
return reply.code(409).send({
error: WORKSPACE_REVISION_UNAVAILABLE_MESSAGE,
code: "workspace_revision_unavailable",
});
}
}
const provider = b.provider ?? s.provider;
const model = b.model ?? s.model;
const thinking = b.thinking ?? s.thinking;
if (allowedModels && provider && model && !allowedModels.includes(`${provider}/${model}`)) {
return reply.code(400).send({ error: "Selected model is not allowed by this workspace." });
}
// A persisted session is resumable without keeping Pi alive. New work replaces every
// runtime owned by this principal, while runtimes belonging to other users remain intact.
// Optional chaining preserves the deliberately narrow manager stubs used by route tests.
for (const id of d.mgr.teardownForPrincipal?.(principal) ?? []) boundRuntimes.delete(id);
const ensure = await d.readiness.ensure(workspaceConfigPath ?? "", principal);
if (!ensure.ok) return reply.code(503).send({ error: READINESS_FAILURE_MESSAGE });
// Local-only: verify the DWH is reachable BEFORE creating the session, so a dropped
// VPN surfaces as an up-front alert instead of a session that spawns Pi and then dies
// in bootstrap retrieval. `code` lets the client show a specific message.
if (d.dwhPrecheck) {
const ping = await runner.dbPing(workspaceConfigPath);
if (!ping.ok) {
console.error(`[dwh-precheck] refusing new session — DWH unreachable: ${ping.detail}`);
return reply.code(503).send({ error: DWH_UNREACHABLE_MESSAGE, code: "dwh_unreachable" });
const provider = b.provider ?? s.provider;
const model = b.model ?? s.model;
const thinking = b.thinking ?? s.thinking;
if (allowedModels && provider && model && !allowedModels.includes(`${provider}/${model}`)) {
return reply.code(400).send({ error: "Selected model is not allowed by this workspace." });
}
}
if (provider && model) {
let available: Awaited<ReturnType<ListModelsFn>>;
// A persisted session is resumable without keeping Pi alive. New work replaces every
// runtime owned by this principal, while runtimes belonging to other users remain intact.
// Optional chaining preserves the deliberately narrow manager stubs used by route tests.
for (const id of d.mgr.teardownForPrincipal?.(principal) ?? []) boundRuntimes.delete(id);
const ensure = await d.readiness.ensure(workspaceConfigPath ?? "", principal);
if (!ensure.ok) return reply.code(503).send({ error: READINESS_FAILURE_MESSAGE });
// Local-only: verify the DWH is reachable BEFORE creating the session, so a dropped
// VPN surfaces as an up-front alert instead of a session that spawns Pi and then dies
// in bootstrap retrieval. `code` lets the client show a specific message.
if (d.dwhPrecheck) {
const ping = await runner.dbPing(workspaceConfigPath);
if (!ping.ok) {
console.error(`[dwh-precheck] refusing new session — DWH unreachable: ${ping.detail}`);
return reply.code(503).send({ error: DWH_UNREACHABLE_MESSAGE, code: "dwh_unreachable" });
}
}
if (provider && model) {
let available: Awaited<ReturnType<ListModelsFn>>;
try {
available = await d.listModels();
} catch {
return reply.code(503).send({
error: MODEL_UNAVAILABLE_MESSAGE,
code: "model_unavailable",
});
}
const selectedAvailable = available.some(
(candidate) => candidate.provider === provider && candidate.id === model,
);
if (!selectedAvailable) {
return reply.code(503).send({
error: MODEL_UNAVAILABLE_MESSAGE,
code: "model_unavailable",
});
}
}
// Browser choices are copied to the persisted manifest together with the immutable
// registry snapshot. The legacy fallback stays available for sessions created before
// the browser-local preference migration.
let id: string;
try {
available = await d.listModels();
} catch {
return reply.code(503).send({
error: MODEL_UNAVAILABLE_MESSAGE,
code: "model_unavailable",
({ id } = await runner.sessionNew({
question: b.question, name: b.name, workspaceConfigPath,
workspaceId, workspaceRevision, provider, model, thinking,
}));
manifestPersisted = true;
if (revisionLease) {
await revisionLease.markPersisted().catch((error: unknown) => {
console.error(
`[session:${id}] revision lease hand-off failed:`,
error instanceof Error ? error.message : "unknown error",
);
});
}
} catch { return storageFailure(reply); }
const options = {
provider, model, thinking,
author: principal.displayName ?? principal.subject,
principal,
question: b.question,
};
let rt: ReturnType<PiProcessManager["createFor"]> | undefined;
try {
rt = d.mgr.createFor(id, options);
bindRuntime(id, rt, runner, workspaceConfigPath);
} catch (error) {
if (rt) d.mgr.teardownIfCurrent(id, rt);
console.error(
`[pi:${id}] runtime construction failed:`,
error instanceof Error ? error.message : "unknown error",
);
await runner.failSession(id, workspaceConfigPath).catch((persistenceError: unknown) => {
console.error(`[session:${id}] failSession persistence failed:`, persistenceError);
});
return reply.code(503).send({ error: BOOTSTRAP_FAILURE_MESSAGE });
}
const selectedAvailable = available.some(
(candidate) => candidate.provider === provider && candidate.id === model,
info(id, "Session created");
bootstrap(
id, rt, runner, workspaceConfigPath, d.mgr.configure(rt, options),
runner.searchPack(b.question, id, workspaceConfigPath),
() => d.mgr.start(id, rt, options),
);
if (!selectedAvailable) {
return reply.code(503).send({
error: MODEL_UNAVAILABLE_MESSAGE,
code: "model_unavailable",
return { id };
} finally {
if (revisionLease && !manifestPersisted) {
await revisionLease.abort().catch((error: unknown) => {
console.error(
"[session] revision lease cleanup failed:",
error instanceof Error ? error.message : "unknown error",
);
});
}
}
// Browser choices are copied to the persisted manifest together with the immutable
// registry snapshot. The legacy fallback stays available for sessions created before
// the browser-local preference migration.
let id: string;
try {
({ id } = await runner.sessionNew({
question: b.question, name: b.name, workspaceConfigPath,
workspaceId, workspaceRevision, provider, model, thinking,
}));
} catch { return storageFailure(reply); }
const options = {
provider, model, thinking,
author: principal.displayName ?? principal.subject,
principal,
question: b.question,
};
let rt: ReturnType<PiProcessManager["createFor"]> | undefined;
try {
rt = d.mgr.createFor(id, options);
bindRuntime(id, rt, runner, workspaceConfigPath);
} catch (error) {
if (rt) d.mgr.teardownIfCurrent(id, rt);
console.error(
`[pi:${id}] runtime construction failed:`,
error instanceof Error ? error.message : "unknown error",
);
await runner.failSession(id, workspaceConfigPath).catch((persistenceError: unknown) => {
console.error(`[session:${id}] failSession persistence failed:`, persistenceError);
});
return reply.code(503).send({ error: BOOTSTRAP_FAILURE_MESSAGE });
}
info(id, "Session created");
bootstrap(
id, rt, runner, workspaceConfigPath, d.mgr.configure(rt, options),
runner.searchPack(b.question, id, workspaceConfigPath),
() => d.mgr.start(id, rt, options),
);
return { id };
});
app.get("/sessions", async (req, reply) => {
const principal = getPrincipal(req);
+8
View File
@@ -143,3 +143,11 @@ export function resolveRuntimeBindings(
embedding: resolveBinding(workspace, "EMBEDDING", env, secretRoots),
};
}
/**
* SSH bindings are currently probe-only: diagnostics owns a short-lived tunnel, while the
* session runtime has no tunnel owner. Keep activation fail-closed until that lifecycle exists.
*/
export function supportsSessionRuntime(bindings: RuntimeBindings): boolean {
return bindings.dwh.transport !== "ssh_tunnel" && bindings.vector.transport !== "ssh_tunnel";
}
+13 -1
View File
@@ -535,7 +535,9 @@ function diagnosticError(code: WorkspaceErrorCode, field?: string): Diagnostic {
? "Installation binding is missing or invalid."
: code === "semantic_index_incompatible"
? "Semantic index metadata is incompatible with this workspace."
: "Connector diagnostic failed.",
: code === "workspace_not_activatable"
? "This transport can be tested, but it is not available to runtime sessions."
: "Connector diagnostic failed.",
};
}
@@ -851,6 +853,16 @@ export function createWorkspaceDiagnoser(
}
}
// The concrete SSH adapter deliberately owns only a bounded diagnostic tunnel and closes it
// in `finally`. Until a session runtime owns an equivalent long-lived tunnel, a successful
// probe is connectivity evidence only and must never be advertised as activatable.
if (
(bindings.dwh.transport === "ssh_tunnel" || bindings.vector.transport === "ssh_tunnel")
&& !diagnostics.some((diagnostic) => diagnostic.level === "error")
) {
diagnostics.push(diagnosticError("workspace_not_activatable"));
}
return {
activatable: !diagnostics.some((diagnostic) => diagnostic.level === "error"),
diagnostics,
+139 -1
View File
@@ -28,6 +28,15 @@ export interface WorkspaceRevision {
state: "operational" | "migration_required";
}
export interface SessionRevisionLease {
workspace: WorkspaceDescriptor;
revision: WorkspaceRevision;
/** Mark the manifest durable; retention removes the lease only after observing that manifest. */
markPersisted(): Promise<void>;
/** Remove a lease for a session that failed before its manifest was durable. */
abort(): Promise<void>;
}
export type PublishWorkspaceRequest =
| { action: "create"; workspace: CanonicalWorkspace; baseCommit: string }
| { action: "update"; workspace: CanonicalWorkspace; baseCommit: string; baseBlob: string }
@@ -56,6 +65,14 @@ interface SnapshotManifest extends ActiveState {
files: Record<string, string>;
}
interface RevisionLeaseRecord {
version: 1;
token: string;
workspaceId: string;
commit: string;
state: "creating" | "persisted";
}
type LegacyWorkspaceRevision = Omit<WorkspaceRevision, "state">;
interface LegacyActiveState {
@@ -178,6 +195,56 @@ export class WorkspaceRegistry {
}
}
/**
* Resolve the active revision and create its cross-process retention lease under the same
* repository lock. The lease bridges the interval before `session_manifest.yaml` is durable.
*/
async acquireSessionRevision(id: string): Promise<SessionRevisionLease> {
await this.repository.ensureLayout();
return await this.lock.run(async () => {
const state = await this.activeState();
const revision = state.revisions.find((candidate) => candidate.id === id);
if (!revision) throw new WorkspaceRegistryError("workspace_invalid", "Workspace is unavailable");
let workspace: WorkspaceDescriptor;
try {
workspace = parseWorkspaceYaml(await readFile(revision.snapshotPath, "utf8"));
} catch (error) {
throw workspaceError(error);
}
const token = randomUUID();
const record: RevisionLeaseRecord = {
version: 1,
token,
workspaceId: id,
commit: revision.commit,
state: "creating",
};
const path = await this.writeRevisionLease(record, true);
let localState: RevisionLeaseRecord["state"] | "aborted" = "creating";
return {
workspace,
revision,
markPersisted: async () => {
if (localState === "persisted") return;
if (localState === "aborted") throw new WorkspaceRegistryError(
"workspace_invalid", "Workspace revision lease is unavailable",
);
await this.lock.run(async () => {
await this.replaceRevisionLease(path, { ...record, state: "persisted" });
});
localState = "persisted";
},
abort: async () => {
if (localState !== "creating") return;
await this.lock.run(async () => { await rm(path, { force: true }); });
localState = "aborted";
},
};
});
}
/** Read a retained immutable snapshot for a session pinned to a historical commit. */
async readPinned(id: string, commit: string): Promise<{ workspace: WorkspaceDescriptor; workspaceConfigPath: string }> {
const snapshotPath = this.snapshotPath(safeCommit(commit), id);
@@ -195,9 +262,12 @@ export class WorkspaceRegistry {
* a partial, per-user list could otherwise remove another user's resumable workspace pin.
*/
async reconcileSnapshotRetention(referencedCommits: readonly string[]): Promise<void> {
const retained = new Set(referencedCommits.map(safeCommit));
const manifestReferences = new Set(referencedCommits.map(safeCommit));
const retained = new Set(manifestReferences);
await this.repository.ensureLayout();
await this.lock.run(async () => {
const leases = await this.revisionLeases();
for (const { record } of leases) retained.add(record.commit);
retained.add((await this.activeState()).head);
const entries = await readdir(this.repository.snapshotsPath, { withFileTypes: true });
for (const entry of entries) {
@@ -210,9 +280,77 @@ export class WorkspaceRegistry {
if (!current.isDirectory() || current.isSymbolicLink()) continue;
await rm(path, { recursive: true, force: true });
}
// A persisted lease is handed off only when this exact authoritative scan has observed a
// manifest pin for its commit. A stale scan therefore keeps the lease and cannot prune it.
for (const { path, record } of leases) {
if (record.state === "persisted" && manifestReferences.has(record.commit)) {
await rm(path, { force: true });
}
}
});
}
private revisionLeaseDirectory(): string {
return join(this.repository.statePath, "revision-leases");
}
private async writeRevisionLease(record: RevisionLeaseRecord, exclusive: boolean): Promise<string> {
const directory = this.revisionLeaseDirectory();
await mkdir(directory, { recursive: true, mode: 0o700 });
const path = join(directory, `${record.token}.json`);
await writeFile(path, JSON.stringify(record), {
encoding: "utf8",
mode: 0o600,
flush: true,
...(exclusive ? { flag: "wx" } : {}),
});
return path;
}
private async replaceRevisionLease(path: string, record: RevisionLeaseRecord): Promise<void> {
const staging = `${path}.staging-${randomUUID()}`;
try {
await writeFile(staging, JSON.stringify(record), {
encoding: "utf8", mode: 0o600, flag: "wx", flush: true,
});
await rename(staging, path);
} catch (error) {
await rm(staging, { force: true });
throw error;
}
}
private async revisionLeases(): Promise<Array<{ path: string; record: RevisionLeaseRecord }>> {
const directory = this.revisionLeaseDirectory();
await mkdir(directory, { recursive: true, mode: 0o700 });
const entries = await readdir(directory, { withFileTypes: true });
const leases: Array<{ path: string; record: RevisionLeaseRecord }> = [];
for (const entry of entries) {
if (!entry.isFile() || entry.isSymbolicLink() || !/^[0-9a-f-]{36}\.json$/.test(entry.name)) {
throw new WorkspaceRegistryError("workspace_invalid", "Workspace revision lease is invalid");
}
const path = join(directory, entry.name);
let record: RevisionLeaseRecord;
try {
record = JSON.parse(await readFile(path, "utf8")) as RevisionLeaseRecord;
} catch {
throw new WorkspaceRegistryError("workspace_invalid", "Workspace revision lease is invalid");
}
if (
record.version !== 1
|| `${record.token}.json` !== entry.name
|| !/^[0-9a-f-]{36}$/.test(record.token)
|| !/^[a-z][a-z0-9-]{2,62}$/.test(record.workspaceId)
|| !/^[0-9a-f]{40}$/.test(record.commit)
|| (record.state !== "creating" && record.state !== "persisted")
) {
throw new WorkspaceRegistryError("workspace_invalid", "Workspace revision lease is invalid");
}
leases.push({ path, record });
}
return leases;
}
/**
* Publish canonical YAML and derived public documentation as one optimistic Git revision.
* The browser never provides paths or generated artifacts; those are derived server-side.
+95
View File
@@ -30,6 +30,7 @@ const defaultWorkspaceRegistry = {
function buildApp(config: Parameters<typeof buildRealApp>[0], deps: Record<string, unknown> = {}) {
return buildRealApp(config, {
workspaceRuntimeSupport: () => true,
...deps,
workspaceRegistry: { ...defaultWorkspaceRegistry, ...(deps.workspaceRegistry as object | undefined) },
} as any);
@@ -324,6 +325,100 @@ test("creates a session from the active immutable workspace revision", async ()
}));
});
test("rejects an SSH-only workspace before persisting or starting a session", async () => {
const sessionNew = vi.fn(async () => ({ id: "must-not-exist" }));
const ensure = vi.fn(async () => ({ ok: true }));
const createFor = vi.fn();
const abort = vi.fn(async () => {});
const markPersisted = vi.fn(async () => {});
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: { sessionNew, searchPack: async () => {} } as any,
readiness: { ensure } as any,
mgr: { get: () => undefined, createFor } as any,
getSettings: () => ({ workspace: "ssh-workspace" }) as any,
workspaceRuntimeSupport: vi.fn(() => false),
workspaceRegistry: {
acquireSessionRevision: vi.fn(async () => ({
workspace: {
workspace: { schema_version: 2, id: "ssh-workspace", name: "SSH", language: "en" },
dwh: {
engine: "postgres", database: "postgres", schema: "public",
supported_transports: ["ssh_tunnel"],
},
semantic_index: {
vector_store: {
engine: "pgvector", database: "postgres", schema: "vectors",
collection: "documents", dimensions: 768, distance: "cosine",
supported_transports: ["ssh_tunnel"],
},
embedding: {
provider: "ollama_compatible", model: "nomic-embed-text", dimensions: 768,
},
},
llm_policy: { allowed: ["zai/glm-5.2"] },
},
revision: {
id: "ssh-workspace", commit: "a".repeat(40), blob: "b".repeat(40),
snapshotPath: `/data/workspace-registry/snapshots/${"a".repeat(40)}/ssh-workspace.yaml`,
state: "operational",
},
abort,
markPersisted,
})),
} as any,
});
const response = await app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
expect(response.statusCode).toBe(409);
expect(response.json()).toMatchObject({ code: "workspace_not_activatable" });
expect(ensure).not.toHaveBeenCalled();
expect(sessionNew).not.toHaveBeenCalled();
expect(createFor).not.toHaveBeenCalled();
expect(abort).toHaveBeenCalledOnce();
expect(markPersisted).not.toHaveBeenCalled();
});
test("hands a revision lease to retention only after the session manifest is durable", async () => {
const persisted = deferred<{ id: string }>();
const markPersisted = vi.fn(async () => {});
const abort = vi.fn(async () => {});
const acquireSessionRevision = vi.fn(async () => ({
workspace: { llm_policy: { allowed: ["zai/glm-5.2"] } },
revision: {
id: "leased", commit: "a".repeat(40), blob: "b".repeat(40),
snapshotPath: `/data/workspace-registry/snapshots/${"a".repeat(40)}/leased.yaml`,
state: "operational",
},
markPersisted,
abort,
}));
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: { sessionNew: () => persisted.promise, searchPack: async () => {} } as any,
readiness: { ensure: async () => ({ ok: true }) } as any,
mgr: {
get: () => undefined,
createFor: () => ({ bridge: { onClientEvent: () => {} } }),
configure: async () => {},
start: () => {},
} as any,
getSettings: () => ({ workspace: "leased", provider: "zai", model: "glm-5.2" }) as any,
listModels: async () => [{ provider: "zai", id: "glm-5.2", name: "GLM", reasoning: true }],
workspaceRuntimeSupport: () => true,
workspaceRegistry: { acquireSessionRevision } as any,
});
const request = app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
await new Promise((resolve) => setImmediate(resolve));
expect(markPersisted).not.toHaveBeenCalled();
expect(abort).not.toHaveBeenCalled();
persisted.resolve({ id: "leased-session" });
expect((await request).statusCode).toBe(200);
expect(markPersisted).toHaveBeenCalledOnce();
expect(abort).not.toHaveBeenCalled();
});
test("creates a session from the configured default workspace revision when workspaceId is omitted", async () => {
const sessionNew = vi.fn(async () => ({ id: "default-pinned" }));
const registry = {
+27
View File
@@ -522,6 +522,33 @@ test("retains a historical snapshot while a resumable manifest still references
expect(existsSync(registry.snapshotPath(currentCommit, "psd-clinical"))).toBe(true);
});
test("a session revision lease survives stale retention scans until its manifest is observed", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
const registry = new WorkspaceRegistry(config(root, remote.remote));
await registry.bootstrap();
const lease = await registry.acquireSessionRevision("psd-clinical");
writeFileSync(join(remote.source, "workspaces", "psd-clinical.yaml"), validYaml.replace(
"name: Policlinico San Donato", "name: Concurrent revision",
));
await git(remote.source, ["add", "workspaces/psd-clinical.yaml"]);
await git(remote.source, ["commit", "-m", "Publish while session is starting"]);
await git(remote.source, ["push", "origin", "main"]);
await registry.pull();
await registry.reconcileSnapshotRetention([]);
expect(existsSync(registry.snapshotPath(remote.initialCommit, "psd-clinical"))).toBe(true);
await lease.markPersisted();
await registry.reconcileSnapshotRetention([]);
expect(existsSync(registry.snapshotPath(remote.initialCommit, "psd-clinical"))).toBe(true);
await registry.reconcileSnapshotRetention([remote.initialCommit]);
await registry.reconcileSnapshotRetention([]);
expect(existsSync(registry.snapshotPath(remote.initialCommit, "psd-clinical"))).toBe(false);
});
test("lists operational descriptors retained after their workspace was removed from the active revision", async () => {
const remote = await fixture();
const root = join(remote.root, "registry");
@@ -1,6 +1,7 @@
import { expect, test } from "vitest";
import { parse } from "yaml";
import { renderRuntimeConfig, type RuntimeBindings, type RuntimePaths } from "../src/workspaces/runtime-renderer.js";
import { supportsSessionRuntime } from "../src/workspaces/bindings.js";
import { parseWorkspaceYaml } from "../src/workspaces/schema.js";
const workspace = parseWorkspaceYaml(`workspace:
@@ -90,6 +91,18 @@ const directBindings: RuntimeBindings = {
},
};
test("runtime support stays fail-closed for either SSH connector", () => {
expect(supportsSessionRuntime(directBindings)).toBe(true);
expect(supportsSessionRuntime({
...directBindings,
dwh: { ...directBindings.dwh, transport: "ssh_tunnel" },
})).toBe(false);
expect(supportsSessionRuntime({
...directBindings,
vector: { ...directBindings.vector, transport: "ssh_tunnel" },
})).toBe(false);
});
test("renders a direct PostgreSQL binding to the legacy harness shape", () => {
const yaml = renderRuntimeConfig(workspace, directBindings, paths);
const rendered = parse(yaml);
+6 -2
View File
@@ -336,7 +336,7 @@ llm_policy:
}));
});
test("uses a loopback-only SSH tunnel for the bounded connector probe", async () => {
test("does not advertise a probe-only SSH tunnel as usable by runtime sessions", async () => {
const adapters = successfulAdapters();
const sshBindings: RuntimeBindings = {
...bindings,
@@ -360,7 +360,11 @@ test("uses a loopback-only SSH tunnel for the bounded connector probe", async ()
const result = await diagnose(adapters)(workspace, sshBindings, { writeProbe: false });
expect(result.activatable).toBe(true);
expect(result.activatable).toBe(false);
expect(result.diagnostics).toContainEqual(expect.objectContaining({
level: "error",
code: "workspace_not_activatable",
}));
expect(adapters.withSshTunnel).toHaveBeenCalledWith(expect.objectContaining({
localHost: "127.0.0.1",
localPort: 0,