From ce2285955ba9e7adaa53f911b6327ea02147b1ce Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 25 Oct 2026 15:07:58 -0700 Subject: [PATCH] chore(lint): enforce catchConditionalRefailToCatchIf --- apps/server/src/assets/AttachmentUpload.ts | 5 +- .../CheckpointRestoreSafety.ts | 28 +++++----- .../src/orchestration-v2/EffectWorker.ts | 16 +++--- .../ProviderTurnStartService.ts | 16 +++--- .../orchestration-v2/RunExecutionService.ts | 14 ++--- .../ThreadPullRequestService.ts | 14 ++--- .../ThreadSettlementService.ts | 54 +++++++++---------- .../src/persistence/initializeV2Database.ts | 13 +++-- .../src/pullRequest/PullRequestService.ts | 10 ++-- .../SourceControlRepositoryService.ts | 1 + .../src/server/AcpRegistrySupport.ts | 5 +- .../src/server/AcpSessionRuntime.ts | 21 ++++---- .../src/server/xaiAcpExtension.ts | 7 ++- .../src/server/GitCafePullRequestProvider.ts | 2 + tsconfig.base.json | 1 + 15 files changed, 105 insertions(+), 102 deletions(-) diff --git a/apps/server/src/assets/AttachmentUpload.ts b/apps/server/src/assets/AttachmentUpload.ts index f3cc445abf55d..d1a1b055bfe11 100644 --- a/apps/server/src/assets/AttachmentUpload.ts +++ b/apps/server/src/assets/AttachmentUpload.ts @@ -230,8 +230,9 @@ export const storeAttachmentUpload = Effect.fn("AttachmentUpload.store")(functio }), }).pipe( Effect.as(true), - Effect.catch((error) => - error.reason._tag === "AlreadyExists" ? Effect.succeed(false) : Effect.fail(error), + Effect.catchIf( + (error) => error.reason._tag === "AlreadyExists", + () => Effect.succeed(false), ), ); }), diff --git a/apps/server/src/orchestration-v2/CheckpointRestoreSafety.ts b/apps/server/src/orchestration-v2/CheckpointRestoreSafety.ts index 5888feed349a7..268549d7bf7fe 100644 --- a/apps/server/src/orchestration-v2/CheckpointRestoreSafety.ts +++ b/apps/server/src/orchestration-v2/CheckpointRestoreSafety.ts @@ -34,15 +34,12 @@ export const isCheckpointRestoreIsolated = Effect.fn("orchestrationV2.isCheckpoi if (!shared && worktreePath !== null) { // Storage cleanup may have removed the worktree; rollback recreates it. const resolve = (target: string) => - fileSystem - .realPath(target) - .pipe( - Effect.catch((error) => - error.reason._tag === "NotFound" - ? Effect.succeed(path.resolve(target)) - : Effect.fail(error), - ), - ); + fileSystem.realPath(target).pipe( + Effect.catchIf( + (error) => error.reason._tag === "NotFound", + (_error) => Effect.succeed(path.resolve(target)), + ), + ); const cwd = yield* resolve(scope.cwd); const worktreeCwd = yield* resolve(worktreePath); shared = cwd !== worktreeCwd; @@ -74,13 +71,12 @@ export const isCheckpointRestoreIsolated = Effect.fn("orchestrationV2.isCheckpoi for (const candidate of paths) { if (checkedPaths.has(candidate)) continue; checkedPaths.add(candidate); - const otherCwd = yield* fileSystem - .realPath(candidate) - .pipe( - Effect.catch((error) => - error.reason._tag === "NotFound" ? Effect.succeed(null) : Effect.fail(error), - ), - ); + const otherCwd = yield* fileSystem.realPath(candidate).pipe( + Effect.catchIf( + (error) => error.reason._tag === "NotFound", + (_error) => Effect.succeed(null), + ), + ); if (otherCwd !== null && (contains(cwd, otherCwd) || contains(otherCwd, cwd))) { shared = true; break; diff --git a/apps/server/src/orchestration-v2/EffectWorker.ts b/apps/server/src/orchestration-v2/EffectWorker.ts index f7b6ba5eb34bf..112043704c7c1 100644 --- a/apps/server/src/orchestration-v2/EffectWorker.ts +++ b/apps/server/src/orchestration-v2/EffectWorker.ts @@ -177,14 +177,14 @@ export const layerExecutor: Layer.Layer< cause, }), ), - Effect.catch((cause) => - request.subagent === undefined && - isNonRetryableProviderTurnControlFailure( - request.type, - Cause.pretty(Cause.fail(cause)), - ) - ? Effect.void - : Effect.fail(cause), + Effect.catchIf( + (cause) => + request.subagent === undefined && + isNonRetryableProviderTurnControlFailure( + request.type, + Cause.pretty(Cause.fail(cause)), + ), + () => Effect.void, ), Effect.andThen((outcome) => request.subagent !== undefined && outcome === undefined diff --git a/apps/server/src/orchestration-v2/ProviderTurnStartService.ts b/apps/server/src/orchestration-v2/ProviderTurnStartService.ts index e383835f83568..a6fdcabc0b3a4 100644 --- a/apps/server/src/orchestration-v2/ProviderTurnStartService.ts +++ b/apps/server/src/orchestration-v2/ProviderTurnStartService.ts @@ -505,14 +505,14 @@ export const layer: Layer.Layer< path: worktreePath, }), ), - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("provider turn start failed to recreate worktree", { - threadId: projection.thread.id, - worktreePath, - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("provider turn start failed to recreate worktree", { + threadId: projection.thread.id, + worktreePath, + cause: Cause.pretty(cause), + }), ), ); } diff --git a/apps/server/src/orchestration-v2/RunExecutionService.ts b/apps/server/src/orchestration-v2/RunExecutionService.ts index 7878befe0e852..2ea57af67dc71 100644 --- a/apps/server/src/orchestration-v2/RunExecutionService.ts +++ b/apps/server/src/orchestration-v2/RunExecutionService.ts @@ -926,13 +926,13 @@ export const layer: Layer.Layer< ordinalWithinScope: Math.max(0, input.run.ordinal - 1), }) .pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning( - "orchestration V2 checkpoint baseline capture failed; starting provider without a baseline", - { runId: input.run.id }, - ), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (_cause) => + Effect.logWarning( + "orchestration V2 checkpoint baseline capture failed; starting provider without a baseline", + { runId: input.run.id }, + ), ), ); if ( diff --git a/apps/server/src/orchestration-v2/ThreadPullRequestService.ts b/apps/server/src/orchestration-v2/ThreadPullRequestService.ts index 4ff74ccf3a872..7b3c09ba15b8a 100644 --- a/apps/server/src/orchestration-v2/ThreadPullRequestService.ts +++ b/apps/server/src/orchestration-v2/ThreadPullRequestService.ts @@ -318,13 +318,13 @@ export const make = Effect.gen(function* () { }); pendingBackfill.delete(thread.id); }).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("thread pull request update failed", { - threadId: thread.id, - cause: Cause.pretty(cause), - }).pipe(Effect.tap(() => Effect.sync(() => failBackfill([thread])))), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("thread pull request update failed", { + threadId: thread.id, + cause: Cause.pretty(cause), + }).pipe(Effect.tap(() => Effect.sync(() => failBackfill([thread])))), ), ), { discard: true }, diff --git a/apps/server/src/orchestration-v2/ThreadSettlementService.ts b/apps/server/src/orchestration-v2/ThreadSettlementService.ts index 35ed1d9721348..1077f341f61b2 100644 --- a/apps/server/src/orchestration-v2/ThreadSettlementService.ts +++ b/apps/server/src/orchestration-v2/ThreadSettlementService.ts @@ -350,13 +350,13 @@ export const make = Effect.gen(function* () { }, (effect, thread) => effect.pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("automatic thread settlement skipped", { - threadId: thread.id, - cause: Cause.pretty(cause), - }).pipe(Effect.as(null)), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("automatic thread settlement skipped", { + threadId: thread.id, + cause: Cause.pretty(cause), + }).pipe(Effect.as(null)), ), ), ); @@ -507,13 +507,13 @@ export const make = Effect.gen(function* () { discard: true, }); }).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("automatic thread settlement skipped", { - threadIds: group.map((thread) => thread.id), - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("automatic thread settlement skipped", { + threadIds: group.map((thread) => thread.id), + cause: Cause.pretty(cause), + }), ), ), { concurrency: 8, discard: true }, @@ -525,12 +525,12 @@ export const make = Effect.gen(function* () { threadId?: ThreadId, ) => sweep(mergedPullRequest, threadId).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("automatic thread settlement sweep failed", { - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("automatic thread settlement sweep failed", { + cause: Cause.pretty(cause), + }), ), ); // A thread that just stopped must not wait behind a full sweep or another thread's host @@ -590,13 +590,13 @@ export const make = Effect.gen(function* () { }, (effect, threadId) => effect.pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.failCause(cause) - : Effect.logWarning("cleaning up a settled thread failed", { - threadId, - cause: Cause.pretty(cause), - }), + Effect.catchCauseIf( + (cause) => !Cause.hasInterruptsOnly(cause), + (cause) => + Effect.logWarning("cleaning up a settled thread failed", { + threadId, + cause: Cause.pretty(cause), + }), ), ), ); diff --git a/apps/server/src/persistence/initializeV2Database.ts b/apps/server/src/persistence/initializeV2Database.ts index 7f63bb9c4c137..755907b5076f9 100644 --- a/apps/server/src/persistence/initializeV2Database.ts +++ b/apps/server/src/persistence/initializeV2Database.ts @@ -39,13 +39,12 @@ export const initializeV2Database = Effect.fn("initializeV2Database")(function* } }); // Publish only a complete snapshot, without replacing an existing V2 database. - yield* fs - .link(snapshotPath, destinationPath) - .pipe( - Effect.catch((error) => - error.reason._tag === "AlreadyExists" ? Effect.void : Effect.fail(error), - ), - ); + yield* fs.link(snapshotPath, destinationPath).pipe( + Effect.catchIf( + (error) => error.reason._tag === "AlreadyExists", + (_error) => Effect.void, + ), + ); }).pipe( Effect.scoped, Effect.mapError((cause) => new V2DatabaseImportError({ sourcePath, destinationPath, cause })), diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index e967b18ef8c3b..72fb7b13593fa 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -2954,8 +2954,9 @@ export const make = Effect.gen(function* () { ]); // A replacement reader can join a lookup still finishing its previous reader's cancellation. return Cache.get(listCache, key).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) ? Cache.get(listCache, key) : Effect.failCause(cause), + Effect.catchCauseIf( + (cause) => Cause.hasInterruptsOnly(cause), + (_cause) => Cache.get(listCache, key), ), ); }; @@ -3385,8 +3386,9 @@ export const make = Effect.gen(function* () { if (missing.size === 0) return { stats: held }; const key = statsBatchKey(missing.values()); const { result, at } = yield* Cache.get(listStatsCache, key).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) ? Cache.get(listStatsCache, key) : Effect.failCause(cause), + Effect.catchCauseIf( + (cause) => Cause.hasInterruptsOnly(cause), + (_cause) => Cache.get(listStatsCache, key), ), ); for (const [key, ref] of missing) { diff --git a/apps/server/src/sourceControl/SourceControlRepositoryService.ts b/apps/server/src/sourceControl/SourceControlRepositoryService.ts index e89a6330a69e4..e211741baf86a 100644 --- a/apps/server/src/sourceControl/SourceControlRepositoryService.ts +++ b/apps/server/src/sourceControl/SourceControlRepositoryService.ts @@ -382,6 +382,7 @@ export const make = Effect.gen(function* () { options, ).pipe( Effect.as(prepared.remoteUrl), + // @effect-diagnostics-next-line catchConditionalRefailToCatchIf:off - The condition also narrows httpsUrl for the fallback closure. Effect.catch((sshError) => httpsUrl === null || !isSshRejection(sshError) ? Effect.fail(sshError) diff --git a/packages/provider-acp-registry/src/server/AcpRegistrySupport.ts b/packages/provider-acp-registry/src/server/AcpRegistrySupport.ts index fdc002083b93c..d97c110d8611e 100644 --- a/packages/provider-acp-registry/src/server/AcpRegistrySupport.ts +++ b/packages/provider-acp-registry/src/server/AcpRegistrySupport.ts @@ -1294,8 +1294,9 @@ export const makeAcpRegistryCatalog = Effect.fn("AcpRegistryCatalog.make")(funct for (let attempt = 0; attempt < INSTALL_LOCK_RETRY_COUNT; attempt += 1) { const acquired = yield* fileSystem.writeFileString(lockPath, "", { flag: "wx" }).pipe( Effect.as(true), - Effect.catch((error) => - isAlreadyExists(error) ? Effect.succeed(false) : Effect.fail(error), + Effect.catchIf( + (error) => isAlreadyExists(error), + () => Effect.succeed(false), ), ); if (acquired) return; diff --git a/packages/provider-acp/src/server/AcpSessionRuntime.ts b/packages/provider-acp/src/server/AcpSessionRuntime.ts index 5eb4d064a2604..95de8f59d7ef1 100644 --- a/packages/provider-acp/src/server/AcpSessionRuntime.ts +++ b/packages/provider-acp/src/server/AcpSessionRuntime.ts @@ -2221,10 +2221,10 @@ export const make = Effect.fn("AcpSessionRuntime.make")(function* ( ); } const { sessionId, sessionSetupResult } = yield* setupSession.pipe( - Effect.catch((error) => - !isAcpAuthenticationRequired(error) || options.authenticateOnAuthRequired === false - ? Effect.fail(error) - : authenticateAfterRequired(error).pipe(Effect.andThen(setupSession)), + Effect.catchIf( + (error) => + !(!isAcpAuthenticationRequired(error) || options.authenticateOnAuthRequired === false), + (error) => authenticateAfterRequired(error).pipe(Effect.andThen(setupSession)), ), ); @@ -2578,12 +2578,13 @@ export const make = Effect.fn("AcpSessionRuntime.make")(function* ( ), (activePrompt) => Fiber.join(activePrompt.fiber).pipe( - Effect.catchCause((cause) => - options.cancelBehavior !== "wait-for-prompt" && Cause.hasInterruptsOnly(cause) - ? Effect.succeed({ - stopReason: "cancelled", - } satisfies EffectAcpSchema.PromptResponse) - : Effect.failCause(cause), + Effect.catchCauseIf( + (cause) => + options.cancelBehavior !== "wait-for-prompt" && Cause.hasInterruptsOnly(cause), + (_cause) => + Effect.succeed({ + stopReason: "cancelled", + } satisfies EffectAcpSchema.PromptResponse), ), Effect.tap(() => closeActiveAssistantSegment({ queue: eventQueue, assistantSegmentRef }), diff --git a/packages/provider-grok/src/server/xaiAcpExtension.ts b/packages/provider-grok/src/server/xaiAcpExtension.ts index 2f1be07bc4662..a5aa46f6b8367 100644 --- a/packages/provider-grok/src/server/xaiAcpExtension.ts +++ b/packages/provider-grok/src/server/xaiAcpExtension.ts @@ -1524,10 +1524,9 @@ export const makeXAiPromptCompletionRuntime = Effect.fn("makeXAiPromptCompletion .pipe(Effect.forkChild); return yield* Effect.raceFirst( Fiber.join(promptRpcFiber).pipe( - Effect.catchCause((cause) => - Cause.hasInterruptsOnly(cause) - ? Effect.succeed(cancelledResponse) - : Effect.failCause(cause), + Effect.catchCauseIf( + (cause) => Cause.hasInterruptsOnly(cause), + (_cause) => Effect.succeed(cancelledResponse), ), ), Deferred.await(fallback.deferred), diff --git a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts index 79677b3d66f8a..f372c8a2c3330 100644 --- a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts +++ b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts @@ -907,6 +907,7 @@ export const make = Effect.gen(function* () { : readLineStats(input, pull.version).pipe(Effect.orElseSucceed(() => null)), // Reviewers need `pr:review:read`, which a contents-only token lacks. readPages("getChangeRequest", input, `${path}/reviewers`, Json.GitCafeActorEntries).pipe( + // @effect-diagnostics-next-line catchConditionalRefailToCatchIf:off - catchIf widens this paginated result to any in tsc-rs. Effect.catch((error) => isForbidden(error) ? Effect.succeed({ items: [], next: null }) : Effect.fail(error), ), @@ -989,6 +990,7 @@ export const make = Effect.gen(function* () { : // A closed pull request whose branch is gone keeps its head, but git.cafe answers // 404 for commits it no longer has; the conversation still stands without them. readPages(operation, input, `${path}/commits`, Json.GitCafeCommits).pipe( + // @effect-diagnostics-next-line catchConditionalRefailToCatchIf:off - catchIf widens this paginated result to any in tsc-rs. Effect.catch((error) => error.reason === "not-found" ? Effect.succeed({ items: [], next: null }) diff --git a/tsconfig.base.json b/tsconfig.base.json index 47fd6ac6e8078..f4ad537cf4ecc 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -24,6 +24,7 @@ "namespaceImportPackages": ["@effect/platform-node", "effect"], "effectFn": ["span", "inferred-span", "suggested-span"], "diagnosticSeverity": { + "catchConditionalRefailToCatchIf": "error", "tryCatchInEffectGen": "error", "schemaStructWithTag": "error", "preferTypedSchemaDecoder": "error",