Skip to content

fix: pass -- before the ssh host

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 11, 2026 in 2m 15s

Effect Service Conventions: 1 issue found

1 finding

  • apps/server/src/vcs/sshRemoteUrl.ts (lines 23-39): the effectiveHostnames TTL cache is process-global mutable state held outside the Effect environment and uses Date.now() rather than the Effect clock. It survives layer/runtime lifetimes, leaks between tests, cannot be exercised with TestClock, and is keyed only by host so a hostname resolved by GitVcsDriver's cwd-scoped ssh -G probe is reused for other cwds and for the ProcessRunner-backed probe. Suggested fix: own the memoization inside each make (e.g. Effect.cachedFunction/Cache.makeWith over the probe) or in a small ProcessRunner-backed service that keys by probe context.

An inline review comment has been posted for this finding.

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.

Items checked and found compliant

  • Imports: all touched files use subpath namespace imports (import * as Effect from "effect/Effect", import * as Layer from "effect/Layer"); no consolidated named imports from "effect" were introduced. import * as ProcessRunner from "../processRunner.ts" uses the service module namespace with ProcessRunner.ProcessRunner / ProcessRunner.layer; the named imports from ./sshRemoteUrl.ts are for a pure helper plus a standalone type, which the conventions explicitly allow.
  • Dependency acquisition: GitManager.make and RepositoryIdentityResolver acquire ProcessRunner from the environment via yield* ProcessRunner.ProcessRunner; GitVcsDriver builds its probe from the already-acquired VcsProcess. No ManagedRuntime.make, runPromise, or runPromiseExit was introduced.
  • sshConfigProbe(processRunner) was considered against the "factories that accept Foo[\"Service\"]" rule and treated as an acceptable deliberate callback strategy: SshConfigProbe has two real production implementations (ProcessRunner-backed and cwd-scoped VcsProcess-backed), and the owning services still expose ProcessRunner in their make requirements.
  • export const layer = Layer.effect(GitManager, make).pipe(Layer.provide(ProcessRunner.layer)) matches the established pattern in this repo (VcsProcess.ts, ServerEnvironment.ts, terminal/Manager.ts, RepositoryIdentityResolver.ts).
  • No new error classes, discriminators, catchTag/catchIf usage, FooShape types, or compatibility re-export shims were introduced; behavior change is covered by focused tests in apps/server/src/vcs/sshRemoteUrl.test.ts.
  • apps/web/src/state/query.ts: extracting environmentQueryError is a pure helper change with no service-convention impact.

🔍 Agent activity — 1 tool call

💭 Looking at the new sshRemoteUrl.ts file and noticing that effectiveHostnames is a module-level global Map cache — this violates the principle of not hiding dependencies in module globals. I need to think through whether this cache should be refactored into a proper service dependency instead of relying on a mutable global with Date.now() for expiration logic. Also flagging the sshConfigProbe function: it accepts ProcessRunner.ProcessRunner["Service"] as a parameter, which is passing a service instance directly. This looks like it might be a deliberate callback strategy, but I should verify whether it's following the convention that production service construction should acquire dependencies from the environment rather than accepting service implementations as parameters.… I see there's a legacy runProcessCore function already in the codebase that I should leave untouched. Now I'm examining how GitManager.layer is… The Layer.provide(ProcessRunner.layer) pattern on the service layer aligns with how other services in the repo are structured, so that's not a concern. But there's a real issue with the module-level mutable cache in sshRemoteUrl.ts — it's hiding a dependency in a global rather than following the convention of making dependencies explicit through services, and it's also calling Date.now() directly which creates testability problems. The second concern is that sshConfigProbe accepts a ProcessRunner service instance as a parameter, which violates the convention against factories that take service implementations as arguments — those should be dependencies injected through the Effect environment instead. But since the calling code explicitly requires ProcessRunner and passes it through, the dependency is visible in the type signature, making this a borderline case; it's a deliberate callback strategy rather than hidden service injection. I could also rewrite sshConfigProbe as an Effect that requires ProcessRunner in its environment, which would be more explicit about the dependency. The current… Looking at the imports and conventions in query.ts, those are fine — namespace and named imports from effect subpaths follow the existing pattern. GitVcsDriver is straightforward too, using the VcsProcess from the environment and defining a local sshConfigProbe closure. The real issue I'm spotting is a module-level mutable cache called effectiveHostnames in sshRemoteUrl.ts that needs attention. This cache is shared across GitManager, GitVcsDriver, and RepositoryIdentityResolver, and it's using Date.now() directly instead of the Effect clock. The problem isn't just that it's hidden state — it's that the cache is keyed only by hostname, so when GitVcsDriver probes with a cwd-specific ssh config, a result cached from a different cwd gets reused incorrectly. That's a real correctness bug, and it violates the convention against hiding dependencies in module globals. I think this should be flagged: the cache should live in the Effect environment as a proper service instead. The sshConfigProbe factory accepting a ProcessRunner instance looks like it might violate the guidance against factories taking Foo["Service"], but it's actually a legitimate adapter for a callback strategy pattern — GitVcsDriver provides its own probe from VcsProcess, which shows this is genuinely a pluggable strategy, not fake dependency injection. I won't flag that one.… I see there's precedent for module-global maps in the codebase, but I'm questioning whether the global cache really violates the dependency injection principle—it's caching results rather than hiding a dependency itself, though it does obscure the runtime-backed API call underneath. The stronger argument is that it hides state outside the Effect environment using Date.now(), making it non-resettable and non-testable across different working directory contexts. I'm leaning toward flagging this as a concrete violation in an inline comment, though I need to weigh whether it's worth mentioning alongside the ProcessRunner["Service"] dependency issue or if that creates too much noise in the review. The conventions are clear about factories accepting service instances — sshConfigProbe(processRunner) passes a ProcessRunner implementation dependency into production code, which violates the pattern. The better approach would be to have the factory acquire ProcessRunner from the Effect environment directly, letting the dependency be declared explicitly in the effect signature rather than hidden in the function parameter. I'm second-guessing whether this is actually a violation though. The type-only import of ProcessRunner suggests the module is deliberately avoiding a runtime dependency, possibly to prevent a cycle, and the service is properly acquired by its owning module. I'll flag both cases but keep the comments brief and focused on the canonical alternative. Now I'm looking at the module-level TTL cache that uses Date.now(). This feels like a real issue—it's mutable state living outside the Effect environment, persists across layer lifetimes and tests, and relies on ambient Date.now() instead of the Effect clock, which breaks testability and isolation. The bigger problem is that the cache key only considers the host, ignoring the probe context or working directory. That means a result from one GitVcsDriver's cwd-scoped probe gets reused for other cwds and for GitManager's non-cwd ProcessRunner, which is a correctness bug. I should recommend moving the memoization into each service's make function using Effect's caching primitives with the Effect clock, and keying on the full probe context instead. I'll add a comment spanning lines 23-39 on the right side of the diff to flag this issue. For the test file, since this is a behavior change rather than just a mechanical refactor, adding tests is appropriate. I'll submit the review with REQUEST_CHANGES to highlight the correctness concern.…

1. modify_pr — Reviewed the new sshRemoteUrl helper and its consumers (GitManager, GitVcsDriver, RepositoryIdentityResolver) against the Effect service conventions. Layer/dependency wiring follows the repo pattern (yield* ProcessRunner.ProcessRunner in make, Layer.provide(ProcessRunner.layer) on the exported layer), and the SshConfigProbe callback is a legitimate strategy since GitVcsDriver supplies a cwd-scoped VcsProcess variant. One finding: process-wide mutable cache state kept outside the Effect environment.


Agent Credits: 21 credits