Repository navigation
Effect Service Conventions: 1 issue found
1 finding
packages/shared/src/shell.ts(L504-L510): the new command-resolution memo is a module-global mutableMap(commandResolutionCache) with hand-rolled TTL and eviction. This hides a stateful dependency ofresolveCommandPathForPlatformin module scope: its lifetime is unbounded, it is not resettable per runtime/test, and it is shared across all consumers regardless of the layer that providedFileSystem/Path. Own the cache in a layer (e.g.Cache.makeWithinsidemake, as inapps/server/src/project/RepositoryIdentityResolver.ts) and acquire it from the environment so the requirement is visible in the effect type.
No other convention violations were found in the changed scope.
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.
Files reviewed (in scope)
apps/server/src/process/externalLauncher.ts/.test.ts—Effect.cachedWithTTLis created insidemake, so the memo's lifetime is layer-scoped; imports are subpath namespaces (effect/Effect,effect/Layer,effect/testing/TestClock); theContext.Servicetag keeps its inline interface and the module keeps realmake+export const layer. No issue.apps/server/src/ws.ts— constant/comment change only.packages/client-runtime/src/connection/supervisor.ts/.test.ts— new wake-probe state usesRef.make/Ref.getAndSetinside the existing scopedmake; retry constants and probe timeout are plain values. No newManagedRuntime.make,runPromise,runPromiseExit,Layer.succeed, or service-instance parameters. No issue.packages/client-runtime/src/state/shell.ts/shell-sync.test.ts— warm-resume logic stays inside the existingEffect.fnconstructor and continues to acquireEnvironmentSupervisor,EnvironmentCacheStore, andShellSnapshotLoaderfrom the environment. Behavior change is covered by updated tests. No issue.packages/shared/src/observability.ts—truncateTraceAttributesis a pure, non-mutating helper on already-serialized records; attribute clamping keeps logged/serialized values bounded, consistent with the safe-attribute rule. No issue.packages/shared/src/shell.ts— newClocknamespace import is correct; the module-global cache is the reported finding. The pre-existingData.TaggedErrorCommandResolutionErrorwas not touched and is out of scope.
Out-of-scope files (patches/[email protected], pnpm-lock.yaml, apps/web/** UI-only markup changes) were used only as context.
Checks performed
Searched touched files and affected call sites for consolidated from "effect" imports, standalone FooShape interfaces, Layer.succeed wrapping runtime-backed APIs, ManagedRuntime.make, .runPromise / .runPromiseExit, service-instance (Foo["Service"]) parameters, catchTag/catchIf recovery of known tags, and thin error-constructor helpers. None were introduced.
🔍 Agent activity — 1 tool call
💭 I'm reviewing the diff to check for Effect service convention violations across the modified files, which include the external launcher test and implementation, some WebSocket configuration, and several chat-related React components. I'm noticing a potential issue in packages/shared/src/shell.ts where a module-global mutable Map is used for command resolution caching. The convention warns against hiding dependencies in module globals, though this is technically a cache rather than a service dependency. Effect provides proper caching utilities like Effect.cachedWithTTL, which the external launcher uses correctly, whereas shell.ts implements its own hand-rolled global Map. It's borderline whether this counts as a violation since it's hidden mutable state rather than a dependency, but it does create shared state that could cause issues across tests and runtimes. I'm noticing that the existing code already uses
Data.TaggedErrorfor CommandResolutionError, which is pre-existing and not something I'm introducing here. There's also a module-level mutable cache that could be a borderline convention violation around hiding dependencies in module globals, but since the instructions say to report only concrete violations I'm introducing, and this is just process-global state rather than a dependency, I'll leave it as is. TheexternalLauncherimplementation looks solid with proper Effect caching. Theshell.tsmodule uses a global Map as a cache, which technically bends the conventions around hiding dependencies in module globals—though a cache is arguably different from a dependency. A strict Effect purist might flag this, but it's a pragmatic choice that doesn't violate the spirit of the rules. Looking at the imports and module structure—the namespaced imports and named imports from local modules likeEnvironmentSupervisorandEnvironmentRegistryfollow the existing style. The supervisor.ts changes adding a Ref for wakeProbeFailed are straightforward. The main concern is the module-global mutable cache in packages/shared/src/shell.ts, which violates the convention against hiding dependencies in module globals or singleton closures. A memo cache is effectively hidden mutable process state shared across all Effect runtimes rather than being injectable or testable. Effect providesEffect.cachedWithTTLandCachefor this purpose—the same PR uses them in externalLauncher.ts. The cache does use Clock, which is good, but it's still global mutable state in a shared library. This feels like a judgment call on whether it's a "clear" violation versus an optional style preference, since the rule specifically targets service-shape issues and dependency hiding rather than caching patterns explicitly. The real concern is test pollution: the global cache persists between tests in the same process, and while the externalLauncher test uses a unique PATH to avoid key collisions, the cache itself isn't reset between tests. With TestClock starting at 0 in tests, there's potential for cross-test interference depending on how other tests interact with the clock and cache state. Looking at the conventions around dependency acquisition and runtime boundaries, the module-global cache in shell.ts is a candidate finding since it hides state rather than using Effect-native services. Given the guidance to report concrete violations in the changed scope, I'm leaning toward flagging this as a real issue. The module-level cache inshell.tsviolates the codebase's own conventions—caches should live insidemakeviaCache/LayerMaprather than persisting as hidden global state across calls. This matches the check's rule against hiding dependencies in module globals, making it a legitimate finding.
1. modify_pr — One finding: the new command-resolution memo lives in a module-global mutable Map in packages/shared/src/shell.ts rather than being owned by a layer/service, which hides the cache's lifetime from the Effect environment. Everything else reviewed (the Effect.cachedWithTTL memo in ExternalLauncher.make, the supervisor Ref state, the shell-state warm-resume path, and the trace-attribute helpers) follows the service conventions: subpath namespace imports, inline Context.Service interfaces, real make/layer exports, and no new ManagedRuntime/runPromise boundaries or service-instance injection.
Agent Credits: 245 credits