Skip to content

fix(desktop): determine fnm usage from PowerShell profile content

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 14, 2026 in 7m 18s

Effect Service Conventions: 5 issues found

Five concrete issues were found in the changed scope and posted as inline review comments.

apps/desktop/src/shell/DesktopShellEnvironment.ts

  • L395-397: Effect.catchAll does not exist in Effect v4 ([email protected]); use Effect.orElseSucceed(() => false) as elsewhere in this file.
  • L428: same removed Effect.catchAll API; use Effect.orElseSucceed(() => true).
  • L418: catchIf predicate checks err._tag === "SystemError" && err.reason === "NotFound", but PlatformError carries _tag: "PlatformError" and a nested reason object — the platform code is err.reason._tag. As written it is a type error and would never recover a missing profile, permanently disabling the new fast path.

apps/desktop/src/shell/DesktopShellEnvironment.test.ts

  • L363 (and L445): PlatformError.systemError takes _tag: SystemErrorTag (e.g. "NotFound") and has no reason option, so the stub fails to typecheck.
  • L358 (and L440): env omits USERPROFILE, so checkProfileMentionsFnm short-circuits to true, both PowerShell probes run, and the commands.length === 0 assertions fail — the fast-path branch stays uncovered.

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: the two changed TypeScript files under apps/desktop/src/shell/, plus the service make/layer at the end of DesktopShellEnvironment.ts and the test harness runShellEnvironment.

Conventions verified as satisfied:

  • Subpath namespace imports (effect/Effect, effect/FileSystem, effect/Layer, ...) are used consistently in both files, including the newly added effect/FileSystem import.
  • installWindowsEnvironment acquires FileSystem from the environment via yield* FileSystem.FileSystem and declares it in Effect.fn.Return<..., ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem>; installShellEnvironment, make, and layer continue to expose that requirement. No service instances are threaded as constructor parameters in production code.
  • The test-only Effect.provideService(FileSystem.FileSystem, input.fs) seam and Layer.succeed stubs are legitimate test seams, not hidden runtimes; no ManagedRuntime.make / runPromise was introduced.
  • Error class definitions (Schema.TaggedErrorClass, structural message getters, Effect.catchTags in runCommandOutput) are unchanged and still conform.
  • The dropped concurrency rationale comment was restored alongside the new fast-path note, so the previously flagged comment-preservation issue is resolved.

API facts were confirmed against the vendored upstream reference in .repos/effect-smol (migration/error-handling.md renames catchAll → catch; PlatformError.ts shows systemError(options) with _tag: SystemErrorTag and a nested reason, and FileSystem.exists itself matches e.reason._tag === "NotFound").


🔍 Agent activity — 1 tool call

