fix: close internal semantic review gaps

This commit is contained in:
2026-08-08 23:27:58 +02:00
parent 43d8063922
commit 2c4534d968
18 changed files with 806 additions and 77 deletions
+43
View File
@@ -1,6 +1,21 @@
import { expect, test } from "vitest";
import { ReadinessManager } from "../src/runtime/readiness-manager.js";
const workspace = {
workspace: { schema_version: 3, id: "psd", name: "PSD", language: "it" },
dwh: {
engine: "postgres", database: "warehouse", schema: "public",
supported_transports: ["postgres_direct"],
},
semantic_index: {
vector_store: { engine: "qdrant", collection: "psd", dimensions: 1024, distance: "cosine" },
embedding: {
provider: "ollama_internal", model: "qwen3-embedding:0.6b", dimensions: 1024,
},
},
llm_policy: { allowed: ["zai/glm-5.2"] },
} as const;
function deferred<T>() {
let resolve!: (value: T) => void;
const promise = new Promise<T>((r) => { resolve = r; });
@@ -60,3 +75,31 @@ test("readiness does not cache failed results", async () => {
await expect(readiness.ensure("psd")).resolves.toMatchObject({ ok: true });
expect(calls).toBe(2);
});
test("readiness checks Qdrant before Ollama and skips Ollama on semantic incompatibility", async () => {
let ollamaCalls = 0;
const tht = {
qdrantEnsure: async () => ({ ok: false, code: "semantic_index_incompatible" }),
ollamaEnsure: async () => { ollamaCalls += 1; return { ok: true }; },
} as any;
const readiness = new ReadinessManager(tht, 60);
await expect(readiness.ensure("/registry/psd.yaml", undefined, workspace as any)).resolves.toEqual({
ok: false,
code: "semantic_index_incompatible",
});
expect(ollamaCalls).toBe(0);
});
test("readiness returns a sanitized activation code when a semantic probe throws", async () => {
const tht = {
qdrantEnsure: async () => { throw new Error("dial http://qdrant:6333/private"); },
ollamaEnsure: async () => ({ ok: true }),
} as any;
const readiness = new ReadinessManager(tht, 60);
await expect(readiness.ensure("/registry/psd.yaml", undefined, workspace as any)).resolves.toEqual({
ok: false,
code: "workspace_not_activatable",
});
});
+111 -7
View File
@@ -14,17 +14,34 @@ import { validateDeclarativePiConfig } from "../src/pi/managed-config.js";
const FAKE = path.resolve("../harness/tests/fake_pi/fake_pi_rpc.mjs");
const SCRIPT = path.resolve("../harness/tests/fake_pi/scripts/f1_disambiguation.json");
function operationalWorkspace(id = "default") {
return {
workspace: { schema_version: 3, id, name: id, language: "en" },
dwh: {
engine: "postgres", database: "warehouse", schema: "public",
supported_transports: ["postgres_direct"],
},
semantic_index: {
vector_store: {
engine: "qdrant", collection: id, dimensions: 1024, distance: "cosine",
},
embedding: {
provider: "ollama_internal", model: "qwen3-embedding:0.6b", dimensions: 1024,
},
},
llm_policy: {
allowed: ["zai/glm-5.2", "deepseek/deepseek-v4-pro", "local-qwen/qwen3.6-35b-a3b"],
},
} as const;
}
const defaultWorkspaceRegistry = {
list: vi.fn(async () => [{
id: "default", commit: "e".repeat(40), blob: "f".repeat(40),
snapshotPath: `/data/workspace-registry/snapshots/${"e".repeat(40)}/default.yaml`, state: "operational",
}]),
read: vi.fn(async (id: string) => ({
workspace: {
llm_policy: {
allowed: ["zai/glm-5.2", "deepseek/deepseek-v4-pro", "local-qwen/qwen3.6-35b-a3b"],
},
},
workspace: operationalWorkspace(id),
revision: {
id, commit: "e".repeat(40), blob: "f".repeat(40),
snapshotPath: `/data/workspace-registry/snapshots/${"e".repeat(40)}/${id}.yaml`, state: "operational",
@@ -33,9 +50,13 @@ const defaultWorkspaceRegistry = {
};
function buildApp(config: Parameters<typeof buildRealApp>[0], deps: Record<string, unknown> = {}) {
const thtRunner = deps.thtRunner
? { qdrantEnsure: async () => ({ ok: true }), ...(deps.thtRunner as object) }
: undefined;
return buildRealApp(config, {
workspaceRuntimeSupport: () => true,
...deps,
...(thtRunner ? { thtRunner } : {}),
workspaceRegistry: { ...defaultWorkspaceRegistry, ...(deps.workspaceRegistry as object | undefined) },
} as any);
}
@@ -663,7 +684,7 @@ test("session lifecycle locates a B session when installation default is A", asy
],
readPinned: vi.fn(async (id: string, revision: string) => {
expect([id, revision]).toEqual(["b-workspace", "c".repeat(40)]);
return { workspace: { llm_policy: { allowed: ["zai/glm-5.2"] } }, workspaceConfigPath: bPinnedPath };
return { workspace: operationalWorkspace(id), workspaceConfigPath: bPinnedPath };
}),
} as any,
});
@@ -932,7 +953,7 @@ test("POST /sessions/:id/resume uses the manifest's retained workspace revision"
getSettings: () => ({ workspace: "legacy" }) as any,
workspaceRegistry: {
readPinned: vi.fn(async () => ({
workspace: { workspace: { id: "psd-clinical" } },
workspace: operationalWorkspace("psd-clinical"),
revision: {
id: "psd-clinical", commit: "a".repeat(40), blob: "b".repeat(40),
snapshotPath: "/data/workspace-registry/snapshots/aaaaaaaa/psd-clinical.yaml", state: "operational",
@@ -968,6 +989,61 @@ test("POST /sessions/:id/resume returns a sanitized error when its retained revi
expect(response.json()).toMatchObject({ code: "workspace_revision_unavailable" });
});
test("POST /sessions/:id/resume rejects a pinned schema-v2 workspace before readiness or runtime", async () => {
const readiness = vi.fn(async () => ({ ok: true }));
const reopenSession = vi.fn(async () => {});
const acquireWorkspaceRuntime = vi.fn();
const createFor = vi.fn();
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
sessionShow: async () => ({
status: "open", archived: false,
workspace_id: "psd-clinical", workspace_revision: "a".repeat(40),
}),
reopenSession,
acquireWorkspaceRuntime,
} as any,
readiness: { ensure: readiness } as any,
mgr: { get: () => undefined, createFor } as any,
getSettings: () => ({ workspace: "legacy" }) as any,
workspaceRegistry: {
readPinned: vi.fn(async () => ({
workspace: {
workspace: { schema_version: 2, id: "psd-clinical", name: "PSD", language: "it" },
dwh: {
engine: "postgres", database: "warehouse", schema: "public",
supported_transports: ["rest_api"],
},
semantic_index: {
vector_store: {
engine: "pgvector", database: "warehouse", schema: "vectors",
collection: "documents", dimensions: 768, distance: "cosine",
supported_transports: ["rest_api"],
},
embedding: {
provider: "ollama_compatible", model: "nomic-embed-text", dimensions: 768,
},
},
llm_policy: { allowed: ["zai/glm-5.2"] },
},
workspaceConfigPath: `/data/workspace-registry/snapshots/${"a".repeat(40)}/psd-clinical.yaml`,
})),
} as any,
});
const response = await app.inject({ method: "POST", url: "/sessions/pinned-v2/resume" });
expect(response.statusCode).toBe(409);
expect(response.json()).toEqual({
code: "workspace_revision_unavailable",
error: "Session workspace configuration is unavailable. Check configuration and try again.",
});
expect(readiness).not.toHaveBeenCalled();
expect(reopenSession).not.toHaveBeenCalled();
expect(acquireWorkspaceRuntime).not.toHaveBeenCalled();
expect(createFor).not.toHaveBeenCalled();
});
test("a pruned pin blocks Resume but not active or mutation lifecycle routes", async () => {
const activePath = "/registry/snapshots/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/b-workspace.yaml";
const prunedError = "cannot read /registry/snapshots/secret-pruned-revision/b-workspace.yaml";
@@ -2340,11 +2416,35 @@ test("POST /sessions readiness failure returns one fixed public message without
expect(res.statusCode).toBe(503);
expect(res.json()).toEqual({
error: "Session services are not ready. Check configuration and connectivity, then try again.",
code: "workspace_not_activatable",
});
expect(res.body).not.toMatch(/secret\.invalid|DO_NOT_LEAK|\/srv\/private\/model-key/);
expect(createdCalled).toBe(false);
});
test.each(["semantic_index_incompatible", "workspace_not_activatable"] as const)(
"POST /sessions does not persist when Qdrant readiness returns %s",
async (code) => {
const sessionNew = vi.fn(async () => ({ id: "must-not-exist" }));
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: { sessionNew } as any,
readiness: { ensure: async () => ({ ok: false, code }) } as any,
getSettings: () => ({ workspace: "psd" }) as any,
});
const response = await app.inject({
method: "POST", url: "/sessions", payload: { question: "q" },
});
expect(response.statusCode).toBe(503);
expect(response.json()).toEqual({
error: "Session services are not ready. Check configuration and connectivity, then try again.",
code,
});
expect(sessionNew).not.toHaveBeenCalled();
},
);
test("POST /sessions returns storage 503 before creating a Pi runtime when session persistence fails", async () => {
let piCreated = false;
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
@@ -2365,8 +2465,10 @@ test("POST /sessions returns storage 503 before creating a Pi runtime when sessi
test("POST /sessions proceeds when ollamaEnsure succeeds", async () => {
let ensureWs: string | undefined;
const qdrantEnsure = vi.fn(async () => ({ ok: true }));
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
qdrantEnsure,
ollamaEnsure: async (ws: string) => { ensureWs = ws; return { ok: true }; },
searchPack: async () => {},
sessionNew: async () => ({ id: "s1" }),
@@ -2376,6 +2478,7 @@ test("POST /sessions proceeds when ollamaEnsure succeeds", async () => {
});
const res = await app.inject({ method: "POST", url: "/sessions", payload: { question: "q" } });
expect(res.json()).toEqual({ id: "s1" });
expect(qdrantEnsure).toHaveBeenCalledWith(operationalWorkspace("psd"), 60);
expect(ensureWs).toContain(`/snapshots/${"e".repeat(40)}/psd.yaml`);
});
@@ -2574,6 +2677,7 @@ test("POST /sessions/:id/resume readiness failure returns the same fixed public
expect(res.statusCode).toBe(503);
expect(res.json()).toEqual({
error: "Session services are not ready. Check configuration and connectivity, then try again.",
code: "workspace_not_activatable",
});
expect(res.body).not.toMatch(/secret\.invalid|DO_NOT_LEAK|\/srv\/private\/resume-key/);
});
+7 -8
View File
@@ -238,7 +238,7 @@ test("validates a canonical workspace and runs the injected installation diagnos
expect(diagnose).not.toHaveBeenCalled();
});
test("runs the injected installation diagnostic for a migration-required v2 workspace when legacy bindings resolve", async () => {
test("rejects a migration-required v2 workspace before resolving semantic diagnostics", async () => {
const diagnose = vi.fn(async () => ({
activatable: false,
diagnostics: [{ level: "error" as const, code: "binding_missing" as const, field: "THT_WS_PSD_CLINICAL_VECTOR_BASE_URL", message: "Installation binding is missing or invalid." }],
@@ -257,13 +257,12 @@ test("runs the injected installation diagnostic for a migration-required v2 work
try {
const testResult = await app.inject({ method: "POST", url: "/workspaces/psd-clinical/test", payload: {} });
expect(testResult.statusCode).toBe(200);
expect(testResult.json()).toMatchObject({ activatable: false, diagnostics: [{ code: "binding_missing" }] });
expect(diagnose).toHaveBeenCalledWith(workspaceV2, expect.objectContaining({
dwh: expect.objectContaining({ transport: "rest_api", missing: [] }),
vector: expect.objectContaining({ transport: "rest_api", missing: [] }),
embedding: expect.objectContaining({ transport: "rest_api", missing: [] }),
}), { writeProbe: false });
expect(testResult.statusCode).toBe(400);
expect(testResult.json()).toEqual({
code: "workspace_not_activatable",
message: "Workspace cannot be activated on this installation.",
});
expect(diagnose).not.toHaveBeenCalled();
} finally {
process.env = originalEnv;
}
+100
View File
@@ -0,0 +1,100 @@
import { expect, test, vi } from "vitest";
import { ThtRunner } from "../src/tht/tht-runner.js";
import type { CanonicalWorkspace } from "../src/workspaces/schema.js";
const keywordIndexes = [
"content_hash", "document_id", "kind", "record_key", "record_kind",
"vector_generation", "workspace_id", "workspace_revision",
];
const workspace: CanonicalWorkspace = {
workspace: { schema_version: 3, id: "psd", name: "PSD", language: "it" },
dwh: {
engine: "postgres", database: "warehouse", schema: "public",
supported_transports: ["postgres_direct"],
},
semantic_index: {
vector_store: { engine: "qdrant", collection: "psd", dimensions: 1024, distance: "cosine" },
embedding: {
provider: "ollama_internal", model: "qwen3-embedding:0.6b", dimensions: 1024,
},
},
llm_policy: { allowed: ["zai/glm-5.2"] },
};
function runner(request: (...args: any[]) => Promise<any>) {
return new ThtRunner({
thtBin: "tht",
harnessDir: "/harness",
configPath: "config/tht.yaml",
semanticRuntime: {
internalQdrantUrl: "http://qdrant:6333",
internalEmbeddingUrl: "http://embedding:11434",
internalEmbeddingModel: "qwen3-embedding:0.6b",
internalEmbeddingDimensions: 1024,
},
qdrantRequest: request,
});
}
function response(status: number, body: unknown) {
return {
ok: status >= 200 && status < 300,
status,
json: async () => body,
};
}
function collection(overrides: Record<string, unknown> = {}) {
return {
result: {
config: { params: { vectors: { size: 1024, distance: "Cosine" } } },
payload_schema: Object.fromEntries(keywordIndexes.map((field) => [field, { data_type: "keyword" }])),
...overrides,
},
};
}
test("Qdrant readiness uses only the internal URL and accepts the exact collection contract", async () => {
const request = vi.fn(async () => response(200, collection()));
await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({ ok: true });
expect(request).toHaveBeenCalledOnce();
expect(request.mock.calls[0][0]).toBe("http://qdrant:6333/collections/psd");
expect(request.mock.calls[0][1]).toMatchObject({ method: "GET", signal: expect.any(AbortSignal) });
});
test("Qdrant readiness classifies a missing collection as semantic incompatibility", async () => {
const request = vi.fn(async () => response(404, { status: "error", detail: "secret" }));
await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({
ok: false,
code: "semantic_index_incompatible",
});
});
test.each([
["dimensions", collection({
config: { params: { vectors: { size: 768, distance: "Cosine" } } },
})],
["distance", collection({
config: { params: { vectors: { size: 1024, distance: "Dot" } } },
})],
["payload indexes", collection({ payload_schema: { workspace_id: { data_type: "keyword" } } })],
])("Qdrant readiness rejects incompatible %s", async (_label, body) => {
const request = vi.fn(async () => response(200, body));
await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({
ok: false,
code: "semantic_index_incompatible",
});
});
test("Qdrant readiness sanitizes unreachable internal service failures", async () => {
const request = vi.fn(async () => { throw new Error("connect http://qdrant:6333/private"); });
await expect(runner(request).qdrantEnsure(workspace, 3)).resolves.toEqual({
ok: false,
code: "workspace_not_activatable",
});
});
+3
View File
@@ -662,6 +662,9 @@ test("lists a schema v2 descriptor as migration_required and refuses to acquire
await expect(registry.acquireSessionRevision("psd-clinical")).rejects.toMatchObject({
code: "workspace_invalid",
});
await expect(registry.readPinned("psd-clinical", remote.initialCommit)).rejects.toMatchObject({
code: "workspace_invalid",
});
});
test("lists operational descriptors retained after their workspace was removed from the active revision", async () => {
+23 -2
View File
@@ -42,6 +42,18 @@ llm_policy:
allowed: [zai/glm-5.2]
`;
const migrationRequiredWorkspace = canonicalWorkspace
.replace("schema_version: 3", "schema_version: 2")
.replace(
" engine: qdrant\n collection: psd-clinical",
" engine: pgvector\n database: analytics\n schema: vectors\n collection: documents",
)
.replace(" dimensions: 1024", " dimensions: 768")
.replace(" distance: cosine", " distance: cosine\n supported_transports: [rest_api]")
.replace(" provider: ollama_internal", " provider: ollama_compatible")
.replace(" model: qwen3-embedding:0.6b", " model: nomic-embed-text")
.replace(" dimensions: 1024", " dimensions: 768");
afterEach(() => {
vi.unstubAllEnvs();
roots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true }));
@@ -51,7 +63,7 @@ async function git(cwd: string, args: string[]): Promise<string> {
return (await runFile("git", args, { cwd })).stdout.trim();
}
async function fixture() {
async function fixture(workspaceSource = canonicalWorkspace) {
const root = mkdtempSync(join(tmpdir(), "tht-runtime-handoff-"));
roots.push(root);
const remote = join(root, "remote.git");
@@ -65,7 +77,7 @@ async function fixture() {
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);
writeFileSync(join(source, "workspaces", "psd-clinical.yaml"), workspaceSource);
await git(source, ["add", "workspaces/psd-clinical.yaml"]);
await git(source, ["commit", "-m", "Canonical workspace"]);
await git(source, ["remote", "add", "origin", remote]);
@@ -160,6 +172,15 @@ test("separate runtime leases hand off one stable logical workspace identity", a
}
});
test("ThtRunner refuses to render a migration-required registry snapshot", async () => {
const f = await fixture(migrationRequiredWorkspace);
const runner = runnerFor(f);
expect(() => runner.acquireWorkspaceRuntime(f.revision.snapshotPath)).toThrow(
"Workspace descriptor requires explicit migration to schema version 3",
);
});
test("local GET sessions mine uses the real canonical handoff and returns an empty inventory", async () => {
const f = await fixture();
const app = buildApp(loadConfig({