Skip to content

fix(settings): inline History privacy UI and extract More browsers

MacroscopeApp / Macroscope - Effect Service Conventions failed Aug 15, 2026 in 3m 43s

Effect Service Conventions: 1 issue found

1 finding

  • apps/desktop/src/computerHistory/manager.ts — ComputerHistoryOperationError captures only operation plus an opaque Schema.Defect() cause. The resolved Computer History root (from stateDir) and, for removeMemory/revealMemory, the memory path are known at every wrapping site and should be retained as stable structural attributes so failures are diagnosable without unwrapping cause. Inline comment posted with a suggested field shape.

Everything else in scope matched the conventions.

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.

Scope examined

  • apps/desktop/src/computerHistory/manager.ts (new service), resolveBinary.ts
  • apps/desktop/src/ipc/methods/computerHistory.ts, computerUse.ts, DesktopIpcHandlers.ts, DesktopApp.ts, main.ts
  • apps/server/src/computerHistory/service.ts, apps/server/src/desktopControl/desktopMcpBinary.ts, desktopMcpLaunch.ts (+ tests)
  • Affected adapter call sites: CodexAdapter.ts, CodexSessionRuntime.ts, ClaudeAdapter.ts, CursorAdapter.ts, GrokAdapter.ts, server.ts
  • packages/contracts/*, packages/shared/src/computerHistory/*, packages/effect-codex-app-server/src/client.ts

Checks that passed

  • Imports: Effect modules imported as subpath namespaces (effect/Effect, effect/Layer, effect/Context, effect/Schema); no consolidated from "effect" imports introduced. Local service modules are imported as namespaces (* as ComputerHistoryManager, * as ServerSettings, * as ComputerHistoryService); named imports are limited to contracts schemas and pure helpers, which the conventions allow.
  • Service definition: ComputerHistoryManager declares its interface inline in Context.Service, with no standalone FooShape; module order is imports → error → tag → make → layer; make is real construction and layer = Layer.effect(...) is appropriate (finalizer registered via Effect.addFinalizer, matching existing services in this repo).
  • Dependency acquisition / runtime boundaries: no ManagedRuntime.make, runPromise, or runPromiseExit added in domain services; stateDir/ComputerHistorySettings parameters are pure configuration, not service injection; makeResolveEnabledDesktopMcp and the CodexAdapter context loader acquire services via yield* Service and re-provide captured instances to keep session methods at R = never, matching the established pattern in this codebase.
  • Errors: single service-level error with a multi-value operation discriminator and a generic message derived from it (an explicitly allowed model); cause preserves the underlying failure; Effect.catchTags used at the bootstrap recovery site rather than catchTag/catchIf.
  • Change discipline: tests added are behavioral (approval mapping, permissions, binary resolution, build staging), not tests to prove a mechanical refactor; no compatibility re-export shims introduced.

🔍 Agent activity — 1 tool call

💭 I need to switch approaches since the stat_only flag didn't work as expected and gave me the full diff anyway. Let me pull the list of changed files directly from the GitHub API instead. I need to look at the .tsx files too since the TypeScript analysis was incomplete — those React components likely contain the ManagedRuntime and runPromise patterns I'm tracking. From what I've gathered so far, the desktop manager service uses a Schema.TaggedErrorClass with Schema.Defect() for error handling, following a specific convention. The error tagging here is acceptable — it uses a generic ComputerHistoryOperationError with an operation discriminator and derives the message from that structural attribute, preserving the cause. However, there's a gap: the error doesn't capture path or resource context like stateDir or the actual file paths involved, which the convention says should be retained when available. That's a minor violation worth flagging. The service also uses plain mutable state with a comment referencing Macroscope conventions, and internally relies on imperative Promise-based machinery within the constructor using E. tryPromise, which is acceptable. The layer setup looks correct with Layer.effect and a real make function, but there's a question about whether make uses Effect.addFinalizer — if it does, that requires Scope, which means the code should use Layer.scoped instead of Layer.effect. The type annotation declares no requirements, so if Scope leaked it wouldn't compile. In Effect v4, layers automatically provide scope access during construction, so Layer.effect should handle this, but I should verify against similar patterns in the repo. The computerHistory/service.ts module exports standalone Effect functions and a layer rather than wrapping them in a Context.Service tag — it's more of a helpers module than a formal service, which might be acceptable depending on the conventions being followed. For desktopMcpLaunch.ts, the makeResolveEnabledDesktopMcp function captures services and uses Effect.provideService to close over them, which raises a question about whether this violates the principle of not hiding dependencies in closures over singleton services. However, looking at how it's actually implemented — the dependencies are acquired from the environment at construction time via yield* rather than passed as parameters — this pattern seems legitimate and consistent with the codebase's approach. The same pattern appears in CodexAdapter.ts where services are acquired from the environment at construction and then provided to per-session effects, which also looks acceptable. Now I need to check the TypeScript changes and other files that were in the truncated diff, particularly the web components. Let me try to get a list of all changed files, maybe using git diff with stats only for TypeScript files to keep the output manageable. I'll use a git diff command to scan the TypeScript files directly, filtering for just the .tsx files without pulling full context to keep the payload minimal. Looking at the Effect service conventions violations in the changed TypeScript files, particularly in the desktop app's computer history manager. The code uses mutable state within the service constructor closure, which the comments suggest violates Macroscope conventions around hiding dependencies in module globals, though it's not technically a module-level global violation. I'm also checking whether Effect.addFinalizer with Layer.effect matches the codebase pattern — need to see if other services use Layer.scoped instead. There's also a potential issue with error handling in revealMemory and removeMemory where the path parameter is available but omitted from the error context, which violates the guidance to retain real path/resource context at the wrapping site. In the server-side computer history service, Effect.tryPromise is being used without a catch handler, which needs verification against the error handling conventions. Looking at the desktop control and adapter modules, the dependency injection patterns check out — services are properly acquired from the environment, and error handling uses Effect.catchTags correctly. The logging does capture the full error cause, which could be verbose but appears intentional. Now I need to verify whether the check-run instructions around dependency acquisition and runtime boundaries catch anything — searching for ManagedRuntime and runPromise usage in the diff, and checking if Layer.scoped patterns with finalizers are valid in this Effect version. I see that Layer.scoped isn't in the repo, so Layer.effect handles scoping by default in this version of Effect. Now I'm checking whether Effect.addFinalizer gets used inside make functions provided by Layer.effect elsewhere in the codebase. Looking at whether there are actual convention violations to report, I'm examining the error handling more closely. The ComputerHistoryOperationError follows the accepted pattern—it includes a cause with schema defects and the operation itself, with a generic message derived from the operation type. However, there's a legitimate finding here: the convention says to "retain any real path, resource, request, or entity context available at the wrapping site," and while root/path context is available, it's being omitted from the error. Adding root and path to the error would be a moderate-confidence improvement. For runDaemonOp, it's not just a pure error factory—it wraps a promise with Effect.tryPromise and has real behavior, so that's fine. The layer export in the service module uses Layer.effectDiscard for a background loop, which doesn't require a service tag, and the Effect.tryPromise call catches into UnknownError which then gets ignored or succeeds, so errors are being swallowed there. Looking at the bigger picture in server.ts, the layer wiring is composing ServerSettingsLayerLive twice, but since Effect memoizes layers by reference within a single build, that should be fine. The real question is whether this violates the convention about not creating per-feature managed runtimes. Then there's makeResolveEnabledDesktopMcp — it captures services and re-provides them, which could look like "fake dependency injection" or closures over singletons. But it's actually acquiring from the environment legitimately, which is the documented pattern for keeping R=never on adapter session methods. I should check if this pattern already exists elsewhere in the codebase to see if it's an accepted local convention. The captured-service-and-reprovide pattern is already established in this codebase, so that's not a concern. Now I'm checking the "Imports and module namespaces" rule against the modified Effect service code. In DesktopApp.ts, the addition of named imports from @t3tools/contracts/settings for schema and config values is actually allowed by the guidelines, which explicitly permit named imports from whole packages like @t3tools/contracts. Looking at the other files, manager.ts imports pure helpers from @t3tools/shared/computerHistory, which is fine. In desktopMcpBinary.ts, the named imports of HostProcessEnvironment and HostProcessPlatform from @t3tools/shared/hostProcess need checking—the rule allows namespace imports when callers access service/tag members, but it's optional ("may"), so I should verify what convention the repo already uses for this module. The helper imports in ClaudeAdapter.ts and CodexAdapter.ts are straightforward named imports from non-service modules, which are allowed. In CodexAdapter.ts, there's a namespace import from the computer history service module, which follows the correct pattern. Now I'm checking the test files and other imports: GrokAdapter.test.ts imports ServerSettingsService by name directly from serverSettings.ts, which seems to violate the namespace import rule at service boundaries—though the rule doesn't explicitly exempt tests. I need to see how existing tests in the repo handle service imports to determine if this is acceptable or if it should be a namespace import instead. I'm deciding to skip the style issue since it's a legacy pattern and the guidelines say not to fail on optional preferences. For the desktopMcpLaunch test, I see it's using a test seam with a failing service injected through the Effect provider, which is an allowed testing pattern. Let me dig deeper into the strongest possible findings here. Looking at the dependency acquisition and runtime boundaries in manager.ts, I notice it uses a sync helper for resolving the desktop MCP binary path with fs.existsSync — that's imperative but wrapped inside Effect.tryPromise rather than hidden in a Layer.succeed, so it doesn't violate the convention. The server side uses proper FileSystem and Path services while the desktop deliberately uses a sync version for Electron main, which is intentional and acceptable. Node fs operations are allowed with diagnostics off. Now I need to check if there's any runPromise or ManagedRuntime usage in the changed files. Let me check the git history to see if this file was already reviewed in earlier commits on this branch, since the Macroscope Effect Service Conventions mention suggests the author has already addressed prior feedback. I should also look at existing review comments on the PR to understand what's been flagged and resolved so I don't duplicate any concerns. I'm checking whether the TypeScript file diff was complete or if there were any truncated sections I might have missed in my review. Now I'm evaluating the strongest findings to report. The ComputerHistoryOperationError stands out as a genuine violation — it wraps a multi-step pipeline (layout ensure, read, compose) in a single generic error without capturing resource context like the state directory or file path, which violates the convention of including stable, serializable domain context in errors. The message derivation and Schema.Defect() usage both check out fine — the operation field provides structural identification. In DesktopApp.ts, there's some redundancy with nested Effect.catchTags followed by an outer Effect.catch, but that's not a violation since the outer catch is intentionally handling the entire channel. Looking at the computerHistory service module, it exports a layer and standalone Effect functions without a Context.Service, which is appropriate since it's a helper module rather than a service — no need for a make function when the module doesn't own service construction. The desktopMcpLaunch.ts module correctly has both a resolver function and a maker variant that captures services. In computerHistory, failures are kept typed in Effect with orElseSucceed rather than bypassing the layer, which follows the convention. The tryPromise call converts errors to UnknownError and immediately swallows them to undefined, so defining a structured TaggedErrorClass would be pointless given the result is discarded anyway. The desktop manager has some dead code and uses a mutable state object in the service closure instead of Ref, but neither violates stated conventions. That leaves only Finding 1 as a potential violation — I need to examine it more closely to determine if it's substantial enough to fail the check, since the instructions say to only fail on clear convention violations, not optional style preferences. Looking at the error handling guidance more carefully: the rules call for capturing stable, serializable context like operation, stage, resource path, and normalized status. Here the error class only captures operation and cause, missing the stateDir and path that are available at every call site. That's a concrete gap against the stated conventions, not just a style preference. I should add an inline comment suggesting the error class include root and optional path attributes. Beyond that, Effect.catchTags in DesktopApp looks correct, and the Context imports in the client and session runtime files check out — those are using the right patterns. Looking at the schema predicates in CodexSessionRuntime, the private isMcpToolApprovalMeta wrapped by isMcpToolApproval isn't redundant since the wrapper adds a mode check and takes different parameters. The meta schema declaration and other approval-related constants are fine too. Now I'll check the errors file in effect-codex-app-server. For the new ComputerHistoryManager service in apps/desktop/src/computerHistory/manager.ts, the file layout follows conventions — it's a new file so the hoisting rule doesn't apply, and the namespace import alias correctly uses the module namespace even though it differs from the filename. The Layer.Layer<ComputerHistoryManager> type annotation works fine since Layer.effect provides the layer's scope, making requirements never. On the production service construction side, the manager takes stateDir as a parameter from callers rather than acquiring DesktopEnvironment directly — since stateDir is just a pure configuration value (a string path), passing it as a parameter is explicitly allowed and doesn't violate the dependency acquisition rule. I need to resolve the conflict between the check's default-to-failure instruction and the JSON schema requirement. Since I do have a finding to report, I'll output the JSON response with a failure state rather than the plain "All clear" text. Let me verify the finding is solid and get the exact line numbers from the file to include in my report. I'm considering adding a root field and optional path field to the schema alongside the existing operation and cause fields, though I'm realizing this might require multiple changes across the file rather than a single inline suggestion.

1. modify_pr — Reviewed the new/changed Effect service code (apps/desktop/src/computerHistory/manager.ts, apps/desktop/src/ipc/methods/*, apps/server/src/computerHistory/service.ts, apps/server/src/desktopControl/*, adapter call sites). Module namespacing, Context.Service shape, make/layer exports, Effect.catchTags usage and runtime boundaries all look consistent with the conventions. One finding on error context.


Agent Credits: 293 credits