Skip to content

fix: address review findings in resolution cache and trace truncation

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 7, 2026 in 5m 4s

Effect Service Conventions: 1 issue found

1 finding

  • packages/shared/src/shell.ts (L504-L510): the new command-resolution memo is a module-global mutable Map (commandResolutionCache) with hand-rolled TTL and eviction. This hides a stateful dependency of resolveCommandPathForPlatform in 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 provided FileSystem/Path. Own the cache in a layer (e.g. Cache.makeWith inside make, as in apps/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.cachedWithTTL is created inside make, so the memo's lifetime is layer-scoped; imports are subpath namespaces (effect/Effect, effect/Layer, effect/testing/TestClock); the Context.Service tag keeps its inline interface and the module keeps real make + 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 uses Ref.make / Ref.getAndSet inside the existing scoped make; retry constants and probe timeout are plain values. No new ManagedRuntime.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 existing Effect.fn constructor and continues to acquire EnvironmentSupervisor, EnvironmentCacheStore, and ShellSnapshotLoader from the environment. Behavior change is covered by updated tests. No issue.
  • packages/shared/src/observability.ts — truncateTraceAttributes is 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 — new Clock namespace import is correct; the module-global cache is the reported finding. The pre-existing Data.TaggedError CommandResolutionError was 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.TaggedError for 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. The externalLauncher implementation looks solid with proper Effect caching. The shell.ts module 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 like EnvironmentSupervisor and EnvironmentRegistry follow 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 provides Effect.cachedWithTTL and Cache for 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 in shell.ts violates the codebase's own conventions—caches should live inside make via Cache/LayerMap rather 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