diff --git a/apps/cloud/src/mcp-session.e2e.node.test.ts b/apps/cloud/src/mcp-session.e2e.node.test.ts index b0fb06992..d90d89ba3 100644 --- a/apps/cloud/src/mcp-session.e2e.node.test.ts +++ b/apps/cloud/src/mcp-session.e2e.node.test.ts @@ -146,10 +146,19 @@ const openSession = ( return { client, clientTransport, serverTransport }; }), ({ clientTransport, serverTransport }) => - Effect.promise(async () => { - await clientTransport.close().catch(() => undefined); - await serverTransport.close().catch(() => undefined); - }), + Effect.all( + [ + Effect.tryPromise({ + try: () => clientTransport.close(), + catch: (cause) => cause, + }).pipe(Effect.ignore), + Effect.tryPromise({ + try: () => serverTransport.close(), + catch: (cause) => cause, + }).pipe(Effect.ignore), + ], + { discard: true }, + ), ).pipe(Effect.map(({ client }) => ({ client }))); const nextOrgId = (() => { diff --git a/apps/cloud/src/observability.ts b/apps/cloud/src/observability.ts index ae9f624ae..6a3bde420 100644 --- a/apps/cloud/src/observability.ts +++ b/apps/cloud/src/observability.ts @@ -42,6 +42,7 @@ export const sentryPayloadForCause = ( if (Cause.isCause(input)) { const pretty = Cause.pretty(input); const errors = Cause.prettyErrors(input); + // oxlint-disable-next-line executor/no-error-constructor -- boundary: Sentry captureException needs an Error-like primary payload for pretty Effect causes return { primary: errors[0] ?? new Error(pretty), pretty }; } return { primary: input, pretty: null }; diff --git a/apps/cloud/src/org/handlers.ts b/apps/cloud/src/org/handlers.ts index 6093fec02..8ec4d6014 100644 --- a/apps/cloud/src/org/handlers.ts +++ b/apps/cloud/src/org/handlers.ts @@ -1,5 +1,5 @@ import { HttpApiBuilder } from "effect/unstable/httpapi"; -import { Effect } from "effect"; +import { Cause, Effect } from "effect"; import { UserStoreService } from "../auth/context"; import { AuthContext } from "../auth/middleware"; @@ -103,7 +103,7 @@ const reserveMemberSlot = Effect.gen(function* () { Effect.catchCause((cause) => Effect.gen(function* () { yield* Effect.logError("members.seats lookup failed; failing closed").pipe( - Effect.annotateLogs({ "org.id": auth.organizationId, cause: String(cause) }), + Effect.annotateLogs({ "org.id": auth.organizationId, cause: Cause.pretty(cause) }), ); return yield* new Forbidden(); }), diff --git a/apps/cloud/src/routes/billing_.plans.tsx b/apps/cloud/src/routes/billing_.plans.tsx index ae95a4674..95e719786 100644 --- a/apps/cloud/src/routes/billing_.plans.tsx +++ b/apps/cloud/src/routes/billing_.plans.tsx @@ -346,6 +346,7 @@ function SlackContactCta() { } setSubmitting(true); setError(null); + // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: browser fetch submit path maps network failures to public UI copy try { const res = await fetch("/api/contact/slack", { method: "POST", @@ -358,7 +359,10 @@ function SlackContactCta() { turnstileToken, }), }); - const data = (await res.json().catch(() => ({}))) as { url?: string; error?: string }; + const data = (await res.json().then( + (value) => value, + () => ({}), + )) as { url?: string; error?: string }; if (!res.ok) { setError(data.error ?? "Something went wrong. Please try again."); return; diff --git a/apps/cloud/src/secrets-isolation.e2e.node.test.ts b/apps/cloud/src/secrets-isolation.e2e.node.test.ts index 64773bf66..5973b522a 100644 --- a/apps/cloud/src/secrets-isolation.e2e.node.test.ts +++ b/apps/cloud/src/secrets-isolation.e2e.node.test.ts @@ -27,7 +27,7 @@ // SDK-level change — coverage for it belongs after the fix. import { describe, expect, it } from "@effect/vitest"; -import { Effect } from "effect"; +import { Effect, Result } from "effect"; import { ScopeId, SecretId } from "@executor-js/sdk"; @@ -237,7 +237,7 @@ describe("cloud secret isolation (HTTP, user-org scope stack)", () => { }) .pipe(Effect.result), ); - expect(result._tag).toBe("Failure"); + expect(Result.isFailure(result)).toBe(true); // And nothing landed in the foreign org — a fresh session pointed // at that org must not see `wrong-scope`. diff --git a/apps/cloud/src/services/__test-harness__/api-harness.ts b/apps/cloud/src/services/__test-harness__/api-harness.ts index 4ae4d5ff5..6fc3587cc 100644 --- a/apps/cloud/src/services/__test-harness__/api-harness.ts +++ b/apps/cloud/src/services/__test-harness__/api-harness.ts @@ -135,6 +135,7 @@ const TestExecutionStackMiddleware = HttpRouter.middleware<{ const request = yield* HttpServerRequest.HttpServerRequest; const orgId = request.headers[TEST_ORG_HEADER]; if (!orgId || typeof orgId !== "string") { + // oxlint-disable-next-line executor/no-effect-escape-hatch, executor/no-error-constructor -- boundary: test HTTP harness has no request context without x-test-org-id return yield* Effect.die(new Error("missing x-test-org-id")); } const userHeader = request.headers[TEST_USER_HEADER]; diff --git a/apps/cloud/src/services/secrets-api.node.test.ts b/apps/cloud/src/services/secrets-api.node.test.ts index 97214f41b..c07150e75 100644 --- a/apps/cloud/src/services/secrets-api.node.test.ts +++ b/apps/cloud/src/services/secrets-api.node.test.ts @@ -2,7 +2,7 @@ // and error fidelity within a single org. import { describe, expect, it } from "@effect/vitest"; -import { Effect } from "effect"; +import { Effect, Result } from "effect"; import { ScopeId, SecretId } from "@executor-js/sdk"; @@ -128,7 +128,7 @@ describe("secrets api (HTTP)", () => { .remove({ params: { scopeId: ScopeId.make(org), secretId: SecretId.make(missing) } }) .pipe(Effect.result), ); - expect(result._tag).toBe("Success"); + expect(Result.isSuccess(result)).toBe(true); }), ); diff --git a/apps/cloud/src/services/sources-refresh.node.test.ts b/apps/cloud/src/services/sources-refresh.node.test.ts index c6ee73334..fd0a6cfd7 100644 --- a/apps/cloud/src/services/sources-refresh.node.test.ts +++ b/apps/cloud/src/services/sources-refresh.node.test.ts @@ -84,9 +84,12 @@ const serveMutableSpec = () => { describe("sources.refresh (HTTP)", () => { it.effect("addSpec from URL → canRefresh:true; refresh re-fetches and updates tools", () => - Effect.gen(function* () { - const server = yield* Effect.promise(() => serveMutableSpec()); - try { + Effect.scoped( + Effect.gen(function* () { + const server = yield* Effect.acquireRelease( + Effect.promise(() => serveMutableSpec()), + (server) => Effect.promise(() => server.close()), + ); const org = `org_${crypto.randomUUID()}`; const namespace = `ns_${crypto.randomUUID().replace(/-/g, "_")}`; @@ -139,10 +142,8 @@ describe("sources.refresh (HTTP)", () => { expect(afterTools.length).toBe(2); expect(afterTools.some((t) => t.name.startsWith("ping"))).toBe(true); expect(afterTools.some((t) => t.name.startsWith("pong"))).toBe(true); - } finally { - yield* Effect.promise(() => server.close()); - } - }), + }), + ), ); it.effect("addSpec from raw text → canRefresh:false; refresh is a no-op", () => diff --git a/apps/cloud/vitest.config.ts b/apps/cloud/vitest.config.ts index 460cd46b8..68be4ca21 100644 --- a/apps/cloud/vitest.config.ts +++ b/apps/cloud/vitest.config.ts @@ -17,6 +17,7 @@ export default defineConfig({ // the socket is closing anyway — so filter it out rather than fail // the run with noise. onUnhandledError(error) { + // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: Vitest unhandled-error hook receives unknown host errors if (error && (error as Error).message === "Stream was cancelled.") { return false; }