Repository navigation
Effect Service Conventions: 1 issue found
1 finding
apps/server/src/vcs/sshRemoteUrl.ts(lines 23-39): theeffectiveHostnamesTTL cache is process-global mutable state held outside the Effect environment and usesDate.now()rather than the Effect clock. It survives layer/runtime lifetimes, leaks between tests, cannot be exercised withTestClock, and is keyed only by host so a hostname resolved byGitVcsDriver's cwd-scopedssh -Gprobe is reused for other cwds and for theProcessRunner-backed probe. Suggested fix: own the memoization inside eachmake(e.g.Effect.cachedFunction/Cache.makeWithover the probe) or in a smallProcessRunner-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 withProcessRunner.ProcessRunner/ProcessRunner.layer; the named imports from./sshRemoteUrl.tsare for a pure helper plus a standalone type, which the conventions explicitly allow. - Dependency acquisition:
GitManager.makeandRepositoryIdentityResolveracquireProcessRunnerfrom the environment viayield* ProcessRunner.ProcessRunner;GitVcsDriverbuilds its probe from the already-acquiredVcsProcess. NoManagedRuntime.make,runPromise, orrunPromiseExitwas introduced. sshConfigProbe(processRunner)was considered against the "factories that acceptFoo[\"Service\"]" rule and treated as an acceptable deliberate callback strategy:SshConfigProbehas two real production implementations (ProcessRunner-backed and cwd-scopedVcsProcess-backed), and the owning services still exposeProcessRunnerin theirmakerequirements.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/catchIfusage,FooShapetypes, or compatibility re-export shims were introduced; behavior change is covered by focused tests inapps/server/src/vcs/sshRemoteUrl.test.ts. apps/web/src/state/query.ts: extractingenvironmentQueryErroris a pure helper change with no service-convention impact.
🔍 Agent activity — 1 tool call
💭 Looking at the new sshRemoteUrl.ts file and noticing that
effectiveHostnamesis 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 thesshConfigProbefunction: it acceptsProcessRunner.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 legacyrunProcessCorefunction already in the codebase that I should leave untouched. Now I'm examining how GitManager.layer is… TheLayer.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 callingDate.now()directly which creates testability problems. The second concern is thatsshConfigProbeaccepts 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 rewritesshConfigProbeas 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. ThesshConfigProbefactory accepting a ProcessRunner instance looks like it might violate the guidance against factories takingFoo["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 usingDate.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 theProcessRunner["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 aProcessRunnerimplementation dependency into production code, which violates the pattern. The better approach would be to have the factory acquireProcessRunnerfrom 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 ofProcessRunnersuggests 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 usesDate.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 ambientDate.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'smakefunction 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