Repository navigation
Effect Service Conventions: 2 issues found
Two error-modeling convention violations in the new Effect service.
apps/desktop/src/windowCapture/DesktopWindowCapture.ts:38-44—DesktopWindowCaptureErrorstores an unstructuredmessage: Schema.Stringas its only attribute alongsideSchema.Defect(), so the failure is not structurally identifiable and one generic error covers four operations (capture, list-pending, read, acknowledge) without the capture id/path context available at each failure site. Use distinct error classes, or anoperationdiscriminator plus capture id withmessagederived from those attributes.apps/desktop/src/windowCapture/DesktopWindowCapture.ts:64-66—captureError(message, cause)is a pass-through helper whose only behavior isnew DesktopWindowCaptureError({ ... }); construct the error at each failure boundary instead.
Both findings were posted as inline review comments.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/effect-service-conventions.md
More information about how Check Run Agents work can be found in our Help Center.
Scope reviewed: apps/desktop/src/windowCapture/DesktopWindowCapture.ts, apps/desktop/src/windowCapture/windowCapture.ts, apps/desktop/src/ipc/methods/windowCapture.ts, apps/desktop/src/ipc/DesktopIpcHandlers.ts, apps/desktop/src/ipc/methods/clientSettings.ts, apps/desktop/src/main.ts, apps/desktop/src/app/DesktopApp.ts, packages/contracts/src/{ipc,settings,orchestration}.ts, and affected web/server call sites.
Checked and considered acceptable:
- Module shape: subpath namespace imports (
effect/Effect,effect/Layer, …), named imports from@t3tools/contractsand from the pure helper module./windowCapture.ts, local service modules imported as namespaces (DesktopEnvironment.DesktopEnvironment,DesktopWindow.DesktopWindow). - Canonical file order, inline interface on
Context.Service, exportedmake, exportedlayerviaLayer.effect;Effect.addFinalizerinsidemakematches existing desktop services (DesktopSshPasswordPrompts,DesktopApp). - Dependency acquisition: all Effect dependencies obtained with
yield* Foo.Foo; no service instances passed as parameters, noLayer.succeedhiding runtime-backed APIs. Effect.runPromiseWith(context)at line 186/278 bridges the ElectronglobalShortcutnative callback, consistent withDesktopWindow.ts,DesktopApplicationMenu.ts,DesktopClerk.ts, andDesktopIpc.ts; not flagged as a hidden runtime.Effect.catch(() => Effect.void)usages intentionally handle the whole error channel, socatchTagsis not required.- Web/React code (
WindowCaptureCoordinator.tsx,WindowCaptureSettings.tsx) is a framework boundary and contains no Effect service definitions.
🔍 Agent activity — 9 tool calls
💭 I'm examining the DesktopWindowCapture.ts file, particularly whether using runPromise inside a service constructor to bridge Electron's global shortcut callback is justified as a native callback boundary versus violating the convention that such runtime calls belong at explicit application boundaries. I'm also noting that DesktopWindowCaptureError models message as an unstructured Schema.String field and uses Schema.Defect() for cause, which conflicts with the guidance to derive messages from structured attributes and to ensure Schema.Defect() usage still identifies the specific failure. The captureError helper is a clear violation since it's purely a thin wrapper constructing the error object from its arguments. I'm also considering the mutable registeredAccelerator variable held in closure state, but that seems more like local state than a hidden dependency, so it's a weaker point to flag against the stated conventions. Checking imports and structure in DesktopWindowCapture.ts — the import styles look fine, and the Context.Service tag setup with make/layer exports follows convention, though I notice read and acknowledge take plain string ids rather than a branded type, which isn't actually a listed violation so I'll let it go. I'm now looking at whether Effect.addFinalizer inside make requires a Scope that Layer.effect doesn't automatically provide, which could be a real correctness issue worth flagging. I should verify how the state and capture methods pull from the environment, and check whether DesktopClientSettings dependencies are properly threaded through the layer setup in main.ts. I'm also noting that persistCapture wraps a multi-step pipeline under a single generic error, which might need finer-grained failure mapping. Looking at WindowCaptureCoordinator.tsx, it's plain React with promises rather than Effect, and there's an empty catch block that doesn't match Effect conventions. The setFailure logic catching the entire error channel with a no-op seems acceptable, but I want to compare against existing repo patterns—especially how other desktop services handle Electron callbacks with runPromiseWith—before drawing conclusions.
1. git_tools — runPromiseWith|ManagedRuntime.make|runPromise(|runPromiseExit
2. git_tools — Schema.Defect()
💭 Schema.Defect with cause seems common, but I want to check whether the repo pattern uses structured fields plus a derived message. Let me look at DesktopSavedEnvironments to see how that's handled in practice.
3. browse_code — apps/desktop/src/settings/DesktopSavedEnvironments.ts
4. browse_code — apps/desktop/src/windowCapture/DesktopWindowCapture.ts
💭 Looking at DesktopWindowCaptureError, I notice it uses a free-form message string with a Defect cause rather than structured fields — violating the convention that messages should derive from structural attributes, and Defect shouldn't substitute for proper modeling. It also wraps four different failure messages under one generic error, so it really needs an operation literal to distinguish which pipeline step failed, plus context like capture id and directory. The
captureErrorhelper is also a clear violation since its only job is constructing an error instance from arguments, which the convention explicitly disallows. I then checkrunPromiseWithinside the constructor and decide it's fine since the repo widely uses this pattern at native Electron callback boundaries, and here it's wrapping a globalShortcut callback — the silently swallowed error via.catch(() => undefined)is acceptable since failure is already surfaced through setFailure. I also consider whether read/acknowledge taking a plain string id instead of the brandedDesktopWindowCaptureIdtype is worth flagging, but decide it doesn't map to any listed convention, so I skip it. Finally I'm looking at the mutableregisteredAcceleratorvariable to see if it's worth flagging too. I decide the mutable closure state for the accelerator isn't a hidden dependency issue, so I skip flagging it. I move on to checking whetherLayer.effectcombined withEffect.addFinalizeris an established pattern elsewhere in the desktop services, so I search other services for similar scoped-effect usage, and note that the IPC method file and the contracts import both look fine on inspection.
5. git_tools — Effect.addFinalizer
💭 There's an existing pattern I can follow here—DesktopSshPasswordPrompts uses addFinalizer, so let me check how its layer constructor is set up.
6. git_tools — export const layer
7. browse_code — apps/desktop/src/ssh/DesktopSshPasswordPrompts.ts
💭 Layer.effect with addFinalizer is consistent with the repo's scoped layer pattern, so that's fine. I'm also noting the
messagefield naming clash in the TaggedErrorClass—worth checking if there's prior art in the repo usingmessage: Schema.Stringdirectly versus structured fields with a getter, to be fair to existing conventions.
8. git_tools — ^ message: Schema.String,
💭 I'm drafting the first inline comment for the new error class, flagging that it should use structured attributes like an operation literal and optional captureId with a derived message getter, instead of a single generic message string, and I'll suggest a concrete diff. The second comment will target the helper function around lines 64-66. Writing suggested code... Since this snippet alone doesn't cover the call-site changes needed, I'll present it as a unified diff instead of a suggestion block. I also want to flag that the error messages shown to users in toasts come from
error.message, so switching to an operation-derived message changes caller-visible text — worth noting both the generic approach and the alternative of keeping distinct error classes for caller-visible messages, before finalizing the review comment.
9. modify_pr — Reviewed the new Effect service (apps/desktop/src/windowCapture/DesktopWindowCapture.ts), its IPC methods, layer wiring, and contract additions. Module shape (namespace imports, Context.Service with inline interface, exported make/layer, dependencies acquired via yield*) follows the conventions; the Effect.runPromiseWith bridge for the Electron globalShortcut callback matches the existing desktop native-callback pattern. Two error-modeling issues below.
Agent Credits: 126 credits