fix: align workspace diagnostic contracts

This commit is contained in:
2026-08-04 00:37:57 +02:00
parent f6494fd8ef
commit 565e93a456
12 changed files with 346 additions and 42 deletions
+30 -2
View File
@@ -10,6 +10,13 @@ import {
type WorkspaceDescriptor,
} from "./schema.js";
export interface RuntimeBindings {
dwh: ResolvedBinding;
vector: ResolvedBinding;
vectorWriter: ResolvedBinding;
embedding: ResolvedBinding;
}
export interface ResolvedBinding {
transport: DwhTransport | VectorTransport;
values: Record<string, string>;
@@ -63,12 +70,19 @@ function isSafeSecretFile(path: string, secretRoots: readonly string[]): boolean
}
function requiredSuffixes(
workspace: WorkspaceDescriptor,
role: InstallationRole,
transport: DwhTransport | VectorTransport,
): readonly InstallationSuffix[] {
if (role === "EMBEDDING") return EMBEDDING_REQUIRED_SUFFIXES;
if (role === "VECTOR_WRITER") return ["API_KEY_FILE"];
return REQUIRED_SUFFIXES[role][transport] ?? [];
const required = REQUIRED_SUFFIXES[role][transport] ?? [];
const diagnostic = role === "DWH"
? workspace.diagnostics?.dwh_rest
: workspace.diagnostics?.vector_rest?.metadata;
return transport === "rest_api" && diagnostic?.auth === "none"
? required.filter((suffix) => suffix !== "API_KEY_FILE")
: required;
}
/**
@@ -98,7 +112,7 @@ export function resolveBinding(
missing.push(transportVariable.name);
}
const required = new Set(requiredSuffixes(role, selectedTransport));
const required = new Set(requiredSuffixes(canonical, role, selectedTransport));
const values: Record<string, string> = {};
for (const variable of variables) {
if (variable.suffix === "TRANSPORT") continue;
@@ -115,3 +129,17 @@ export function resolveBinding(
return { transport: selectedTransport, values, missing };
}
/** Resolve all runtime roles together so optional writer credentials cannot be smuggled into reader bindings. */
export function resolveRuntimeBindings(
workspace: WorkspaceDescriptor,
env: NodeJS.ProcessEnv,
secretRoots: readonly string[],
): RuntimeBindings {
return {
dwh: resolveBinding(workspace, "DWH", env, secretRoots),
vector: resolveBinding(workspace, "VECTOR", env, secretRoots),
vectorWriter: resolveBinding(workspace, "VECTOR_WRITER", env, secretRoots),
embedding: resolveBinding(workspace, "EMBEDDING", env, secretRoots),
};
}
+19 -9
View File
@@ -131,7 +131,9 @@ export interface WriteDiagnosticRecordRequest {
credentialFile?: string;
tlsCaFile?: string;
baseUrl?: string;
diagnostic?: RestDiagnosticRequest;
diagnostic?: RestDiagnosticRequest & {
response: { operation: string };
};
}
export interface DirectProtocolFactory {
@@ -145,7 +147,7 @@ export interface DatabaseDiagnosticClient {
export interface DatabaseDiagnosticClientFactory {
connect(request: {
host: string; port: number; database: string; user: string; credentialFile: string; tlsCaFile: string; signal: AbortSignal;
host: string; port: number; database: string; user: string; credentialFile: string; tlsCaFile?: string; signal: AbortSignal;
}): Promise<DatabaseDiagnosticClient>;
}
@@ -296,11 +298,13 @@ export function createConcreteDiagnosticAdapters(
},
};
const databaseClient = dependencies.databaseClient ?? {
async connect(request: { host: string; port: number; database: string; user: string; credentialFile: string; tlsCaFile: string; signal: AbortSignal }) {
async connect(request: { host: string; port: number; database: string; user: string; credentialFile: string; tlsCaFile?: string; signal: AbortSignal }) {
const client = new Client({
host: request.host, port: request.port, database: request.database, user: request.user,
password: (await readFile(request.credentialFile, "utf8")).trim(),
ssl: { ca: await readFile(request.tlsCaFile, "utf8"), rejectUnauthorized: true },
ssl: request.tlsCaFile
? { ca: await readFile(request.tlsCaFile, "utf8"), rejectUnauthorized: true }
: { rejectUnauthorized: true },
connectionTimeoutMillis: 5_000,
});
const abort = () => { void client.end(); };
@@ -317,7 +321,7 @@ export function createConcreteDiagnosticAdapters(
};
const directProtocol = dependencies.directProtocol ?? {
async probe(request: ConnectorDiagnosticRequest): Promise<ConnectorDiagnosticResult> {
if (!request.host || !request.port || !request.user || !request.credentialFile || !request.tlsCaFile
if (!request.host || !request.port || !request.user || !request.credentialFile
|| !(await secretPresent(request.credentialFile))) {
throw new Error("direct probe failed");
}
@@ -388,7 +392,7 @@ export function createConcreteDiagnosticAdapters(
async inspectVector(request) {
if (request.transport === "pgvector_direct" || request.transport === "ssh_tunnel") {
const resource = request.resource;
if (!request.host || !request.port || !request.user || !request.credentialFile || !request.tlsCaFile
if (!request.host || !request.port || !request.user || !request.credentialFile
|| !resource?.database || !resource.schema || !(await secretPresent(request.credentialFile))) {
throw new Error("vector metadata adapter is unavailable");
}
@@ -454,7 +458,10 @@ export function createConcreteDiagnosticAdapters(
signal: request.signal,
redirect: "error",
});
if (!response.ok) throw new Error("vector write adapter is unavailable");
const payload = await response.json().catch(() => undefined) as Record<string, unknown> | undefined;
if (!response.ok || !payload || payload[request.diagnostic.response.operation] !== "create") {
throw new Error("vector write adapter is unavailable");
}
},
async removeDiagnosticRecord(request) {
if (!request.baseUrl || !request.diagnostic || request.tlsCaFile) throw new Error("vector write adapter is unavailable");
@@ -468,7 +475,10 @@ export function createConcreteDiagnosticAdapters(
signal: request.signal,
redirect: "error",
});
if (!response.ok) throw new Error("vector write adapter is unavailable");
const payload = await response.json().catch(() => undefined) as Record<string, unknown> | undefined;
if (!response.ok || !payload || payload[request.diagnostic.response.operation] !== "remove") {
throw new Error("vector write adapter is unavailable");
}
},
};
}
@@ -789,7 +799,7 @@ export function createWorkspaceDiagnoser(
&& bindings.vector.transport === "rest_api"
&& !diagnostics.some((diagnostic) => diagnostic.level === "error")
) {
const credentialFile = bindings.vector.values[bindingName(canonical, "VECTOR_WRITER", "API_KEY_FILE")];
const credentialFile = bindings.vectorWriter.values[bindingName(canonical, "VECTOR_WRITER", "API_KEY_FILE")];
if (!credentialFile) return { activatable: true, diagnostics };
const readerCredentialFile = bindings.vector.values[bindingName(canonical, "VECTOR", "API_KEY_FILE")];
if (readerCredentialFile && await sameSecretFile(credentialFile, readerCredentialFile)) {
+2 -7
View File
@@ -1,13 +1,8 @@
import { stringify } from "yaml";
import { buildInstallationContract } from "./contracts.js";
import { validateCanonicalWorkspace, type WorkspaceDescriptor } from "./schema.js";
import type { ResolvedBinding } from "./bindings.js";
export interface RuntimeBindings {
dwh: ResolvedBinding;
vector: ResolvedBinding;
embedding: ResolvedBinding;
}
import type { ResolvedBinding, RuntimeBindings } from "./bindings.js";
export type { RuntimeBindings } from "./bindings.js";
export interface RuntimePaths {
sessions: string;
+10 -2
View File
@@ -25,7 +25,11 @@ export interface CanonicalDiagnostics {
metadata: RestDiagnosticRequest & {
response: { collection: string; dimensions: string; distance: string };
};
reversible_probe?: RestDiagnosticRequest & { method: "POST" };
reversible_probe?: RestDiagnosticRequest & {
method: "POST";
auth: Exclude<DiagnosticAuthMode, "none">;
response: { operation: string };
};
};
embedding?: RestDiagnosticRequest & { response: { model: string; dimensions: string } };
}
@@ -124,7 +128,11 @@ const vectorMetadataDiagnostic = restDiagnosticRequest.extend({
distance: responseField,
}).strict(),
}).strict();
const reversibleVectorProbe = restDiagnosticRequest.extend({ method: z.literal("POST") }).strict();
const reversibleVectorProbe = restDiagnosticRequest.extend({
method: z.literal("POST"),
auth: z.enum(["bearer", "x-api-key"]),
response: z.object({ operation: responseField }).strict(),
}).strict();
const embeddingDiagnostic = restDiagnosticRequest.extend({
response: z.object({ model: responseField, dimensions: responseField }).strict(),
}).strict();
@@ -82,6 +82,7 @@ const directBindings: RuntimeBindings = {
THT_WS_PSD_CLINICAL_VECTOR_TLS_CA_FILE: "/run/secrets/vector-ca.pem",
},
},
vectorWriter: { transport: "rest_api", missing: [], values: {} },
embedding: {
transport: "rest_api",
missing: [],
+59 -1
View File
@@ -2,7 +2,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, expect, test } from "vitest";
import { resolveBinding } from "../src/workspaces/bindings.js";
import { resolveBinding, resolveRuntimeBindings } from "../src/workspaces/bindings.js";
import { parseWorkspaceYaml } from "../src/workspaces/schema.js";
const workspace = parseWorkspaceYaml(`workspace:
@@ -56,6 +56,64 @@ test("marks a portable workspace non-activatable when its local REST key file is
expect(result.missing).toContain("THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE");
});
test("does not require a REST secret file when its declared diagnostic uses auth none", () => {
const unauthenticatedWorkspace = parseWorkspaceYaml(`workspace:
schema_version: 2
id: psd-clinical
name: Policlinico San Donato
language: it
dwh:
engine: postgres
database: postgres
schema: datawarehouse
supported_transports: [rest_api]
semantic_index:
vector_store:
engine: pgvector
database: postgres
schema: vectors
collection: clinical_documents
dimensions: 768
distance: cosine
supported_transports: [rest_api]
embedding:
provider: ollama_compatible
model: nomic-embed-text-v2-moe
dimensions: 768
diagnostics:
dwh_rest:
method: POST
path: /rpc/ping
auth: none
response: { database: database, schema: schema }
vector_rest:
metadata:
method: GET
path: /vector/metadata
auth: none
response: { collection: collection, dimensions: dimensions, distance: distance }
embedding:
method: GET
path: /models
auth: none
response: { model: model, dimensions: dimensions }
llm_policy:
allowed: [zai/glm-5.2]
`);
const bindings = resolveRuntimeBindings(unauthenticatedWorkspace, {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "rest_api",
THT_WS_PSD_CLINICAL_DWH_BASE_URL: "https://dwh.example.test",
THT_WS_PSD_CLINICAL_VECTOR_TRANSPORT: "rest_api",
THT_WS_PSD_CLINICAL_VECTOR_BASE_URL: "https://vector.example.test",
THT_WS_PSD_CLINICAL_EMBEDDING_BASE_URL: "https://embedding.example.test",
}, ["/run/secrets"]);
expect(bindings.dwh.missing).toEqual([]);
expect(bindings.vector.missing).toEqual([]);
expect(bindings.embedding.missing).toEqual([]);
});
test("resolves direct bindings from the stable workspace namespace", () => {
const password = secretPath("dwh-password");
const result = resolveBinding(workspace, "DWH", {
@@ -147,6 +147,7 @@ llm_policy:
THT_WS_PSD_CLINICAL_VECTOR_PASSWORD_FILE: "/run/secrets/vector-reader",
},
},
vectorWriter: { transport: "rest_api", missing: [], values: {} },
embedding: {
transport: "rest_api",
missing: [],
+98 -10
View File
@@ -9,6 +9,7 @@ import {
createWorkspaceDiagnoser,
type DiagnosticAdapters,
} from "../src/workspaces/diagnostics.js";
import { resolveRuntimeBindings } from "../src/workspaces/bindings.js";
import type { RuntimeBindings } from "../src/workspaces/runtime-renderer.js";
import { parseWorkspaceYaml } from "../src/workspaces/schema.js";
@@ -56,6 +57,7 @@ diagnostics:
method: POST
path: /vector/diagnostic-probe
auth: bearer
response: { operation: operation }
`);
const writerWorkspace = parseWorkspaceYaml(`workspace:
@@ -88,6 +90,11 @@ semantic_index:
llm_policy:
allowed: [zai/glm-5.2]
diagnostics:
dwh_rest:
method: POST
path: /rpc/ping
auth: bearer
response: { database: database, schema: schema }
vector_rest:
metadata:
method: GET
@@ -98,6 +105,7 @@ diagnostics:
method: POST
path: /vector/diagnostic-probe
auth: bearer
response: { operation: operation }
`);
const bindings: RuntimeBindings = {
@@ -123,6 +131,7 @@ const bindings: RuntimeBindings = {
THT_WS_PSD_CLINICAL_VECTOR_TLS_CA_FILE: "/run/secrets/vector-ca",
},
},
vectorWriter: { transport: "rest_api", missing: [], values: {} },
embedding: {
transport: "rest_api",
missing: [],
@@ -142,9 +151,13 @@ const writerBindings: RuntimeBindings = {
values: {
THT_WS_PSD_CLINICAL_VECTOR_BASE_URL: "https://vector.example.test",
THT_WS_PSD_CLINICAL_VECTOR_API_KEY_FILE: "/run/secrets/vector-reader-key",
THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE: "/run/secrets/vector-writer-key",
},
},
vectorWriter: {
transport: "rest_api",
missing: [],
values: { THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE: "/run/secrets/vector-writer-key" },
},
};
function successfulAdapters(overrides: Partial<DiagnosticAdapters> = {}): DiagnosticAdapters {
@@ -448,6 +461,40 @@ test("requires a matching embedding model vector and removes its unique write pr
}));
});
test("passes the resolver's distinct vector-writer binding to the diagnoser", async () => {
const directory = await mkdtemp(join(tmpdir(), "thothii-diagnostic-bindings-"));
const readerKey = join(directory, "reader-key");
const writerKey = join(directory, "writer-key");
const dwhKey = join(directory, "dwh-key");
await Promise.all([
writeFile(readerKey, "reader\n", { mode: 0o600 }),
writeFile(writerKey, "writer\n", { mode: 0o600 }),
writeFile(dwhKey, "dwh\n", { mode: 0o600 }),
]);
const adapters = successfulAdapters();
try {
const resolved = resolveRuntimeBindings(writerWorkspace, {
THT_WS_PSD_CLINICAL_DWH_TRANSPORT: "rest_api",
THT_WS_PSD_CLINICAL_DWH_BASE_URL: "https://dwh.example.test",
THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE: dwhKey,
THT_WS_PSD_CLINICAL_VECTOR_TRANSPORT: "rest_api",
THT_WS_PSD_CLINICAL_VECTOR_BASE_URL: "https://vector.example.test",
THT_WS_PSD_CLINICAL_VECTOR_API_KEY_FILE: readerKey,
THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE: writerKey,
THT_WS_PSD_CLINICAL_EMBEDDING_BASE_URL: "https://embedding.example.test",
}, [directory]);
await diagnose(adapters)(writerWorkspace, resolved, { writeProbe: true });
expect(resolved.vectorWriter.values).toEqual({
THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE: writerKey,
});
expect(adapters.writeDiagnosticRecord).toHaveBeenCalledWith(expect.objectContaining({ credentialFile: writerKey }));
} finally {
await rm(directory, { recursive: true, force: true });
}
});
test("keeps a reader-only workspace activatable without a vector write probe", async () => {
const adapters = successfulAdapters();
@@ -476,11 +523,8 @@ test("rejects a writer credential that aliases the reader credential", async ()
const adapters = successfulAdapters();
const aliasedBindings: RuntimeBindings = {
...writerBindings,
vector: { ...writerBindings.vector, values: {
...writerBindings.vector.values,
THT_WS_PSD_CLINICAL_VECTOR_API_KEY_FILE: readerKey,
THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE: writerAlias,
} },
vector: { ...writerBindings.vector, values: { ...writerBindings.vector.values, THT_WS_PSD_CLINICAL_VECTOR_API_KEY_FILE: readerKey } },
vectorWriter: { ...writerBindings.vectorWriter, values: { THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE: writerAlias } },
};
try {
@@ -555,6 +599,29 @@ test("requires an authenticated TLS database query before direct diagnostics suc
}
});
test("uses system trust for direct and SSH PostgreSQL diagnostics when no CA binding exists", async () => {
const directory = await mkdtemp(join(tmpdir(), "thothii-diagnostic-"));
const passwordFile = join(directory, "password");
await writeFile(passwordFile, "password\n", { mode: 0o600 });
const query = vi.fn(async () => ({ rows: [{ database: "warehouse", schema: "datawarehouse" }] }));
const connect = vi.fn(async () => ({ query, end: vi.fn(async () => undefined) }));
const adapter = createConcreteDiagnosticAdapters({ databaseClient: { connect } } as any);
try {
for (const transport of ["postgres_direct", "ssh_tunnel"] as const) {
await expect(adapter.probeConnector({
role: "dwh", transport, host: "127.0.0.1", port: 5432, user: "reader", credentialFile: passwordFile,
resource: { database: "warehouse", schema: "datawarehouse" }, timeoutMs: 5000,
signal: new AbortController().signal,
})).resolves.toMatchObject({ tlsVerified: true, authenticated: true });
}
expect(connect).toHaveBeenCalledTimes(2);
expect(connect).toHaveBeenNthCalledWith(1, expect.objectContaining({ tlsCaFile: undefined }));
expect(connect).toHaveBeenNthCalledWith(2, expect.objectContaining({ tlsCaFile: undefined }));
} finally {
await rm(directory, { recursive: true, force: true });
}
});
test("selects the vector index containing the declared vector column for direct metadata", async () => {
const directory = await mkdtemp(join(tmpdir(), "thothii-diagnostic-"));
const passwordFile = join(directory, "password");
@@ -597,20 +664,41 @@ test("applies declared auth modes and rejects private CA files across vector RES
const keyFile = join(directory, "api-key");
const caFile = join(directory, "ca.pem");
await Promise.all([writeFile(keyFile, "writer-key\n", { mode: 0o600 }), writeFile(caFile, "private-ca\n")]);
const fetchSpy = vi.fn(async () => new Response(JSON.stringify({ collection: "clinical_documents", dimensions: 768, distance: "cosine", model: "embed" }), { status: 200, headers: { "content-type": "application/json" } }));
const fetchSpy = vi.fn(async () => new Response(JSON.stringify({ collection: "clinical_documents", dimensions: 768, distance: "cosine", model: "embed", operation: "create" }), { status: 200, headers: { "content-type": "application/json" } }));
vi.stubGlobal("fetch", fetchSpy);
const adapter = createConcreteDiagnosticAdapters();
const signal = new AbortController().signal;
try {
await adapter.inspectVector({ transport: "rest_api", baseUrl: "https://vector.example.test", collection: "clinical_documents", timeoutMs: 1, signal, diagnostic: { method: "GET", path: "/metadata", auth: "none", response: { collection: "collection", dimensions: "dimensions", distance: "distance" } } });
await adapter.probeEmbedding({ baseUrl: "https://embed.example.test", model: "embed", timeoutMs: 1, signal, credentialFile: keyFile, diagnostic: { method: "POST", path: "/embed", auth: "x-api-key", response: { model: "model", dimensions: "dimensions" } } });
await adapter.writeDiagnosticRecord({ baseUrl: "https://vector.example.test", credentialFile: keyFile, collection: "clinical_documents", dimensions: 768, id: "diagnostic:test", timeoutMs: 1, signal, diagnostic: { method: "POST", path: "/probe", auth: "none" } });
expect(fetchSpy.mock.calls[0]?.[1]).toMatchObject({ headers: {} });
expect(fetchSpy.mock.calls[1]?.[1]).toMatchObject({ headers: { "x-api-key": "writer-key" } });
expect(fetchSpy.mock.calls[2]?.[1]).toMatchObject({ headers: expect.not.objectContaining({ authorization: expect.anything() }) });
await expect(adapter.inspectVector({ transport: "rest_api", baseUrl: "https://vector.example.test", credentialFile: keyFile, tlsCaFile: caFile, collection: "clinical_documents", timeoutMs: 1, signal, diagnostic: { method: "GET", path: "/metadata", auth: "bearer", response: { collection: "collection", dimensions: "dimensions", distance: "distance" } } })).rejects.toThrow("vector metadata adapter is unavailable");
await expect(adapter.probeEmbedding({ baseUrl: "https://embed.example.test", credentialFile: keyFile, tlsCaFile: caFile, model: "embed", timeoutMs: 1, signal, diagnostic: { method: "POST", path: "/embed", auth: "bearer", response: { model: "model", dimensions: "dimensions" } } })).rejects.toThrow("embedding probe failed");
await expect(adapter.removeDiagnosticRecord({ baseUrl: "https://vector.example.test", credentialFile: keyFile, tlsCaFile: caFile, collection: "clinical_documents", id: "diagnostic:test", dimensions: 768, timeoutMs: 1, signal, diagnostic: { method: "POST", path: "/probe", auth: "bearer" } })).rejects.toThrow("vector write adapter is unavailable");
await expect(adapter.removeDiagnosticRecord({ baseUrl: "https://vector.example.test", credentialFile: keyFile, tlsCaFile: caFile, collection: "clinical_documents", id: "diagnostic:test", dimensions: 768, timeoutMs: 1, signal, diagnostic: { method: "POST", path: "/probe", auth: "bearer", response: { operation: "operation" } } })).rejects.toThrow("vector write adapter is unavailable");
} finally {
vi.unstubAllGlobals();
await rm(directory, { recursive: true, force: true });
}
});
test("validates that the reversible writer response confirms each requested operation", async () => {
const directory = await mkdtemp(join(tmpdir(), "thothii-diagnostic-"));
const keyFile = join(directory, "writer-key");
await writeFile(keyFile, "writer\n", { mode: 0o600 });
const fetchSpy = vi.fn(async (_url: string, init: RequestInit) => new Response(JSON.stringify({
operation: JSON.parse(String(init.body)).operation === "create" ? "create" : "not-removed",
}), { status: 200, headers: { "content-type": "application/json" } }));
vi.stubGlobal("fetch", fetchSpy);
const request = {
baseUrl: "https://vector.example.test", credentialFile: keyFile, collection: "clinical_documents",
dimensions: 768, id: "diagnostic:test", timeoutMs: 5000, signal: new AbortController().signal,
diagnostic: { method: "POST" as const, path: "/probe", auth: "bearer" as const, response: { operation: "operation" } },
};
try {
const adapter = createConcreteDiagnosticAdapters();
await expect(adapter.writeDiagnosticRecord(request)).resolves.toBeUndefined();
await expect(adapter.removeDiagnosticRecord(request)).rejects.toThrow("vector write adapter is unavailable");
} finally {
vi.unstubAllGlobals();
await rm(directory, { recursive: true, force: true });
+19
View File
@@ -98,6 +98,7 @@ test("requires explicit vector database and schema identities with strict diagno
+ " method: POST\n"
+ " path: /rpc/diagnostic_vector_probe\n"
+ " auth: bearer\n"
+ " response: { operation: operation }\n"
+ " embedding:\n"
+ " method: GET\n"
+ " path: /models\n"
@@ -134,6 +135,24 @@ test("requires explicit vector database and schema identities with strict diagno
.toThrow(/response field/i);
});
test("requires a reversible writer probe to declare the response operation it verifies", () => {
const writerProbe = validYaml.replace("llm_policy:\n", `diagnostics:
vector_rest:
metadata:
method: GET
path: /metadata
auth: bearer
response: { collection: collection, dimensions: dimensions, distance: distance }
reversible_probe:
method: POST
path: /diagnostic-probe
auth: bearer
llm_policy:
`);
expect(() => parseWorkspaceYaml(writerProbe)).toThrow(/response|operation/i);
});
test("keeps v1 descriptors readable but requires explicit migration before v2 operations", () => {
const v1WithoutVectorIdentity = validYaml.replace("schema_version: 2", "schema_version: 1").replace(
" database: postgres\n schema: vectors\n", "",