// Cloud: the org identity a session needs at init travels in the session props // the worker already resolved — it is NOT re-read from Postgres inside the // session Durable Object. // // The defect this pins: on EVERY cold DO init the DO opened a brand-new // Postgres connection purely to re-read the `organizations` row the worker had // just read microseconds earlier on the same request (the worker threw it away: // the auth principal hardcoded an empty organization name). When that fresh // connection could not be established the whole `initialize` died — a hard, // client-visible failure on a healthy database, because the only unhealthy // thing was a socket nothing needed to open. // // Two contracts, in the order a user meets them: // // 1. the session opens and WORKS — initialize mints a session id and the same // id then serves `tools/list`, all off the identity the props carried; // 2. the whole request performs exactly ONE organization read (the worker's // own authorization check) and the DO opens no database connection of its // own to name the org. // // (2) is asserted on the EXPORTED spans, and the two planes — worker and // Durable Object — export independently, so the count is only taken once a // worker-plane span for this same request has landed. Without that wait a // still-pending worker batch would make a two-read request look like a one-read // request, and the assertion would pass for the wrong reason. // // The failure half of the fix (an unreachable directory becomes a bounded retry // and a retryable 503 rather than a bare 500) is not reachable from here: the // harness runs one single-process PGlite shared by the worker and the DO, and // the worker's own authorization reads that same row on the same request — so // freezing the database fails the request before init identically on both sides // of the fix. Those branches are pinned in // `apps/cloud/src/mcp/session-meta.node.test.ts`. import { randomBytes } from "node:crypto"; import { expect } from "@effect/vitest"; import { Effect } from "effect"; import { scenario } from "../src/scenario"; import { Mcp, Target, Telemetry } from "../src/services"; import type { Identity } from "../src/target"; const JSON_AND_SSE = "application/json, text/event-stream"; const INITIALIZE_REQUEST = { jsonrpc: "2.0" as const, id: 1, method: "initialize", params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "executor-e2e-session-cold-init", version: "0.0.1" }, }, }; const INITIALIZED_NOTIFICATION = { jsonrpc: "2.0" as const, method: "notifications/initialized", }; const TOOLS_LIST_REQUEST = { jsonrpc: "2.0" as const, id: 2, method: "tools/list", params: {}, }; const emailOf = (identity: Identity): string => identity.credentials?.email ?? identity.label; /** A client-supplied W3C trace context, so every span this one request produces * — worker plane and Durable Object alike — is addressable by one trace id. */ const newTraceContext = (): { readonly traceId: string; readonly traceparent: string } => { const traceId = randomBytes(16).toString("hex"); const spanId = randomBytes(8).toString("hex"); return { traceId, traceparent: `00-${traceId}-${spanId}-01` }; }; const mcpPost = ( url: string, init: { readonly bearer: string; readonly sessionId?: string; readonly traceparent?: string; readonly body: unknown; }, ): Promise => fetch(url, { method: "POST", headers: { accept: JSON_AND_SSE, "content-type": "application/json", authorization: `Bearer ${init.bearer}`, ...(init.sessionId ? { "mcp-session-id": init.sessionId } : {}), ...(init.traceparent ? { traceparent: init.traceparent } : {}), }, body: JSON.stringify(init.body), }); scenario( "MCP session cold init · the org identity rides in the session props instead of a second Postgres read", { timeout: 120_000 }, Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const telemetry = yield* Telemetry; const identity = yield* target.newIdentity(); const bearer = yield* mcp.mintBearer(emailOf(identity)); const trace = newTraceContext(); // ---- 1. the session opens, and it works ------------------------------- const response = yield* Effect.promise(() => mcpPost(target.mcpUrl, { bearer, traceparent: trace.traceparent, body: INITIALIZE_REQUEST, }), ); yield* Effect.promise(() => response.text()); expect(response.status, "initialize opens a session").toBe(200); const sessionId = response.headers.get("mcp-session-id"); expect(sessionId, "the session id is minted").toBeTruthy(); const initialized = yield* Effect.promise(() => mcpPost(target.mcpUrl, { bearer, sessionId: sessionId ?? "", body: INITIALIZED_NOTIFICATION, }), ); yield* Effect.promise(() => initialized.text()); expect(initialized.status, "the client completes the handshake").toBe(202); // The session built from the props-carried identity actually serves work — // the production symptom was an `initialize` that never got this far. const tools = yield* Effect.promise(() => mcpPost(target.mcpUrl, { bearer, sessionId: sessionId ?? "", body: TOOLS_LIST_REQUEST, }), ); const toolsBody = yield* Effect.promise(() => tools.text()); expect(tools.status, "the session serves requests once open").toBe(200); expect(toolsBody, "the session advertises the execute tool").toContain("execute"); // ---- 2. one organization read for the whole init request -------------- // The DO really did resolve meta on this request (a cold init), and it // resolved it from the props the worker handed over. const resolveSpan = yield* telemetry.expectSpan({ traceId: trace.traceId, operation: "McpSessionDOSqlite.resolveSessionMeta", }); expect(resolveSpan.span.status, "the cold init resolved its meta without failing").toBe("ok"); // The worker plane exports on its own batch, independently of the DO's. // Wait for a worker-plane span from this same request before counting, or // an unflushed worker batch would hide the very read being counted. yield* telemetry.expectSpan({ traceId: trace.traceId, operation: "mcp.request" }); // One org read for the whole request: the worker's own authorization // lookup. A second one means the DO reopened a connection to re-read a row // the request already had. const orgReads = yield* telemetry.searchSpans({ traceId: trace.traceId, operation: "user_store.getOrganization", }); expect( orgReads.length, "only the worker's authorization check reads the organization row", ).toBe(1); // …and the DO's own database step never ran at all. `resolveSessionMeta` // has already landed and this span is its child, so its absence here is // absence, not lag. const doDatabaseReads = yield* telemetry.searchSpans({ traceId: trace.traceId, operation: "mcp.session.resolve_organization", }); expect( doDatabaseReads.length, "the session DO opens no connection of its own to name the organization", ).toBe(0); // …because the identity it used is the one the worker handed it. expect( resolveSpan.span.tags["mcp.session.meta_source"], "the session meta comes from the props the worker already resolved", ).toBe("props"); }), );