Skip to content

Merge origin/main into t3code/ssh-connect-setup

MacroscopeApp / Macroscope - Effect Service Conventions failed Jul 19, 2026 in 4m 10s

Effect Service Conventions: 1 issue found

One convention violation found in the changed Effect code:

  • apps/server/src/cloud/CliTokenManager.ts (line 224): outOfBandOAuthLogin recovers the statically known TimeoutError tag with Effect.catchIf(Cause.isTimeoutError, …). The convention requires catching statically known tagged failures with Effect.catchTags. The sibling login flow in the same file already does this for the identical TimeoutError after its own Effect.timeout, so the two paths are inconsistent. Switching also removes the last use of the Cause import added in this PR, which should then be dropped.

An inline review comment with a suggested fix has been posted.

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.

Prior-run findings were re-checked and are all resolved at the reviewed head (5b707d7):

  • apps/server/src/cli/connect.ts now uses Effect.catchTags in authorizeCli, disconnectCloud, and offerBootService.
  • apps/server/src/cloud/CliTokenManager.ts login flow now uses Effect.catchTags({ TimeoutError }).
  • BootServiceCommandError no longer carries a detail field that copied cause.message; it now captures structural attributes (step, exitCode, stdoutLength, stderrLength) with cause, and derives its message from step/exitCode.

Other areas reviewed and found compliant:

  • Namespace imports for Effect subpaths and local service modules (BootService, ProcessRunner); named imports retained only for pure helpers/constants/types from @t3tools/shared/connectAuth.
  • bootService.ts service definition: inline Context.Service interface, real exported make, exported layer, BootService["Service"][...] type annotations, dependencies (ProcessRunner, FileSystem, Path, host references) acquired from the environment; the optional host parameter is a pure value seam, not service injection.
  • Error classes use Schema.TaggedErrorClass with messages derived from structural attributes and preserve cause.

Methodology: reviewed the full diff plus the current contents of bootService.ts, cli/connect.ts, and cloud/CliTokenManager.ts; confirmed Cause. is referenced only at line 224.


🔍 Agent activity — 1 tool call