💭 I'm checking the current state of the files to see what's changed. The test file is now importing FileSystem from the effect library, and the program variable has been refactored into a const-based ternary expression. Let me examine the actual code at head to understand the full picture. Looking at the main service changes in that section. I'm checking whether catchIf with a structural predicate on _tag === "SystemError" combined with reason === "NotFound" aligns with the convention—the tag check alone would violate it, but the platform error code inspection might justify the approach. Looking at alternatives like catchTags seems more verbose, and I'm also noticing the pattern elsewhere in the file uses orElseSucceed for existence checks, plus a catchAll that wipes the error channel entirely, which the convention says is appropriate when the entire error path is intentionally handled. I should verify whether this codebase is using Effect v4, where catchAll was renamed to catch — if that's the case, this would be a compile error. Let me search the repo to see how catchAll is actually being used. I'm noticing that Effect.catchAll only appears in the new code, which suggests it might not exist in this version of Effect (v4 alpha) — Effect.catch is likely the correct API name here. Let me search the repo to confirm how Effect.catch is actually being used. I'm noticing that Effect.catchAll appears at lines 396 and 428, but this is effect v4 beta where that API was removed and replaced with Effect.catch — so these are compile errors. Let me check the actual effect v4 version to confirm whether catchAll might still exist as a deprecated alias. Looking at the conventions in the codebase, when the entire error channel is handled intentionally, the pattern should be Effect.orElseSucceed(() => false) like what's already used at line 555 for the exists function. For the catchIf usage that discriminates on _tag and reason, the more idiomatic approach would be Effect.catchTags with a handler that can re-fail if needed, though that gets a bit complex with the reason filtering. Let me verify whether Effect.catchAll actually exists in effect v4 beta 103 by checking the patch files or searching the docs, and also confirm whether previous review comments already flagged this issue. I'm flagging two Effect.catchAll compile errors under the "Errors and predicates" section since they fall within the catch conventions check. I also need to look into how err._tag === "SystemError" maps to v4's PlatformError type—readFileString fails with a PlatformError union of BadArgument and SystemError, so I should verify the shape in the effect v4 module. The test file constructs PlatformError.systemError with specific fields, but I need to check if that API actually accepts those arguments in the real implementation. I'm realizing the error predicate in production is checking for the wrong tag and structure — it's looking for _tag === "SystemError" with a reason string, but the actual error has _tag === "PlatformError" and reason is an object instead. Since the predicate never matches, file read failures fall through to a catch-all handler that returns true, which means missing profile files incorrectly trigger the fast path when they shouldn't. The type system would also catch these issues — comparing _tag to a string literal when it's narrowed to "PlatformError" is a TS error, and comparing reason (an object) to a string is another type error. The fix is to use catchTags with the correct structure: checking err.reason._tag === "NotFound" instead. The test file has similar issues — it's constructing the error with invalid parameters like _tag: "SystemError" and a reason field that shouldn't exist. This falls squarely within the "Errors and predicates" section of the Effect Service Conventions check, so I should flag both the catch-API predicate issue and the test file's malformed error construction as part of the same finding. Now I need to verify the layer dependency chain — DesktopShellEnvironment.layer is provided first, leaving FileSystem unmet, then provideService satisfies it downstream in the pipe. That's valid. But I should check whether the layer internally provides NodeServices or if the mock filesystem actually gets used by the layer's requirements. There are a few other things to verify: whether providing NodeServices after provideService causes any duplication, the timeout duration and concurrency settings look fine, and the fast path's mutation of config.env.PATH with the static paths — the original code merged these the same way, so that should be okay. Now I need to check the layer definition to make sure the FileSystem requirement is properly exposed in the service's layer declaration. I've identified two instances in the DesktopShellEnvironment file where Effect.catchAll is being used, but this method was renamed to Effect.catch in Effect v4. The codebase uses Effect.orElseSucceed(() => false) as the standard pattern for handling existence checks, so I'll flag both occurrences and suggest the appropriate fix. For the first suggestion, I'm replacing the removed catchAll API with orElseSucceed, wrapping the false return in a function. For the second comment, the catchIf predicate is checking the wrong path—since errors here are always PlatformError, I just need to check err.reason._tag === "NotFound" directly, removing the redundant _tag check on the error itself. Now I'm looking at the test file to identify where PlatformError.systemError is being constructed incorrectly. The current code is passing _tag and reason as separate fields, but the actual constructor expects _tag to be the error tag directly (like "NotFound") along with metadata fields like module, method, and pathOrDescriptor. I need to find the exact line numbers for the two occurrences in the test file to comment on them. Looking at test 437, I'm tracing through the logic where LOCALAPPDATA points to the mock directory and the node.exe path exists within it. The test expects this to return true, which checks out. But then I notice USERPROFILE isn't set in this test case, and the checkProfileMentionsFnm function returns true when the profile is missing—which means skipProfile gets set to false, not true as I'd expect for an unsafe condition. This causes the probes to run when they shouldn't, resulting in 2 commands instead of 0, so the test fails. This is the same pattern as the PROGRAMFILES issue flagged before, just hitting a different environment variable. The code was updated in response to that earlier flag, but USERPROFILE still has the same problem. I should probably mention this since it's a distinct issue, though I'm weighing whether test logic falls within the scope of checking Effect service conventions—the prior runs did flag test issues, so it seems fair to include it briefly. I'm realizing both test 1 and test 4 fail for the same reason — neither sets USERPROFILE, so checkProfileMentionsFnm short-circuits to true on line 408, leaving skipProfile false and causing both probe runs to execute. I should consolidate this into a single comment anchored at line 358 that covers both failing assertions and suggests adding USERPROFILE to the test environments. However, test 1 has an additional wrinkle: the fs mock's readFileString fails with a PlatformError, and the predicate check is broken, so it falls through to catchAll and returns true anyway. That means test 1 would also need the predicate fix from the other comment to properly yield an empty string. I'll note this interdependency when describing the fix. For test 2, when USERPROFILE is set and readFileString returns the fnm content, profileMentionsFnm becomes true, the probes run, and we get 2 commands—test passes. Test 3 checks the profile-without-fnm case: nodeFound sees node.exe in the path, staticPaths combines PATH with the known Windows directories from USERPROFILE, so the skip triggers and we get 0 commands—also passes. There's a minor issue with unused parameters in the readFileString mocks at lines 389 and 420 where the path argument isn't used, which could trigger a noUnusedParameters lint error, but that's probably out of scope here. Now I'm identifying the exact line numbers for the review comments—these are all added lines in the diff. I need to post 4 comments targeting the specific production lines that were added, starting with line 396 in DesktopShellEnvironment.ts for the catchAll logic. I'll add separate inline comments for each fix rather than grouping them together—that keeps things clearer. So line 428 gets its own suggestion to use Effect.orElseSucceed(() => true) instead of Effect.catchAll, and I'm aiming for around 5 comments total across the different issues to avoid cluttering the response. I've identified five issues total across the review. Now I'll write up the findings with the title "Effect Service Conventions: 5 issues found".

1. modify_pr — Reviewed the new Windows fast-path against the Effect service conventions. The service definition/layer shape is fine (FileSystem is acquired from the environment and surfaced in Effect.fn.Return), but the new error-handling code uses APIs/predicates that do not exist in this repo's Effect v4 line, and two of the new tests cannot pass as written.


Agent Credits: 262 credits