Repository navigation
Effect Service Conventions: 1 issue found
Findings (1 new)
apps/server/src/mcp/EnvironmentPreferencesMcpService.test.ts(lines 38-51) — the new test module imports two local service modules with renamed member aliases (layer as threadManagementLayer,layer as environmentMcpLayer,make as makeEnvironmentMcpService), erasing the module namespace at a service boundary. Convention is a namespace import (ThreadManagement.layer,EnvironmentMcp.make,EnvironmentMcp.layer); the siblingEnvironmentMcpService.test.tsalready imports without aliasing.
Carried over from a prior run (not re-posted)
apps/server/src/orchestration-v2/KeyedSerialExecutor.ts—ThreadDispatchLockV2is declared in the generic executor utility module and its layer is exported asthreadDispatchLockLayerrather than a canonicalexport const layerin a module that owns the service. Unchanged at this head commit.
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.
Reviewed in scope
apps/server/src/mcp/EnvironmentMcpService.tsapps/server/src/mcp/EnvironmentMcpService.test.tsapps/server/src/mcp/EnvironmentPreferencesMcpService.test.tsapps/server/src/mcp/toolkits/environment/{handlers,tools,registration.test}.tsapps/server/src/mcp/toolkits/worktree/registration.test.tsapps/server/src/orchestration-v2/{KeyedSerialExecutor,Orchestrator,runtimeLayer}.tsapps/server/src/orchestration-v2/testkit/ProviderReplayHarness.tspackages/contracts/src/environmentMcp.tspackages/shared/src/t3McpToolPresentation.tsand its test
Checked and found acceptable
EnvironmentMcpServiceacquiresThreadManagementServiceandThreadDispatchLockV2from the environment withyield*;makeis exported for real construction andlayer = Layer.effect(...)remains canonical. No service-instance parameters,ManagedRuntime.make, orrunPromisewere introduced in production code.- Explicit
Effect.provideServiceof service instances inEnvironmentPreferencesMcpService.test.tsand the instrumentedKeyedSerialExecutorare legitimate test seams, not production injection. threadDispatchLockLayerappearing both insideorchestratorProvidedand inOrchestrationV2LayerLiveis a single module-level layer reference, so Effect layer memoization yields one shared executor — consistent with the existing documented pattern forproviderContinuationRequestsLayer.- The added
thread_not_found/permission_deniedcodes extend the pre-existingEnvironmentMcpFailurecode discriminator; this is an established service-level error shape in this file, not a newly introduced switch-in-message pattern. - The new tests accompany a real behavior change (a new mutating MCP tool), so they are not "tests added solely to prove a mechanical refactor".
- Effect library modules are imported as subpath namespaces throughout the touched files;
@t3tools/contractsnamed imports are correct per the convention.
🔍 Agent activity — 13 tool calls
💭 Checking whether the flagged
threadDispatchLockLayernaming issue was addressed between the two runs—comparing cfc5143 against fef7ed3, the diff shows it's still unchanged in KeyedSerialExecutor.ts, so I shouldn't repost that comment. Now I'm scanning the rest of the files for other potential issues.
1. browse_code — apps/server/src/orchestration-v2/KeyedSerialExecutor.ts
2. browse_code — apps/server/src/mcp/EnvironmentMcpService.ts
💭 The threadDispatchLockLayer naming issue was already flagged in a prior run, so I'll skip re-raising it since the instructions say not to duplicate comments unless the code changed. I should check the diff between fef7ed3 and cfc5143 to see if anything new was introduced.
3. git_tools — fef7ed3..cfc5143
💭 Since the previous commit got force-pushed away, I should check the current state for any remaining or new violations. Looking at the test file, I notice it imports
layerandmakewith aliases from EnvironmentMcpService, which erases the module namespace — something the convention flags, though this is happening in a test file rather than at a service boundary. I'm also seeingthreadDispatchLockLayerand a named import ofThreadDispatchLockV2from KeyedSerialExecutor.ts, which relates to the same underlying issue — the recommendation to move it into a dedicated module and consume it as a namespace at those call sites. I want to check whetherThreadManagementServiceis imported the same way elsewhere as a pre-existing pattern, and whether the new test file usesLayer.succeedor a ManagedRuntime setup. Now I'm looking at howEnvironmentMcpFailureextends its existing switch-based message getter with new codes like "thread_not_found" and "permission_denied" — this is a pre-existing pattern being extended rather than a new violation, so I'd rather not flag it as new. I'm also comparing the changed error messages, starting with howcapability_denied's message text was altered. The convention about preserving exposed error messages during structural refactors doesn't quite apply here since this is a behavior extension for a new mutation tool, not a refactor, so broadening the message seems reasonable. Now I want to check howThreadManagementServiceis imported elsewhere — whether it's used as a namespace import — and look more closely at registration.test.ts and handlers.ts to find the main thing worth flagging.
4. git_tools — ThreadManagementService.ts"
5. git_tools — KeyedSerialExecutor
💭 I'm weighing whether adding a service tag to KeyedSerialExecutor.ts fits the module's purpose, since it's a generic utility rather than a service module, and the canonical
layerexport convention would clash with its existingmakeKeyedSerialExecutorexport. I already flagged this exact issue in a prior run and the code didn't change, so I shouldn't repeat it. Now I'm scanning for genuinely new findings, starting withEnvironmentPreferencesMcpService.test.ts— a test file named after a service that doesn't seem to exist, which is a stretch of a violation but worth noting since tests should be co-located with their actual service module. I'm also noticing that this new test file importslayerandmakefromEnvironmentMcpService.tsunder aliases likeenvironmentMcpLayer, which is exactly the pattern I've been told to flag — aliasing that erases the module namespace. TheProviderReplayHarness.tsoption for a dispatch lock layer looks fine as test harness code, and I'm deciding not to flag the elaborate race test since it seems justified by an actual behavior change rather than being a superfluous addition. The error message switch andrequireCapabilityimplementation both look like pre-existing patterns, so I'm moving on to check whetherEnvironmentMcpServicedependencies are properly declared in the tools registration file.
6. browse_code — apps/server/src/mcp/toolkits/environment/tools.ts
7. browse_code — apps/server/src/mcp/EnvironmentPreferencesMcpService.test.ts
💭 I'm noticing the new test file imports the module with a namespace alias instead of importing the specific exports, which obscures the module boundary the convention cares about—that's a clear violation worth flagging. I'm now weighing whether it's still worth calling out even though it's in a test file rather than production code. Since the convention doesn't carve out an exception for tests, I'll flag it. There's another similarly aliased import in the same file that violates the same rule, so I can fold both into a single comment rather than raising them separately. I also notice other files in the repo already use this same aliasing pattern, but since this is a newly introduced file, the instruction to treat the convention as authoritative for new code applies, so I'll still flag it—just at low severity since it's a well-established pattern elsewhere. I'm now looking at another test file that pipes a service constructor through a provided dependency to see if that raises any similar concerns. That pattern is fine since it's an explicit, scoped instance provided within the test. The handwritten service stub in the settings module test is also acceptable as a test layer. Turning to whether there's a genuine dependency-acquisition issue: the environment service now correctly depends on the thread management and lock services via generator yields. But looking at the orchestrator's layer signature, it now requires the lock service too, and I'm noticing runtimeLayer.ts appears to provide that lock layer twice—once inside the orchestrator-provided layer and again in the merged layer. If that's true, it would create two separate executor instances instead of one, undermining the intended single admission boundary. I need to look at runtimeLayer.ts directly to confirm.
8. browse_code — apps/server/src/orchestration-v2/runtimeLayer.ts
💭 Since
threadDispatchLockLayeris a module-level const used as the same reference in both spots, Effect's layer memoization should treat it as shared within a single build, so it gets constructed only once across the whole graph. Turning toEnvironmentMcpService.ts, I notice line 21 importsThreadDispatchLockV2by name from a service-owning module rather than as a namespace, but that's already covered by an earlier comment recommending namespace consumption at those call sites, so I can skip it here. The new test file's aliased imports (layer as environmentMcpLayer,make as makeEnvironmentMcpService,layer as threadManagementLayer) are a distinct, genuine convention issue worth flagging separately since they're in their own module. I need to pin down exact line numbers for the diff comment—since it's a new file, all lines 30-51 are eligible. I'm also verifying theEnvironmentMcpService.test.tsmock layer usage looks fine, checking thatEnvironmentMcpWritingInstructionsin contracts uses the filter helper correctly, and confirming whetherSchemais imported as a namespace fromeffect/SchemainenvironmentMcp.ts.
9. browse_code — packages/contracts/src/environmentMcp.ts
10. browse_code — packages/contracts/src/environmentMcp.ts
💭 I'm noting that
loadCurrentCaller's error mapping drops the underlying cause when converting toEnvironmentMcpFailure, but since that failure type has nocausefield and other similar helpers do the same thing, this looks like a pre-existing pattern rather than something new to flag. I'll set this aside and instead focus on flagging the alias-import issue in the new test file, since it echoes the same namespace-import concern already raised about KeyedSerialExecutor. Checking a few other spots — the export change to EnvironmentMcpService.ts, the harness parameter naming, and the empty mock setup — all look fine and canonical. I want to look over handlers.ts in full before deciding if anything else deserves a comment.
11. browse_code — apps/server/src/mcp/toolkits/environment/handlers.ts
💭 I'm weighing whether the alias imports in the new test file are worth flagging, but that seems more like a style preference than a real convention violation. Actually, there's a more genuine issue: the test file is named
EnvironmentPreferencesMcpService.test.tsbut it's actually testingEnvironmentMcpService, referencing a module name that doesn't exist. I'm also checking other candidates — the canonical single-file ordering convention applies to source files, not tests, and the existing test naming already matches an established file. None of these feel like solid violations, so I'll settle on flagging the alias imports as the one real issue to raise.
12. browse_code — apps/server/src/mcp/EnvironmentMcpService.test.ts
💭 I notice the existing test file doesn't use the alias while the new file does, so given mixed precedent I'll flag this with a soft suggestion rather than a hard requirement. I'm also puzzling over conflicting instructions about the check defaulting to failure only with findings versus stopping immediately when clear, which seems to clash with the expected JSON schema output. Since fixing the import alias would require touching multiple call sites throughout the file rather than a single self-contained hunk, I'll skip providing a diff and just describe the recommended change in the comment itself before posting it.
13. modify_pr — One convention finding on the new test module's service imports. The prior finding on threadDispatchLockLayer in KeyedSerialExecutor.ts still stands but is not re-posted.
Agent Credits: 95 credits
Token usage: 20 uncached input · 491,027 cache-read input · 66,881 cache-write input · 9,550 output
Agent Credits may also include non-token charges from external tools such as web research.