Repository navigation
Effect Service Conventions: 4 issues found
Four convention violations were found in newly added Effect code and reported as inline review comments.
infra/relay/src/environments/ManagedEndpointReaper.ts(lines 81-85, 98-102): usesEffect.catchTag; tagged failures must be handled withEffect.catchTags({ ... }), even for a single tag.infra/relay/src/environments/ManagedEndpointProvider.ts(lines 637-648): newEffect.catchTagin the tunnel-release path; should beEffect.catchTags({ ... })(the module already usescatchTagselsewhere).apps/server/src/server.ts(lines 641-643): startup managed-tunnel recovery wraps failures withEffect.catchCause, which also swallows interruption and reduces it to a warning; useEffect.catchto handle only the typed error channel, matching the adjacentreconcileDesiredCloudLinkblock.
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 reviewed: changed .ts files under apps/**, packages/**, and infra/** (apps/server/src/cloud/ManagedEndpointRuntime.ts, apps/server/src/cloud/http.ts, apps/server/src/server.ts, infra/relay/src/environments/ManagedEndpointAllocations.ts, ManagedEndpointProvider.ts, new ManagedEndpointReaper.ts, infra/relay/src/http/Api.ts, infra/relay/src/worker.ts, infra/relay/src/persistence/schema.ts, infra/relay/src/deploymentConfig.ts, packages/contracts/src/relay.ts) plus the associated tests as context.
Items verified as conforming:
- New
ManagedEndpointReapermodule follows the canonical layout (namespace imports fromeffect/*subpaths, error/result declarations,Context.Servicetag with an inline interface,make, thenexport const layer). - Dependencies (
RelayConfiguration,ManagedEndpointTunnelClient,ManagedEndpointAllocations,ManagedEndpointProvider) are acquired from the environment withyield* Foo.Foo; service instances are passed explicitly only in test harnesses viaLayer.succeed/Layer.mock, which is an accepted test seam. - No
ManagedRuntime.make,runPromise, orrunPromiseExitintroduced in service code. - Local service modules are imported as namespaces;
@t3tools/contractskeeps named imports as expected. ManagedEndpointAllocationPersistenceErrorextends the existing multi-valueoperationdiscriminator (enable-recovery,list-tunnels) with a message derived from structural attributes and preservescause; no new single-value discriminators or message-only wrappers were introduced.- Backend behavior changes (recovery endpoint, reaper sweep, link-state serialization) are covered by focused tests using test layers rather than mocked business logic.
🔍 Agent activity — 9 tool calls
💭 I'm reviewing the ManagedEndpointReaper.ts file against Effect service conventions, checking that imports use namespace style and that the definition order follows imports, constants/interfaces, Context.Service tag, make, then layer—though I notice a helper function placed between sections that needs checking. I found a convention violation: the code uses
Effect.catchTagfor a single tagged error, but conventions requireEffect.catchTagseven for one tag. I'm spotting this pattern used twice in thedeleteOrphanfunction within ManagedEndpointReaper.ts, and also once more in ManagedEndpointProvider.ts's release function—confirming these are all new additions in the diff. I also want to double-check whethercatchTagis even the recommended API here versuscatchTags, and I'm noting a minor concern about using "*" as a sentinel value for userId/environmentId in an error context, though that may be acceptable given the schema requirements. I'm also verifying that exported predicates likeisManagedEndpointNotFoundfollow the direct schema predicate convention. Now I'm checking the semaphore setup in server.ts and the runtime, then scanning forcatchTagusage and lint rules across the repo. I'm also flagging thathttp.ts'srecoverManagedCloudTunnelmaps errors without preserving the underlying cause, which conflicts with the convention of retaining the original error ascausewhen wrapping failures.
1. git_tools — Effect.catchTag(
2. git_tools — catchTags
💭 I notice catchTag exists in legacy code but the repo prefers catchTags, and the instructions explicitly say not to use catchTag. I'm checking the three new occurrences in this PR—ManagedEndpointProvider.ts:637 and ManagedEndpointReaper.ts:81, 98—to confirm they're newly introduced rather than pre-existing, since ManagedEndpointProvider.ts already had catchTags elsewhere at lines 356, 400, and 420. Now I want to check ManagedEndpointReaper.ts's current state to get accurate line numbers for those occurrences.
3. browse_code — infra/relay/src/environments/ManagedEndpointReaper.ts
4. browse_code — infra/relay/src/environments/ManagedEndpointProvider.ts
💭 I'm reconsidering the
catchTagconvention here — statically known tagged failures should useEffect.catchTags, even for a single tag, rather thancatchIfwith a schema predicate orcatchTag. Though this handler applies a structural predicate on the underlying platform cause and re-fails otherwise, it's still fundamentally recovering a known tag, so the same convention should apply. Now I'm checking whetherEffect.catchmight even suffice given the error channel only containsManagedEndpointTunnelClientError, though the convention still points tocatchTags. I also want to look at theManagedEndpointAllocationPersistenceErrorusage with wildcarduserId/environmentIdfields inlistByTunnelNamesto understand how that error class is structured.
5. browse_code — infra/relay/src/environments/ManagedEndpointAllocations.ts
💭 I notice the
list-tunnelserror uses wildcard placeholders for userId/environmentId, which feels like a minor smell but isn't explicitly forbidden by convention — I'll set this aside and focus on the more clear-cut catchTag issue instead. Looking more closely, the enableRecovery error spreads the input object which includesenvironmentPublicKey, a field not declared in the error schema. Since spreads bypass TypeScript's excess property checks, this could compile fine but let an unmodeled field leak into the error payload — though since it's a public key rather than a credential, it's likely too minor to flag. I want to check the recover function and error class definitions in the http.ts file to see how causes are handled.
6. browse_code — apps/server/src/cloud/http.ts
7. git_tools — class EnvironmentHttpInternalServerError
💭 These contract errors following the existing pattern with just a message field seem fine as-is. Now I'm checking server.ts's startup reconciliation logic, where the guidance says interruption should be preserved rather than downgraded to a warning—I need to verify whether the catchCause wrapper around recoverManagedCloudTunnel also inadvertently swallows interrupts, since catchCause typically catches all causes including interruption. Comparing this to nearby code that uses catch (error channel only, which preserves interruption) versus this new catchCause usage that turns interrupts into warning logs, I'm weighing whether this qualifies as the "repairs multiple independent entities" case the convention describes, since here it's just a single tunnel for a single environment. I'm also noting worker.ts has a similar pre-existing catchCause pattern for cron pruning that seems acceptable, so I need to figure out whether this server.ts instance is actually problematic given it runs both inside a forked stream loop and elsewhere at startup.
8. browse_code — apps/server/src/server.ts
💭 I notice catchCause is already an established pattern elsewhere in the code for shutdown handling, which makes me wonder whether using it here versus a plain catch could affect how interruption during shutdown gets handled—specifically whether catchCause would inadvertently swallow an interrupt signal instead of letting it propagate. Since the adjacent code uses
Effect.catchfor the same startup pattern, I'll flag this as a secondary finding tied to the interruption-preservation instruction, since catchCause also swallows defects and interrupts that might not be intended. Now I'm double-checking import conventions in ManagedEndpointRuntime.ts and server.ts—confirming Semaphore and Stream are imported correctly, and that the Api.ts handler's use of catchTags looks fine before moving on to check the relay HTTP adapter file. I'm reviewing recoverEnvironmentTunnelRecord's cleanup logging, function naming conventions, and checking whether the switch from Layer.provide to Layer.provideMerge in layerCloudflareBindings is an acceptable dependency exposure change for the reaper. I'm also verifying layerTunnelClient exists before reconsidering the list-tunnels error handling with wildcard userId/environmentId values against the domain-error conventions. The "*" sentinel produces a slightly misleading error message, which feels like a minor modeling issue worth a low-severity note rather than a hard finding. More concerning is that the enableRecovery error spreads input containing environmentPublicKey, a field not declared in its schema, so I want to check whether Schema.TaggedErrorClass strictly validates assigned fields or just retains extras until serialization drops them — that would make it a latent inconsistency worth flagging. I'm settling on two real findings: the three catchTag usages, and catchCause swallowing interruption during startup recovery. I've confirmed catchTags can return a failure from its handler, so now I need to pin down exact diff line numbers for inline comments — checking whether ManagedEndpointReaper.ts lines 81/98 and ManagedEndpointProvider.ts line 637 fall within the diff hunks so comments land correctly on the right side. Now I'm drafting the actual suggested replacement code for each spot, converting catchTag chains to catchTags blocks. Writing suggestion diffs... I confirm the indentation matches at line 637 and 648. Then I'm checking a fourth comment in server.ts around lines 641-643, verifying the indentation of a catchCause-to-catch change, and considering whether to include this comment given the risk that it might swallow defects unintentionally. Since the adjacent code uses Effect.catch, I'll include this as a suggestion related to interruption handling. Given the convention violations I've found, this check should default to failure status. I'm ready to post the review now.
9. modify_pr — Effect service conventions: four findings, all in newly added error-handling code (three Effect.catchTag usages and one startup Effect.catchCause). Details inline.
Agent Credits: 124 credits