💭 I'm reviewing this PR against the Effect service conventions to see if previous issues have been resolved. The prior run flagged problems with catchTag usage and a BootServiceCommandError detail field issue, so I'm checking the current diff to determine whether those have been addressed. Looking at the changes in apps/server/src/cli/connect.ts, I can see the code is now using Effect.catchTags in multiple locations, which suggests an attempt to fix the earlier flagged issues. The message getter now properly constructs the error message based on the step and exit code, addressing the earlier concern about exposing raw content. Now I'm examining the current imports in CliTokenManager.ts to check for any new violations in how dependencies are being pulled in. Looking at the error handling in authorizeCli, I see it's translating errors at a boundary—passing through structured domain errors like quit errors and token manager errors while wrapping anything unknown in a new authorization error. The store operation was also added to CloudCliTokenManager, and I'm checking whether the previous wrapError removal was handled correctly. The diff removes that curried helper and replaces it with an inline Effect.mapError, which aligns with the convention against single-use error wrapper helpers. Now I'm examining the structure of bootService.ts to verify it follows the service definition conventions—checking the order of imports, constants, error definitions, and the Context.Service setup. The error class wraps multiple filesystem operations without capturing specific context like the operation type or file path, which violates the convention of mapping failures with stable domain context rather than bundling an entire multi-step pipeline into one generic error. However, I need to consider whether this is actually a clear violation worth flagging—the convention does allow semantically precise error tags to preserve real causes without inventing redundant fields, and path context is technically available at the wrapping site even if it's not being captured. This feels borderline, so I should focus on the clearest violations instead. Let me search through the actual current code to see if there are any remaining instances of singular catchTag that I might have missed, since the diff represents the full scope of changes. Let me pull up those files to see what's actually there. I need to restructure the repo parameter as an object that includes the URL. Checking whether the previous issues with catchTag have been resolved in the current code—I see connect.ts is now using catchTags, and I need to verify that CliTokenManager.ts has the fix applied as well. I'm also noting that the store method was added to CloudCliTokenManager in connect.ts, and the test file is mocking it with an unused operation. Looking at the isCloudCliTokenManagerError constant, it's a direct Schema.is call used locally in connect.ts without being wrapped in a redundant function, so that doesn't violate the convention about exporting schema predicates. Now I need to check CliTokenManager.ts to see its current state and look for any remaining issues. I'm checking the codebase for any new convention violations introduced in the current version, starting with the imports in connect.ts to see if they're following the expected patterns. The PR adds namespace imports for Terminal, Crypto, and Schema which follow the convention correctly. However, I'm noticing that CloudCliAuthorizationError stores a validation message string as its cause field rather than an underlying error object, which is a borderline case—the convention expects the exact underlying value as cause, but here it's a domain-level validation message instead. More concerning is BootServiceInstallError, which wraps about eight different filesystem operations (directory creation, file existence checks, reads/writes) all under a single generic error with just a cause field and no context about which operation failed or what path was involved. The convention explicitly discourages this pattern of wrapping multi-step pipelines in one generic error without capturing stable domain context like operation type or resource identifiers. This is a genuine violation worth flagging, though I should check how BootServiceComm handles similar scenarios first. The cause field actually preserves the path context through Effect's FileSystem errors, so the generic message is defensible even without an explicit step field. This probably isn't a clear enough violation to flag. Let me look for stronger violations elsewhere, like how Effect.ignore is being used. TokenManager captures the services context and re-provides them to dependent operations—this is a legitimate pattern since the dependencies are still declared in the make function's requirements, not hidden away. Let me verify the Effect.catch usage at line 398 in connect.ts to see if there are any remaining issues. The error handler after catchTags processes the remaining error channel (BootServiceCommandError | BootServiceInstallError) with Effect.catch, which aligns with the convention that this operator is appropriate when handling the entire remaining error channel intentionally. The earlier catchTags at line 395-397 correctly handles the BootServiceUnsupportedError case specifically, so that's properly addressed. Now I'm checking whether there are any new convention violations introduced in the current version compared to the prior commit, and reviewing a few additional spots like the CloudCliTokenManagerError union predicate to ensure everything's consistent. The three error classes—BootServiceCommandError, BootServiceInstallError, and BootServiceUnsupportedError—are genuinely distinct and handled separately in the catch block, so that's fine. BootServiceCommandError uses its multi-value step field to drive the user-facing message, which follows the acceptable pattern of a service-level error with an operation discriminator. I'm leaning toward not flagging BootServiceInstallError since the underlying cause is an Effect PlatformError. But actually, the convention is pretty clear: I should map failures where context is known rather than wrapping an entire pipeline in one generic error, and each wrapping site does have a known path. The inconsistency bothers me—command errors capture step for context, but install and filesystem errors don't, even though they could. That's worth addressing. I'm weighing whether this is a concrete enough violation to flag given the "defaults to failure" check. The prior run's findings on BootServiceCommandError were addressed thoroughly, which suggests the author is responsive to feedback. Let me verify whether the current code is the same version that was reviewed before or if there's been an update. Checking the newly added imports in publicConfig.ts — those are also pure helper constants from the connectAuth module, which follows the allowed pattern for named imports. The HostProcessExecutablePath and HostProcessArguments are Context references imported as named imports in bootService.ts and connect.ts, which aligns with how the existing HostProcessEnvironment and HostProcessPlatform are already handled. For the service modules, I'm using namespace imports like ProcessRunner to access the service and its layer, which follows the established pattern. Looking at the structure of bootService.ts, the order flows through imports, constants, pure helpers, then the BootServicePlan interface before moving into error classes, the Context.Service tag with its inline interface definition, the make function, and finally the layer export. This arrangement is fine since pure helpers and plan interfaces don't conflict with the convention around error and schema declarations. The interface is properly defined inline within Context.Service without a separate shape type, and the type annotations use the correct BootService["Service"]["install"] pattern. Both make and layer are properly exported, with layer correctly using Layer.effect to wrap the service creation. For the test file addition, the stub store dependency is acceptable since BootServiceHost is just pure configuration values rather than an actual service dependency. ProcessRunner is correctly acquired from the environment via yield* ProcessRunner.ProcessRunner rather than being injected as a parameter, which maintains proper dependency handling. Everything appears to align with the established conventions. Now I'm reconsidering the BootServiceInstallError one more time — Effect's FileSystem operations fail with PlatformError that includes the path in the cause, and the generic user-facing message is appropriate for a CLI context. The structural details are preserved in the error cause, so this is defensible and doesn't constitute a clear violation. Given all this analysis, the review looks clean. But I'm hitting a conflict in the instructions: the customer's check definition says to respond with exactly "All clear" when there are no findings, yet the outer system requires JSON output conforming to a specific schema. The JSON schema is the mandatory infrastructure format, so I need to set the AA_state to "success" with a concise summary rather than just the plain text response. Since the check defaults to failure and I found no findings, success is the appropriate conclusion. Let me verify I'm being thorough though—I should double-check the diff between the prior run and current state to confirm the previous issues were actually fixed and nothing new was introduced. Looking at the helper functions like checkConnectAuthCode returning a union type and the timeout handling in outOfBandOAuthLogin, these don't violate the service error conventions since they're pure functions or properly using the Effect error channel. Actually, I'm spotting an inconsistency in how timeouts are handled. The login() function uses Effect.catchTags to handle TimeoutError, but outOfBandOAuthLogin uses Effect.catchIf(Cause.isTimeoutError, ...) instead. The convention is clear that statically known tagged failures should use catchTags, not catchIf with a predicate—even though Cause.isTimeoutError is a structural predicate on the Cause level rather than a schema predicate. So Cause.isTimeoutError is a type guard that checks if a value is a TimeoutError, and when used as the catchIf predicate, it's really just checking the _tag field. The convention says to use catchTags for known tag variants instead, so this should be refactored to Effect.catchTags({ TimeoutError: ... }) to match the pattern used elsewhere in the same file at lines 332-334. This is a clear inconsistency worth flagging. The convention's intent is clearly about using catchTags for known tagged failures, and the inconsistency is reinforced by the fact that the same file handles the identical error type with catchTags just a few functions down. While Cause.isTimeoutError is technically a built-in structural guard rather than a schema predicate, the spirit of the convention and the codebase's own pattern suggest this warrants a note about the inconsistency. The previous run flagged four catchTag issues, so this single catchIf case is analogous and consistency suggests I should report it as well. Let me think through whether using Cause.isTimeoutError as a catchIf predicate is actually problematic here. done with catchTags elsewhere in the same file, and TimeoutError is a statically-known tagged failure that should follow the same pattern as other tagged errors in the codebase. The inline comment here is legitimate and actionable—it aligns with the convention of using precise, focused feedback on the smallest relevant line range, which matches what the previous review established. Now I'm weighing whether to post comments directly to the PR or just include findings in the check summary. The customer's instructions don't explicitly mandate posting review comments, but the prior run did post them, so I should verify what the check's actual requirements are. I'm checking whether the catchIf at line 224 in outOfBandOAuthLogin was flagged in the prior run or if it's a new finding. The previous run caught a different issue at line 227 in the login flow (now fixed to use catchTags), so this appears to be a separate location that wasn't previously reviewed. chTags({ ... }), including when handling only one tag. Do not use catchIfwith a schema predicate merely to recover one or more known_tagvariants, and do not usecatchTag. ... catchIfremains appropriate for genuinely structural predicates such as inspecting an underlying platform error code."Cause.isTimeoutError— is it a "schema predicate"? No, it's from the Cause module. Is it recovering a known_tagvariant? Yes, TimeoutError. The rule "Do not use catchIf with a schema predicate merely to recover... known _tag variants" — the qualifier "with a schema predicate" limits the prohibition to schema predicates.Cause.isTimeoutErroris not a schema predicate, so strictly it's not prohibited by that specific clause. But wait, the convention also says "Do not usecatchIf... to recover one or more known _tagvariants" — that's a separate prohibition without the schema predicate qualifier. SocatchIfwithCause.isTimeoutErrorviolates this broader rule, making line 224 genuinely inconsistent with the convention. The strongest argument is consistency: TimeoutError is a statically known tagged failure, andcatchTagsis the prescribed tool for exactly this pattern. I'll flag this as a finding but frame it as a suggestion rather than a strict violation, keeping the tone aligned with how I've handled similar cases, and mark it as a failure to match the prior behavior. I can verify thatCause.isTimeoutErrorexists andcatchTags({ TimeoutError })is the proper equivalent — line 332 already demonstrates this exact pattern afterEffect.timeout, so applying it to line 224 is straightforward and consistent with the codebase. The handler parameter at line 224 is labeled causebut actually receives the TimeoutError value itself, which I should clarify in my comment. Writing the inline suggestion... I'm realizing that swapping tocatchTagswould leave theCauseimport unused, which means this wouldn't be a fully self-contained inline suggestion since it would require removing that import as well. That violates the constraint that inline suggestions shouldn't require other changes. I'll go with an inline suggestion for the catchTags change on line 224 and explicitly call out in prose that the Cause import added in this PR becomes unused and should be removed. That way the suggestion is actionable and the import cleanup is clear. I'll post the review comment and mark the check as failed. I'll post the inline comment through the GitHub PR review API, setting the appropriate fields for a single-line comment on the right side of the diff. Now I'm verifying that line 224 is actually part of the diff — sinceoutOfBandOAuthLoginis entirely new code added in this PR, lines 206-242 should all be on the RIGHT side. I'll write the comment explaining thatEffect.catchIfwith a predicate should follow the convention of usingEffect.catchTagsfor known tagged failures instead. The suggestion replaces the predicate-based catch with a tagged catch that handlesTimeoutErrordirectly, which matches what the siblingloginflow already does. This also makes theCauseimport unnecessary, so I should note that it can be removed. I'm checking whether thecatchIfat line 224 inoutOfBandOAuthLoginwas already present before this rename commit, or if it's something new that should've been flagged in the prior review. The previous feedback focused oncatchTagat line 227, so I need to verify if thiscatchIfusage is actually a separate concern that wasn't addressed yet. I've confirmed thatCause` is only referenced once at line 224, so removing it from the imports is necessary. Now I'm drafting the GitHub PR review with an inline comment spanning lines 224-226 on the right side, and I'll include a suggestion block to show the fix. Since the suggestion alone won't cover the import removal, I'll note that in the comment text and flag it clearly so reviewers know to delete the unused import after applying the suggestion. Let me write the review body concisely.

1. modify_pr — One Effect convention issue found in the out-of-band OAuth flow. Prior findings (catchTag usages and the BootServiceCommandError detail field) have all been resolved.

💭 I'm preparing to output the JSON validation result, which shows a failure with one finding that I need to document with a title and summary.


Agent Credits: 63 credits